في Spring Cloud ، نستخدم Hystrix لتنفيذ قواطع الدوائر. في Zuul ، يتم استخدام semaphores افتراضيًا (يتم خيوط Hystrix بشكل افتراضي). يمكننا استخدام عزل مؤشر الترابط من خلال التكوين.
عند استخدام عزل مؤشر الترابط ، هناك مشكلة يجب حلها ، أي في بعض سيناريوهات العمل ، يتم تمرير البيانات في مؤشرات الترابط من خلال ThreadLocal. من الجيد استخدام الإشارات ، ويأتي من الطلب ، ولكن العملية اللاحقة هي كل شيء عن موضوع واحد.
عندما يكون وضع العزلة مرتبطًا ، سيضع Hystrix الطلب في تجمع مؤشر ترابط Hystrix للتنفيذ. في هذا الوقت ، سيصبح الطلب مؤشر ترابط B ، وسوف يختفي ThreadLocal حتماً.
دعنا نحاكي هذه العملية من خلال عمود بسيط:
الفئة العامة CustomThreadLocal {static threadlocal <string> threadlocal = new threadlocal <> () ؛ Public Static Void Main (String [] args) {new thread (new RunNable () {Override public void run () {customThreadLocal.Threadlocal.set ("Apes") ؛ New Service (). }} class service {public void call () {system.out.println ("service:" + thread.currentThRead (). getName ()) ؛ System.out.println ("Service:" + CustomThreadLocal.ThreadLocal.get ()) ؛ New Dao (). call () ؛ }} class dao {public void call () { System.out.println ("============================================================================================================================ ======================================================================================================================================================== ======================================================================================================================================================== ================================================================================================================================================ CustomThreadlocal.Threadlocal.get ()) ؛نحن نحدد مؤشر ترابط في الفئة الرئيسية لتمرير البيانات ، ثم يتم إنشاء مؤشر ترابط ، ويتم استدعاء طريقة المكالمات في الخدمة في الخيط ، ويتم تعيين قيمة في Threadlocal ، ويتم الحصول على القيمة في Threadlocal في الخدمة ، ثم يتم استدعاء طريقة المكالمات في DAO ، والتي يتم الحصول عليها أيضًا في مؤشر الترابط. دعنا نديرها لنرى التأثير:
الخدمة: Thread-0
الخدمة: القرود والسماء
=====================================
DAO: Thread-0
داو: عالم العالم
يمكنك أن ترى أن العملية بأكملها يتم تنفيذها في نفس الخيط ، وتم الحصول على القيمة في ThreadLocal بشكل صحيح. لا توجد مشكلة في هذا الموقف.
بعد ذلك ، نقوم بتحويل البرنامج ، ونقوم بإجراء تبديل مؤشرات الترابط ، واتصل بالدعوة في DAO لإعادة تنفيذ مؤشر ترابط:
الفئة العامة CustomThreadLocal {static threadlocal <string> threadlocal = new threadlocal <> () ؛ Public Static Void Main (String [] args) {new thread (new RunNable () {Override public void run () {customThreadLocal.Threadlocal.set ("Apes") ؛ New Service (). }} class service {public void call () {system.out.println ("service:" + thread.currentThRead (). getName ()) ؛ System.out.println ("Service:" + CustomThreadLocal.ThreadLocal.get ()) ؛ // new dao (). call () ؛ New Thread (new RunNable () {Override public void run () {new dao (). call () ؛}}). start () ؛ }} class dao {public void call () { System.out.println ("================================================================================ ================================================================================================================================ =========================================================================================================================== ================================================================================================================================ System.out.println ("dao:" + thread.currentThread (). getName ()) ؛ركض مرة أخرى لترى التأثير:
الخدمة: Thread-0
الخدمة: القرود والسماء
=====================================
DAO: Thread-1
داو: فارغة
يمكنك أن ترى أن هذا الطلب قد تم الانتهاء منه بواسطة خيطين. لا يزال بإمكانك الحصول على قيمة threadlocal في الخدمة. لا يمكنك الحصول عليه في DAO لأنه تم تبديل الخيط. هذه هي المشكلة التي ستضيعها بيانات ThreadLocal في البداية.
لذا ، فإن كيفية حل هذه المشكلة بسيطة للغاية ، فأنت بحاجة فقط إلى تغيير سطر واحد من التعليمات البرمجية:
static threadlocal <string> threadlocal = new ertliTableThreadLocal <> () ؛
تغيير threadlocal إلى inheritablethreadlocal ، دعونا نلقي نظرة على التأثير بعد التحول:
الخدمة: Thread-0
الخدمة: القرود والسماء
=====================================
DAO: Thread-1
داو: عالم العالم
يمكن الحصول على القيمة بشكل طبيعي. يحدث erranitablethreadlocal عن طريق حل المشكلة التي لا يمكن أن تحصل على threadlocal من القيمة بسبب تبديل مؤشر الترابط هذا.
لفهم مبدأ erneritablethreadlocal ، يجب أن نفهم أولاً مبدأ threadlocal. دعونا نقدم بإيجاز مبدأ Threadlocal:
يحتوي كل مؤشر ترابط على خاصية threadlocals من نوع threadlocalmap. فئة threadlocalmap تعادل الخريطة. المفتاح هو threadlocal نفسه ، والقيمة هي القيمة التي حددناها.
يقوم مؤشر ترابط الفئة العامة بتنفيذ Runnable {threadlocal.throadlocalmap threadlocals = null ؛} عندما نمرر threadlocal.set ("القرود والعالم") ؛ ، نضع زوجًا رئيسيًا في خاصية Threadlocals في هذا الموضوع. المفتاح هو مؤشر الترابط الحالي ، والقيمة هي القيمة التي تقوم بتعيينها.
مجموعة void العامة (t value) {thread t = thread.currentThRead () ؛ threadlocalmap خريطة = getMap (t) ؛ if (map! = null) map.set (هذا ، القيمة) ؛ Else CreateMap (t ، القيمة) ؛} عندما نستخدم طريقة threadlocal.get () ، نحصل على القيمة المحددة بواسطة هذا الموضوع بناءً على مؤشر الترابط الحالي كمفتاح.
public t get () {thread t = thread.currentThRead () ؛ threadlocalmap خريطة = getMap (t) ؛ if (map! = null) {threadlocalmap.entry e = map.getentry (this) ؛ if (e! = null) {suppressWarnings ("unchected") t result = (t) e.value ؛ نتيجة العودة }} الإرجاع setInitialValue () ؛}من خلال المقدمة أعلاه ، يمكننا أن نفهم أن ThreadLocal يمكنه تمرير البيانات باستخدام thread.currentThread () للحصول عليها ، أي طالما أنها في نفس الخيط ، يمكن الحصول على القيمة الموجودة في المقدمة.
إذا قامت العملية التالية بإعادة إنشاء مؤشر ترابط بعد أن قامت ThreadLocal بتعيين القيمة ، وتغيرت Thread.currentThread () في هذا الوقت ، فلن تحصل بالتأكيد على القيمة التي حددتها من قبل. لاستنساخ محدد للمشاكل ، يرجى الرجوع إلى الكود الخاص بي أعلاه.
ثم لماذا يكون inheritablethreadlocal موافق؟
وراثة الفئة الموروثة revlocal threadlocal وإعادة كتابة 3 أساليب. عند إنشاء مؤشر ترابط مؤشر ترابط جديد على مؤشر الترابط الحالي ، سيتم تمرير متغيرات مؤشر الترابط هذه من مؤشر الترابط الحالي إلى مثيل مؤشر الترابط الجديد.
إن الفئة العامة ويرثراويدلوكال <T> يمتد Threadlocal <T> { /** * يحسب القيمة الأولية للطفل لهذا المتغير المحلي الخيط الوراثي * كدالة لقيمة الوالد في الوقت الذي يتم فيه إنشاء مؤشر ترابط الطفل *. يتم استدعاء هذه الطريقة من داخل مؤشر ترابط الوالد * قبل بدء تشغيل الطفل. * <p> * هذه الطريقة تُرجع فقط وسيطة الإدخال الخاصة بها ، ويجب تجاوزها * إذا كان هناك حاجة إلى سلوك مختلف. * * param parentValue قيمة مؤشر ترابط الأصل * @Return القيمة الأولية لخيط الطفل */ محمي t childvalue (t parentValue) {return ParentValue ؛ } /*** احصل على الخريطة المرتبطة بـ threadlocal. * * param t الخيط الحالي */ threadlocalmap getMap (thread t) {return t.inheritablethreadlocals ؛ } /*** قم بإنشاء الخريطة المرتبطة بـ threadlocal. * * param t مؤشر الترابط الحالي * param value value للإدخال الأولي للجدول. */ void createMap (thread t ، t firstvalue) {t.inheritablethreadlocals = new threadlocalmap (هذا ، أول) ؛ }}من خلال الكود أعلاه ، يمكننا أن نرى أن inheritablethreadlocal إعادة كتابة الطرق الثلاث الثلاثة childvalue و getMap و CreateMap. عندما نضع القيمة فيه ، يتم حفظ القيمة في inheritableThreadlocals ، بدلاً من المجلدات السابقة.
النقطة الرئيسية هنا. لماذا يمكننا الحصول على القيمة في ThreadLocal في الخيط السابق عند إنشاء مجموعة جديدة من مؤشرات الترابط؟ والسبب هو أنه عند إنشاء مؤشر ترابط جديد ، سيتم تخصيص عوامل ترابط وراثي في مؤشر الترابط السابق إلى العداوات الموروثة من مؤشر الترابط الجديد ، الذي يدرك نقل البيانات بهذه الطريقة.
رمز المصدر في البداية في طريقة مؤشر الترابط init ، على النحو التالي:
if (parent.inheritablethreadlocals! = null) this.inheritablethreadlocals = threadlocal.createinHeritedMap (parent.inheritablethreadlocals) ؛
CreateInheritedMap على النحو التالي:
static threadlocalmap createInheritedMap (threadlocalmap parentMap) {return new threadlocalmap (parentMap) ؛ }رمز التخصيص:
private threadlocalmap (threadlocalmap parentMap) {entry [] parentTable = parentMap.table ؛ int len = parentTable.length ؛ setThreshold (LEN) ؛ جدول = إدخال جديد [LEN] ؛ لـ (int j = 0 ؛ j <len ؛ j ++) {entry e = parentTable [j] ؛ if (e! = null) {suppressWarnings ("uncheced") threadlocal <object> key = (threadlocal <object>) e.get () ؛ if (key! = null) {object value = key.ChildValue (e.Value) ؛ الإدخال C = إدخال جديد (مفتاح ، قيمة) ؛ int h = key.throadlocalhashCode & (len - 1) ؛ بينما (الجدول [h]! = null) h = nextIndex (h ، len) ؛ الجدول [H] = C ؛ حجم ++ ؛ }}}}حتى هذه النقطة ، من خلال erneritablethreadlocals ، يمكننا تمرير القيمة في خيط الطفل المحلي عندما ينشئ مؤشر الترابط الأصل خيط الطفل. يمكن أن تلبي هذه الميزة معظم الاحتياجات ، ولكن هناك مشكلة خطيرة أخرى أنه إذا كانت إعادة استخدام الخيط ، فستكون هناك مشاكل. على سبيل المثال ، إذا كان إعادة استخدام مؤشر الترابط ، فسيستخدم errinitableThreadlocals في تجمع مؤشرات الترابط لتمرير القيم ، لأن erranitableThreadlocals سوف تمر فقط بالقيم عند إنشاء مؤشر ترابط جديد. ThreadReuse لن يقوم بهذه العملية. حتى لحل هذه المشكلة ، يجب عليك تمديد فئة الخيط بنفسك لتنفيذ هذه الوظيفة.
لا تنس أننا نفعل Java. العالم مفتوح المصدر لديه كل ما تحتاجه. أدناه أوصي بمكتبة Java التي تم تنفيذها ، وهي المصدر المفتوح المصدر المفتوح في Alibaba.
عنوان Github: https://github.com/alibaba/transmittable-thread-local
تتمثل الوظيفة الرئيسية في حل مشكلة توصيل القيمة المرسللة عند استخدام تجمعات مؤشرات الترابط والمكونات الأخرى التي تقوم بتخزين مؤشرات التخزين المؤقت ، وحل مشكلة توصيل السياق أثناء التنفيذ غير المتزامن.
يمكن لـ JDK's Wernitablethreadlocal فئة إكمال قيمة تمرير الخيط الأصل إلى مؤشر ترابط الطفل. ومع ذلك ، بالنسبة للحالة التي يتم فيها استخدام تجمع مؤشرات الترابط والمكونات الأخرى التي يتم إنشاء مؤشرات الترابط ذاكرة التخزين المؤقت ، يتم إنشاء مؤشر الترابط بواسطة تجمع الخيوط ، ويتم تخزين مؤشر الترابط واستخدامه بشكل متكرر ؛ في هذا الوقت ، لا معنى لها تمرير القيمة المترابط لعلاقة مؤشر ترابط الوالدين والطفل. ما يحتاجه التطبيق هو في الواقع تمرير قيمة Threadlocal عند إرسال المهمة إلى مجموعة مؤشرات الترابط إلى تنفيذ المهمة.
تنقسم طرق الاستخدام المحلي القابل للإرسال إلى ثلاثة أنواع: تعديل قابلة للتشغيل وقابل للاتصال ، وتعديل تجمع الخيوط ، و Agent Java لتعديل فئات تنفيذ مجموعة مؤشرات ترابط JDK
بعد ذلك ، دعنا نوضح طريقة تعديل تجمع الخيوط. أولاً ، دعنا نأخذ حالة غير طبيعية ، الرمز هو كما يلي:
الفئة العامة CustomThreadLocal {static threadlocal <string> threadlocal = new erranitablethreadlocal <> () ؛ STATIC ExecutorService Pool = Executors.NewFixedThreadPool (2) ؛ public static void main (string [] args) {for (int i = 0 ؛ i <100 ؛ i ++) {int j = i ؛ pool.execute (new thread (new RunNable () {Override public void run () {customThreadLocal.Threadlocal.set ("APE World"+J) ؛ New Service (). Call () ؛}})) ؛ }}} class service {public void call () {customThreadlocal.pool.execute (new RunNable () {Override public void run () {new dao (). call () ؛}}) ؛ }} class dao {public void call () {system.out.println ("dao:" + customThreadLocal.Threadlocal.get ()) ؛ }}نتيجة تشغيل الكود أعلاه غير صحيحة ، والإخراج كما يلي:
DAO: APE World 99
DAO: APE World 99
DAO: APE World 99
DAO: APE World 99
DAO: APE World 99
DAO: APE World 99
DAO: APE World 99
DAO: APE World 99
DAO: APE World 99
DAO: APE World 99
DAO: APE World 99
DAO: APE World 99
DAO: APE World 99
يجب أن يكون الصحيح من 1 إلى 100. بسبب إعادة استخدام مؤشر الترابط ، سيتم استبدال القيمة فقط إذا تم استبدالها.
بعد ذلك ، استخدم Transmittable-Thread-Local لتحويل الكود الإشكالي وإضافة تبعية Maven للرواية المحمولة:
<Rependency> <roupeD> com.alibaba </rougiD> <StifactId> transmittable-thread-local </stifactid> <الإصدار> 2.2.0 </version> </التبعية>
ما عليك سوى تعديل مكانين ، وقم بتعديل تجمع الخيوط واستبدال errantablethreadlocal:
transmittableThReadLocal ثابت <string> threadlocal = جديد transmittablethreadlocal <> ()
النتائج الصحيحة هي كما يلي:
DAO: APE World 85
داو: القرود والعالم 84
داو: القرود والعالم 86
داو: القرود والعالم 87
داو: القرود والعالم 88
DAO: APE World 90
داو: القرود والعالم 89
DAO: APE World 91
DAO: APE World 93
DAO: APE World 92
DAO: APE World 94
DAO: APE World 95
DAO: APE World 97
DAO: APE World 96
DAO: APE World 98
DAO: APE World 99
في هذه المرحلة ، يمكننا حل ناقل بيانات Threadlocal في تجمعات مؤشرات الترابط بشكل مثالي. القراء الأعزاء في حيرة مرة أخرى. العنوان لا يتعلق بكيفية حل هذه المشكلة في Spring Cloud. لقد وجدت أيضًا هذه المشكلة في زول. تم إخبار الحل للجميع. أما بالنسبة لكيفية حل هذه المشكلة في زول ، يحتاج الجميع إلى التفكير في الأمر بنفسك. سوف أشاركها معك إذا كان لديك وقت لاحق.
ما سبق هو كل محتوى هذه المقالة. آمل أن يكون ذلك مفيدًا لتعلم الجميع وآمل أن يدعم الجميع wulin.com أكثر.