Dans Spring Cloud, nous utilisons Hystrix pour implémenter des disjoncteurs. Dans Zuul, les sémaphores sont utilisés par défaut (Hystrix est enfileux par défaut). Nous pouvons utiliser l'isolement du thread via la configuration.
Lorsque vous utilisez l'isolement des threads, il y a un problème qui doit être résolu, c'est-à-dire dans certains scénarios commerciaux, les données sont passées dans les threads via ThreadLocal. Il est bon d'utiliser des sémaphores, et il provient de la demande, mais le processus ultérieur est tout un thread.
Lorsque le mode d'isolement est fileté, Hystrix mettra la demande dans le pool de threads Hystrix pour l'exécution. À l'heure actuelle, une demande deviendra un thread B, et le threadlocal disparaîtra inévitablement.
Simulons ce processus via une colonne simple:
classe publique 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"); nouveau service (). Call ();}}). start (); }} class Service {public void call () {System.out.println ("service:" + thread.currentThread (). getName ()); System.out.println ("Service:" + CustomThreadLocal.ThreadLocal.get ()); new Dao (). Call (); }} classe dao {public void call () { System.out.println ("================================================================================================. =================================================================================================================================. =================================================================================================================================. ===================================================================================================================================. CustomThreadLocal.ThreadLocal.get ());Nous définissons un threadlocal dans la classe principale pour passer des données, puis un thread est créé, et la méthode d'appel dans le service est appelée dans le thread, et une valeur est définie dans ThreadLocal, et la valeur en threadlocal est obtenue dans le service, puis la méthode d'appel dans DAO est appelée, qui est également obtenue dans ThreadLocal. Exécutons-le pour voir l'effet:
Service: Thread-0
Service: singes et paradis
================================================.
DAO: Thread-0
Dao: le monde du monde
Vous pouvez voir que l'ensemble du processus est exécuté dans le même thread et que la valeur en threadlocal a été correctement obtenue. Il n'y a aucun problème dans cette situation.
Ensuite, nous transformons le programme, effectuons une commutation de thread et appelons l'appel dans DAO pour redémarrer une exécution de thread:
classe publique 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"); nouveau service (). Call ();}}). start (); }} class Service {public void call () {System.out.println ("service:" + thread.currentThread (). getName ()); System.out.println ("Service:" + CustomThreadLocal.ThreadLocal.get ()); // new Dao (). Call (); nouveau thread (new Runnable () {@Override public void run () {new Dao (). Call ();}}). start (); }} classe dao {public void call () { System.out.println("=================================================================================== ==========================================================================================================. ==========================================================================================================. ==========================================================================================================. System.out.println ("dao:" + thread.currentThread (). GetName ());Courez à nouveau pour voir l'effet:
Service: Thread-0
Service: singes et paradis
================================================.
DAO: Thread-1
Dao: null
Vous pouvez voir que cette demande a été remplie par deux threads. Vous pouvez toujours obtenir la valeur de ThreadLocal dans le service. Vous ne pouvez pas l'obtenir dans DAO car le thread a été commuté. C'est le problème que les données du threadlocal seront perdues au début.
Alors, comment résoudre ce problème est en fait très simple, il vous suffit de modifier une ligne de code:
static threadlocal <string> threadLocal = new héritagetHreadLocal <> ();
Modifier le threadlocal pour hériter de la lecture, jetons un coup d'œil à l'effet après la transformation:
Service: Thread-0
Service: singes et paradis
================================================.
DAO: Thread-1
Dao: le monde du monde
La valeur peut être obtenue normalement. Héritage en alentant la lecture est causé par la résolution du problème que ThreadLocal ne peut pas obtenir la valeur due à cette commutation de thread.
Pour comprendre le principe de héritage Présentez brièvement le principe du threadlocal:
Chaque thread a une propriété ThreadLocals de type ThreadLocalmap. La classe ThreadLocalmap est équivalente à une carte. La touche est elle-même ThreadLocal et la valeur est la valeur que nous définissons.
Le thread de classe publique implémente Runnable {ThreadLocal.ThreadLocalmap ThreadLocals = NULL;} Lorsque nous passons threadLocal.set ("singes et monde");, nous mettons une paire de valeurs de clé dans la propriété ThreadLocals dans ce fil. La clé est le thread actuel et la valeur est la valeur que vous définissez.
public void set (t valeur) {thread t = thread.currentThread (); ThreadLocalmap map = getmap (t); if (map! = null) map.set (this, valeur); else CreateMap (t, valeur);} Lorsque nous utilisons la méthode threadLocal.get (), nous obtenons la valeur définie par ce thread en fonction du thread actuel comme touche.
public t get () {thread t = thread.currentThread (); ThreadLocalmap map = getmap (t); if (map! = null) {threadLocalmap.entry e = map.getEntry (this); if (e! = null) {@SuppressWarnings ("Unchecked") t result = (t) e.Value; Résultat de retour; }} return setInitialValue ();}Grâce à l'introduction ci-dessus, nous pouvons comprendre que ThreadLocal peut transmettre des données à l'aide de thread.currentThread () pour l'obtenir, c'est-à-dire tant qu'il est dans le même thread, la valeur définie à l'avant peut être obtenue.
Si l'opération suivante recrée un thread après que le threadLocal ait défini la valeur et que Thread.CurrentThread () a changé pour le moment, vous n'obtiendrez certainement pas la valeur que vous définissez avant. Pour une reproduction spécifique de problèmes, veuillez vous référer à mon code ci-dessus.
Alors pourquoi hériter-t-il est-il ok?
La classe inhérente de la réadlation hérite des méthodes de 3 méthodes ThreadLocal et réécrites. Lors de la création d'un nouveau thread d'instance de thread sur le thread actuel, ces variables de thread seront passées du thread actuel vers l'instance de thread nouvelle.
La classe publique Héréation HéritableThreadLocal <T> étend ThreadLocal <T> {/ ** * calcule la valeur initiale de l'enfant pour cette variable héritable de thread-local * en fonction de la valeur du parent au moment de la création du thread de l'enfant *. Cette méthode est appelée à partir du fil du parent * avant le démarrage de l'enfant. * <p> * Cette méthode renvoie simplement son argument d'entrée et doit être remplacé * Si un comportement différent est souhaité. * * @param parentValue La valeur du thread parent * @return la valeur initiale du thread de l'enfant * / Protected t ChildValue (t parentValue) {return parentValue; } / ** * Obtenez la carte associée à un threadlocal. * * @param t Le thread actuel * / threadLocalmap getmap (thread t) {return t.InheritableThreadLocals; } / ** * Créez la carte associée à un threadlocal. * * @param t Le thread actuel * @Param FirstValue Valeur pour l'entrée initiale de la table. * / void CreateMap (thread t, t FirstValue) {t.InheritableThreadLocals = new ThreadLocalmap (this, FirstValue); }}Grâce au code ci-dessus, nous pouvons voir que l'héritage a réécrit les trois méthodes ChildValue, GetMap et CreateMap. Lorsque nous y définissons la valeur, la valeur est enregistrée dans HéritableThreadLocals, au lieu des ThreadLocals précédents.
Le point clé est ici. Pourquoi pouvons-nous obtenir la valeur dans ThreadLocal dans le thread précédent lors de la création d'un nouveau pool de threads? La raison en est que lorsqu'un nouveau thread est créé, le héritage de réadaptation du thread précédent sera attribué à l'héritage de réadaptation du nouveau thread, ce qui réalise la transmission de données de cette manière.
Le code source est initialement dans la méthode du thread init, comme suit:
if (parent.inheritableThreadLocals! = null) this.inheritableThreadLocals = threadLocal.CreateInheritedMap (parent.inheritableThreadLocals);
Créer lame héritée comme suit:
statique threadLocalmap createIhheritedMap (threadLocalmap parentMap) {return new ThreadLocalmap (parentMap); }Code d'attribution:
TikeLocalmap privé (threadLocalmap parentMap) {entrée [] parentTable = parentMap.Table; int len = parentTable.length; SetThreshold (len); table = nouvelle entrée [len]; pour (int j = 0; j <len; j ++) {entrée e = parentTable [j]; if (e! = null) {@SuppressWarnings ("Unchecked") ThreadLocal <objet> key = (ThreadLocal <Bject>) e.get (); if (key! = null) {objet valeur = key.childValue (e.Value); Entrée C = nouvelle entrée (clé, valeur); int h = key.threadLocalHashCode & (len - 1); while (table [h]! = null) h = nextIndex (h, len); Tableau [H] = C; taille ++; }}}}Jusqu'à ce point, grâce à HéritableThreadLocals, nous pouvons transmettre la valeur en fil local au fil de l'enfant lorsque le thread parent crée le thread enfant. Cette fonctionnalité peut répondre à la plupart des besoins, mais il y a un autre problème grave que s'il s'agit de réutilisation de discussion, il y aura des problèmes. Par exemple, s'il s'agit de réutilisation de threads, il utilisera HéritableThreadLocals dans le pool de threads pour transmettre des valeurs, car Héréation ThreadReuse ne fera pas cette opération. Donc, pour résoudre ce problème, vous devez étendre vous-même la classe de threads pour implémenter cette fonction.
N'oubliez pas que nous faisons Java. Le monde open source a tout ce dont vous avez besoin. Ci-dessous, je recommande une bibliothèque Java qui a été mise en œuvre, qui est le local à transmission open source d'Alibaba.
Adresse github: https://github.com/alibaba/transmittable-thread-local
La fonction principale est de résoudre le problème de la livraison de valeur threadlocal lors de l'utilisation de pools de threads et d'autres composants qui cachent des threads et résolvent le problème de la livraison de contexte pendant l'exécution asynchrone.
La classe héritée de JDK peut compléter la valeur de la valeur du thread parent au thread enfant. Cependant, pour le cas où le pool de threads est utilisé et d'autres composants qui cachent des filetages, le fil est créé par le pool de threads et le fil est mis en cache et utilisé à plusieurs reprises; À l'heure actuelle, il n'a pas de sens de passer la valeur threadlocale de la relation parent-enfant. Ce dont l'application a besoin, c'est réellement passer la valeur threadlocal lors de la soumission de la tâche au pool de thread à l'exécution de la tâche.
Les méthodes d'utilisation de la local à thread transmittables sont divisées en trois types: modifiez le runnable et appelable, modifiez le pool de threads et l'agent Java pour modifier les classes d'implémentation de pool de threads JDK
Ensuite, démontrons la méthode de modification du pool de threads. Tout d'abord, prenons un cas anormal, le code est le suivant:
classe publique CustomThreadLocal {static threadLocal <string> threadLocal = new hheRitableThreadLocal <> (); Static ExecutorService pool = exécutors.newFixEdThreadpool (2); public static void main (String [] args) {for (int i = 0; i <100; i ++) {int j = i; pool.execute (nouveau thread (new Runnable () {@Override public void run () {CustomThreadLocal.ThreadLocal.set ("APE World" + J); nouveau 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:" + customthaRedLocal.threadlocal.get ()); }}Le résultat de l'exécution du code ci-dessus est incorrect et la sortie est la suivante:
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
Le bon doit être de 1 à 100. En raison de la réutilisation du thread, la valeur ne sera remplacée que si elle est remplacée.
Ensuite, utilisez la transmission-thread-local pour transformer le code problématique et ajouter la dépendance Maven de la transmission-thread-local:
<dependency> <proupId> com.alibaba </rompuprid> <lefactive> transmittable-thread-local </letefactid> <version> 2.2.0 </ version> </dependency>
Modifiez simplement 2 places, modifiez le pool de thread et remplacez l'héritage
STATIC transmitableThreadLocal <string> threadLocal = new transmitableThreAdLocal <> (); static ExecutorService pool = ttleXECUTORS.GetTtleXecutOrService (exécutors.newFixEdThreadPool (2));
Les résultats corrects sont les suivants:
DAO: APE World 85
DAO: APES et World 84
DAO: APES et World 86
DAO: APES et World 87
DAO: singes et monde 88
DAO: APE World 90
DAO: singes et monde 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
À ce stade, nous pouvons parfaitement résoudre la transmission de données threadlocal dans les pools de threads. Chers lecteurs sont à nouveau perplexes. Le titre ne concerne pas la façon de résoudre ce problème dans Spring Cloud. J'ai également trouvé ce problème à Zuul. La solution a été racontée à tout le monde. Quant à la façon de résoudre ce problème à Zuul, tout le monde doit y penser vous-même. Je le partagerai avec vous si vous avez du temps plus tard.
Ce qui précède est tout le contenu de cet article. J'espère que cela sera utile à l'apprentissage de tous et j'espère que tout le monde soutiendra davantage Wulin.com.