Автор: Xianyu Technology - Code Treasure
Сводка перспектив
В нашем ежедневном процессе разработки мы часто сталкиваемся с не связанными между собой людьми.流水账逻辑(завершение данных, логика уведомлений и т. д.), в настоящее время мы обычно используем асинхронную обработку для ускорения ответа. В то же время, с увеличением количества восходящих и нисходящих зависимых сервисов, также может быть сгенерирован ряд соответствующих сервисов. , Вопросы включают, но не ограничиваются问题难以排查, 性能难以保证и т. д. в Xianyu не исключение:
- дополненный
串行操作(Здесь имеется в виду последовательный интерфейс для достижения нескольких сетевых операций ввода-вывода) Существует узкое место в производительности, которое серьезно влияет на интерфейс rt. - Степень унификации логики неудовлетворительна, «свободный полет» логики приводит к ухудшению читаемости кода, а показатели, связанные с логикой модуля, трудно измерить.
Основываясь на приведенном выше описании, мы хотим абстрагировать асинхронный компонент для решения соответствующей проблемы, а затем сначала поставить перед собой несколько небольших целей!
- Мониторинг: все состояния и поведение (успех, сбой, rt и другие ключевые индикаторы) в рамках логического жизненного цикла каждой единицы находятся в пределах области мониторинга.
- Устойчивость к катастрофам: существует резервный механизм, который может быть полезен, когда в логической единице возникает большое количество исключений (обычно тайм-аутов).
- Бесплатный доступ: простой доступ и использование, так что его можно использовать сразу после установки.
взять каштан
сценарий без тайм-аута
Предполагая, что логический блок L имеет три задачи a, b и c, время выполнения равно t1=1, t2=2, t3=3, а время ожидания равно 4. Как показано на рисунке:
Желаемый результат сейчас:
- Время выполнения для логического блока L 3
- Время, необходимое для успешного выполнения задачи один раз, равно 1, время для успешного выполнения задачи b равно 2, а время для успешного выполнения задачи c равно 3.
Сценарий тайм-аута
Предположим, что логический блок L имеет три задачи a, b и c, время выполнения которых равно t1=3, t2=5, t3=6, а время ожидания равно 4. Как показано на рисунке:
Желаемый результат сейчас:
- Время выполнения логического блока L равно 4
- Задача a требует 3 для успешного выполнения один раз, задача b не выполняется, а задача c не выполняется.
- B и C Нитки останавливаются после сбоя и не продолжают заниматься резенными ресурсами пула нитей
план
Обратите внимание, что все приведенные ниже решения основаны на超时场景расширять
Схема акка
- Текущая служба получает соответствующий LogicActor
- LogicActor получает все текущие UnitActor(a, b, c)
- использовать
tellСпособ соответственно сообщения a, b, c, обратите внимание, что этот процесс является асинхронным - a возвращается нормально, b и c оба тайм-аута
- LogicActor перейдет к текущей задаче обратного отсчета независимо от времени ожидания
- Вернитесь к объединению данных, когда все задачи будут выполнены
- Наконец, верните данные результата
LogicActor — субъект логической единицы, отвечающий за распределение задач и объединение данных.
Unitactor- Специфические задачи актер
преимущество
- управляемое сообщением.
- Нет необходимости в дополнительном управлении пулом потоков и устойчивости к исключениям.
- Изящно остановите выполняющийся рабочий процесс (PoinsonPill).
- Те, кто знаком с akka, могут легко начать работу.
недостаток
- Для достижения цели требуется слой инкапсуляции, а это непросто.
- Значительно увеличить сложность системы.
- Это катастрофа для тех, кто не знаком со scala/akka.
Стоимость обучения scala / akka напрямую привела к провалу цели «доступа с нулевой стоимостью», поэтому от нее отказались.
Схема rxjava
Логика реализована с использованием rxjava
- Определите CountDownLatch для управления временем ожидания.
- Определите три задания
- Реализовать асинхронизацию с помощью RXJAVA
- Дайте пул потоков фиксированного размера 10 для обработки
- Операция накопления в уменьшении
- Защелка как единая карточная точка
Тайм-аут здесь влияет на общий балл карты, но я не знаю, какое дело вызвало тайм-аут.
преимущество
- реактивное программирование
- Уменьшение размера кода
- Блокировать работу пула потоков
- в упаковке
#timeoutи#onErrorReturnметод, существующий модуль обработки тайм-аута не нужно инкапсулировать дважды
недостаток
- Для достижения цели требуется слой упаковки
- Существует модуль обработки времени ожидания, но внедрение бизнес-единицы уничтожит исходный пакет RXJAVA, и сложность преобразования велика.
Хотя "толерантность к катастрофам"(
onErrorReturn), но это не может быть «отслеживаемым», потому что после тайм-аута у нас нет возможности узнать, какой бизнес истек, поэтому мы сдаемся.
Комплексное решение - пакет на основе пакета JUC
трудность
Как реализовать мониторинг логического блока
- Глобально внутри группы
traceIdИнкапсулировать (ThreadLocal), используя пул потоков丢失上下文, так что соответствующий журнал невозможно отследить. - существует
bizCodeконцепции удобно контролировать логическую единицу и каждую операцию внутри логической единицы.
Как очистить потоки тайм-аута, чтобы они не занимали ресурсы пула потоков
вышесказанноеCallableиRunnableПо истечении времени выполнения его необходимо остановить, чтобы он не продолжал занимать ресурсы пула потоков.
Как контролировать параллелизм (тайм-аут)
Есть много инструментов для контроля тайм-аута
- CountDownLatch: java.util.concurrent.CountDownLatch#await(long, java.util.concurrent.TimeUnit) Тайм-аут не вызовет исключения. Для определения превышения тайм-аута можно использовать только логическое значение, что означает, что его нельзя обрабатывается потоком исключений короткого замыкания.
- CyclicBarrier: java.util.concurrent.CyclicBarrier#await(long, java.util.concurrent.TimeUnit) не подходит для инкапсуляции исключений, например
- Теперь есть четыре потока a, b, c, d.Время ожидания составляет 2 с.Из них поток d выполняется в течение 3 с, и происходит следующее
- Первая завершенная задача для входа в барьер. Ожидание вызовет исключение тайм-аута, а остальные четыре задачи вызовут сломанный барьер
- Задача тайм-аута, наконец, завершит последующие действия и продолжит занимать ресурсы.
- Теперь есть четыре потока a, b, c, d.Время ожидания составляет 2 с.Из них поток d выполняется в течение 3 с, и происходит следующее
- Семафор: Не соответствует условиям использования, но подробнее.
- Future: java.util.concurrent.Future#get(long, java.util.concurrent.TimeUnit), futureList будет считываться последовательно, в результате чего T>=Max(t1,t2,t3)
Типичный пример ошибки
- Отправить будущее в соответствующий список
- Получите соответствующий результат, перейдя
- После получения результата проделайте операцию накопления
- Последний результат вывода и время выполнения
Хотя указанные выше три задачи выполняются одновременно, из-за
future.get(long, TimeUnit)блокируется, тайм-аут здесь не пройдет. Как показано выше, окончательное время может быть3+2+1=6s.
Общая программа
Диаграмма основных классов выглядит следующим образом:
- ConcurrentCallable — это класс, используемый извне.
- BizBaseCallable наследует Callable, который инкапсулирует внутренний контекст и соответствующее бизнес-подразделение.
- Для решения проблемы мониторинга тайм-аутов бизнеса BizCountDownLatch добавляет атрибуты пула бизнес-единиц
- BizBaseCallable инкапсулирует контекст представления ctx, чтобы гарантировать, что контекст не будет потерян в случае многопоточности (совместимость с traceId внутри группы)
Модернизация CountDownLatch — возможность мониторинга
Унаследуйте CountDownLatch и переопределите методы #await() и countDown().
- Концепция дополнительной бизнес-единицы (bizCode)
- Соответствующий bizSet на самом деле является незавершенным бизнес-пулом.После завершения бизнеса удалите соответствующую бизнес-единицу
- Переопределите метод countDown, чтобы представить его, удалив бизнес-подразделение.
已完成 - Исключение выдается, когда время ожидания истекает, имитируя среду короткого замыкания, и верхний уровень обрабатывает его единообразно.
- Незавершенная задача выброшена в исключение тайм-аута
Инкапсуляция логики мониторинга верхнего уровня
Обработка тайм-аута ConcurrentCallable — аварийное восстановление
-
监控逻辑Содержит обработку логики тайм-аута - Когда Callable получает возвращаемое значение, период ожидания получения дается
0: Полные завершены, не завершены сверхурочно. - Прервите выполнение вызываемого объекта с помощью future.cancel
- По умолчанию возвращается откат (обратная операция), а откат реализуется по умолчанию как
renturn null;. - Здесь необходимо подставить значение по умолчанию, чтобы его можно было легко передать при вызове внешнего слоя.
list.forEach(l -> deal(l))для обработки логики списка
Посмотрите на приведенный выше пример — доступ с нулевой стоимостью
- Размещение контекста моделирования, ThreadLocal инкапсулируется в TestCtx
- 4000L указывает, что время ожидания составляет 4000 мс, что называется именем «L».
- Добавьте задачи a, b, c в компонент
- Наконец, показана обработка после получения данных.
- После завершения a он будет в
countDownLatchУдалите соответствующий блок из - Когда время истечет, b и c будут принудительно завершены
Это завершает可监控, 容灾性, 零成本接入Асинхронный компонент .
резюме
Таким образом, мы наконец выбрали «JUC (обновленная версия)» в качестве нашего асинхронного компонента.
В этой статье в основном представлены компоненты асинхронного планирования, используемые внутри Xianyu.Бизнес-компоненты, полученные из бизнес-сценариев, могут не обладать сильной универсальностью, но я надеюсь дать некоторые полезные уроки из предыстории, целей и выбора схемы в тексте. .