sentinel
sentinel -- 用于服务容错
为微服务集成Sentinel
- 首先要导入对应依赖
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
- 在对应微服务下面的配置文件新增配置
spring:
cloud:
sentinel:
transport:
dashboard: localhost:8080
- 启动控制台,通过访问localhost:8080进入控制台
- 账号密码默认是sentinel
- 在对应的微服务的controller下添加两个测试函数,重启这个服务下的主函数
@RestController
public class SentinelController {
@RequestMapping("/sentinel1")
public String sentinel1() {
//模拟一次网络延时
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "sentinel1";
}
@RequestMapping("/sentinel2")
public String sentinel2() {
return "测试高并发下的问题";
}
}
- 在浏览器中查询这个服务端口下新增的测试接口
- 例如:http://localhost:8091/sentinel2
- 能得到对应结果
- 在sentinel控制台也会有对应的显示,这说明sentinel配置是正确的,并且可以看到每个资源名后面都对应四个按钮,流控,熔断,热点,授权,这就是sentinel的主要功能,通过这些功能就能实现对微服务稳定性的保障
流量控制
- 本质是某个资源在单位时间内最多允许多少流量,超额之后应该怎么处理
- 阈值类型:
- QPS:每秒允许通过的请求数
- 并发线程数:同一时刻允许并行发生的线程数
- 流控模式:
- 直接:只统计当前该资源自己的流量,这是默认模式,超过阈值就限流
- 关联:规则配置在当前资源上,但统计的另一个资源,另一个资源流量超过阈值,当前资源会被限流
- 链路:只统计并限制某一条调用链,其他链路不受限制
- 流控效果(只有QPS有):
- 快速失败:一旦QPS超过阈值,就直接限流
- warm up:让阈值按照一个设定时间从一个低阈值缓慢上升
- 排队等待:请求来了先排队,按照固定间隔慢慢放行
- 阈值类型:
- 链路模式中,sentinel会默认给进入SpringMVC的所有的请求都设置在同一个root资源,会导致链路模式失效,所以需要新增一个配置,关闭这种资源聚合
spring:
cloud:
sentinel:
web-context-unify: false
/**
* 链路模式对于新增的方法,一般是不被sentinel监视的,所以需要@SentinelResource(value = "res")注解来标记监控
* sentinel中的资源(在sentinel控制动态的链路中能被识别)
* 1.controller中的接口默认就是资源
* 2.使用@SentinelResource注解定义的资源
*/
熔断降级
- 本质是解决调用链路中某个资源不稳定,存在请求响应时间长或异常比例升高时,达到阈值后暂时切断调用
- 熔断策略:
- 慢调用比例:设置一个RT(
Response Time)临界值,超过这个临界值算慢调用,统计窗口请求达到最小请求数,且慢调用比例超过比例阈值,就熔断;经过设定好的熔断时间后,如果再来的请求响应时间小于慢调用RT则结束熔断,如果大于就再次熔断 - 异常比例:窗口请求达到最小请求数,且异常比例超过阈值,就熔断;经过熔断时长后,请求成功就结束,不成功就再次熔断
- 异常数:窗口业务异常总数超过阈值,就熔断;后续判断同上
- 慢调用比例:设置一个RT(
- 熔断策略:
热点参数限流
- 本质是对于经常被访问的热点数据,对其进行限制,热点限流仅对包含热点参数的资源调用生效
- 普通限流是整个资源的QPS,热点限流是某个参数值的QPS
- 注:一定需要在请求方法上贴@SentinelResource,否则热点规则无效
- 参数索引:对第几个方法参数做限流
- 单击阈值:每秒允许的请求数
- 统计窗口时长:
系统规则
- 本质上是从应用整体维度来看,只对入口流量生效
- LOAD:系统load1超过阈值,且并发线程数超过系统容量会触发
- RT:所有入口请求的平均RT超过阈值
- 线程数:所有入口请求的并发线程数超过阈值
- 入口QPS:所有入口流量总请求数超过阈值
- CPU使用率:CPU使用率超过阈值
授权规则
- 本质上是按照调用来源设置黑白名单,黑名单禁止访问,白名单允许访问
@Component
public class MyRequestParser implements RequestOriginParser {
//让sentinel解析请求头中有没有对应的要求字段
@Override
public String parseOrigin(HttpServletRequest httpServletRequest) {
//从头文件中获取serviceName,也就是黑白名单对应的名字
String serviceName = httpServletRequest.getHeader("serviceName");
if (serviceName == null || "".equals(serviceName)) {
return "error";
}
return serviceName;
}
}
sentinel的自定义异常返回
- 我们可以自定义异常返回,就是在请求被拒绝的时候弹出来的提示
- FlowException 限流异常
- DegradeException 降级异常
- ParamFlowException 参数限流异常
- AuthorityException 授权异常
- SystemBlockException 系统负载异常
@Component
public class MySentinelExceptionHandler implements BlockExceptionHandler {
/**
* 自定义异常处理 - sentinel在请求进入controller前就拦下
* @param httpServletRequest 请求
* @param httpServletResponse 响应
* @param e 异常的父类
* @throws Exception
*/
@Override
public void handle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, BlockException e) throws Exception {
Map<String, Object> map = new HashMap<>();
if (e instanceof FlowException){
//限流异常
map.put("code",511);
map.put("message","限流异常");
}else if (e instanceof DegradeException) {
//降级异常
map.put("code",512);
map.put("message","降级异常");
}else if (e instanceof ParamFlowException){
//热点异常
map.put("code",513);
map.put("message","热点异常");
}else if (e instanceof AuthorityException){
//授权异常
map.put("code",514);
map.put("message","授权异常");
}else {
//系统异常
map.put("code",515);
map.put("message","系统异常");
}
httpServletResponse.setContentType("application/json;charset=utf-8");
httpServletResponse.getWriter().write(JSON.toJSONString(map));
}
}
@RestControllerAdvice
public class MyExceptionHandler {
//sentinel在@SentinelResource这层拦截,就会在这个函数被捕获
@ExceptionHandler(BlockException.class)
public Object handleBlockException(HttpServletRequest request, HttpServletResponse response, BlockException e) throws Exception {
Map<String, Object> map = new HashMap<>();
if (e instanceof FlowException){
//限流异常
map.put("code",511);
map.put("message","限流异常");
}else if (e instanceof DegradeException) {
//降级异常
map.put("code",512);
map.put("message","降级异常");
}else if (e instanceof ParamFlowException){
//热点异常
map.put("code",513);
map.put("message","热点异常");
}else if (e instanceof AuthorityException){
//授权异常
map.put("code",514);
map.put("message","授权异常");
}else {
//系统异常
map.put("code",515);
map.put("message","系统异常");
}
return map;
}
}
Feign整合Sentinel
- 首先还是在配置文件添加相关权限
feign:
sentinel:
enabled: true
- 创建容错类,在没有出现拒绝访问但数据查询不到等情况提供兜底数据
@Component
public class PorudctFeignFallback implements ProductfeignApi {
@Override
public TShopProduct get(Integer id) {
TShopProduct product = new TShopProduct();
product.setPid(-1L);
product.setPname("商品错误");
product.setPprice(0.0);
product.setStock(0);
return product;
}
}
- 在之前创建的feign类中修改注解FeignClient,添加fallback,就是降级调用的数据
//先尝试远端,远端没有,就是降级调用fallback
@FeignClient(name = "product",fallback = PorudctFeignFallback.class)
public interface ProductfeignApi {
@GetMapping("/product/get/{id}")
public TShopProduct get(@PathVariable("id") Integer id);
}
- 目的是为了使用户可以正常使用其他模块的微服务,避免这个服务下的所有微服务模块宕机,让用户受到过多影响
总结
sentinel就是保证微服务稳定性的,主动放弃一小部分非关键请求来保证系统在高峰使用期能够稳定被使用,避免因为请求过多造成服务崩溃,影响用户体验

浙公网安备 33010602011771号