Слияние и понижение версии
Когда последующая служба (приправы, обвалка) внезапно становится недоступной или по какой-то причине реагирует слишком медленно (3 минуты на покупку продуктов и полчаса в очереди), вышестоящая служба, чтобы обеспечить доступность своей общей услуги (может t ждать), больше не продолжать вызывать целевую службу, возвращаться напрямую и быстро освобождать ресурсы. Возобновите вызов, если целевой сервис улучшится. Это называется сервисным предохранителем.
Из-за долгой очереди Сяоянь решил отказаться от последующего процесса и предоставил блюда «пониженного качества». Это называется деградацией сервиса.
Есть много способов сплавить
Существует много способов снижения качества услуг, таких как ограничение тока, переключение и слияние.
Что касается автоматических выключателей, в Spring Cloud есть библиотека для понижения версии автоматических выключателей Hystrix, а Sentinel с открытым исходным кодом от Alibaba также можно использовать в распределенных проектах для достижения целей автоматического выключателя и понижения. И Hystrix, и Sentinel необходимо внедрять сторонние компоненты и понимать принцип реализации, что не подходит для использования в простых сценариях.
Использование рукописных предохранителей
// 初始化一个熔断器
private CircuitBreaker breaker = new CircuitBreaker(0.1, 10, true, "serviceDemo");
public void doSomething() {
// 每次调用都检查服务状态
breaker.checkStatus();
// 如果熔断器返回 true 认为服务可用,继续执行逻辑
if (breaker.isWorked()) {
try {
service.doSomething();
} catch (Exception e) {
e.printStackTrace();
// 出现调用失败,记录失败次数
breaker.addFailTimes();
} finally {
// 每一次调用,增加调用次数
breaker.addInvokeTimes();
}
}
// 服务不可用,执行降级逻辑
}
В этом псевдокоде автоматический выключатель выполняет три функции:
Проверить состояние службы и вывести журнал статистики
Возвращает статус службы breaker.isWorked()
Запишите количество вызовов и отказов в качестве основы для слияния.
Реализация предохранителя
public class CircuitBreaker {
/**
* 记录失败次数
*/
private AtomicLong failTimes =
new AtomicLong(0);
/**
* 记录调用次数
*/
private AtomicLong invokeTimes =
new AtomicLong(0);
/**
* 降级阈值,比如 0.1
* 请求失败次数/请求总次数的比例
*/
private double failedRate = 0.1;
/**
* 降级最小条件,请求总次数大于该值
* 才会执行阈值判断
* 比如 设置为 10 ,
* 当请求次数大于10次时才会执行判断
*/
private double minTimes;
/**
* 熔断开关,默认关闭
*/
private boolean enabled;
/**
* 熔断后是否发送邮件告警
*/
private boolean mail;
/**
* 熔断后是否发送短信告警
*/
private boolean sms;
/**
* 熔断器名字
*/
private String name;
/**
* 保存上一次统计的时间戳,记录单位是分钟
*/
private AtomicLong currentTime =
new AtomicLong(
System.currentTimeMillis() / 60000);
/**
* 记录服务是否是不可用状态
*/
private AtomicBoolean isFailed =
new AtomicBoolean(false);
/**
* 服务宕掉的状态放到线程容器中
*/
private ThreadLocal<Boolean> fail =
new ThreadLocal<Boolean>();
private Logger log =
LoggerFactory.getLogger(getClass());
/**
* 构造熔断器
*
* @param failedRate 熔断的阈值,
* 请求失败次数/请求总次数
* @param minTimes 熔断的最小条件,
* 请求总次数大于该值才会根据阈值判断,
* 执行降级操作
* @param enabled 是否需开启熔断操作
*/
public CircuitBreaker(double failedRate,
double minTimes,
boolean enabled,
String name) {
fail.set(false);
this.failedRate = failedRate;
this.minTimes = minTimes;
this.enabled = enabled;
this.name = name;
}
/**
* 判断服务是否是失败状态
*
* @return
*/
public boolean isFailed() {
return isFailed.get();
}
/**
* 增加错误次数
*/
public void addFailTimes() {
fail.set(true);
if (enabled) {
failTimes.incrementAndGet();
}
}
/**
* 增加一次调用次数
*/
public void addInvokeTimes() {
if (enabled) {
invokeTimes.incrementAndGet();
}
}
/**
* 判断服务是否可用
*
* @return
*/
public boolean isWorked() {
if (!enabled) {
return true;
}
// 当服务不可用时,牺牲掉 1% 的流量做探活请求
if (isFailed.get() &&
System.currentTimeMillis() % 100 == 0) {
return true;
}
if (isFailed.get()) {
fail.set(true);
return false;
}
return true;
}
public void checkStatus() {
if (!enabled) {
return;
}
long newTime =
System.currentTimeMillis() / 60000;
if ((newTime > currentTime.get())
&& (invokeTimes.get() > minTimes)) {
double percent =
failTimes.get() * 1.0 /
invokeTimes.get();
if (percent > failedRate) {
if (isFailed.get()) {
// 日志输出
if (mail) {
// 发送邮件通知
}
} else {
// 日志输出
isFailed.set(true);
if (sms) {
// 发送短信通知
}
if (mail) {
// 发送邮件通知
}
}
} else { // 服务恢复
if (isFailed.get()) {
// 日志输出
if (sms) {
// 发送短信通知
}
if (mail) {
// 发送邮件通知
}
}
isFailed.set(false);
}
if (log.isInfoEnabled()) {
// 日志输出
}
currentTime.set(newTime);
failTimes.set(0);
invokeTimes.set(0);
}
}
}
Главная идея:
Объединение на основе статистической информации и объединение, если доля ошибочных запросов превышает пороговое значение.
Период статистики находится на минутном уровне (статистика за 1 минуту достигает порога)
Если общее количество запросов не достигает minTimes в течение нескольких минут, автоматический выключатель не будет выполнен (частота запросов слишком низкая, и статистическая информация не имеет смысла).
-
Даже если достигнуто состояние автоматического выключателя, 1% (изменяемых) запросов по-прежнему приносится в жертву для проверки.
isFailed.get()&&System.currentTimeMillis() % 100 == 0
Преимущества и недостатки
Hystrix предоставляет ряд функций защиты услуг, таких как сервисный предохранитель и изоляция потоков. Наши автоматические выключатели с рукописным вводом могут предоставлять только ручные автоматические выключатели на основе вызывающего абонента.
Hystrix предоставляет два метода: пул потоков и семафор. Функция рукописного предохранителя является относительно единственной на основе статистической информации, а степень детализации в минутах относительно грубая.
Методы императивного программирования и регистрации обратных вызовов Hystrix имеют высокую сложность кода. В процессе вторжения в код рукописный предохранитель более ориентирован на процесс и имеет низкую стоимость понимания.
После удаления комментариев и недопустимых пустых строк фактически эффективный код составляет менее 100 строк, и мы использовали менее 100 строк кода для реализации функции прерывателя цепи. Хотя в крупномасштабных сценариях обслуживания есть много дефектов, я надеюсь, что это может дать вам хотя бы представление.
Подписывайтесь на меня
Добро пожаловать, чтобы обратить внимание и связаться со мной в любое время ~