نظرة عامة
هذا هو المواصفة الخاصة ببروتوكول الاتصال “Garlic Farm”، والمبنية على JRaft، ورمز “exts” الخاص بها للتنفيذ عبر TCP، وتطبيقها النموذجي “dmprinter” JRAFT .
لم نتمكن من العثور على أي تنفيذ يحتوي على بروتوكول اتصال موثق. ومع ذلك، فإن تنفيذ JRaft بسيط بما يكفي لأن نفحص الكود ثم نوثق بروتوكوله. هذا الاقتراح هو نتيجة لذلك الجهد.
سيكون هذا هو الطريقة الخلفية لتنسيق الراوترات التي تنشر إدخالات في Meta LeaseSet. انظر الاقتراح 123.
الأهداف
- حجم صغير للشفرة
- مبني على تنفيذ موجود
- لا توجد كائنات جافا متسلسلة أو أي ميزات أو ترميزات خاصة بجافا
- أي عملية إقلاع (bootstrapping) خارج نطاق هذا الاقتراح. يُفترض أن يكون هناك خادم آخر على الأقل مُدخل مسبقًا أو مُهيأ خارج نطاق هذا البروتوكول.
- دعم حالات الاستخدام خارج النطاق (out-of-band) واستخدام I2P داخليًا.
التصميم
بروتوكول Raft ليس بروتوكولًا ملموسًا؛ بل يعرّف فقط آلة حالة. لذلك نوثق البروتوكول الملموس لـ JRaft ونبني بروتوكولنا عليه. لا توجد تغييرات على بروتوكول JRaft سوى إضافة مراسلة مصادقة (authentication handshake).
يُنتخب Raft قائدًا (Leader) مهمته نشر سجل (log). يحتوي السجل على بيانات تكوين Raft وبيانات التطبيق. تحتوي بيانات التطبيق على حالة راوتر كل خادم ووجهة مجموعة Meta LS2. تستخدم الخوادم خوارزمية مشتركة لتحديد الناشر ومحتويات Meta LS2. ناشر Meta LS2 ليس بالضرورة قائد Raft.
المواصفات
يتم بروتوكول الاتصال عبر مقابس SSL أو مقابس I2P غير مشفرة. تُمرّر مقابس I2P عبر وكيل HTTP. لا يوجد دعم لمقابس clearnet غير مشفرة.
مراسلة المصادقة
غير معرّفة بواسطة JRaft.
الأهداف:
- طريقة مصادقة باستخدام اسم المستخدم/كلمة المرور
- معرف الإصدار
- معرف المجموعة (cluster)
- قابلة للتوسيع
- سهولة التوجيه عند استخدام مقابس I2P
- عدم التعرض غير الضروري للخادم كخادم Garlic Farm
- بروتوكول بسيط بحيث لا يتطلب تنفيذ خادم ويب كامل
- التوافق مع المعايير الشائعة، بحيث يمكن للتنفيذات استخدام مكتبات قياسية إذا رغبت
سنستخدم مراسلة تشبه WebSocket ومصادقة HTTP Digest RFC 2617 . مصادقة HTTP الأساسية (Basic) حسب RFC 2617 غير مدعومة. عند التوجيه عبر وكيل HTTP، يتم التواصل مع الوكيل حسب المواصفات في RFC 2616 .
بيانات الاعتماد
ما إذا كانت أسماء المستخدمين وكلمات المرور خاصة بكل مجموعة أو بكل خادم، يعتمد على التنفيذ.
طلب HTTP 1
يرسل المُبْدِع ما يلي.
جميع الأسطر تنتهي بـ CRLF حسب المطلوب في HTTP.
GET /GarlicFarm/CLUSTER/VERSION/websocket HTTP/1.1
Host: (ip):(port)
Cache-Control: no-cache
Connection: close
(أي رؤوس أخرى تُهمل)
(سطر فارغ)
CLUSTER هو اسم المجموعة (الافتراضي "farm")
VERSION هو إصدار Garlic Farm (حاليًا "1")
استجابة HTTP 1
إذا لم يكن المسار صحيحًا، يرسل المستلم استجابة قياسية “HTTP/1.1 404 Not Found”، كما في RFC 2616 .
إذا كان المسار صحيحًا، يرسل المستلم استجابة قياسية “HTTP/1.1 401 Unauthorized”، متضمنًا رأس مصادقة HTTP Digest WWW-Authenticate، كما في RFC 2617 .
ثم يغلق الطرفان المقابس.
طلب HTTP 2
يرسل المُبْدِع ما يلي، كما في RFC 2617 .
جميع الأسطر تنتهي بـ CRLF حسب المطلوب في HTTP.
GET /GarlicFarm/CLUSTER/VERSION/websocket HTTP/1.1
Host: (ip):(port)
Cache-Control: no-cache
Connection: keep-alive, Upgrade
Upgrade: websocket
(رؤوس Sec-Websocket-* إذا تم التوجيه)
Authorization: (رأس تفويض HTTP Digest حسب RFC 2617)
(أي رؤوس أخرى تُهمل)
(سطر فارغ)
CLUSTER هو اسم المجموعة (الافتراضي "farm")
VERSION هو إصدار Garlic Farm (حاليًا "1")
استجابة HTTP 2
إذا لم تكن المصادقة صحيحة، يرسل المستلم استجابة قياسية أخرى “HTTP/1.1 401 Unauthorized”، كما في RFC 2617 .
إذا كانت المصادقة صحيحة، يرسل المستلم الاستجابة التالية، كما في بروتوكول WebSocket.
جميع الأسطر تنتهي بـ CRLF حسب المطلوب في HTTP.
HTTP/1.1 101 Switching Protocols
Connection: Upgrade
Upgrade: websocket
(رؤوس Sec-Websocket-*)
(أي رؤوس أخرى تُهمل)
(سطر فارغ)
بعد استلام هذا، يبقى المقبس مفتوحًا. يبدأ بروتوكول Raft كما هو محدد أدناه، على نفس المقبس.
التخزين المؤقت
يجب تخزين بيانات الاعتماد لمدة ساعة على الأقل، بحيث يمكن للاتصالات اللاحقة الانتقال مباشرة إلى “طلب HTTP 2” أعلاه.
أنواع الرسائل
توجد نوعان من الرسائل: طلبات واستجابات. قد تحتوي الطلبات على إدخالات السجل (Log Entries)، وهي ذات حجم متغير؛ لا تحتوي الاستجابات على إدخالات السجل، وهي ذات حجم ثابت.
أنواع الرسائل من 1 إلى 4 هي رسائل RPC القياسية المعرفة بواسطة Raft. هذا هو جوهر بروتوكول Raft.
أنواع الرسائل من 5 إلى 15 هي رسائل RPC الموسع المعرفة بواسطة JRaft، لدعم العملاء، التغييرات الديناميكية للخوادم، و مزامنة السجل بكفاءة.
أنواع الرسائل من 16 إلى 17 هي رسائل RPC لضغط السجل (Log Compaction) المعرفة في قسم 7 من Raft.
| الرسالة | الرقم | يرسلها | يرسل إلى | ملاحظات |
|---|---|---|---|---|
| RequestVoteRequest | 1 | Candidate | Follower | RPC قياسي لـ Raft؛ لا يجب أن يحتوي على إدخالات السجل |
| RequestVoteResponse | 2 | Follower | Candidate | RPC قياسي لـ Raft |
| AppendEntriesRequest | 3 | Leader | Follower | RPC قياسي لـ Raft |
| AppendEntriesResponse | 4 | Follower | Leader / Client | RPC قياسي لـ Raft |
| ClientRequest | 5 | Client | Leader / Follower | الاستجابة هي AppendEntriesResponse؛ يجب أن تحتوي فقط على إدخالات سجل التطبيق |
| AddServerRequest | 6 | Client | Leader | يجب أن يحتوي على إدخال سجل ClusterServer واحد فقط |
| AddServerResponse | 7 | Leader | Client | سيرسل القائد أيضًا JoinClusterRequest |
| RemoveServerRequest | 8 | Follower | Leader | يجب أن يحتوي على إدخال سجل ClusterServer واحد فقط |
| RemoveServerResponse | 9 | Leader | Follower | |
| SyncLogRequest | 10 | Leader | Follower | يجب أن يحتوي على إدخال سجل LogPack واحد فقط |
| SyncLogResponse | 11 | Follower | Leader | |
| JoinClusterRequest | 12 | Leader | New Server | دعوة للانضمام؛ يجب أن يحتوي على إدخال سجل Configuration واحد فقط |
| JoinClusterResponse | 13 | New Server | Leader | |
| LeaveClusterRequest | 14 | Leader | Follower | أمر بالمغادرة |
| LeaveClusterResponse | 15 | Follower | Leader | |
| InstallSnapshotRequest | 16 | Leader | Follower | قسم 7 من Raft؛ يجب أن يحتوي على إدخال سجل SnapshotSyncRequest واحد فقط |
| InstallSnapshotResponse | 17 | Follower | Leader | قسم 7 من Raft |
الإقامة
بعد مراسلة HTTP، تكون تسلسل الإقامة كما يلي:
الخادم الجديد أليس المتابع العشوائي بوب
ClientRequest ------->
<--------- AppendEntriesResponse
إذا قال بوب إنه القائد، استمر أدناه.
وإلا، يجب على أليس فصل الاتصال من بوب والاتصال بالقائد.
الخادم الجديد أليس القائد تشارلي
ClientRequest ------->
<--------- AppendEntriesResponse
AddServerRequest ------->
<--------- AddServerResponse
<--------- JoinClusterRequest
JoinClusterResponse ------->
<--------- SyncLogRequest
OR InstallSnapshotRequest
SyncLogResponse ------->
OR InstallSnapshotResponse
تسلسل الفصل:
المتابع أليس القائد تشارلي
RemoveServerRequest ------->
<--------- RemoveServerResponse
<--------- LeaveClusterRequest
LeaveClusterResponse ------->
تسلسل الانتخابات:
المرشح أليس المتابع بوب
RequestVoteRequest ------->
<--------- RequestVoteResponse
إذا فازت أليس بالانتخابات:
القائد أليس المتابع بوب
AppendEntriesRequest ------->
(heartbeat)
<--------- AppendEntriesResponse
التعريفات
- المصدر (Source): يحدد مُرسِل الرسالة
- الوجهة (Destination): يحدد مُستقبِل الرسالة
- المصطلحات (Terms): انظر Raft. تُهيأ إلى 0، وتزداد بشكل رتيب
- الفهارس (Indexes): انظر Raft. تُهيأ إلى 0، وتزداد بشكل رتيب
الطلبات
تحتوي الطلبات على رأس وإدخالات سجل صفر أو أكثر. تحتوي الطلبات على رأس بحجم ثابت وإدخالات سجل اختيارية بحجم متغير.
رأس الطلب
رأس الطلب بحجم 45 بايت، كما يلي. جميع القيم بدون إشارة وبنظام big-endian.
نوع الرسالة: 1 بايت
المصدر: معرف، عدد صحيح 4 بايت
الوجهة: معرف، عدد صحيح 4 بايت
المصطلح: المصطلح الحالي (انظر الملاحظات)، عدد صحيح 8 بايت
المصطلح الأخير للسجل: عدد صحيح 8 بايت
فهرس السجل الأخير: عدد صحيح 8 بايت
فهرس الالتزام: عدد صحيح 8 بايت
حجم إدخالات السجل: الحجم الكلي بالبايت، عدد صحيح 4 بايت
إدخالات السجل: انظر أدناه، الطول الكلي حسب المحدد
ملاحظات
في RequestVoteRequest، المصطلح هو مصطلح المرشح. وإلا، فهو المصطلح الحالي للقائد.
في AppendEntriesRequest، عندما يكون حجم إدخالات السجل صفرًا، تكون هذه الرسالة رسالة نبض (heartbeat) (keepalive).
إدخالات السجل
يحتوي السجل على إدخالات سجل صفر أو أكثر. كل إدخال سجل يكون كما يلي. جميع القيم بدون إشارة وبنظام big-endian.
المصطلح: عدد صحيح 8 بايت
نوع القيمة: 1 بايت
حجم الإدخال: بالبايت، عدد صحيح 4 بايت
الإدخال: الطول حسب المحدد
محتويات السجل
جميع القيم بدون إشارة وبنظام big-endian.
| نوع قيمة السجل | الرقم |
|---|---|
| التطبيق | 1 |
| التكوين | 2 |
| ClusterServer | 3 |
| LogPack | 4 |
| SnapshotSyncRequest | 5 |
التطبيق
محتويات التطبيق مشفرة بـ UTF-8 بصيغة JSON . انظر قسم طبقة التطبيق أدناه.
التكوين
يُستخدم هذا لتمكين القائد من تسلسل تكوين مجموعة جديد ونسخه إلى الأقران. يحتوي على تكوينات ClusterServer صفر أو أكثر.
فهرس السجل: عدد صحيح 8 بايت
فهرس السجل الأخير: عدد صحيح 8 بايت
بيانات ClusterServer لكل خادم:
المعرف: عدد صحيح 4 بايت
طول بيانات النقطة الطرفية: بالبايت، عدد صحيح 4 بايت
بيانات النقطة الطرفية: سلسلة ASCII على شكل "tcp://localhost:9001"، الطول حسب المحدد
ClusterServer
معلومات التكوين لخادم ضمن مجموعة. تُدرج فقط في رسالة AddServerRequest أو RemoveServerRequest.
عند استخدامها في رسالة AddServerRequest:
المعرف: عدد صحيح 4 بايت
طول بيانات النقطة الطرفية: بالبايت، عدد صحيح 4 بايت
بيانات النقطة الطرفية: سلسلة ASCII على شكل "tcp://localhost:9001"، الطول حسب المحدد
عند استخدامها في رسالة RemoveServerRequest:
المعرف: عدد صحيح 4 بايت
LogPack
تُدرج فقط في رسالة SyncLogRequest.
يتم ضغط ما يلي باستخدام gzip قبل الإرسال:
طول بيانات الفهرس: بالبايت، عدد صحيح 4 بايت
طول بيانات السجل: بالبايت، عدد صحيح 4 بايت
بيانات الفهرس: 8 بايت لكل فهرس، الطول حسب المحدد
بيانات السجل: الطول حسب المحدد
SnapshotSyncRequest
تُدرج فقط في رسالة InstallSnapshotRequest.
فهرس السجل الأخير: عدد صحيح 8 بايت
مصطلح السجل الأخير: عدد صحيح 8 بايت
طول بيانات التكوين: بالبايت، عدد صحيح 4 بايت
بيانات التكوين: الطول حسب المحدد
الإزاحة: إزاحة البيانات في قاعدة البيانات، بالبايت، عدد صحيح 8 بايت
طول البيانات: بالبايت، عدد صحيح 4 بايت
البيانات: الطول حسب المحدد
تم: 1 إذا اكتمل، 0 إذا لم يكتمل (1 بايت)
الاستجابات
جميع الاستجابات بحجم 26 بايت، كما يلي. جميع القيم بدون إشارة وبنظام big-endian.
نوع الرسالة: 1 بايت
المصدر: المعرف، عدد صحيح 4 بايت
الوجهة: عادةً معرف الوجهة الفعلي (انظر الملاحظات)، عدد صحيح 4 بايت
المصطلح: المصطلح الحالي، عدد صحيح 8 بايت
الفهرس التالي: يُهيأ إلى فهرس السجل الأخير للقائد + 1، عدد صحيح 8 بايت
مقبول: 1 إذا مُقبولة، 0 إذا لم تُقبل (انظر الملاحظات)، 1 بايت
ملاحظات
معرف الوجهة عادةً هو الوجهة الفعلية لهذه الرسالة. ومع ذلك، بالنسبة لـ AppendEntriesResponse و AddServerResponse و RemoveServerResponse، فهو معرف القائد الحالي.
في RequestVoteResponse، تكون القيمة Is Accepted تساوي 1 للتصويت للمرشح (الطالب)، و 0 في حالة عدم التصويت.
طبقة التطبيق
ينشر كل خادم بشكل دوري بيانات التطبيق في السجل عبر ClientRequest. تحتوي بيانات التطبيق على حالة راوتر كل خادم ووجهة مجموعة Meta LS2. تستخدم الخوادم خوارزمية مشتركة لتحديد الناشر ومحتويات Meta LS2. الخادم الذي لديه “أفضل” حالة حديثة في السجل هو الناشر لـ Meta LS2. ناشر Meta LS2 ليس بالضرورة قائد Raft.
محتويات بيانات التطبيق
محتويات التطبيق مشفرة بـ UTF-8 بصيغة JSON ، لأجل البساطة والتوسع. المواصفة الكاملة لم تُحدد بعد. الهدف هو توفير بيانات كافية لكتابة خوارزمية لتحديد “أفضل” راوتر لنشر Meta LS2، ولتمكين الناشر من امتلاك معلومات كافية لإعطاء أوزان للوجهات في Meta LS2. ستحتوي البيانات على إحصائيات الراوتر والوجهة معًا.
قد تحتوي البيانات اختياريًا على بيانات استشعار عن بُعد حول صحة الخوادم الأخرى، والقدرة على جلب Meta LS. لن تُدعم هذه البيانات في الإصدار الأول.
قد تحتوي البيانات اختياريًا على معلومات تكوين تم نشرها بواسطة عميل مسؤول. لن تُدعم هذه البيانات في الإصدار الأول.
إذا تم سرد “name: value”، فهذا يحدد مفتاح وقيمة الخريطة في JSON. وإلا، المواصفة لم تُحدد بعد.
بيانات المجموعة (المستوى العلوي):
- cluster: اسم المجموعة
- date: تاريخ هذه البيانات (عدد طويل، بالمللي ثانية منذ العصر)
- id: معرف Raft (عدد صحيح)
بيانات التكوين (config):
- أي معلمات تكوين
حالة نشر MetaLS (meta):
- destination: وجهة metals، بترميز base64
- lastPublishedLS: إذا وُجدت، ترميز base64 للـ metals المنشور آخر مرة
- lastPublishedTime: بالمللي ثانية، أو 0 إذا لم يُنشر قط
- publishConfig: حالة تكوين الناشر (تشغيل/إيقاف/تلقائي)
- publishing: حالة ناشر metals (قيمة منطقية true/false)
بيانات الراوتر (router):
- lastPublishedRI: إذا وُجدت، ترميز base64 لمعلومات الراوتر المنشورة آخر مرة
- uptime: وقت التشغيل بالمللي ثانية
- تأخر المهام (Job lag)
- الأنفاق الاستكشافية
- الأنفاق المشاركة
- عرض النطاق المهيأ
- عرض النطاق الحالي
الوجهات (destinations): قائمة
بيانات الوجهة:
- destination: الوجهة، بترميز base64
- uptime: وقت التشغيل بالمللي ثانية
- الأنفاق المهيأة
- الأنفاق الحالية
- عرض النطاق المهيأ
- عرض النطاق الحالي
- الاتصالات المهيأة
- الاتصالات الحالية
- بيانات القائمة السوداء
بيانات استشعار الراوتر البعيد:
- آخر إصدار RI تم رؤيته
- وقت جلب LS
- بيانات اختبار الاتصال
- بيانات ملفات floodfills الأقرب لفترات الزمن بالأمس، اليوم، وغدًا
بيانات استشعار الوجهة البعيدة:
- آخر إصدار LS تم رؤيته
- وقت جلب LS
- بيانات اختبار الاتصال
- بيانات ملفات floodfills الأقرب لفترات الزمن بالأمس، اليوم، وغدًا
بيانات استشعار Meta LS:
- آخر إصدار تم رؤيته
- وقت الجلب
- بيانات ملفات floodfills الأقرب لفترات الزمن بالأمس، اليوم، وغدًا
واجهة الإدارة
لم تُحدد بعد، ربما في اقتراح منفصل. غير مطلوبة للإصدار الأول.
متطلبات واجهة الإدارة:
- دعم وجهات رئيسية متعددة، أي مجموعات افتراضية متعددة (مزارع)
- توفير عرض شامل لحالة المجموعة المشتركة - جميع الإحصائيات المنشورة من الأعضاء، من هو القائد الحالي، إلخ.
- القدرة على إجبار إزالة مشارك أو قائد من المجموعة
- القدرة على إجبار نشر metaLS (إذا كان العقدة الحالية هي الناشر)
- القدرة على استبعاد هاشات من metaLS (إذا كانت العقدة الحالية هي الناشر)
- وظيفة استيراد/تصدير التكوين للنشر الجماعي
واجهة الراوتر
لم تُحدد بعد، ربما في اقتراح منفصل. i2pcontrol غير مطلوب للإصدار الأول وسيتم تضمين التغييرات التفصيلية في اقتراح منفصل.
متطلبات واجهة Garlic Farm إلى الراوتر (في-JVM جافا أو i2pcontrol)
- getLocalRouterStatus()
- getLocalLeafHash(Hash masterHash)
- getLocalLeafStatus(Hash leaf)
- getRemoteMeasuredStatus(Hash masterOrLeaf) // ربما غير مطلوب في النسخة الأولية
- publishMetaLS(Hash masterHash, List
contents) // أو MetaLeaseSet موقع؟ من يوقع؟ - stopPublishingMetaLS(Hash masterHash)
- المصادقة لم تُحدد بعد؟
التبرير
Atomix كبيرة جدًا ولن تسمح لنا بتخصيصها لتشغيل البروتوكول عبر I2P. أيضًا، تنسيق اتصالها غير موثق، ويعتمد على تسلسل جافا.
ملاحظات
القضايا
- لا توجد طريقة للعميل لمعرفة القائد غير المعروف والاتصال به. سيكون تغييرًا بسيطًا أن يرسل المتابع Configuration كإدخال سجل في AppendEntriesResponse.
الهجرة
لا توجد مشكلات توافق مع الإصدارات السابقة.