پرش به مطلب اصلی

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_envelope1048576بایت envelope درخواست.
max_response_envelope1048576بایت envelope پاسخ encodeشده.
max_input_bytes2097152سقف سخت بایت ورودی هر line، شامل درخواست‌های صف‌شده و ورودی ناقص.
max_line_memory8388608charge حافظهٔ نگه‌داشته‌شده در هر line.
max_input_fragments256سقف fragment/ورودی FIFO هر line.
max_queued_requests8درخواست منتظر، علاوه بر یک درخواست فعال.
max_sessions256session زندهٔ هر instance.
max_jobs64job پذیرفته‌شده و تمام‌نشدهٔ هر instance.
handler_working_memory8388608allowance موقت رزروشدهٔ هر handler.
max_memory67108864مجموع حافظهٔ نگه‌داشته‌شده و reservationها.
max_unauthenticated_memory16777216مجموع حافظهٔ اتصال‌هایی که هنوز اعتبارنامهٔ معتبر نفرستاده‌اند.
frame_timeout_ms15000مهلت مطلق تکمیل frame از رسیدن نخستین fragment، شامل زمان ماندن در صف.
request_timeout_ms10000پذیرش request کامل تا انتشار پاسخ، شامل انتظار FIFO/worker.
output_timeout_ms15000انتشار پاسخ تا تحویل به transport، شامل Pause.
idle_timeout_ms60000زمان بی‌کاری بدون 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 آمده است.

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 flagskNodeFlagChainEnd, kNodeFlagBlocksSplice
can_have_prevtrue
can_have_nextfalse
layer_groupkNodeLayer4
layer_group_prev_nodekNodeLayer4
layer_group_next_nodekNodeLayerNone
required_padding_left0 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 اجرا شوند؛ پایان مهلت درخواست صف‌شده برای حفظ ترتیب پاسخ‌ها اتصال را می‌بندد.