1. مقدمة للأقفال الموزعة
تُستخدم الأقفال الموزعة بشكل أساسي لحماية الموارد المشتركة عبر العمليات وعبر المضيفين وعبر الشبكات في بيئة موزعة لتحقيق وصول حصري متبادل لضمان اتساق البيانات.
2. الهندسة المعمارية مقدمة
قبل تقديم استخدام Zookeeper لتنفيذ الأقفال الموزعة ، انظر أولاً إلى مخطط بنية النظام الحالي
Explanation: تمثل المنطقة بأكملها على اليسار مجموعة Zookeeper. الخزانة هي عقدة ثابتة من zookeeper ، و node_1 ، node_2 ، و node_3 هي عقد متسلسلة مؤقتة تحت العقدة المستمرة من الخزانة. يعني Client_1 و Client_2 و Client_N عملاء متعددين ، وخدمة الخدمة تعني الموارد المشتركة التي تتطلب الوصول الحصري بشكل متبادل.
أفكار لاكتساب الأقفال الموزعة
1. الفكرة العامة للحصول على أقفال موزعة
عند الحصول على قفل موزع ، قم بإنشاء عقدة متسلسلة مؤقتة أسفل عقدة الخزانة ، وحذف العقدة المؤقتة عند إطلاق القفل. يقوم العميل باستدعاء طريقة CreateNode لإنشاء عقد متسلسلة مؤقتة ضمن الخزانة ، ثم يستدعي GetChildren ("Locker") للحصول على جميع العقد الفرعية تحت الخزانة. لاحظ أنه لا يوجد مراقب مطلوب في هذا الوقت. بعد أن يحصل العميل على جميع مسارات العقدة الفرعية ، إذا وجد أن رقم عقدة الطفل الذي أنشأته من قبل هو الأصغر ، يُعتبر أن العميل قد حصل على القفل. إذا وجدت أن العقدة التي أنشأتها ليست الأصغر بين جميع أطفال الخزانة ، فهذا يعني أنك لم تحصل على القفل. في هذا الوقت ، يحتاج العميل إلى العثور على العقدة أصغر منه ، ثم استدعاء طريقة الوجود () عليها ، وتسجيل مستمع الحدث عليه. بعد ذلك ، إذا تم حذف العقدة التي تهتم بها ، فسيتلقى مراقب العميل الإشعار المقابل. في هذا الوقت ، ستحدد مرة أخرى ما إذا كانت العقدة التي أنشأتها هي أصغر رقم تسلسلي بين العقد الفرعية الخزفية. حصل Rugao على القفل. إذا لم يكن الأمر كذلك ، كرر الخطوات المذكورة أعلاه للاستمرار في الحصول على عقدة أصغر منك والتسجيل للاستماع. لا يزال هناك العديد من الأحكام المنطقية المطلوبة في العملية الحالية.
2. عملية الخوارزمية الأساسية لاكتساب الأقفال الموزعة
فيما يلي نفس المخطط الانسيابي لتحليل الخوارزمية الكاملة لاكتساب الأقفال الموزعة ، على النحو التالي:
Explanation: عندما يرغب العميل A في الحصول على قفل موزع ، قم أولاً بإنشاء عقدة متسلسلة مؤقتة (Node_N) تحت الخزانة ، ثم الحصول على جميع العقد الفرعية (المستوى الأول) على الفور تحت الخزانة.
في هذا الوقت ، نظرًا لأن العديد من العملاء سوف يتنافسون على الأقفال في نفس الوقت ، فإن عدد العقد الفرعية تحت الخزانة سيكون أكبر من 1. للعقد المتسلسلة ، فإن المميزة هي أن هناك رقمًا رقميًا بعد اسم العقدة. رقم رقم العقدة التي تم إنشاؤها أولاً أصغر من الرقم الذي تم إنشاؤه لاحقًا. لذلك ، يمكن فرز العقد الفرعية من صغيرة إلى كبيرة في ترتيب لاحقة اسم العقدة. وبهذه الطريقة ، فإن أول واحد هو العقدة المتسلسلة التي تم إنشاؤها أولاً. في هذا الوقت ، يمثل العميل الذي يسعى أولاً إلى القفل! في هذا الوقت ، حدد ما إذا كانت أصغر العقدة هي Node_N التي تم إنشاؤها بواسطة العميل A من قبل. إذا كان الأمر كذلك ، فهذا يعني أن العميل A قد اكتسب القفل. إذا لم يكن الأمر كذلك ، فهذا يعني أنه تم الحصول على القفل من قبل العملاء الآخرين. لذلك ، يتعين على العميل A الانتظار حتى يتم إصدار القفل ، أي العميل B الذي اكتسب القفل يحذف العقدة التي أنشأتها.
في هذا الوقت ، سنعرف ما إذا كان العميل B قد أصدر القفل من خلال الاستماع إلى حدث الحذف للعقد المتسلسلة الأصغر من Node_N Times. إذا كان الأمر كذلك ، فإن العميل A يكتسب جميع الأطفال تحت الخزانة مرة أخرى ويقارنهم بعقد Node_N التي تم إنشاؤها بمفردها حتى يكون Node_N الذي تم إنشاؤه بمفرده هو أصغر رقم تسلسل بين جميع أطفال الخزانة ، مما يعني أن العميل A قد اكتسب القفل!
4. تنفيذ رمز الأقفال الموزعة على أساس Zookeeper
1. تحديد واجهة قفل موزعة
واجهة القفل الموزعة المحددة هي كما يلي:
الواجهة العامة distributedlock { / ** الحصول على القفل ، إذا لم يتم الحصول عليها ، انتظر* / public void assure () رمي الاستثناء ؛ / *** الحصول على القفل حتى المهلة* param الوقت الوقت المهلة* وحدة الوحدة وحدة وحدة الوقت وحدة الوقت* RETURN ما إذا كان القفل قد تم الحصول على* athrows استثناء*/ BOOLEAN العامة اكتساب (وقت طويل ، وحدة timunit) الاستثناء ؛ / *** احرص على القفل* athrows استثناء*/ public void release () رمي الاستثناء ؛}2. حدد mutex البسيطة
حدد فئة قفل Mutex ، وتنفيذ واجهة القفل المحددة أعلاه ، وراث فئة قاعدة على أساسها. تستخدم هذه الفئة الأساسية بشكل أساسي للتفاعل مع ZookeEper ، بما في ذلك طريقة لمحاولة الحصول على القفل وقفل الإصدار.
/** يتم تحقيق التنفيذ المحدد لواجهة القفل بشكل أساسي بواسطة فئة الوالدين الموروثة على أساسها. يتم تنفيذ الفئة الأصل استنادًا إلى التفاصيل المحددة لـ ZookeEper لتنفيذ الأقفال الموزعة* /فئة SimplireSistributedLockMutex العامة يمتد الأدوات المستند إلى Zookeeper ، مثل اسم الخزانة: /*يجب أن تكون هذه المعرفة عبارة عن NODE موزعة. قم بإنشاء عقد متسلسلة مؤقتة ضمن هذه العقدة لتنفيذ الأقفال الموزعة*/ BasePath النهائي الخاص ؛ /*بادئة اسم القفل. على سبيل المثال ، فإن العقد المتسلسلة التي تم إنشاؤها تحت الخزانة تبدأ مع القفل ، والتي تسهل تصفية العقد غير ذات الصلة*العقد التي تم إنشاؤها مشابهة لـ: lock-00000001 ، lock-00000002*/ private StaticFinal String_Name = "Lock-" ؛ /* المستخدمة لحفظ العقد المتسلسلة الناجحة التي أنشأها عميل تحت الخزانة ، للعمليات ذات الصلة اللاحقة (مثل الحكم)*/ private String OurlockPath ؛ /*** تستخدم لاكتساب موارد القفل ، والحصول على أقفال من خلال طريقة اكتساب قفل الفئة من الوالدين* param الوقت لاكتساب الوقت المهلة للوقت الزمني لوقت وحدة القفل* param* return return ما إذا كان قد تم الحصول على استثناء من throos ، فقد تم النظر في القفل ، يتم الحصول على القفل على القفل. لمزيد من التفاصيل ، يرجى الرجوع إلى تنفيذ المحاولة. OurlockPath = المحاولات (الوقت ، الوحدة) ؛ إرجاع OurlockPath! = NULL ؛ } /*** تمرير في كائن اتصال عميل zookeeper ، و basepath* param client client zookeeper connect connect connect* @param basepath basepath is عبارة عن عقدة ثابتة* /public simplirestributedlockmutex (zkclientex العقدة *حفظ مرجع BasePath إلى سمة الفئة الحالية */ super (العميل ، BasePath ، lock_name) ؛ this.basepath = basepath ؛ } /** الحصول على القفل حتى المهلة ، ويتم إلقاء استثناء بعد المهلة* /الفراغ العام الحصول على () يلقي الاستثناء {//-1 يعني أن المهلة لم يتم تعيينها ، وأن المهلة تحددها المهلة من قبل ZookeEper if (! }} / *** الحصول على القفل مع timeout* / public boolean الحصول على (وقت طويل ، وحدة الوقت) يلقي الاستثناء {return internallock (الوقت ، الوحدة) ؛ } / ** احرص على القفل* / public void release () رمي الاستثناء {releaselock (ourlockPath) ؛ }}3. تفاصيل تنفيذ الأقفال الموزعة
يعتمد المنطق الرئيسي لاكتساب الأقفال الموزعة على توزيعها ، والتي تنفذ تفاصيل تنفيذ الأقفال الموزعة على أساس zookeeper.
الفئة العامة على أساس عميل ZkClientext النهائي الخاص ؛ مسار السلسلة النهائي الخاص ؛ سلسلة نهائية خاصة Basepath ؛ قفل السلسلة النهائية الخاصة ؛ integer integer integer max_retry_count = 10 ؛ publicistributedlock (عميل ZKClientExt ، مسار السلسلة ، سلسلة القفل) {this.client = client ؛ this.basepath = path ؛ this.path = path.concat ("/"). concat (lockname) ؛ this.lockName = lockname ؛ } private void deleteourpath (String ourpath) يلقي استثناء {client.delete (ourpath) ؛ } سلسلة خاصة CreatelockNode (عميل ZkClient ، مسار السلسلة) يلقي الاستثناء {return client.createephemeralsealiential (path ، null) ؛ } / ** * الطريقة الأساسية للحصول على قفل * param startMillis * param millistowait * param ourpath * @thernors revision * / private boolean waittolock (startMillis long ، millistowait long ، string ourpath) revision {boolean havethelock = false ؛ boolean dodelete = false ؛ جرب {بينما (! havethelock) {// تنفذ هذه الطريقة اكتساب جميع العقد المتسلسلة تحت عقدة الخزانة ، وينفرز من قائمة صغيرة إلى قائمة كبيرة <string> للأطفال = getortedchildren () ؛ سلسلة sequencenodename = our ourpath.substring (basePath.length ()+1) ؛ // احسب موضع الفرز لعقد الطلب التي أنشأها العميل الآن في جميع العقد الفرعية في الخزانة. إذا كان هذا النوع 0 ، فهذا يعني أنه تم الحصول على القفل. int ourIndex = children.indexof (sequencenodename) ؛ /*إذا لم يتم العثور على عقدة الطلب [المؤقتة] التي قمت بإنشائها من قبل في GetortedChildren ، فهذا يعني أنه قد يتم حذف العقدة التي أنشأتها بسبب كسر فلاش الشبكة. يجب إلقاء الاستثناء. دع المستوى السابق معالجة *المستوى السابق هو التقاط الاستثناء وتنفيذ العدد المحدد من المحاكاة. راجع طريقة المحاولة في طريقة المحاولة اللاحقة*/ if (ourIndex <0) {رمي ZkknonodeException الجديد ("غير موجود:" + sequencenodename) ؛ } // إذا كانت العقدة التي أنشأها العميل الحالي أكبر من 0 في قائمة عقدة Locker Child ، فهذا يعني أن العملاء الآخرين قد اكتسبوا القفل // في هذا الوقت ، يحتاج العميل الحالي إلى انتظار العملاء الآخرين لإصدار القفل ، IsgetTheLock = ourindex == 0 ؛ // كيفية تحديد ما إذا كان العملاء الآخرون قد أصدروا القفل؟ احصل على العقدة أصغر من نفسها من قائمة عقدة الطفل وقم بإعداد جلسة استماع لـ IT string pathtowatch = isGetTheLock؟ NULL: الأطفال. if (isgetThelock) {havethelock = true ؛ } آخر {// إذا تم حذف العقدة الأصغر ، فهذا يعني أن عقدة العميل الحالي يجب أن تكون الأصغر ، لذلك استخدم CountDownLatch لتحقيق سلسلة انتظار PrevipeSpath = BasePath .Concat ("/") .Concat (pathtowatch) ؛ المزلاج النهائي للعد التنازلي = New CountDownLatch (1) ؛ Final IzKDatalistener PrevipListener = new izkdatalistener () {// عندما يحدث حدث حذف عقدة صغير ، دع CountDownlatch End وانتظر // في هذا الوقت ، تحتاج إلى السماح للبرنامج بالعودة إلى حين مرة أخرى وتصدر حكمًا جديدًا! public void handledatadeled (سلسلة datapath) يلقي الاستثناء {latch.countdown () ؛ } public void handledatachange (سلسلة datapath ، بيانات الكائن) يلقي الاستثناء {// تجاهل}} ؛ حاول {// استثناء إذا كانت العقدة غير موجودة client.subscribedatachanges (previoursequencepath ، السابق lextener) ؛ if (millistowait! = null) {millistowait - = (system.currentTimeMillis () - startMillis) ؛ startMillis = System.CurrentTimeMillis () ؛ if (millistowait <= 0) {dodelete = true ؛ // توقيت الخروج - حذف استراحة العقدة لدينا ؛ } latch.await (millistowait ، timeunit.microseconds) ؛ } آخر {watch.await () ؛ }} catch (ZknonOdEexception e) {// تجاهل} أخيرًا {client.unsubscribedatachanges (previpequensencepath ، previplistener) ؛ }}}}} catch (استثناء e) {// الاستثناء يجب حذف dodelete = true ؛ رمي ه ؛ } أخيرًا {// إذا كنت بحاجة إلى حذف العقدة if (dodelete) {deleteourpath (ourpath) ؛ }} الإرجاع havethelock ؛ } سلسلة خاصة getlocknodenumber (String Str ، String LockName) {int index = str.lastindexof (lockname) ؛ if (index> = 0) {index += lockname.length () ؛ فهرس العودة <= str.length ()؟ str.substring (الفهرس): "" ؛ } إرجاع str ؛ } قائمة خاصة <string> getortedChildren () يلقي استثناء {try {list <string> children = client.getChildren (basepath) ؛ collections.sort (الأطفال ، المقارنة الجديدة <string> () {public int compare (string lhs ، string rhs) {return getlocknodenumber (lhs ، lockname) .Compareto (getlocknodenber (rhs ، lockname)) ؛}}) ؛ إرجاع الأطفال ؛ } catch (zkknonodeexception e) {client.createpersistent (basepath ، true) ؛ عودة getortedchildren () ؛ }} محمية void releaselock (سلسلة lockpath) يلقي الاستثناء {deleteourpath (lockpath) ؛ } string string المحمي (الوقت الطويل ، وحدة الوقت) يلقي الاستثناء {Final Long startMillis = System.CurrentTimeMillis () ؛ millistowait النهائي الطويل = (الوحدة! = فارغة)؟ Unit.Tomillis (time): null ؛ سلسلة ourpath = null ؛ Boolean hasthelock = false ؛ منطقية isDone = false ؛ int reconcount = 0 ؛ // يتطلب كسر فلاش صافي إعادة المحاولة أثناء (! isDone) {isDone = true ؛ TREE {// createlocknode يستخدم لإنشاء عقدة الطلب [المؤقتة] للعميل لاكتساب القفل تحت الخزانة (عقدة BASEPATH). ourpath = createlocknode (العميل ، المسار) ؛ / *** يتم استخدام هذه الطريقة لتحديد ما إذا كان قد تم الحصول على القفل ، أي ما إذا كانت عقد الطلب التي تم إنشاؤها من قبل نفسك هي الأصغر بين جميع العقد الفرعية في الخزانة* إذا لم يتم الحصول على القفل ، فانتظر إصدار القفل ، وحاول مرة أخرى لاحقًا حتى يتم الحصول على القفل أو توقيته*/ Hasthelock = Waittolock (Startmillis ، Millistowait ، Arophate) ؛ } catch (zkknonodeexception e) {if (recept ++ <max_retry_count) {isDone = false ؛ } آخر {throw e ؛ }}}} if (hasthelock) {return ourPath ؛ } إرجاع فارغ ؛ }ما سبق هو كل محتوى هذه المقالة. آمل أن يكون ذلك مفيدًا لتعلم الجميع وآمل أن يدعم الجميع wulin.com أكثر.