Введение
Легкий мощный компонент управления потоком, обеспечивающий надежность и мониторинг для микросервисов (облегченный контроль потока, понижение версии библиотеки Java).китайский документ
Лавинный эффект
Лавинный эффект, также известный как каскадный сбой (cascading failure), относится к сбою базовой службы, который приводит к сбою службы верхнего уровня, и сбой накатывает все больше и больше, как снежный ком.
Общие отказоустойчивые решения
- тайм-аут
- Ограничение
- Режим Bulkhead: каждый контроллер настраивает пул потоков независимо, не мешая друг другу.
- Режим автоматического выключателя: обслуживание ниже порогового значения (определяемого количеством ошибок/коэффициентом ошибок и т. д.), автоматический выключатель размыкается, и через некоторое время разрешается вызов службы. В случае успеха автоматический выключатель восстановлен.
Интеграция Sentinel
- добавить зависимость
<!--sentinel-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-sentinel</artifactId>
</dependency>
- писать заметки
无
- записать конфигурацию
无
исполнительный/сторожевой узел
Откройте все конечные точки, чтобы увидеть, работает ли это
# actuator
management:
endpoints:
web:
exposure:
# 生产不能全部放开且需配置安全措施
include: '*'
Следующий контент доказывает, что интеграция прошла успешно
Консоль Стража
Загрузите ту же версию Sentinel, которая указана в нашем файле pom.ссылка на скачивание
#启动
java -jar XXX.jar
- Посетите интерфейс входа http://localhost:8080/#/login
- Пароль учетной записи по умолчанию дозорный/дозорный
- Интеграция проекта
spring:
cloud:
sentinel:
transport:
# 指定sentinel控制台地址
dashboard: localhost:8080
- ленивая загрузка Поскольку Sentinel по умолчанию загружается лениво, он будет отображаться только после запроса любого интерфейса.
Описание функции
-
правила управления потоком
- режим управления потоком
-
непосредственный
-
Ассоциация: связанный ресурс достигает порога и ограничивает себя
-
ссылка: трафик по указанной ссылке
- код
// 定义一个common服务 package com.virgo.user.service; import com.virgo.entity.TblCar; /** * @author zhaozha * @date 2019/10/15 下午4:27 */ public interface CommonService { TblCar common(); } package com.virgo.user.service; import com.alibaba.csp.sentinel.annotation.SentinelResource; import com.virgo.entity.TblCar; import org.springframework.stereotype.Service; /** * @author zhaozha * @date 2019/10/15 下午4:28 */ @Service public class CommonServiceImpl implements CommonService { @Override @SentinelResource("common") public TblCar common() { return TblCar.builder().id(1L).build(); } } // controller层同时调用这个服务 @GetMapping("/test/a") public String testA() { commonServiceImpl.common(); return "testA"; } @GetMapping("/test/b") public String testB() { commonServiceImpl.common(); return "testB"; }
- Настройка: ограничивать только ток /test/a, не влияет на /test/b.
-
- Эффект управления потоком
- провалиться быстро
- Сбой напрямую, выдать исключение
- Исходный код: com.alibaba.csp.sentinel.slots.block.flow.controller.DefaultController
- Warm Up
- Начальное значение — порог/коэффициент кода (по умолчанию — 3), пороговое значение достигается после времени прогрева.
- официальная документация
- Источник: com.alibaba.csp.sentinel.slots.block.flow.controller.WarmUpController.
- ждать в очереди
- Даже в очереди порог должен быть установлен на QPS
- официальная документация
- Источник: com.alibaba.csp.sentinel.slots.block.flow.controller.RateLimiterController.
- провалиться быстро
- режим управления потоком
-
правила перехода на более раннюю версию
- стратегия понижения рейтинга
- RT (среднее время ответа в секундах)
- Запросы со средним временем ответа, превышающим пороговое значение и проходящим в пределах временного окна >= 5
- Включение автоматического выключателя после оконного периода
- Максимум RT 4900 (большее значение должно пройти -Dcsp.sentinel.statistic.max.rt=XXXX, чтобы оно вступило в силу)
- Аномальное соотношение (второй уровень)
- Количество исключений (минутный уровень)
- RT (среднее время ответа в секундах)
- стратегия понижения рейтинга
-
Правила точки доступа
- Правила управления потоком на уровне параметров
- Текущий лимит может быть ограничен указанным параметром/указанным значением указанного параметра
- Требуется @SentinelResource
-
системные правила
- LOAD
- Срабатывает, когда загрузка системы1 (загрузка за 1 минуту) превышает пороговое значение, а количество одновременных потоков превышает емкость системы, рекомендуется установить количество ядер процессора * 2 (действительно для Linux/Unix-подобных машин)
- Емкость системы = maxQps (максимальное количество запросов в секунду в секундах) * minRt (минимальное время ответа в секундах)
- команда uptime -> 1 5 15 (средняя загрузка системы/нагрузка)
- RT
- Среднее время восстановления всего входящего трафика достигает порогового значения
- Потоки
- Количество одновременных потоков для всего входящего трафика достигает порогового значения
- Входящие запросы в секунду
- Число запросов в секунду для всего входящего трафика достигло порогового значения
- LOAD
-
Правила авторизации
- Настройка черных и белых списков для доступа к микросервисам
Принцип взаимодействия микросервиса и консоли
Микросервис регистрирует/отправляет сообщения на консоль, а консоль получает/отправляет сообщения через API.
YML-конфигурация
spring:
cloud:
sentinel:
transport:
控制台地址
dashboard:
和控制台通信的ip
client-ip:
和控制通信端口(默认8719,如果已经占用,+1直到找到)
port:
心跳毫秒
heartbeat-interval-ms:
Параметры запуска консоли
элемент конфигурации | По умолчанию | описывать |
---|---|---|
server.port | 8080 | назначенный порт |
csp.sentinel.dashboard.server | localhost:8080 | указанный адрес |
project.name | - | - |
sentinel.dashboard.auth.username[1.6] | sentinel | Учетная запись для входа в панель управления |
sentinel.dashboard.auth.password[1.6] | sentinel | Пароль для входа в личный кабинет |
server.servlet.session.timeout[1.6] | 30 минут | Время истечения сеанса входа в систему (7200: 7200 секунд/60m: 60 минут) |
API (может защищать произвольные ресурсы)
@GetMapping("/test/sentinel/api")
public String testSentinelApi(@RequestParam(required = false) String a) {
String resourceName = "test-sentinel-api";
ContextUtil.enter(resourceName, "test");
Entry entry = null;
try {
entry = SphU.entry(resourceName);
if (StringUtils.isBlank(a)) {
throw new IllegalArgumentException("参数不可为空");
}
return a;
} catch (BlockException e) {
return "限流/降级了";
} catch (IllegalArgumentException e) {
Tracer.trace(e);
return "参数非法";
} finally {
if (entry != null) {
entry.exit();
}
ContextUtil.exit();
}
}
@SentinelResource
Упростите свой код с помощью @SentinelResource
package com.virgo.user.sentinel;
import com.alibaba.csp.sentinel.slots.block.BlockException;
/**
* @author zhaozha
* @date 2019/10/16 下午3:11
*/
public class ControllerBlockHandlerClass {
/**
* 处理限流/降级
*
* @param a
* @param e
* @return
*/
public static String block(String a, BlockException e) {
return "降级/限流了";
}
}
@GetMapping("/test/sentinel/api")
@SentinelResource(
value = "test-sentinel-api",
blockHandler = "block",
blockHandlerClass = com.virgo.user.sentinel.ControllerBlockHandlerClass.class
)
public String testSentinelApi(@RequestParam(required = false) String a) {
if (StringUtils.isBlank(a)) {
throw new IllegalArgumentException("参数不可为空");
}
return a;
}
Интеграция RestTemplate
- аннотация
@Bean
@LoadBalanced
@SentinelRestTemplate
public RestTemplate restTemplate(){
return new RestTemplate();
}
- выключатель
resttemplate:
sentinel:
enabled:
Feign интегрирует Sentinel
feign:
#feign整合sentinel
sentinel
enable: true
Пользовательское ответное сообщение
- fallback
package com.virgo.user.feignclient;
import com.virgo.entity.TblCar;
import com.virgo.user.configuration.GlobalFeignConfiguration;
import com.virgo.user.feignclient.fallback.LockCenterFeignClientFallback;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
/**
* @author zhaozha
* @date 2019/10/11 下午12:52
*/
@FeignClient(name = "lock-center", configuration = GlobalFeignConfiguration.class,fallback = LockCenterFeignClientFallback.class)
public interface LockCenterFeignClient {
@GetMapping("/lock/test/{id}")
TblCar findById(@PathVariable(value="id") Long id);
}
package com.virgo.user.feignclient.fallback;
import com.virgo.entity.TblCar;
import com.virgo.user.feignclient.LockCenterFeignClient;
import org.springframework.stereotype.Component;
/**
* @author zhaozha
* @date 2019/10/16 下午3:36
*/
@Component
public class LockCenterFeignClientFallback implements LockCenterFeignClient {
@Override
public TblCar findById(Long id) {
return TblCar.builder().level(1).build();
}
}
- fallbackFactory (может перехватывать исключения)
package com.virgo.user.feignclient;
import com.virgo.entity.TblCar;
import com.virgo.user.configuration.GlobalFeignConfiguration;
import com.virgo.user.feignclient.fallbackFactory.LockCenterFeignClientFallbackFactory;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
/**
* @author zhaozha
* @date 2019/10/11 下午12:52
*/
@FeignClient(name = "lock-center", configuration = GlobalFeignConfiguration.class,fallbackFactory = LockCenterFeignClientFallbackFactory.class)
public interface LockCenterFeignClient {
@GetMapping("/lock/test/{id}")
TblCar findById(@PathVariable(value="id") Long id);
}
package com.virgo.user.feignclient.fallbackFactory;
import com.virgo.entity.TblCar;
import com.virgo.user.feignclient.LockCenterFeignClient;
import feign.hystrix.FallbackFactory;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
/**
* @author zhaozha
* @date 2019/10/16 下午4:02
*/
@Component
@Slf4j
public class LockCenterFeignClientFallbackFactory implements FallbackFactory<LockCenterFeignClient> {
@Override
public LockCenterFeignClient create(Throwable throwable) {
return new LockCenterFeignClient() {
@Override
public TblCar findById(Long id) {
log.info("限流/降级",throwable);
return TblCar.builder().level(1).build();
}
};
}
}
Как использовать
Как использовать | Как использовать | инструкции |
---|---|---|
Кодирование | API | try...catch...finally |
Метод аннотации | @SentinelResource | blockHandler/fallback |
RestTemplate | @SentinelRestTemplate | blockHandler/fallback |
Feign | feign.sentinel.enabled | fallback/fallbackFactory |
Сохранение конфигурации
-
Облачная служба Alibaba (AHAS)
- Адрес открытия:ahas.console.aliyun.com
- Инструкции по открытию:help.aliyun.com/document_…
Управление потоком кластера
оптимизация
- Ошибка оптимизации страницы (UrlBlockHandler)
package com.virgo.user.sentinel;
import com.alibaba.csp.sentinel.adapter.servlet.callback.UrlBlockHandler;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.alibaba.csp.sentinel.slots.block.authority.AuthorityException;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeException;
import com.alibaba.csp.sentinel.slots.block.flow.FlowException;
import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowException;
import com.alibaba.csp.sentinel.slots.system.SystemBlockException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.virgo.dto.CommonResult;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* @author zhaozha
* @date 2019/10/17 上午9:49
*/
@Component
public class VirgoUrlBlockHandler implements UrlBlockHandler {
@Override
public void blocked(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, BlockException e) throws IOException {
// 统一返回对象
CommonResult commonResult = null;
// 流控
if(e instanceof FlowException){
commonResult = CommonResult.builder().status(100).msg("流控异常").build();
}
// 降级
else if(e instanceof DegradeException){
commonResult = CommonResult.builder().status(101).msg("降级异常").build();
}
// 热点
else if(e instanceof ParamFlowException){
commonResult = CommonResult.builder().status(102).msg("热点异常").build();
}
// 系统
else if(e instanceof SystemBlockException){
commonResult = CommonResult.builder().status(102).msg("系统异常").build();
}
// 授权
else if (e instanceof AuthorityException){
commonResult = CommonResult.builder().status(102).msg("授权异常").build();
}
httpServletResponse.setStatus(500);
httpServletResponse.setCharacterEncoding("utf-8");
httpServletResponse.setHeader("Content-Type", "application/json;charset=utf-8");
httpServletResponse.setContentType("application/json;charset=utf-8");
new ObjectMapper()
.writeValue(
httpServletResponse.getWriter(),
commonResult
);
}
}
- Различать происхождение (RequestOriginParser)
package com.virgo.user.sentinel;
import com.alibaba.csp.sentinel.adapter.servlet.callback.RequestOriginParser;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
/**
* @author zhaozha
* @date 2019/10/17 上午11:51
*/
@Component
public class MyRequestOriginParser implements RequestOriginParser {
@Override
public String parseOrigin(HttpServletRequest httpServletRequest) {
// todo 改成header
String origin = httpServletRequest.getParameter("origin");
if (StringUtils.isBlank(origin)) {
throw new IllegalArgumentException("origin must be specified");
}
return origin;
}
}
- Поддержка спокойных URL-адресов (UrlCleaner)
package com.virgo.user.sentinel;
import com.alibaba.csp.sentinel.adapter.servlet.callback.UrlCleaner;
import org.apache.commons.lang.math.NumberUtils;
import org.springframework.stereotype.Component;
import java.util.Arrays;
/**
* @author zhaozha
* @date 2019/10/17 下午12:05
*/
@Component
public class VirgoUrlCleaner implements UrlCleaner {
@Override
public String clean(String originUrl) {
// todo 多参数
String[] split = originUrl.split("/");
return Arrays.stream(split)
.map(string -> {
if (NumberUtils.isNumber(string)) {
return "{number}";
}
return string;
})
.reduce((a, b) -> a + "/" + b)
.orElse("");
}
}
настроить
Производственная среда
- Али АХАС