تجعل ترويسة Idempotency-Key (مسودّة IETF) طلبَ POST المُعاد آمنًا: يخزّن الخادم النتيجة الأولى لكل مفتاح ضمن نافذة إزالة تكرار ويعيد تشغيلها لأي تكرار، فيمنح تقديمًا مرةً واحدة بالضبط. وتحديد المعدّل يحمي الخدمة: تتلقّى الطلبات فوق الحدّ 429 Too Many Requests مع Retry-After، وتعلن ترويسات RateLimit الحصّة المتبقّية بحيث يكبح العملاء أنفسهم.
ترويسة Idempotency-Key
تقديم الرسائل طلبُ POST، وهو غير متكافئ، لكن الشبكات تجبر العملاء على إعادة الإرسال حين تُفقَد استجابة — فينشأ الإرسال المزدوج الكلاسيكي. وتوحّد مسودّة Idempotency-Key من فريق عمل HTTP-API في IETF العلاجَ: يولّد العميل مفتاحًا فريدًا (UUID) لكل عملية منطقية ويرسله في ترويسة الطلب Idempotency-Key. ويسجّل الخادم المفتاح مع نتيجة الطلب الأول، وإن رأى المفتاح نفسه ثانيةً لا ينفّذ مرةً ثانية — بل يعيد النتيجة المخزّنة. فإعادة الإرسال بعد انتهاء مهلة مضمونة الأمان: إمّا لم يصل الطلب الأول قطّ (فينفّذ هذا) أو وصل (فتُعاد نتيجته). والمفتاح يخصّ العملية لا الاتصال، فيعيد العميل استخدام المفتاح نفسه عبر كل إعادة إرسال لتلك العملية.
# Generic IETF-draft example (not the BroadNet API, which takes no
# Idempotency-Key header: see /developers/idempotency)
POST /v1/messages HTTP/1.1
Host: api.example
Idempotency-Key: 8f14e45f-ea7b-4c0f-9b2a-1d2c3e4f5a6b
Content-Type: application/json
{ "to": "+15551234567", "text": "..." }
# first call -> 202 Accepted, id 01HZY8Q3M6 (executed, stored)
# same key -> 202 Accepted, id 01HZY8Q3M6 (replayed, not re-sent)نافذة إزالة التكرار وإعادة تشغيل الاستجابة والتعارضات
مفتاح التكافؤ لا يُتذكَّر إلى الأبد؛ يحتفظ به الخادم لنافذة إزالة تكرار (24 ساعة عادةً) طويلة بما يكفي لتغطية أي إعادة إرسال واقعية، ثم تنتهي. وضمن تلك النافذة يعيد تكرارُ المفتاح تشغيلَ رمز الحالة والجسم المخزّنين حرفيًا — الـ 202 نفسه ومعرّف الرسالة نفسه — بحيث يرى العميل بالضبط ما كان سيراه لو وصلت الاستجابة الأولى. وحالتان طرفيّتان تحتاجان معالجة صريحة. فإن أُعيد استخدام مفتاح بجسم طلب مختلف، فذلك خلل عميل ويعيد الخادم 409 Conflict بدل إرسال الشيء الخطأ بصمت. وإن وصلت إعادة إرسال بينما الطلب الأول لا يزال قيد التنفيذ، يعيد الخادم تعارضًا أو «قيد التقدّم» بدل بدء تنفيذ ثانٍ، فلا يستطيع التزامنُ هزمَ الضمان.
| الحالة | سلوك الخادم | الحالة |
|---|---|---|
| مفتاح جديد | نفّذ، ثم احفظ المفتاح والنتيجة | عادي (مثلاً 202) |
| تكرار المفتاح، المتن نفسه | أعِد الاستجابة المخزّنة، ولا تُعِد التنفيذ | الحالة الأصلية |
| تكرار المفتاح، متن مختلف | رفض — المفتاح مرتبط بالحمولة الأولى | 409 Conflict |
| تكرار أثناء معالجة الطلب الأول | لا تبدأ تنفيذًا ثانيًا | 409 / قيد التنفيذ |
| انتهت صلاحية المفتاح (بعد انقضاء النافذة) | يُعامل كجديد | عادي |
تحديد المعدّل و429
تحمي منصّة المراسلة المشتركة نفسها ومستأجريها الآخرين بتقييد سرعة نداء أي عميل، وتشير إلى تجاوز بحالة محدّدة. ويُعرّف RFC 9110 وRFC 6585 رمز 429 Too Many Requests لهذا بالضبط: أرسل العميل طلبات كثيرة جدًا في مدة معيّنة. والخادم الحَسَن السلوك يقرن 429 بترويسة Retry-After تخبر العميل كم ينتظر قبل المحاولة ثانيةً (إمّا تأخير بالثواني أو تاريخ HTTP). والتزام العميل احترامُها — يتوقّف، وينتظر الفترة المشار إليها، ثم يستأنف — بدل إعادة الإرسال فورًا، مما يعمّق الحِمل الزائد فحسب. والـ 429 عابر وآمن إعادة إرساله، بخلاف 4xx التي تدلّ على طلب مشوّه.
HTTP/1.1 429 Too Many Requests
Retry-After: 2
RateLimit: limit=100, remaining=0, reset=2
RateLimit-Policy: 100;w=1
Content-Type: application/problem+json
{ "type": "https://example/problems/rate-limited",
"title": "Too Many Requests", "status": 429 }ترويستا RateLimit وRateLimit-Policy
التفاعل مع 429 بعد وقوعها إهدار؛ ومسودّة ترويسات RateLimit من IETF تتيح للعميل رؤية ميزانيته قبل تجاوزها. فعلى الاستجابات الاعتيادية يعلن الخادم الحصّة الراهنة بترويسة RateLimit تحمل الحدّ والمخصَّص المتبقّي والثواني حتى إعادة ضبط النافذة، وترويسة RateLimit-Policy تصف السياسة نفسها (مثلًا 100 طلب لكل نافذة ثانية واحدة). والعميل الذي يقرأ هذه يستطيع ضبط وتيرته — يبطئ كلّما اقترب المتبقّي من الصفر — ولا يُطلِق 429 البتّة. وهذا يحوّل تحديد المعدّل من رفض عقابي بعد الوقوع إلى إشارة تعاونية، وهو غاية توحيد أسماء الترويسات بحيث تستهلكها أي مكتبة عميل.
| الترويسة | ينقل | مثال |
|---|---|---|
| 429 Too Many Requests | حالة تجاوز الحد المسموح به | سطر الحالة |
| Retry-After | كم من الوقت يجب الانتظار قبل إعادة المحاولة | Retry-After: 2 |
| RateLimit | الحد / المتبقي / إعادة التعيين للنافذة | limit=100, remaining=17, reset=1 |
| RateLimit-Policy | سياسة الحصص المعمول بها | 100;w=1 (100 per 1s) |
دلو الرموز وكيفية فرض الحدود
خوارزمية دلو الرموز هي الآليّة المعتادة خلف هذه الترويسات لأنها تقيّد المعدّل المستدام مع احتمال دفعات قصيرة. فالدلو يحمل عددًا ثابتًا أقصى من الرموز ويعاد ملؤه بمعدّل مطّرد؛ ويزيل كل طلب رمزًا، والطلب الذي يجد الدلو فارغًا يُرفَض بـ 429. وحجم الدلو يحدّد الدفعة التي يستطيع العميل إنفاقها دفعة واحدة، ومعدّل إعادة الملء يحدّد السقف طويل الأمد، فيستطيع العميل إرسال دفعة سريعة بعد فترة خمول لكنه لا يتجاوز المتوسّط. وقيمة remaining في RateLimit هي فعليًا عدد الرموز، وreset هو موعد اكتمال ملء الدلو — وهي بالضبط المعلومة التي يحتاجها عميلٌ مضبوط الوتيرة. وهذا التحكّم في التدفّق عند طبقة HTTP هو نظير REST لتحجيم نافذة SMPP وكبح enquire_link على الجلسة الثنائية.
الضغط العكسي من العميل
نصف السلوك الجيّد تجاه تحديد المعدّل من جانب العميل هو الضغط العكسي: تشكيل الحِمل الصادر بحيث يبقى ضمن الميزانية المعلَنة بدل إطلاق كل شيء وامتصاص 429. وعمليًا يعني ذلك مُحدِّدًا من جانب العميل (دلو رموز يحاكي الخادم)، وقراءةَ ترويسات RateLimit لضبط الوتيرة، وعند 429 التوقّفَ لفترة Retry-After مع تشويش زمني قبل الاستئناف. ولإرسال جماعي يعني هذا قِسمة التقديمات إلى السقف المستدام بدل إغراقها، فتبقى المنصّة سليمة ويبقى زمن استجابة العميل نفسه متوقَّعًا. وتوثّق BroadNet سلوك التكافؤ وتحديد المعدّل الفعلي لديها للمطوّرين.
الأسئلة المتكررة
ماذا تفعل ترويسة Idempotency-Key؟
تجعل طلب POST المُعاد آمنًا. يرسل العميل مفتاحًا فريدًا لكل عملية منطقية؛ وينفّذ الخادم الطلب الأول بذلك المفتاح ويخزّن النتيجة، ثم يعيد تشغيل النتيجة المخزّنة لأي تكرار للمفتاح نفسه ضمن نافذة إزالة تكرار. فإعادة الإرسال بعد استجابة مفقودة إمّا تنفّذ مرة واحدة أو تعيد التشغيل — دون إرسال مزدوج أبدًا. وهي محدَّدة في مسودّة فريق عمل HTTP-API في IETF.
كم يتذكّر الخادم مفتاح التكافؤ؟
لنافذة إزالة تكرار — نحو 24 ساعة عادةً — طويلة بما يكفي لتغطية أي إعادة إرسال واقعية، وبعدها ينتهي المفتاح ويُعامَل إعادة استخدامه معاملة الجديد. وضمن النافذة، يعيد تكرارُ المفتاح تشغيلَ رمز الحالة والجسم الأصليين حرفيًا بدل إعادة التنفيذ.
ماذا يحدث إن أعدت استخدام مفتاح تكافؤ بجسم مختلف؟
يرفضه الخادم بـ 409 Conflict. فالمفتاح مربوط بحمولة الطلب الأول، لذا إعادة استخدامه بجسم مختلف تُعامَل معاملة خطأ عميل بدل إرسال الرسالة الخطأ بصمت. أعِد استخدام المفتاح نفسه لإعادات إرسال العملية المطابقة فقط.
ماذا ينبغي أن يفعل العميل حين يتلقّى 429؟
يتوقّف ويحترم ترويسة Retry-After — ينتظر الثواني المشار إليها (أو حتى التاريخ المعطى)، ثم يستأنف — بدل إعادة الإرسال فورًا، مما يعمّق الحِمل الزائد فحسب. والـ 429 عابر وآمن إعادة إرساله بعد انقضاء الانتظار، ويُفضَّل بتشويش زمني طفيف بحيث لا يستأنف عملاء كثيرون في تزامن تامّ.
لماذا ترويستا RateLimit وRateLimit-Policy؟
تتيحان للعميل رؤية حصّته قبل تجاوزها. فـ RateLimit تحمل الحدّ والمخصَّص المتبقّي وموعد إعادة الضبط للنافذة الراهنة؛ وRateLimit-Policy تصف السياسة (مثلًا 100 طلب في الثانية). والعميل الذي يقرأهما يستطيع ضبط وتيرته وتجنّب إطلاق 429 البتّة. وكلتاهما محدَّدتان في مسودّة IETF.
كيف يرتبط دلو الرموز بترويسات RateLimit؟
دلو الرموز هو آليّة الفرض: دلوٌ بحجم ثابت يعاد ملؤه بمعدّل مطّرد، وينفق كل طلب رمزًا، ودلوٌ فارغ يعطي 429. وقيمة remaining في RateLimit هي جوهريًا عدد الرموز الراهن، وreset هو موعد إعادة ملء الدلو — فالعميل القارئ لتلك الترويسات يعرف بالضبط كم بقي لديه من دفعة ومعدّل مستدام.
مواصفات المصدر
تستند هذه الصفحة إلى المعايير المنشورة. يفتح في علامة تبويب جديدة.