предисловие
Недавно я участвовал в построении стабильности сервиса в группе на работе, разобрался с нашим текущим статусом сервиса и подключился к собственной разработанной компанией платформе гарантии стабильности. Я изучил самостоятельно разработанные компоненты в компании и популярный в отрасли Hystrix.Большое количество адаптивных реализаций RxJava в Netflix Hystrix действительно сбивает с толку. Итак, вот некоторые практики и очки знаний Hystrix.
зачем это делать
Стабильность сервисов является важным краеугольным камнем устойчивого развития компании.При быстром развитии объемов бизнеса некоторые сервисы, которые обычно работают нормально, будут сталкиваться с различными аварийными ситуациями, а в распределенной системе каждый сервис сам по себе имеет много проблем.Управляющие факторы, такие как как медленная обработка пула потоков, что приводит к тайм-аутам запросов, недостатку ресурсов, что приводит к отклонению запросов и даже к прямой недоступности службы, простоям, зависанию базы данных, зависанию кеша, зависанию системы сообщений...
Для некоторых неосновных услуг, если возникает большое количество исключений, могут использоваться технические средства для понижения уровня услуг и предоставления неблагоприятных услуг, чтобы обеспечить гибкую доступность услуг и избежать лавинных эффектов.
Например: система, которая опирается на 30 служб, каждая служба доступна на 99,99 %, 99,99 % в 30-й степени ≈ 99,7 %, 0,3 % означает, что 100 миллионов запросов будут иметь 3 000 000 отказов, что соответствует примерно 3 000 000 раз в месяц. . 2-часовая служба нестабильна.По мере увеличения количества зависимостей службы общая доступность службы будет ухудшаться. Предположим, что одна из внешних зависимостей нашего текущего сервиса выходит из строя.Это может быть тайм-аут сетевого джиттера, или сервис зависает и вызывает тайм-аут запроса.Это выглядит следующим образом через короткое время:
Медленно, большое количество бизнес-потоков будет заблокировано при обращении к неисправной службе, запрос будет поставлен в очередь, ответ службы будет медленным, системные ресурсы будут постепенно потребляться, и в конечном итоге служба выйдет из строя. страшнее то, что это воздействие будет продолжать передаваться вверх, что приведет к лавинному обвалу обслуживания.
как это сделать
-
Удалить зависимости:Удаление гребня, изоляция. Например, система сводит к минимуму зависимость от третьих лиц, основные и неосновные бизнес-сервисы разделены, изоляция каждого сценария внутри сервиса на уровне пула потоков
-
Ослабление зависимостей:Обход, кэш.
-
Зависимости управления:Понижение предохранителя, ограничение тока обслуживания, установка разумного тайм-аута и повторная попытка. Избегайте каскадных сбоев
Показатели доступности
Отраслевой стандарт высокой доступности измеряется временем простоя системы:
Во-первых, рассортируйте сервисные зависимости каждой бизнес-ссылки и количество вызовов зависимостей, а также определите, какие сервисы сильно зависят, а какие слабо.Сильная и слабая зависимость от определения отрасли
Чувственный:То есть, когда есть проблема с нижестоящим зависимым сервисом, текущая система будет затронута в некоторой степени.То, что пользователи чувствуют, является сильной зависимостью, а то, что они не чувствуют, является слабой зависимостью.
причина:Зависимости, которые не влияют на основные бизнес-процессы и доступность системы, можно назвать слабыми зависимостями, и наоборот.
Для сильных зависимостей постарайтесь ухудшить логику службы, потому что в конце концов это повлияет на основную ссылку. Для слабых зависимостей его можно взорвать в любой момент.
Установите разумные тайм-ауты и повторные попытки
Зависимости от внешних систем и базовых компонентов, таких как кэши и очереди сообщений. Если предположить, что у этих проверяющих сторон внезапно возникнут проблемы, время отклика нашей системы составит: внутреннее время + время ожидания проверяющей стороны * количество повторных попыток. Если установлен слишком большой тайм-аут и слишком много повторных попыток, система не вернется в течение длительного времени, что может привести к переполнению пула соединений и зависанию системы; если тайм-аут установлен слишком короткий, доступность система будет сокращена.
- Прежде всего, необходимо исследовать период тайм-аута для зависимой службы, чтобы вызвать сам нисходящий. Период тайм-аута вызывающей стороны больше, чем время, в течение которого проверяющая сторона вызывает нисходящий поток.
- Рассчитайте 99% времени отклика этого интерфейса и добавьте 50% к установленному времени ожидания. Если интерфейс зависит от третьей стороны, а третья сторона сильно колеблется, она также может следовать 95% времени отклика.
- Количество повторных попыток Если важность системной службы высока, по умолчанию она обычно повторяется три раза. В противном случае нет необходимости повторять попытку.
Hystix
Возьмем, к примеру, Hystix, популярный в отрасли предохранитель и компонент понижения версии, чтобы изучить его основной принцип работы.
Блок-схема работы Hystix
Следующий более подробный анализ, каждый этап операции происходит в котором:
-
построить
HystrixCommandилиHystrixObservableCommandобъект.Первым шагом является построение
HystrixCommandилиHystrixObservableCommandОбъект, который будет представлять один из ваших запросов зависимостей, передает конструктору параметры, требуемые зависимостью запроса.если построить
HystrixCommandЗависимость возвращает один ответ, например:HystrixCommand command = new HystrixCommand(arg1, arg2);Если зависимость должна возвращать
ObservableЧтобы выдать ответ, вам нужно построитьHystrixObservableCommandобъект для завершения, например:HystrixObservableCommand command = new HystrixObservableCommand(arg1, arg2); -
Выполнение заказа
Есть 4 способа выполнить команду Hystrix.
-
execute()— Метод блокируется, получая один ответ от зависимого запроса (или выбрасывая исключение при ошибке). -
queue()— Возвращает объект Future, содержащий один ответ на запрос зависимости. -
observe()— Подписка на объект Observable, представляющий ответ, возвращаемый из запроса зависимости. -
toObservable()— Возвращает объект Observable, который будет выполнять команды Hystrix и выдавать ответы только тогда, когда вы подписываетесь на него.
K value = command.execute(); Future<K> fValue = command.queue(); Observable<K> ohValue = command.observe(); //hot observable Observable<K> ocValue = command.toObservable(); //cold observable -
метод вызова синхронноexecute()на самом деле звонкиqueue().get()метод,queue()Вызов методаtoObservable().toBlocking().toFuture()То есть, в конце концов, каждая HystrixCommand реализуется через Observable, даже если эти команды просто возвращают простое одиночное значение.
-
Кэшируется ли ответ
Если кеш запроса для этой команды уже включен, и ответ на этот запрос уже существует в кеше, ответ, содержащий кешированный ответ, будет возвращен немедленно.
Observable. -
Автоматический выключатель разомкнут?
Когда команда выполняется, Hystrix проверяет, включен ли лупер.
Если автоматический выключатель включен (или отключен), Hystrix больше не будет выполнять присвоение имен, а вместо этого направит напрямую к первому
8Шаг, получите резервный метод и выполните резервную логику.Если автоматический выключатель замкнут,
5шаг, чтобы проверить, достаточно ли мощности для выполнения задачи. (Емкость включает в себя емкость пула потоков, емкость очереди и т. д.). -
Заполнены ли пул потоков, очередь и семафор
Если пул потоков или очередь, связанная с командой, заполнена, Hystrix больше не будет выполнять команду, а немедленно перейдет к
8Шаг, выполните резервную логику. -
Расчет показателей цепи [Состояние цепи]
Hystrix будет сообщать об успехах, неудачах, отклонениях и тайм-ауте циклу.Лупер содержит ряд данных скользящего окна и использует эти данные для статистики.
Он использует эту статистику, чтобы решить, следует ли сработать автоматический выключатель.Если он должен сработать, он не будет зависеть от запроса в течение определенного периода времени.Если он исправен, автоматический выключатель будет снова включен.
-
Получить откат
В случае сбоя выполнения команды Hystrix попытается выполнить пользовательскую логику Fallback:
- когда
construct()илиrun()Во время выполнения метода возникает исключение. - Когда автоматический выключатель разомкнут, выполнение команды переходит в состояние срабатывания.
- Когда пул потоков и очередь или семафор для выполнения команды заполнены.
- Время выполнения команды истекло.
- когда
Принципы дизайна Hystrix
1. Предотвратить сбой одной службы и исчерпать контейнеры всей системной службы
2. Используйте быстрый сбой вместо постановки в очередь (каждая зависимая служба поддерживает небольшой пул потоков или семафор, когда пул потоков или семафор заполнен, служба будет немедленно отклонена без очереди) и постепенное ухудшение службы; возвращение в нормальное состояние после отказ, быстрое восстановление
3. Обеспечивает мониторинг и оповещение практически в реальном времени, что позволяет быстро обнаруживать и устранять неисправности. Информация мониторинга включает в себя успешный запрос, сбой (исключение, созданное клиентом), тайм-аут и отклонение потока. Если процент ошибок при доступе к зависимым службам превышает пороговое значение, срабатывает автоматический выключатель, после чего служба прекращает все запросы к определенной службе на определенный период времени.
4. Инкапсулируйте все службы, зависящие от запросов, в объекты HystrixCommand или HystrixObservableCommand, а затем выполняйте эти запросы в отдельном потоке. Используйте методы изоляции, чтобы ограничить влияние сбоя любой зависимости на систему. Каждая зависимая служба поддерживает небольшой пул потоков (или семафор), когда пул потоков или семафор заполнен, служба будет немедленно отклонена без очереди
Особенности Hystrix
выключатель
На рисунке ниже показаноHystrixCommandиHystrixObservableCommandКак работать сHystrixCircuitBrokerвзаимодействовать.
- Предположим, что запросы в цикле соответствуют определенному порогу (
HystrixCommandProperties.circuitBreakerRequestVolumeThreshold()) - Предположим, что процент возникающих ошибок превышает установленный порог возникновения ошибки.
HystrixCommandProperties.circuitBreakerErrorThresholdPercentage() - Состояние петлителя задается
CLOSEпревратиться вOPEN - Если лупер открыт, все запросы будут сброшены лупером.
- через определенное время
HystrixCommandProperties.circuitBreakerSleepWindowInMilliseconds(), будет пропущен следующий запрос (в полуоткрытом состоянии), если запрос не пройден, лупер вернется во время спящего окнаOPEN, в случае успешного выполнения запроса лупер выключится и снова включится1Логика шагов.
На следующем рисунке представлена блок-схема автоматического восстановления предохранителя:
Когда возникает проблема, Hystrix проверяет временное окно (окно) определенной продолжительности (10 с на рисунке), достаточно ли запросов в этом временном окне, и если запросов достаточно, была ли частота ошибок. порог достигнут, если он достигнут, механизм предохранителя автоматического выключателя будет активирован.В это время, если есть другой запрос, он пойдет прямо на резервный путь. После размыкания автоматического выключателяsleep window(5s на рисунке), каждый раз, когда он проходитsleep window, когда приходит запрос, прерыватель цепи отправляет запрос удаленной службе и позволяет ему проверить, была ли восстановлена нижестоящая служба. повторно запрошен в удаленной службе. В противном случае сохраните состояние сбоя.sleep windowМеханизм реализации аналогичен проблеме сдвига окна, чтобы найти наибольшую ценность во вступительном экзамене в школу!
fallback
Изоляция ресурсов
Hystrix использует шаблон перегородки, чтобы изолировать зависимости друг от друга и ограничить одновременный доступ к любой из них.
-
Потоки и пулы потоков
Клиент (сторонний пакет, сетевой вызов и т. д.) будет выполняться в отдельном потоке и будет изолирован от потока вызывающей задачи, чтобы предотвратить блокировку вызывающим потоком вызывающего потока из-за длительного времени. потребляется вызывающим вызовом зависимостей.
[Hystrix uses separate, per-dependency thread pools as a way of constraining any given dependency so latency on the underlying executions will saturate the available threads only in that pool]
Netflix разработала Hystrix и решила использовать потоки и пулы потоков для реализации механизмов изоляции по нескольким причинам:
- Многие приложения вызывают несколько различных серверных служб в качестве зависимостей.
- Каждая служба предоставляет собственный пакет клиентской библиотеки.
- Библиотечный пакет каждого клиента постоянно находится в состоянии изменения.
- [Client library logic can change to add new network calls]
- Каждый пакет клиентской библиотеки может содержать другую логику для повторных попыток, анализа данных, кэширования и т. д.
- Клиентские библиотеки, как правило, являются «черными ящиками» для пользователя в отношении деталей реализации и шаблонов доступа к сети. Конфигурация по умолчанию и т. д. непрозрачны.
- [В нескольких реальных производственных сбоях определение было «о, что-то изменилось, и свойства должны быть скорректированы» или «клиентская библиотека изменила свое поведение».]
- Даже если сам клиент не изменится, сама служба может измениться, и эти факторы могут повлиять на производительность службы и привести к сбою конфигурации клиента.
- Транзитивные зависимости могут привести к появлению других клиентских библиотек, которые не ожидаются и могут быть неправильно настроены.
- Большая часть доступа к сети выполняется синхронно.
- Сбои и задержки могут возникать и в клиентском коде, а не только в сетевых вызовах.
сигнал
| тип | преимущество | недостаточный | Быть применимым |
|---|---|---|---|
| нить | Поддержка очередей и тайм-аутов, поддержка асинхронных вызовов | Вызов потока и переключение создают дополнительные накладные расходы | Ненадежные клиенты (например, стабильность сторонних сервисов не может быть спекулятивной) |
| сигнал | Легкий и без лишних накладных расходов | Не поддерживает очередь задач и тайм-аут, не поддерживает асинхронность | Надежные клиенты, услуги высокочастотной и высокоскоростной связи (шлюз, кэш) |
элемент конфигурации параметра
связанный с пулом потоков
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty;
import static com.netflix.hystrix.contrib.javanica.conf.HystrixPropertiesManager.*;
/*
* 注意: @HystrixCommand 注解方式依赖 AOP, 不支持在同一个类的内部方法之间直接调用, 必须将被调用类作为 bean 注入并调用
*/
public class DemoCircuitBreakerAnnotation {
/**
* 使用 THREAD 模式及线程池参数、通用参数说明
*/
@HystrixCommand(
groupKey = "GroupAnnotation",
commandKey = "HystrixAnnotationThread",
fallbackMethod = "HystrixAnnotationThreadFallback",
/*
* 线程池名, 具有同一线程池名的方法将在同一个线程池中执行
*
* 默认值: 方法的groupKey
*/
threadPoolKey = "GroupAnnotationxThreadPool",
threadPoolProperties = {
/*
* 线程池Core线程数及最大线程数
*
* 默认值: 10
*/
@HystrixProperty(name = CORE_SIZE, value = "10"),
/*
* 线程池线程 KeepAliveTime 单位: 分钟
*
* 默认值: 1
*/
@HystrixProperty(name = KEEP_ALIVE_TIME_MINUTES, value = "1"),
/*
* 线程池最大队列长度
*
* 默认值: -1, 此时使用 SynchronousQueue
*/
@HystrixProperty(name = MAX_QUEUE_SIZE, value = "100"),
/*
* 达到这个队列长度后, 线程池开始拒绝后续任务
*
* 默认值: 5, MaxQueueSize > 0 时有效
*/
@HystrixProperty(name = QUEUE_SIZE_REJECTION_THRESHOLD, value = "90"),
},
commandProperties = {
/*
* 以 THREAD (线程池)模式执行, run 方法将被一个线程池中的线程执行
*
* 注意: 由于有额外的线程调度开销, THREAD 模式的性能不如 NONE 和 SEMAPHORE 模式, 但隔离性比较好
*
* 默认值: THREAD
*/
@HystrixProperty(name = EXECUTION_ISOLATION_STRATEGY, value = "THREAD"),
/*
* 方法执行超时后是否中断执行线程
*
* 默认值: true, THREAD 模式下有效
*/
@HystrixProperty(name = EXECUTION_ISOLATION_THREAD_INTERRUPT_ON_TIMEOUT, value = "true"),
/*
* 超时时间参数
* 在 THREAD 模式下, 方法超时后 Hystrix 默认会中断原方法的执行线程, 并标记这次方法的执行结果为失败(影响方法的健康值)
* 同时另开一个线程执行 fallback, 最终返回 fallback 的结果
*
* 默认值: 1000
*/
@HystrixProperty(name = EXECUTION_ISOLATION_THREAD_TIMEOUT_IN_MILLISECONDS, value = "500")
/*
* 其余参数参考上面的例子, 或者使用默认值
*/
})
public String HystrixAnnotationThread(String param) {
return "Run with " + param;
}
public String HystrixAnnotationThreadFallback(String param, Throwable ex) {
return String.format("Fallback with param: %s, exception: %s", param, ex);
}
}
Корреляция семафоров
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty;
import static com.netflix.hystrix.contrib.javanica.conf.HystrixPropertiesManager.*;
/*
* 注意: @HystrixCommand 注解方式依赖 AOP, 不支持在同一个类的内部方法之间直接调用, 必须将被调用类作为 bean 注入并调用
*/
public class DemoCircuitBreakerAnnotation {
/**
* 使用 SEMAPHORE 模式及通用参数说明
*/
@HystrixCommand(
groupKey = "GroupAnnotation",
commandKey = "HystrixAnnotationSemaphore",
fallbackMethod = "HystrixAnnotationSemaphoreFallback",
commandProperties = {
/*
* 以 SEMAPHORE (信号量)模式执行, 原方法将在调用此方法的线程中执行
*
* 如果原方法无需信号量限制, 可以选择使用 NONE 模式
* NONE 模式相比 SEMAPHORE 模式少了信号量获取和判断的步骤, 效率相对较高, 其余执行流程与 SEMAPHORE 模式相同
*
* 默认值: THREAD
*/
@HystrixProperty(name = EXECUTION_ISOLATION_STRATEGY, value = "SEMAPHORE"),
/*
* 执行 run 方法的信号量上限, 即由于方法执行未完成停留在 run 方法内的线程最大个数
* 执行线程退出 run 方法后释放信号量, 其他线程获取不到信号量无法执行 run 方法
*
* 默认值: 1000, SEMAPHORE 模式下有效
*/
@HystrixProperty(name = EXECUTION_ISOLATION_SEMAPHORE_MAX_CONCURRENT_REQUESTS, value = "100"),
/*
* 执行 fallback 方法的信号量上限
*
* 注意: 所有模式(NONE|SEMAPHORE|THREAD) fallback 的执行都受这个参数影响
*
* 默认值: Integer.MAX_VALUE
*/
@HystrixProperty(name = FALLBACK_ISOLATION_SEMAPHORE_MAX_CONCURRENT_REQUESTS, value = "1000"),
/*
* 超时时间参数
* 在 SEMAPHORE 模式下, 方法超时后 Hystrix 不会中断原方法的执行线程, 只标记这次方法的执行结果为失败(影响方法的健康值)
* 同时另开一个线程执行 fallback, 最终返回 fallback 的结果
*
* 默认值: 1000
*/
@HystrixProperty(name = EXECUTION_ISOLATION_THREAD_TIMEOUT_IN_MILLISECONDS, value = "500"),
/*
* 方法各项指标值存活的滑动时间窗口长度, 每经过一个时间窗口长度重置各项指标值, 比如: 方法的健康值
*
* 默认值: 10000
*/
@HystrixProperty(name = METRICS_ROLLING_STATS_TIME_IN_MILLISECONDS, value = "10000"),
/*
* 滑动时间窗口指标采样的时间分片数, 分片数越高时, 指标汇总更新的频率越高, 指标值的实时度越好, 但同时也占用较多 CPU
* 采样过程: 将一个滑动时间窗口时长根据分片数等分成多个时间分片, 每经过一个时间分片将最新一个时间分片的内积累的统计数据汇总更新到时间窗口内存活的已有指标值中
*
* 注意: 这个值只影响 Hystrix Monitor 上方法指标值的展示刷新频率,不影响熔断状态的判断
*
* 默认值: 10
*/
@HystrixProperty(name = METRICS_ROLLING_STATS_NUM_BUCKETS, value = "10"),
/*
* 健康值采样的间隔, 相当于时间片长度, 每经过一个间隔将这个时间片内积累的统计数据汇总更新到时间窗口内存活的已有健康值中
*
* 健康值主要包括: 方法在滑动时间窗口内的总执行次数、成功执行次数、失败执行次数
*
* 默认值: 500
*/
@HystrixProperty(name = METRICS_HEALTH_SNAPSHOT_INTERVAL_IN_MILLISECONDS, value = "500"),
/*
* 一个滑动时间窗口内, 方法的执行次数达到这个数量后方法的健康值才会影响方法的熔断状态
*
* 默认值: 20
*/
@HystrixProperty(name = CIRCUIT_BREAKER_REQUEST_VOLUME_THRESHOLD, value = "10"),
/*
* 一个采样滑动时间窗口内, 方法的执行失败次数达到这个百分比且达到上面的执行次数要求后, 方法进入熔断状态, 后续请求将执行 fallback 流程
*
* 默认值: 50
*/
@HystrixProperty(name = CIRCUIT_BREAKER_ERROR_THRESHOLD_PERCENTAGE, value = "50"),
/*
* 熔断状态停留时间, 方法进入熔断状态后需要等待这个时间后才会再次尝试执行原方法重新评估健康值. 再次尝试执行原方法时若请求成功则重置健康值
*
* 默认值: 5000
*/
@HystrixProperty(name = CIRCUIT_BREAKER_SLEEP_WINDOW_IN_MILLISECONDS, value = "5000")
})
public String HystrixAnnotationSemaphore(String param) {
return "Run with " + param;
}
public String HystrixAnnotationSemaphoreFallback(String param, Throwable ex) {
return String.format("Fallback with param: %s, exception: %s", param, ex);
}
}
Ссылаться на
Как работает Hystrix (официальный перевод документа) Часто используемые инструменты для систем высокой доступности (1) — переход на более раннюю версию услуги Hystrix