Spring Cloud에서는 Hystrix를 사용하여 회로 차단기를 구현합니다. Zuul에서 세마포어는 기본적으로 사용됩니다 (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 (). call ()}). start (); }} class service {public void call () {system.out.println ( "service :" + thread.currentthread (). getName ()); System.out.println ( "서비스 :" + CustomThreadlocal.threadlocal.get ()); new dao (). call (); }} class dao {public void call () { System.out.println ( "================================================================================================ ================================================================================================================================= ================================================================================================================================= ========================================================================================================================== CustomThreadLocal.threadlocal.get ());우리는 메인 클래스에서 스레드로 말을 정의하여 데이터를 전달한 다음 스레드가 생성되고 서비스의 호출 메소드가 스레드에서 호출되고 값이 STREBRECAL에서 설정되고 SuredLocal의 값이 서비스에서 얻어지고 DAO의 호출 메소드가 호출되어 ThreadLocal에서 얻습니다. 효과를보기 위해 실행합시다.
서비스 : 스레드 -0
봉사 : 유인원과 천국
==================================
DAO : 스레드 -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 (). call ()}). start (); }} class service {public void call () {system.out.println ( "service :" + thread.currentthread (). getName ()); System.out.println ( "서비스 :" + CustomThreadlocal.threadlocal.get ()); // new dao (). call (); 새 스레드 (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 ());효과를보기 위해 다시 실행하십시오.
서비스 : 스레드 -0
봉사 : 유인원과 천국
==================================
DAO : 스레드 -1
DAO : NULL
이 요청이 두 스레드로 완료되었음을 알 수 있습니다. 서비스에서 여전히 ThreadLocal의 가치를 얻을 수 있습니다. 스레드가 전환 되었기 때문에 DAO에서 얻을 수 없습니다. 이것은 ThreadLocal의 데이터가 처음에 손실 될 것이라는 문제입니다.
따라서이 문제를 해결하는 방법은 실제로 매우 간단합니다. 한 줄의 코드 만 변경하면됩니다.
정적 ThreadLocal <String> ThreadLocal = New InheritableThreadlocal <> ();
threadlocal을 inheritableThreadlocal로 변경하고 변환 후 효과를 살펴 보겠습니다.
서비스 : 스레드 -0
봉사 : 유인원과 천국
==================================
DAO : 스레드 -1
다오 : 세계의 세계
값은 정상적으로 얻을 수 있습니다. inheritableThreadlocal은 스레드 스위치로 인해 ThreadLocal이 값을 얻을 수없는 문제를 해결함으로써 발생합니다.
상인 관계가있는 원칙을 이해하려면 먼저 ThreadLocal의 원리를 이해해야합니다. ThreadLocal의 원리를 간단히 소개하겠습니다.
각 스레드에는 Type ThreadLocalMap의 ThreadLocals 속성이 있습니다. ThreadLocalMap 클래스는 맵과 같습니다. 핵심은 ThreadLocal 자체이며 값은 우리가 설정 한 값입니다.
공개 클래스 스레드 구현 실행 가능 {threadlocal.threadlocalmap rdulelocals = null;} 우리가 ThreadLocal.set ( "Apes and World")을 통과하면이 스레드의 ThreadLocals 속성에 키 값 쌍을 넣습니다. 핵심은 현재 스레드이고 값은 설정 한 값입니다.
public void set (t value) {Thread T = Thread.CurrentThread (); ThreadLocalMap map = getMap (t); if (map! = null) map.set (this, value); else createmap (t, value);} ThreadLocal.get () 메소드를 사용하면 현재 스레드를 기준 으로이 스레드에서 설정 한 값을 키로 얻습니다.
public t get () {Thread T = Thread.CurrentThread (); ThreadLocalMap map = getMap (t); if (map! = null) {ThreadLocalMap.entry e = map.getEntry (this); if (e! = null) {@suppresswarnings ( "확인되지 않은") t result = (t) e.value; 반환 결과; }} return setInitialValue ();}위의 소개를 통해 ThreadLocal은 Thread.CurrentThread ()를 사용하여 데이터를 전달하여 얻을 수 있음을 이해할 수 있습니다.
Threadlocal이 값을 설정 한 후 다음 작업이 스레드를 재현하고 현재로서는 스레드를 변경 한 경우 이전에 설정 한 값을 얻지 못할 것입니다. 특정 문제를 구체적으로 재생하려면 위의 내 코드를 참조하십시오.
그렇다면 왜 상인 관계가있는 이유는 무엇입니까?
상인방 클래스는 ThreadLocal 및 다시 작성된 3 가지 방법을 상속합니다. 현재 스레드에서 새 스레드 인스턴스 스레드를 만들 때이 스레드 변수는 현재 스레드에서 새 스레드 인스턴스로 전달됩니다.
공개 클래스 inheritableThreadlocal <t>는 threadlocal <t> { /** * 확장 된 스레드 * 스레드가 생성 될 때 부모의 값의 함수 로서이 상속 된 스레드-로컬 * 변수에 대한 자식의 초기 값을 계산합니다. 이 방법은 아이가 시작되기 전에 부모 * 실 내에서 호출됩니다. * <p> *이 메소드는 단지 입력 인수를 반환하며 다른 동작이 원하는 경우 상환되어야합니다. * * * @param parentValue 부모 스레드의 값 * @return Child Thread의 초기 값 */ Protected t ChildValue (t parentvalue) {return parentvalue; } /*** threadLocal과 관련된 맵을 가져옵니다. * * @param t 현재 스레드 */ ThreadLocalMap getMap (스레드 t) {return t.inHeritableThreadLocals; } /*** threadLocal과 관련된 맵을 만듭니다. * * @param t 현재 스레드 * @param firstValue 값 테이블의 초기 항목에 대한 값. */ void createmap (스레드 t, t firstValue) {t.inheritableThreadlocals = new ThreadLocalMap (this, firstValue); }}위의 코드를 통해 상인방이 세 가지 메소드 childvalue, getmap 및 createmap을 다시 작성했음을 알 수 있습니다. 값을 설정하면 값이 이전 ThreadLocals 대신 InheritableThreadlocals에 저장됩니다.
핵심 요점은 여기에 있습니다. 새 스레드 풀을 만들 때 이전 스레드에서 ThreadLocal에서 값을 얻을 수있는 이유는 무엇입니까? 그 이유는 새 스레드가 만들어지면 이전 스레드의 heritableThreadlocals가 새 스레드의 henheritableThreadLocals에 할당되어 이러한 방식으로 데이터 전송을 실현하기 때문입니다.
소스 코드는 처음에 다음과 같이 스레드 init 메소드에 있습니다.
if (parent.inheritableThreadlocals! = null) this.inHeritableThreadLocals = threadLocal.createInheritedMap (parent.inheritableThreadlocals);
다음과 같이 createInHeritedMap :
static roodlocalmap createInHeritedMap (ridureLocalMap parentmap) {return new ThreadLocalMap (parentmap); }할당 코드 :
개인 ThreadLocalMap (strooklocalmap parentmap) {entry [] parenttable = parentmap.table; int len = parenttable.length; setThreshold (Len); 테이블 = 새 항목 [렌]; for (int j = 0; if (e! = null) {@suppresswarnings ( "선택 취소") ThreadLocal <botor> key = (ThreadLocal <botor>) e.get (); if (key! = null) {object value = key.childValue (e.value); 입력 C = 새 항목 (키, 값); int h = key.threadlocalhashcode & (len -1); while (표 [h]! = null) h = NextIndex (h, len); 표 [H] = C; 크기 ++; }}}}이 시점까지, 상인 류드 레드 로컬을 통해, 부모 스레드가 자식 스레드를 생성 할 때 아동 스레드로 로컬 값을 전달할 수 있습니다. 이 기능은 대부분의 요구를 충족시킬 수 있지만 스레드 재사용이라면 문제가있을 것이라는 또 다른 심각한 문제가 있습니다. 예를 들어, 스레드 재사용 인 경우 스레드 풀에서 inheritableThreadlocals를 사용하여 값을 전달합니다. inheritableThreadlocals는 새 스레드를 생성 할 때만 값을 전달하기 때문입니다. ThreadReuse는이 작업을 수행하지 않습니다. 따라서이 문제를 해결하려면이 기능을 구현하려면 스레드 클래스를 직접 확장해야합니다.
우리가 Java를하고 있다는 것을 잊지 마십시오. 오픈 소스 세계에는 필요한 모든 것이 있습니다. 아래에서는 Alibaba의 오픈 소스 전송 가능한 스레드 로컬 인 Java 라이브러리를 권장합니다.
github 주소 : https://github.com/alibaba/transmittable-thread-local
주요 기능은 스레드 풀 및 스레드를 캐시하는 다른 구성 요소를 사용할 때 스레드 로컬 값 전달 문제를 해결하고 비동기 실행 중 컨텍스트 전달 문제를 해결하는 것입니다.
JDK의 inheritableThreadlocal 클래스는 부모 스레드의 값을 자식 스레드로 전달할 수 있습니다. 그러나 스레드 풀이 사용되는 경우 및 스레드를 캐시하는 기타 구성 요소의 경우 스레드가 스레드 풀에 의해 생성되며 스레드는 캐싱되어 반복적으로 사용됩니다. 현재, 부모-자식 스레드 관계의 ThreadLocal 값을 통과하는 것은 의미가 없습니다. 응용 프로그램에 필요한 것은 실제로 Task를 작업 실행에 스레드 풀에 제출할 때 ThreadLocal 값을 전달하는 것입니다.
전송 가능한 스레드-로컬 사용 방법은 3 가지 유형으로 나뉩니다. 런 가능 및 호출 가능 수정, 스레드 풀 수정 및 Java 에이전트를 수정하여 JDK 스레드 풀 구현 클래스를 수정하십시오.
다음으로 스레드 풀의 수정 방법을 시연하겠습니다. 먼저, 비정상적인 경우를 보자. 코드는 다음과 같습니다.
공개 클래스 CustomThreadLocal {STATIC THREADLOCAL <string> ThreadLocal = New heritableThreadLocal <> (); 정적 ExecutorService Pool = Executor.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이어야합니다. 스레드 재사용으로 인해 값은 교체 된 경우에만 대체됩니다.
다음으로, 전송 가능한 스레드-로컬을 사용하여 문제가있는 코드를 변환하고 전송 가능한 스레드-로컬의 maven 종속성을 추가하십시오.
<pectionency> <groupid> com.alibaba </groupid> <artifactid> 투과 가능-스레드-로컬 </artifactid> <버전> 2.2.0 </version> </fectionency>
2 개의 장소를 수정하고 스레드 풀을 수정하고 inheritableThreadlocal을 교체하십시오.
정적 TransmittableThreadLocal <string> ThreadLocal = New TransmittableThreadlocal <> (); 정적 ExecutOrserVice Pool = ttlexEcutors.getTtlexEcutorService (Executors.newFixedThreadPool (2));
올바른 결과는 다음과 같습니다.
DAO : Ape World 85
DAO : 유인원과 세계 84
Dao : Apes and World 86
Dao : Apes and World 87
Dao : Apes and World 88
DAO : Ape World 90
DAO : 유인원과 세계 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
이 시점에서 스레드 풀에서 스레드 로컬 데이터의 전송을 완벽하게 해결할 수 있습니다. 친애하는 독자들은 다시 당황합니다. 제목은 스프링 클라우드 에서이 문제를 해결하는 방법에 관한 것이 아닙니다. 또한 Zuul 에서이 문제를 발견했습니다. 해결책은 모든 사람에게 들었습니다. Zuul 에서이 문제를 해결하는 방법에 관해서는 모든 사람이 직접 생각해야합니다. 나중에 시간이 있으면 공유하겠습니다.
위는이 기사의 모든 내용입니다. 모든 사람의 학습에 도움이되기를 바랍니다. 모든 사람이 wulin.com을 더 지원하기를 바랍니다.