APIServer
APIServer operationهای صریحاً ثبتشدهٔ nodeها را روی stream بایتی قابل اعتماد
ارائه میکند. node انتهایی است و خودش socket باز نمیکند. دسترسی با اعتبارنامهٔ
ثابت و مستقل اپراتور و مجوز دقیق هر operation انجام میشود. schema درخواست و
نتیجه مسئولیت node هدف است؛ status در AuthenticationClient
و API مدیریتی AuthenticationServer را ببینید.
جایگاه
TcpListener -> APIServer
TcpListener -> HttpServer -> APIServer
UdpListener -> TcpOverUdpServer -> APIServer
اتصال مستقیم UdpListener -> APIServer پشتیبانی نمیشود؛ frameهای API به stream
مرتب و قابل اعتماد نیاز دارند. ترکیب UDP به TcpOverUdpClient متناظر در سمت
caller نیاز دارد. ترکیب کامل و پردازش body در HTTP را در
مثالهای transport ببینید.
APIServer caller را احراز هویت میکند اما transport را رمزنگاری نمیکند. مثال محلی را روی loopback bind کنید؛ دسترسی راه دور به transport رمزنگاریشده با احراز هویت و بررسی هویت peer نیاز دارد. توکن API جلوی مصرف منابع بدون احراز هویت را نمیگیرد؛ دسترسی شبکه را محدود کنید یا peer را در transport احراز هویت کنید. محدودیت ورودی، حملهٔ انبوه اتصال را حذف نمیکند.
تنظیمات
این یک قطعهٔ node از نوع APIServer است. allowlist آن فرض میکند
AuthenticationClient به نام auth-client در config بارشده با نام منطقی
edge-europe و AuthenticationServer به نام auth-db در config به نام
central وجود دارند:
{
"name": "management-api",
"type": "APIServer",
"settings": {
"clients": [
{
"name": "monitor",
"token": "<64 hexadecimal characters representing 32 random bytes>",
"allow": [
{"config": "edge-europe", "node": "auth-client", "operations": ["status"]},
{"config": "central", "node": "auth-db", "operations": ["status", "get-user", "list-users"]}
]
}
]
}
}
placeholder را با token تصادفی تازه، مثلاً خروجی
python3 -c 'import secrets; print(secrets.token_hex(32))'، جایگزین کنید. token
از رمز کاربران ترافیک و secret مربوط به upstream در AuthenticationClient جداست.
هنگام آفلاین یا ردشدن AuthenticationClient، دسترسی تشخیصی باقی میماند.
clients نباید خالی باشد. نام clientها و tokenهای decodeشدهٔ ۳۲ بایتی باید یکتا
باشند. هر ورودی allow نام دقیق یک config منطقی، node عمومی سطح بالا و فهرست
غیرخالی operationها را مشخص میکند؛ wildcard پشتیبانی نمیشود. هدف ناموجود یا
operation ثبتنشده در آمادهسازی startup رد میشود. درخواست بدون مجوز دقیق در
زمان اجرا forbidden میگیرد و درخواست مجاز از descriptor تغییرناپذیرِ آمادهشده
استفاده میکند. وجود نماد قدیمی TunnelApi
operation را خودکار بیرونی نمیکند. هر نوشتن مجوز صریح خودش را میخواهد؛ مجوز
خواندن، ویرایش، ساخت و مدیریت رمز یکدیگر را بهصورت ضمنی مجاز نمیکنند.
با حضور APIServer، تمام configهای بارشده باید name منطقی غیرخالی و یکتا داشته
باشند. selector نام فایل نیست و config تازهای بار نمیکند. نام config/node به
حروف حساس و حداکثر ۲۵۶ بایت UTF-8 است. childهای داخلی تولیدشده خصوصیاند.
selector فقط با نام node زمانی کار میکند که نام آن میان تمام nodeهای اعلامشدهٔ
سطح بالا، پیش از فیلتر ثبت API یا مجوز، یکتا باشد. هرجا نام ممکن است تکرار شود،
نام config را هم بدهید.
محدودیتهای اختیاری
همهٔ مقدارها عدد صحیح مثبت و محدودند؛ صفر حفاظت را غیرفعال نمیکند. هندسهٔ ناسازگار که یک request/response محدود را جا ندهد در startup رد میشود. envelope بدون prefix حساب میشود؛ بودجهٔ حافظه، charge تخصیص و copyهای موقت مربوط را نیز در بر میگیرد.
| تنظیم | پیشفرض | کاربرد |
|---|---|---|
max_request_envelope | 1048576 | بایت envelope درخواست. |
max_response_envelope | 1048576 | بایت envelope پاسخ encodeشده. |
max_input_bytes | 2097152 | سقف سخت بایت ورودی هر line، شامل درخواستهای صفشده و ورودی ناقص. |
max_line_memory | 8388608 | charge حافظهٔ نگهداشتهشده در هر line. |
max_input_fragments | 256 | سقف fragment/ورودی FIFO هر line. |
max_queued_requests | 8 | درخواست منتظر، علاوه بر یک درخواست فعال. |
max_sessions | 256 | session زندهٔ هر instance. |
max_jobs | 64 | job پذیرفتهشده و تمامنشدهٔ هر instance. |
handler_working_memory | 8388608 | allowance موقت رزروشدهٔ هر handler. |
max_memory | 67108864 | مجموع حافظهٔ نگهداشتهشده و reservationها. |
max_unauthenticated_memory | 16777216 | مجموع حافظهٔ اتصالهایی که هنوز اعتبارنامهٔ معتبر نفرستادهاند. |
frame_timeout_ms | 15000 | مهلت مطلق تکمیل frame از رسیدن نخستین fragment، شامل زمان ماندن در صف. |
request_timeout_ms | 10000 | پذیرش request کامل تا انتشار پاسخ، شامل انتظار FIFO/worker. |
output_timeout_ms | 15000 | انتشار پاسخ تا تحویل به transport، شامل Pause. |
idle_timeout_ms | 60000 | زمان بیکاری بدون frame/request/output جاری. |
سقف هر تنظیم: 64 MiB برای هر envelope درخواست/پاسخ، 256 MiB ورودی، 1 GiB برای حافظهٔ هر line یا handler،
2 GiB حافظهٔ کل، 65,536 session/job/fragment، 1,024 درخواست صفشده و UINT32_MAX یعنی 4,294,967,295
میلیثانیه برای هر timeout است. سقف max_unauthenticated_memory به max_memory و هندسهٔ زیر وابسته است.
تعداد client از 1 تا 256، allow هر client از 1 تا 512 و operation هر allow از 1 تا 64 است. نام client و
labelهای config/node غیرخالی و حداکثر 256 بایت UTF-8؛ نام operation غیرخالی و حداکثر 64 بایت است.
Token دقیقاً 32 بایت در 64 نویسهٔ hex است؛ label/operation برابر * پذیرفته نیست.
این سقفها مستقل نیستند. با R = max_request_envelope، S = max_response_envelope، I = max_input_bytes،
H = handler_working_memory و J = H + 2*R + 2*S + 128 KiB، startup این شرطها را بررسی میکند:
I >= R + 4وS >= 1024.max_line_memory >= I + max(2*(R + 1), 3*R + 2) + S + 128 KiB.Hتخصیص واقعی pool برای نتیجهٔ 4096 بایتی و 8192 بایت metadata را پوشش دهد.max_memory >= max_line_memory + J.max_line_memory <= max_unauthenticated_memory <= max_memory - J.
parser زمان اجرا قواعد سخت UTF-8، NUL، کلید تکراری، عمق/تعداد مقدار و مصرف کامل
ورودی را بدون پیام stderr مخصوص پیکربندی حفظ میکند؛ تشخیص خطای startup/configuration
باقی میماند. UTF-8 نامعتبر یا NUL در خروجی handler از نوع UTF-8، خطای
invalid_response بدون body نتیجه میدهد؛ نتیجهٔ معتبرِ بزرگ خطای
response_too_large میدهد. هر دو پس از dispatch هستند و نوشتن ممکن است commit
شده باشد. عملیات base64 میتواند بایت دلخواه برگرداند.
ورودی قطرهای مهلت frame را reset نمیکند. framing یا envelope/schema خراب و احراز هویت نامعتبر پس از حداکثر یک خطای محدود اتصال را میبندند. خطاهای timeout و invalid_encoding در gateway نیز اتصال را میبندند. پس از انتشار پاسخ کامل، deadline درخواست پایان مییابد و deadline مستقل خروجی آغاز میشود. timeout یا قطع، job را abandoned میکند؛ reservation آن تا لغو واقعی یا پایان handler محدودِ در حال اجرا آزاد نمیشود.
هدر طول فقط اندازهٔ اعلامشده را ثبت میکند و body نرسیده را تخصیص نمیدهد. ظرفیت frame برای بایتهای رسیده هندسی رشد میکند و هنگام copy، تخصیص قدیم و جدید همزمان charge میشوند. frame، درخت JSON، body رمزگشاییشده، carrierهای ورودی، خروجی و jobهای جداشده نیز در بودجه حساب میشوند. پاکسازی، ظرفیت واقعی تخصیصیافته را آزاد میکند.
watermark فشار بایتی فقط هدر و body کپیشدهٔ frame ناقص جاری را کم میکند:
input_bytes - (header_size + frame_used). این بایتها همچنان در سقف سخت ورودی
حساب میشوند. درخواست کامل صفشده، backlog خام و محدودیت fragment/entry فشار
ایجاد میکنند. ادامهٔ محدود parser پس از تخلیهٔ backlog و کار قدیمی، وقتی فقط
ورودی ناقص مانده source را Resume میکند تا درخواست مجاز بزرگ تکمیل شود.
فشار پیشبینانه نیز وقتی درخواستهای کامل یا فعال قدیمی بودجهٔ لازم برای frame بعدی را نگه داشتهاند، ورودی را
Pause میکند. پیش از معلومشدن هدر، اندازهٔ max_request_envelope + 4 و پس از آن اندازهٔ کامل اعلامشده ملاک است؛
بایتهای قبلاً bufferشده دوباره شمرده نمیشوند. پایان درخواست یا تخلیهٔ خروجی این hold را آزاد و بدون ورودی تازه
Resume میکند. frame ناقص بهتنهایی نباید برای بایتهای موردنیاز خودش متوقف بماند. Pause توصیهای است؛ ورودی دیررس،
چسبیده یا ناسازگار همچنان با سقفهای سخت و deadline مطلق frame محدود میشود.
max_jobs=64 سقف است، نه تضمین ظرفیت همزمان. با پیشفرض فعلی، دامنهٔ مشترک 64 MiB تقریباً هفت job کوچک با
allowance کاری 8 MiB هر handler را پیش از هزینهٔ قابلتوجه session جا میدهد. ورودی/خروجی نگهداشتهشده و هندسهٔ
تخصیص میتوانند این تعداد را کمتر کنند؛ allowance هر handler مستقل از سقف تعداد job باقی میماند.
حافظهٔ کاری باید تخصیص واقعی استخر worker برای نتیجهٔ ۴۰۹۶ بایتی و ۸۱۹۲ بایت فرادادهٔ تجزیه را پوشش دهد. محدودیت کوچکتر پاکت پاسخ، برای نتیجهٔ معتبرِ بزرگ یک خطای محدود response_too_large برمیگرداند.
پروتکل و مثالها
درخواست و پاسخ با طول چهار بایتی big-endian و سپس envelope از نوع UTF-8 JSON شروع میشوند. envelope، caller و هدف و operation را مشخص میکند و body opaque مربوط به node را حمل میکند. APIServer احراز هویت، مجوز دقیق و metadata عملیات را بررسی میکند؛ هدف body خودش را parse میکند. ID درخواست پاسخ را مرتبط میکند و نوشتن را idempotent نمیکند. قرارداد کامل framing، envelope، خطا و تحویل در راهنمای پروتکل API آمده است.
- اجرای کلاینت API: راهاندازی کامل روی loopback، فایل token و کلاینت عمومی Python.
- مدیریت حسابهای احراز هویت: خواندن/فهرست، ساخت، ویرایش/غیرفعالکردن و تغییر رمز همراه تصمیم صریح پس از پاسخ نامعلوم.
- transportهای API: TCP مستقیم، framing در body HTTP/1.1 و stream قابل اعتماد روی UDP.
- ثبت operation در node: قرارداد پیادهسازی برای ارائهٔ node دیگر از این gateway.
schema عملیات در مرجع node مالک باقی میماند. گمشدن پاسخ نوشتن ممکن است پس از commit موفق در حافظه رخ دهد؛ پیش از تصمیم به درخواست تازه، قواعد بازیابی همان operation را بخوانید.
lifecycle و metadata
تمام lineهای پذیرفتهشده برای APIServer از نوع borrowed normal هستند و هرگز lineDestroy() نمیشوند. state پیش از Est سمت قبلی آماده و پاسخها downstream ارسال میشوند. در هر اتصال فقط یک operation dispatch میشود و بقیه FIFO هستند. Pause/Resume تولیدکنندهٔ پاسخ را کنترل میکند؛ deadline و سقفها فعال میمانند. job جداشده فقط token session/request دارد؛ تنها worker اتصال میتواند session زنده را resolve کند و line را لمس کند. shutdown پیش از quiescence هدفها admission عمومی API را میبندد؛ مالک واقعی lineها پیش از نابودی registry آنها را drain میکند.
| ویژگی | مقدار |
|---|---|
| node flags | kNodeFlagChainEnd, kNodeFlagBlocksSplice |
can_have_prev | true |
can_have_next | false |
layer_group | kNodeLayer4 |
layer_group_prev_node | kNodeLayer4 |
layer_group_next_node | kNodeLayerNone |
required_padding_left | 0 bytes |
پاکسازی حافظهٔ متعلق به API
APIServer پیش از آزادسازی، body و token درخواست، frame و پاسخ نگهداشتهشده و token باینری تنظیمات را پاک میکند. بایتهای ورودی مصرفشده یا کنارگذاشتهشده پیش از بازگشت بافر به pool پاک میشوند. درخت کامل JSON در مسیر موفق و رد اعتبارسنجی بعدی پاک میشود. dispatcher کپی request/response متعلق به job را در آخرین release، شامل cancellation، پاک میکند؛ payload پاسخ برگشتی handler نیز پیش از pool reuse پاک میشود. بافر request پس از واگذاری متعلق به handler است و dispatcher بدون برگشت آن دوباره به آن دست نمیزند. این قرارداد شامل درخت config، درخت ناقص داخلی parser در خطای syntax، بافر واگذارشده به همسایه، کپی OS یا همهٔ کپیهای process نیست.
پیش از تأیید اعتبارنامه، هر تخصیص نگهداشتهشده از بودجهٔ جداگانهٔ max_unauthenticated_memory نیز مصرف میکند.
پیشفرض آن بزرگترِ max_line_memory و یکچهارم max_memory است؛ باید یک line کامل را جا بدهد و در بودجهٔ کلی
برای یک job جا باقی بگذارد. پس از تأیید اعتبارنامه، این charge اضافی آزاد میشود، حتی اگر عملیات مجاز نباشد.
هر envelope بعدی همچنان جداگانه احراز هویت میشود. پرشدن بودجهٔ ناشناس فقط line مربوط را میبندد و ظرفیت باقیمانده
برای اتصالهای احرازشده قابل استفاده است؛ سقف session و هزینهٔ CPU همچنان مشترکاند. توکن رمزگشاییشده بلافاصله
پس از بررسی اعتبارنامه پاک میشود؛ توکنِ درخواست با schema نامعتبر نیز هنگام تجزیه پاک میشود.
صف درخواستهای client را کوتاه نگه دارید تا درخواستها پیش از request_timeout_ms اجرا شوند؛ پایان مهلت درخواست
صفشده برای حفظ ترتیب پاسخها اتصال را میبندد.