В 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 ("Service:" + CustomThreadLocal.Threadlocal.get ()); new dao (). call (); }} класс dao {public void call () { System.out.println ("================================================================================== ==================================================================================================================== ==================================================================================================================== ======================================================================================================== Customthreadlocal.threadlocal.get ());Мы определяем нитолокальный в основном классе для передачи данных, затем создается поток, и метод вызова в службе вызывается в потоке, а значение устанавливается в потоке Threadlocal, а значение в потоке получает в службе, а затем вызывается метод вызова в DAO, которое также получается в Threadlocal. Давайте запустим его, чтобы увидеть эффект:
Сервис: 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 (). Call ();}}). start (); }} class service {public void call () {system.out.println ("service:" + thread.currentthread (). getName ()); System.out.println ("Service:" + CustomThreadLocal.Threadlocal.get ()); // new dao (). Call (); новый поток (new Runnable () {@Override public void run () {new dao (). call ();}}). start (); }} класс dao {public void call () { System.out.println ("================================================================================== ========================================================================================================= ================================================================================================ ========================================================================================================= System.out.println ("dao:" + thread.currentthread (). GetName ());Забежать снова, чтобы увидеть эффект:
Сервис: Thread-0
Служба: обезьяны и небеса
==============================================================
Дао: Тема-1
Дао: Нуль
Вы можете видеть, что этот запрос был выполнен двумя потоками. Вы все еще можете получить значение Threadlocal в службе. Вы не можете получить его в DAO, потому что поток была переключена. Это проблема, что данные Threadlocal будут потеряны в начале.
Итак, как решить эту проблему, на самом деле очень просто, вам нужно изменить только одну строку кода:
static Threadlocal <String> Threadlocal = new InheritableThreadLocal <> ();
Измените Threadlocal на наследственные читатели, давайте посмотрим на эффект после преобразования:
Сервис: Thread-0
Служба: обезьяны и небеса
==============================================================
Дао: Тема-1
Дао: мир мира
Значение может быть получено нормально. InehoritableThreadLocal вызвана решением проблемы, что ThreadLocal не может получить значение из -за этого переключения потока.
Чтобы понять принцип наследственного числа, мы должны сначала понять принцип нитолокального. Давайте кратко представим принцип Threadlocal:
Каждый поток имеет свойство Threadlocals Type Threadlocalmap. Класс Threadlocalmap эквивалентен карте. Ключ - это Threadlocal, а значение - это значение, которое мы установили.
Поток публичного класса реализует runnable {threadlocal.threadlocalmap threadlocals = null;} Когда мы передаем Threadlocal.Set («Обезьяны и мир»); в этом потоке мы помещаем пару клавишных значений в свойство Threadlocals. Ключ - текущий поток, а значение - это значение, которое вы устанавливаете.
public void set (t value) {Thread t = Thread.currentThread (); Threadlocalmap map = getMap (t); if (map! = null) map.set (это, значение); 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 ("Unchecked") t result = (t) e.value; результат возврата; }} return setinitialValue ();}Благодаря вышеупомянутому введению мы можем понять, что Threadlocal может передавать данные с помощью Thread.currentThread () для получения его, то есть, до тех пор, пока он находится в том же потоке, можно получить значение, установленное впереди.
Если следующая операция воссоздает поток после того, как Threadlocal установила значение, и Thread.CurrentThread () изменился в настоящее время, то вы обязательно не получите значение, которое вы установили ранее. Для конкретного воспроизведения проблем, пожалуйста, обратитесь к моему коду выше.
Тогда почему nehytableThreadLocal OK?
Унаследованные классы наследуют нитолокальные и переписываемые 3 метода. При создании нового потока экземпляра потока в текущем потоке эти переменные потока будут переданы из текущего потока в экземпляр нового потока.
Общедоступный класс enhytableThreadLocal <T> Extends Threadlocal <T> { /** * Вычисляет начальное значение ребенка для этой наследуемой и-локальной *-локальной * переменной в зависимости от значения родителя во время создания поток ребенка *. Этот метод вызывается из потока родительского * до начала ребенка. * <p> * Этот метод просто возвращает свой входной аргумент и должен быть переопределен *, если желательно другое поведение. * * @param ParentValue Значение родительского потока * @Return Первоначальное значение ребенка */ Защищенное t childvalue (t parentvalue) {return parentvalue; } /*** Получите карту, связанную с потоком. * * @param t текущий поток */ threadlocalmap getmap (thread t) {return t.inheritabletreadlocals; } /*** Создать карту, связанную с потоком. * * @param t текущий поток * @param firstvalue для начальной записи таблицы. */ void createmap (поток t, t firstvalue) {t.inheritableThreadLocals = new ThreadlocalMap (this, firstValue); }}Через приведенный выше код мы видим, что унаследованная чистота переписала три метода ChildValue, GetMap и CreateMap. Когда мы устанавливаем значение в нем, значение сохраняется в наследии, а не в предыдущих Threadlocals.
Ключевой момент здесь. Почему мы можем получить значение в Threadlocal в предыдущем потоке при создании нового пула потоков? Причина в том, что когда создается новый поток, inhoritabletreadlocals предыдущего потока будут назначены для наследственных читателей нового потока, который реализует передачу данных таким образом.
Исходный код изначально находится в методе Thread Init, следующим образом:
if (parent.inheritableThreadLocals! = null) this.InheritableTreadlocals = threadlocal.createInheritedMap (parent.inheritableThreadLocals);
CreateInheritedMap следующим образом:
static Threadlocalmap createInheritedMap (Threadlocalmap parentMap) {return new Threadlocalmap (parentMap); }Код назначения:
private Threadlocalmap (Threadlocalmap ParentMap) {intry [] parenttable = parentmap.table; int len = parenttable.length; SetThreshold (Len); Таблица = Новая запись [Лен]; for (int j = 0; j <len; j ++) {inpit e = parenttable [j]; if (e! = null) {@suppresswarnings ("unchecked") Threadlocal <Object> key = (Threadlocal <Object>) e.get (); if (key! = null) {value = value = key.childvalue (e.value); Запись C = новая запись (ключ, значение); int h = key.threadlocalhashcode & (len - 1); while (таблица [h]! = null) h = nextIndex (h, len); Таблица [h] = c; размер ++; }}}}До этого момента, через наследственные читатели, мы можем передать значение в локальном потоке, когда родительский поток создает дочерний поток. Эта функция может удовлетворить большинство потребностей, но есть еще одна серьезная проблема, что если это повторное использование потока, будут проблемы. Например, если это повторное использование потока, он будет использовать enhytableThreadLocals в пуле потоков для передачи значений, потому что inehoritableTreadLocals будут передавать значения только при создании нового потока. ThreadReuse не выполнит эту операцию. Таким образом, чтобы решить эту проблему, вы должны продлить класс потоков самостоятельно, чтобы реализовать эту функцию.
Не забывайте, что мы делаем Java. В мире открытого исходного кода есть все, что вам нужно. Ниже я рекомендую внедренную библиотеку Java, которая является передачей с открытым исходным кодом Alibaba с открытым исходным кодом.
Адрес Github: https://github.com/alibaba/transmittable-thread-local
Основная функция состоит в том, чтобы решить проблему доставки потоков значения при использовании пулов потоков и других компонентов, которые кэшируют потоки, и решают проблему доставки контекста во время асинхронного выполнения.
Класс jdk neheritableThreadLocal может завершить значение передачи родительского потока в детскую потоку. Однако для случая, когда используется пул потоков, и другие компоненты, которые кэшируют потоки, поток создается пулом потоков, а поток кэшируется и используется многократно; В настоящее время не имеет смысла передавать нитолокальное значение отношения потока родителей-ребенка. Что нужно приложению, на самом деле для передачи значения Threadlocal при отправке задачи в пул потоков в выполнение задачи.
Методы использования трансмитации-тока-локали разделены на три типа: изменить запускаемые и призывные, изменить пул потоков и агент Java для изменения классов реализации пула потоков JDK.
Затем давайте продемонстрируем метод модификации пула потоков. Во -первых, давайте возьмем ненормальный случай, код заключается в следующем:
открытый класс CustomThreadLocal {static Threadlocal <String> threadlocal = new enhytableThreadLocal <> (); 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 ();}}); }} класс dao {public void call () {system.out.println ("dao:" + customthreadlocal.treadlocal.get ()); }}Результат запуска приведенного выше кода неверен, а вывод заключается в следующем:
Дао: Ape World 99
Дао: Ape World 99
Дао: Ape World 99
Дао: Ape World 99
Дао: Ape World 99
Дао: Ape World 99
Дао: Ape World 99
Дао: Ape World 99
Дао: Ape World 99
Дао: Ape World 99
Дао: Ape World 99
Дао: Ape World 99
Дао: Ape World 99
Правильный должен быть от 1 до 100. Из -за повторного использования потока значение будет заменено, только если оно будет заменено.
Далее используйте трансмизируемый-нагрузочный-локальный, чтобы преобразовать проблемный код и добавить зависимость Maven от трансмизируемой-нагрузки-локала:
<dependency> <groupid> com.alibaba </groupid> <artifactid> трансмитабельный-thread-local </artifactid> <sersive> 2.2.0 </version> </deperency>
Просто измените 2 места, измените пул потоков и замените inheritableThreadLocal:
статический трансмиттеплетчикатлокал <string> threadlocal = new CommonittableReadLocal <> (); Static ExecutorService Pool = ttlexeCutors.getTtlexeCutorService (executors.newFixedThreadpool (2));
Правильные результаты следующие:
Дао: Ape World 85
Дао: обезьяны и мир 84
Дао: обезьяны и мир 86
Дао: обезьяны и мир 87
Дао: обезьяны и мир 88
Дао: Ape World 90
Дао: обезьяны и мир 89
Дао: Ape World 91
Дао: Ape World 93
Дао: Ape World 92
Дао: Ape World 94
Дао: Ape World 95
Дао: Ape World 97
Дао: Ape World 96
Дао: Ape World 98
Дао: Ape World 99
На этом этапе мы можем идеально решить передачу нитолокальных данных в пулах потоков. Дорогие читатели снова озадачены. Название не о том, как решить эту проблему в Spring Cloud. Я также нашел эту проблему в Zuul. Решение было сказано всем. Что касается того, как решить эту проблему в Zuul, все должны думать об этом сами. Я поделюсь этим с вами, если у вас будет время позже.
Выше всего содержание этой статьи. Я надеюсь, что это будет полезно для каждого обучения, и я надеюсь, что все будут поддерживать Wulin.com больше.