Na Spring Cloud, usamos o Hystrix para implementar disjuntores. Em Zuul, os semáforos são usados por padrão (o Hystrix é rosqueado por padrão). Podemos usar o isolamento do encadeamento através da configuração.
Ao usar o isolamento do thread, há um problema que deve ser resolvido, ou seja, em alguns cenários de negócios, os dados são passados em threads através do ThreadLocal. É bom usar semáforos e vem da solicitação, mas o processo subsequente é sobre um thread.
Quando o modo de isolamento é rosqueado, o Hystrix colocará a solicitação no pool de threads Hystrix para execução. Neste momento, uma solicitação se tornará um thread B, e o Threadlocal desaparecerá inevitavelmente.
Vamos simular esse processo através de uma coluna simples:
classe pública 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 (). Call ();}}). Start (); }} classe Service {public void Call () {System.out.println ("Service:" + Thread.currentThread (). getName ()); System.out.println ("Service:" + CustomThreadLocal.threadlocal.get ()); novo dao (). Call (); }} classe Dao {public void Call () { System.out.println("============================================================================================== ================================================================================================================== ================================================================================================================== =================================================================================================================== CustomThreadlocal.threadlocal.get ());Definimos um threadlocal na classe principal para passar dados, então um thread é criado e o método de chamada no serviço é chamado no encadeamento, e um valor é definido no Threadlocal, e o valor no Threadlocal é obtido no serviço e, em seguida, o método de chamada no DAO é chamado, que também é obtido no Threadlocal. Vamos executá -lo para ver o efeito:
Serviço: Thread-0
Serviço: Macacos e Céu
=====================================
DAO: Thread-0
DAO: O mundo do mundo
Você pode ver que todo o processo é executado no mesmo thread e o valor no Threadlocal foi obtido corretamente. Não há problema nessa situação.
Em seguida, transformamos o programa, executamos a troca de threads e chamamos a chamada no DAO para reiniciar uma execução de threads:
classe pública 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 (). Call ();}}). Start (); }} classe Service {public void Call () {System.out.println ("Service:" + Thread.currentThread (). getName ()); System.out.println ("Service:" + CustomThreadLocal.threadlocal.get ()); // new Dao (). Call (); novo thread (novo runnable () {@Override public void run () {new Dao (). Call ();}}). start (); }} classe Dao {public void Call () { System.out.println ("========================================================================================================================================================================== ====================================================================================================== Depois ============================================================================================================================ Depois ====================================================================================================== Depois System.out.println ("Dao:" + Thread.currentThread (). GetName ());Corra novamente para ver o efeito:
Serviço: Thread-0
Serviço: Macacos e Céu
=====================================
DAO: Thread-1
DAO: NULL
Você pode ver que essa solicitação foi concluída por dois threads. Você ainda pode obter o valor do Threadlocal no serviço. Você não pode obtê -lo no DAO porque o thread foi trocado. Esse é o problema de que os dados do Threadlocal serão perdidos no início.
Então, como resolver esse problema é realmente muito simples, você só precisa alterar uma linha de código:
static threadlocal <string> threadlocal = new héritableThreadLocal <> ();
Altere o ThreadLocal para o HeritableThreadLocal, vamos dar uma olhada no efeito após a transformação:
Serviço: Thread-0
Serviço: Macacos e Céu
=====================================
DAO: Thread-1
DAO: O mundo do mundo
O valor pode ser obtido normalmente. O HeritableThreadLocal é causado pela resolução do problema que o ThreadLocal não pode obter o valor devido a esta comutação de encadeamento.
Para entender o princípio do herdableThreadLocal, devemos primeiro entender o princípio do Threadlocal. Vamos apresentar brevemente o princípio do Threadlocal:
Cada thread possui uma propriedade ThreadLocals do tipo Threadlocalmap. A classe Threadlocalmap é equivalente a um mapa. A chave é a própria Threadlocal e o valor é o valor que definimos.
public class Thread implementa runnable {threadlocal.threadlocalmap threadlocals = null;} Quando passamos por threadlocal.set ("Macacos e mundo");, colocamos um par de valores-chave na propriedade Threadlocals neste thread. A chave é o thread atual e o valor é o valor que você define.
public void set (valor t) {thread t = thread.currentThread (); Threadlocalmap map = getMap (t); if (map! = null) map.set (this, valor); else createMap (t, valor);} Quando usamos o método Threadlocal.get (), obtemos o valor definido por este thread com base no encadeamento atual como a chave.
public t get () {thread t = thread.currentThread (); Threadlocalmap map = getMap (t); if (map! = null) {threadlocalmap.entry e = map.getEntry (this); if (e! = null) {@suppresswarnings ("desmarcado") t resultado = (t) e.value; resultado de retorno; }} retornar setInitialValue ();}Através da introdução acima, podemos entender que o ThreadLocal pode passar dados usando o thread.currentThread () para obtê -los, ou seja, desde que esteja no mesmo thread, o valor definido na frente pode ser obtido.
Se a próxima operação recria um thread após o threadlocal definir o valor, e Thread.currentThread () foi alterado neste momento, você definitivamente não receberá o valor definido antes. Para reprodução específica de problemas, consulte meu código acima.
Então, por que o heritableThreadLocal está ok?
A classe HILEITABLETHREADLOCAL herda os métodos Threadlocal e Reescrito 3. Ao criar um novo encadeamento do encadeamento no encadeamento atual, essas variáveis de encadeamento serão passadas do encadeamento atual para a nova instância do thread.
Classe pública HeritableThreadLocal <T> estende o ThreadLocal <T> { /** * calcula o valor inicial da criança para esse thread-local herdável * variável em função do valor do pai no momento em que o encadeamento da criança * é criado. Este método é chamado de dentro do thread pai * antes do início da criança. * <p> * Este método apenas retorna seu argumento de entrada e deve ser substituído * se um comportamento diferente for desejado. * * @param parentValue o valor do thread pai * @return o valor inicial do thread da criança */ Protected T ChildValue (T ParentValue) {return parentValue; } /*** Obtenha o mapa associado a um threadlocal. * * @param t O thread atual */ threadlocalmap getMap (thread t) {return t.IritableThreadLocals; } /*** Crie o mapa associado a um Threadlocal. * * @param t O tópico atual * @param FirstValue Valor para a entrada inicial da tabela. */ void CreateMap (Thread T, T FirstValue) {t.IritableThreadLocals = new ThreadLocalMap (this, FirstValue); }}Através do código acima, podemos ver que a reescrita do HeritableThReadLocal dos três métodos ChildValue, GetMap e CreateMap. Quando definimos o valor nele, o valor é salvo em héritableThreadLocals, em vez dos threadlocals anteriores.
O ponto principal está aqui. Por que podemos obter o valor no Threadlocal no thread anterior ao criar um novo pool de threads? O motivo é que, quando um novo thread é criado, os hereitableThreadLocals do thread anterior serão atribuídos aos herança de locais do novo thread, que realizam a transmissão de dados dessa maneira.
O código -fonte está inicialmente no método do thread init, como segue:
if (parent.IritableThreadLocals! = null) this.IritableThreadLocals = shaderLocal.createIritedMap (parent.IritableThreadLocals);
CreateIritedMap da seguinte maneira:
estático threadlocalmap createIritedmap (threadlocalmap parentmap) {return threadLocalMap (parentmap); }Código de atribuição:
Private ThreadLocalMap (ThreadLocalMap parentMap) {Entry [] parentTable = parentmap.table; int len = parentTable.length; defesa (LEN); tabela = nova entrada [len]; for (int j = 0; j <len; j ++) {entrada e = parentTable [j]; if (e! = null) {@suppresswarnings ("desmarcado") threadlocal <ject> key = (threadlocal <ject>) e.get (); if (key! = null) {objeto value = key.childValue (e.value); Entrada c = nova entrada (chave, valor); int h = key.threadlocalhashcode & (len - 1); while (tabela [h]! = null) h = nextIndex (h, len); tabela [h] = c; tamanho ++; }}}}Até este ponto, por meio do HeritableThreadLocals, podemos passar o valor no encadeamento local do filho quando o thread pai cria o thread infantil. Esse recurso pode atender à maioria das necessidades, mas há outro problema sério de que, se for reutilização de threads, haverá problemas. Por exemplo, se for reutilização de threads, ele usará o HeritableThReadLocals no pool de threads para passar valores, porque o HereitableThreadLocals passará apenas valores ao criar um novo thread. Threadreuse não fará esta operação. Portanto, para resolver esse problema, você deve estender a classe Thread sozinho para implementar essa função.
Não se esqueça de que estamos fazendo Java. O mundo de código aberto tem tudo o que você precisa. Abaixo, recomendo uma biblioteca Java que foi implementada, que é o local transmissível de código aberto do Alibaba.
Endereço do GitHub: https://github.com/alibaba/transmittable-thread-local
A principal função é resolver o problema da entrega do valor do Threadlocal ao usar pools de threads e outros componentes que cache os encadeamentos e resolver o problema da entrega de contexto durante a execução assíncrona.
A classe INERITABLETHREADLOCAL DA JDK pode concluir a passagem do valor do thread pai para o thread filho. No entanto, para o caso em que o pool de threads é usado e outros componentes que cache threads, o encadeamento é criado pelo pool de threads e o thread é armazenado em cache e usado repetidamente; Neste momento, não faz sentido passar o valor Threadlocal do relacionamento entre pais e filhos. O que o aplicativo precisa é realmente passar no valor Threadlocal ao enviar a tarefa para o pool de threads para a execução da tarefa.
Os métodos de uso do local de fibra transmissível são divididos em três tipos: modificar o Runnable and Callable, modificar o pool de threads e o agente Java para modificar as classes de implementação do JDK Thread Pool
Em seguida, vamos demonstrar o método de modificação do pool de threads. Primeiro, vamos tomar um caso anormal, o código é o seguinte:
classe pública CustomThreadLocal {Static ThreadLocal <String> threadLocal = new InheritableThreadLocal <> (); Pool estático de executores Service = executores.newfixedthreadpool (2); public static void main (string [] args) {for (int i = 0; i <100; i ++) {int j = i; pool.execute (novo thread (novo runnable () {@Override public void run () {CustomThreadLocal.ThreadLocal.Set ("APE World"+J); new Service (). Call ();}})); }}} classe Service {public void Call () {CustomThreadLocal.pool.execute (new Runnable () {@Override public void run () {new Dao (). Call ();}}); }} classe Dao {public void Call () {System.out.println ("Dao:" + CustomThreadLocal.threadlocal.get ()); }}O resultado da execução do código acima está incorreto e a saída é a seguinte:
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
O correto deve ser de 1 a 100. Devido à reutilização do encadeamento, o valor será substituído apenas se for substituído.
Em seguida, use o local transmissível para transformar o código problemático e adicione a dependência do Maven do local transmissível:
<Depencency> <voundid> com.alibaba </frugiD> <stifactId> transmissionable-thread-cal </stifactId> <versão> 2.2.0 </versão </dependency>
Basta modificar 2 lugares, modificar o pool de threads e substituir o HeRitableThreadLocal:
estático transmitableThreadLocal <string> threadlocal = new transmittableThreadLocal <> (); estático executorService pool = ttlexecutores.getttlexecutorService (executores.newfixedThreadpool (2));
Os resultados corretos são os seguintes:
DAO: APE World 85
DAO: Macacos e mundo 84
DAO: Macacos e mundo 86
DAO: Macacos e mundo 87
DAO: Macacos e mundo 88
DAO: APE World 90
DAO: Macacos e mundo 89
DAO: APE World 91
DAO: Macaco Mundo 93
DAO: APE World 92
DAO: APE World 94
DAO: APE World 95
DAO: Macaco Mundo 97
DAO: APE World 96
DAO: APE World 98
DAO: APE World 99
Neste ponto, podemos resolver perfeitamente a transmissão de dados de threadlocal em pools de threads. Caros leitores estão intrigados novamente. O título não é sobre como resolver esse problema na nuvem da primavera. Eu também encontrei esse problema em Zuul. A solução foi contada a todos. Quanto a como resolver esse problema em Zuul, todos precisam pensar sobre isso você mesmo. Vou compartilhar com você se você tiver tempo mais tarde.
O exposto acima é todo o conteúdo deste artigo. Espero que seja útil para o aprendizado de todos e espero que todos apoiem mais o wulin.com.