ใน Spring Cloud เราใช้ Hystrix เพื่อใช้เบรกเกอร์วงจร ใน zuul semaphores ถูกใช้โดยค่าเริ่มต้น (Hystrix ถูกเกลียวตามค่าเริ่มต้น) เราสามารถใช้การแยกเธรดผ่านการกำหนดค่า
เมื่อใช้การแยกเธรดมีปัญหาที่ต้องแก้ไขนั่นคือในสถานการณ์ธุรกิจบางอย่างข้อมูลจะถูกส่งผ่านในเธรดผ่าน ThreadLocal เป็นเรื่องปกติที่จะใช้ Semaphores และมาจากคำขอ แต่กระบวนการที่ตามมานั้นทั้งหมดประมาณหนึ่งเธรด
เมื่อโหมดการแยกถูกเกลียว Hystrix จะนำคำขอลงในพูลเธรด Hystrix สำหรับการดำเนินการ ในเวลานี้คำขอจะกลายเป็นเธรด B และ ThreadLocal จะหายไปอย่างหลีกเลี่ยงไม่ได้
มาจำลองกระบวนการนี้ผ่านคอลัมน์ง่าย ๆ :
คลาสสาธารณะ CustomThreadLocal {Static ThreadLocal <String> threadLocal = new ThreadLocal <> (); โมฆะสาธารณะคงที่หลัก (สตริง [] args) {เธรดใหม่ (ใหม่ runnable () {@Override โมฆะสาธารณะเรียกใช้ () {customThreadLocal.threadLocal.set ("apes"); บริการใหม่ (). call ();}}). start (); }} บริการคลาส {public void call () {system.out.println ("บริการ:" + thread.currentthread (). getName ()); System.out.println ("บริการ:" + customthreadlocal.threadlocal.get ()); ใหม่ dao (). call (); }} คลาส dao {public void call () { System.out.println ("===================================================================================================================== - - - customthreadlocal.threadlocal.get ());เรากำหนด ThreadLocal ในคลาสหลักเพื่อส่งผ่านข้อมูลจากนั้นสร้างเธรดและวิธีการโทรในบริการที่เรียกว่าในเธรดและค่าถูกตั้งค่าใน ThreadLocal และค่าใน ThreadLocal ได้รับในบริการและจากนั้นวิธีการโทรใน DAO จะถูกเรียกซึ่งยังได้รับใน ThreadLocal มาเรียกใช้เพื่อดูเอฟเฟกต์:
บริการ: เธรด -0
บริการ: Apes and Heaven
-
Dao: Thread-0
Dao: โลกของโลก
คุณจะเห็นว่ากระบวนการทั้งหมดดำเนินการในเธรดเดียวกันและค่าใน ThreadLocal ได้รับอย่างถูกต้อง ไม่มีปัญหาในสถานการณ์นี้
ต่อไปเราแปลงโปรแกรมดำเนินการสลับเธรดและโทรสายใน DAO เพื่อรีสตาร์ทเธรดการดำเนินการ:
คลาสสาธารณะ CustomThreadLocal {Static ThreadLocal <String> threadLocal = new ThreadLocal <> (); โมฆะสาธารณะคงที่หลัก (สตริง [] args) {เธรดใหม่ (ใหม่ runnable () {@Override โมฆะสาธารณะเรียกใช้ () {customThreadLocal.threadLocal.set ("apes"); บริการใหม่ (). call ();}}). start (); }} บริการคลาส {public void call () {system.out.println ("บริการ:" + thread.currentthread (). getName ()); System.out.println ("บริการ:" + customthreadlocal.threadlocal.get ()); // ใหม่ dao (). call (); เธรดใหม่ (ใหม่ runnable () {@Override โมฆะสาธารณะ run () {new dao (). call ();}}) เริ่มต้น (); }} คลาส dao {public void call () { System.out.println ("======================================================================================== - - - System.out.println ("dao:" + thread.currentthread (). getName ());วิ่งอีกครั้งเพื่อดูเอฟเฟกต์:
บริการ: เธรด -0
บริการ: Apes and Heaven
-
Dao: Thread-1
dao: null
คุณจะเห็นว่าคำขอนี้เสร็จสมบูรณ์ด้วยสองเธรด คุณยังสามารถรับค่าของ ThreadLocal ในบริการ คุณไม่สามารถรับมันได้ใน DAO เพราะมีการสลับเธรดแล้ว นี่คือปัญหาที่ข้อมูลของ ThreadLocal จะหายไปในตอนต้น
ดังนั้นวิธีการแก้ปัญหานี้นั้นง่ายมากคุณต้องเปลี่ยนรหัสบรรทัดเดียวเท่านั้น:
Static ThreadLocal <String> threadLocal = new MandleItableThreadLocal <> ();
เปลี่ยน ThreadLocal เป็นมรดกที่สืบทอดมาจาก LOCAL ลองมาดูเอฟเฟกต์หลังจากการแปลง:
บริการ: เธรด -0
บริการ: Apes and Heaven
-
Dao: Thread-1
Dao: โลกของโลก
ค่าสามารถรับได้ตามปกติ MandleItableThreadLocal เกิดจากการแก้ปัญหาที่ ThreadLocal ไม่สามารถรับค่าได้เนื่องจากการสลับเธรดนี้
เพื่อให้เข้าใจถึงหลักการของการสืบทอดตำแหน่งที่สืบทอดมาก่อนเราต้องเข้าใจหลักการของ ThreadLocal ก่อน มาแนะนำหลักการของ Threadlocal สั้น ๆ :
แต่ละเธรดมีคุณสมบัติ ThreadLocals ของประเภท ThreadLocalMap คลาส ThreadLocalMap เทียบเท่ากับแผนที่ คีย์คือ ThreadLocal เองและค่าคือค่าที่เราตั้งไว้
เธรดคลาสสาธารณะใช้งาน runnable {threadlocal.threadLocalMap threadLocals = null;} เมื่อเราผ่าน Threadlocal.set ("Apes and World"); เราใส่คู่คีย์-ค่าในคุณสมบัติ ThreadLocals ในเธรดนี้ คีย์คือเธรดปัจจุบันและค่าคือค่าที่คุณตั้งไว้
ชุดโมฆะสาธารณะ (ค่า t) {เธรด t = เธรด currentthread (); threadlocalMap map = getMap (t); if (map! = null) map.set (นี้ค่า); else createMap (t, value);} เมื่อเราใช้เมธอด threadlocal.get () เราจะได้รับค่าที่กำหนดโดยเธรดนี้ตามเธรดปัจจุบันเป็นคีย์
สาธารณะ t get () {เธรด t = thread.currentthread (); threadlocalMap map = getMap (t); if (map! = null) {threadlocalmap.entry e = map.getEntry (นี่); if (e! = null) {@SuppressWarnings ("ไม่ถูกตรวจสอบ") t result = (t) e.value; ผลการกลับมา; }} return setInitialValue ();}ผ่านการแนะนำข้างต้นเราสามารถเข้าใจได้ว่า ThreadLocal สามารถส่งผ่านข้อมูลได้โดยใช้ Thread.currentThread () เพื่อรับมันนั่นคือตราบใดที่มันอยู่ในเธรดเดียวกันสามารถรับค่าที่ตั้งไว้ด้านหน้าได้
หากการดำเนินการถัดไปสร้างเธรดหลังจาก ThreadLocal ได้ตั้งค่าและ thread.currentthread () มีการเปลี่ยนแปลงในเวลานี้คุณจะไม่ได้รับค่าที่คุณตั้งไว้ก่อน สำหรับการทำซ้ำปัญหาเฉพาะโปรดดูรหัสของฉันด้านบน
แล้วทำไมมรดกได้รับการตรวจสอบ
คลาส MandleItableThreadLocal สืบทอด ThreadLocal และเขียนใหม่ 3 วิธี เมื่อสร้างเธรดอินสแตนซ์เธรดใหม่บนเธรดปัจจุบันตัวแปรเธรดเหล่านี้จะถูกส่งผ่านจากเธรดปัจจุบันไปยังอินสแตนซ์เธรดใหม่
คลาสสาธารณะที่ได้รับมรดก rementableThreadLocal <t> ขยาย ThreadLocal <t> { /** * คำนวณค่าเริ่มต้นของเด็กสำหรับตัวแปรเธรดท้องถิ่นที่สืบทอดนี้ * เป็นฟังก์ชันของค่าของผู้ปกครองในเวลาที่เธรดลูก * ถูกสร้างขึ้น วิธีนี้เรียกจากภายในเธรดพ่อแม่ * ก่อนที่เด็กจะเริ่ม * <p> * วิธีนี้เพียงส่งคืนอาร์กิวเมนต์อินพุตและควรถูกแทนที่ * หากต้องการพฤติกรรมที่แตกต่างกัน * * @param parentValue ค่าเธรดพาเรนต์ * @return ค่าเริ่มต้นของเธรดลูก */ การป้องกัน t childValue (t parentValue) {ส่งคืน parentValue; } /*** รับแผนที่ที่เกี่ยวข้องกับ ThreadLocal * * @param t เธรดปัจจุบัน */ ThreadLocalMap getMap (เธรด t) {return t.inherItableThreatLocals; } /*** สร้างแผนที่ที่เกี่ยวข้องกับ ThreadLocal * * @param t เธรดปัจจุบัน * @param ค่า FirstValue สำหรับรายการเริ่มต้นของตาราง */ void createMap (เธรด t, t FirstValue) {T.InherItableThreadLocals = new ThreadLocalMap (นี่, FirstValue); -ผ่านรหัสข้างต้นเราจะเห็นว่ามรดกที่ได้รับการเขียนใหม่ใหม่ทั้งสามวิธี ChildValue, GetMap และ CreateMap เมื่อเราตั้งค่าไว้ในนั้นค่าจะถูกบันทึกไว้ในมรดกที่สืบทอดมาใช้แทน threadlocals ก่อนหน้า
ประเด็นสำคัญคือที่นี่ ทำไมเราถึงได้รับค่าใน ThreadLocal ในเธรดก่อนหน้าเมื่อสร้างพูลเธรดใหม่ เหตุผลก็คือเมื่อมีการสร้างเธรดใหม่มรดกที่สืบทอดมาจากเธรดก่อนหน้านี้จะถูกกำหนดให้กับมรดกที่สืบทอดมาจากเธรดใหม่ซึ่งตระหนักถึงการส่งข้อมูลด้วยวิธีนี้
ซอร์สโค้ดเริ่มแรกในเมธอด init เธรดดังต่อไปนี้:
if (parent.inherItableThreadLocals! = null) this.inherItableThreadLocals = threadlocal.createinheritedMap (parent.inheritableThreatHreatLocals);
CreateInheritedMap ดังนี้:
Static ThreadLocalMap createInheritedMap (ThreadLocalMap parentmap) {ส่งคืน 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 ("ไม่ได้ตรวจสอบ") ThreadLocal <jobch> key = (threadLocal <Ojrop>) e.get (); if (key! = null) {value object = key.childValue (e.value); รายการ c = รายการใหม่ (คีย์, ค่า); int h = key.threadLocalHashCode & (len - 1); ในขณะที่ (ตาราง [h]! = null) h = nextindex (h, len); ตาราง [h] = c; ขนาด ++; -จนถึงจุดนี้ผ่านมรดกที่สืบทอดมาจากเราสามารถส่งผ่านค่าในท้องถิ่นไปยังเธรดลูกเมื่อเธรดพาเรนต์สร้างเธรดลูก คุณลักษณะนี้สามารถตอบสนองความต้องการได้มากที่สุด แต่มีปัญหาร้ายแรงอีกอย่างหนึ่งที่ถ้าเป็นการใช้ซ้ำเธรดจะมีปัญหา ตัวอย่างเช่นหากเป็นการใช้ซ้ำเธรดมันจะใช้ MandleItableThreadLocals ในพูลเธรดเพื่อส่งผ่านค่าเนื่องจาก MandleItableThreadLocals จะผ่านค่าเมื่อสร้างเธรดใหม่ Threadreuse จะไม่ดำเนินการนี้ ดังนั้นในการแก้ปัญหานี้คุณต้องขยายคลาสเธรดด้วยตัวเองเพื่อใช้ฟังก์ชั่นนี้
อย่าลืมว่าเรากำลังทำ Java โลกโอเพ่นซอร์สมีทุกสิ่งที่คุณต้องการ ด้านล่างนี้ฉันขอแนะนำไลบรารี Java ที่ถูกนำไปใช้ซึ่งเป็นโอเพ่นซอร์สของ Alibaba
ที่อยู่ GitHub: https://github.com/alibaba/transmittable-thread-local
ฟังก์ชั่นหลักคือการแก้ปัญหาการส่งมอบค่าเธรดเมื่อใช้พูลเธรดและส่วนประกอบอื่น ๆ ที่แคชเธรดและแก้ปัญหาการส่งมอบบริบทในระหว่างการดำเนินการแบบอะซิงโครนัส
คลาสที่สืบทอดมาของ JDK ของ JDK สามารถกรอกค่าผ่านของเธรดพาเรนต์ไปยังเธรดลูก อย่างไรก็ตามสำหรับกรณีที่มีการใช้พูลเธรดและส่วนประกอบอื่น ๆ ที่แคชเธรดเธรดจะถูกสร้างขึ้นโดยพูลเธรดและเธรดจะถูกแคชและใช้ซ้ำ ๆ ในเวลานี้มันไม่มีความหมายที่จะผ่านค่าเกลียวของความสัมพันธ์เธรดลูก-ลูก สิ่งที่แอปพลิเคชันต้องการคือการส่งผ่านค่าเธรดเมื่อส่งงานไปยังพูลเธรดไปยังการดำเนินการงาน
วิธีการใช้งานแบบ transmittable-thread-local แบ่งออกเป็นสามประเภท: แก้ไข runnable และ callable, แก้ไขพูลเธรดและตัวแทน Java เพื่อแก้ไขคลาสการใช้งานพูลเธรด JDK
ถัดไปให้แสดงวิธีการปรับเปลี่ยนของพูลเธรด ก่อนอื่นให้ใช้กรณีที่ผิดปกติรหัสมีดังนี้:
คลาสสาธารณะ CustomThreadLocal {Static ThreadLocal <String> threadLocal = new MandleItableThreadLocal <> (); ExecutorService Pool = Executors.NewFixedThreadPool (2); โมฆะคงที่สาธารณะหลัก (สตริง [] args) {สำหรับ (int i = 0; i <100; i ++) {int j = i; pool.execute (เธรดใหม่ (ใหม่ runnable () {@Override public void run () {customthreadlocal.threadlocal.set ("Ape World"+J); บริการใหม่ (). call ();}})); }}} บริการคลาส {public void call () {customthreadlocal.pool.execute (ใหม่ runnable () {@Override โมฆะสาธารณะเรียกใช้ () {new dao (). call ();}}); }} คลาส 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 ของ transmittable-thread-local:
<Effercy> <mergiED> com.alibaba </groupId> <ratifactId> transmittable-Thread-local </artifactid> <version> 2.2.0 </version>
เพียงแค่แก้ไข 2 สถานที่แก้ไขพูลเธรดและแทนที่ MandleItableThreadLocal:
Static TransmittableThreadLocal <String> threadLocal = ใหม่ TransMittAbletHreadLocal <> (); ExecutorService Pool = ttlexecutors.getttlexecutorservice (Executors.NewFixedThreadPool (2));
ผลลัพธ์ที่ถูกต้องมีดังนี้:
Dao: Ape World 85
Dao: Apes and World 84
Dao: Apes and World 86
Dao: Apes and World 87
Dao: Apes and World 88
Dao: Ape World 90
Dao: Apes and World 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 ในพูลเธรดได้อย่างสมบูรณ์แบบ ผู้อ่านที่รักทำให้งงอีกครั้ง ชื่อเรื่องไม่เกี่ยวกับวิธีการแก้ปัญหานี้ในฤดูใบไม้ผลิคลาวด์ ฉันยังพบปัญหานี้ใน Zuul ทางออกได้รับการบอกเล่าให้ทุกคน สำหรับวิธีการแก้ปัญหานี้ใน Zuul ทุกคนต้องคิดด้วยตัวเอง ฉันจะแบ่งปันกับคุณถ้าคุณมีเวลาในภายหลัง
ข้างต้นเป็นเนื้อหาทั้งหมดของบทความนี้ ฉันหวังว่ามันจะเป็นประโยชน์ต่อการเรียนรู้ของทุกคนและฉันหวังว่าทุกคนจะสนับสนุน wulin.com มากขึ้น