> 文章列表 > Springboot应用整合Sentinel实现限流、熔断、降级笔记

Springboot应用整合Sentinel实现限流、熔断、降级笔记

Springboot应用整合Sentinel实现限流、熔断、降级笔记

可以使用官方的sentinel

也可使用进行持久化改造后的Sentinel ,本文基于此进行记录。Sentinel持久化到Nacosicon-default.png?t=N2N8https://blog.csdn.net/chenjian723122704/article/details/130098896

Sentinel版本为 1.8.6 

Nacos版本为 2.2.0

关于网关限流、限流和熔断,我自己总结出来了一套规则,有更好的意见希望大家指正。

  1. 网关只做限流,不做熔断。
  2. 普通服务被请求,比如客户请求我们的api服务,此时做限流。
  3. 普通服务调用内部服务,比如API服务,调用账单查询服务,需要做两部操作。1)在api服务的配置文件打开 feign.sentinel.enable=true;2)账单查询服务需要配置熔断规则。  当api的请求账单服务的接口达到熔断的阈值时,api下次再有请求,就会直接进入降级方法,或者抛出异常。

1.启动 Sentinel 服务

java -jar sentinel-dashboard-1.8.6.jar --server.port=8800

2.普通应用的限流策略

新建一个Springboot应用,创建一个用于测试的Controller

/* 测试* @author chenjian* @date 2022/12/30* @version 1.0*/
@Slf4j
@RefreshScope
@RestController
@RequestMapping("/test")
public class Test {@GetMapping("/test")public R test() {return R.ok();}
}/* 返回数据 @author jianchene0*/
@Slf4j
public class R extends HashMap<String, Object> {private static final long serialVersionUID = 1L;public R() {put("code", 0);put("msg", "success");}public static R error() {return error(500, "未知异常,请联系管理员");}public static R error(String msg) {return error(500, msg);}public static R error(int code, String msg) {R r = new R();r.put("code", code);r.put("msg", msg);log.error("失败信息: "+ code + "  ----  " + msg);return r;}public static R ok(String msg) {R r = new R();r.put("msg", msg);return r;}public static R ok(Map<String, Object> map) {R r = new R();r.putAll(map);return r;}public static R ok() {return new R();}@Overridepublic R put(String key, Object value) {super.put(key, value);return this;}/* 检查是否OK* @return*/public boolean isOk() {return (int)this.get("code") == 0;}
}

pom文件中引入依赖

<!-- Sentinel流量监控、限流、熔断降级处理 --><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-sentinel</artifactId></dependency>

若使用改造的支持持久化的Sentinel,还需要引入依赖

        <!-- 引入Nacos数据源 --><dependency><groupId>com.alibaba.csp</groupId><artifactId>sentinel-datasource-nacos</artifactId><version>${sentinel.version}</version></dependency>

 yaml引入sentinel配置、nacos配置及其他配置

# Tomcat
server:tomcat:uri-encoding: UTF-8port: 9803servlet:context-path: /spring:application:name: apicloud:nacos:# nacos.core.auth.enabled=true 开启权限验证discovery:# nacos地址server-addr: 127.0.0.1:8848username: nacospassword: nacosnamespace: group: DEFAULT_GROUPmetadata:management:context-path: ${server.servlet.context-path}/actuatorconfig:server-addr: 127.0.0.1:8848username: nacospassword: nacos#data-id后缀file-extension: yaml# 分组名称namespace: group: DEFAULT_GROUPencode: UTF-8# 修改后自动加载refresh-enabled: true# 允许nacos服务端向本地同步配置enable-remote-sync-config: truesentinel:eager: true  #是否开启网关限流,默认truetransport:# 添加sentinel的控制台地址dashboard: 127.0.0.1:8800#指定应用与Sentinel控制台交互的端口,应用本地会起一个该端口占用的HttpServer# port: 8719web-context-unify: false # 默认将调用链路收敛,导致链路流控效果无效# 持久化配置datasource:flow-rule: # 唯一名称可自定义nacos:# 设置Nacos的连接地址、命名空间和Group IDserver-addr: ${spring.cloud.nacos.discovery.server-addr}username: ${spring.cloud.nacos.discovery.username}password: ${spring.cloud.nacos.discovery.password}namespace: # 为空,使用默认的public# 设置Nacos中配置文件的命名规则dataId: ${spring.application.name}-flow-rulesgroupId: SENTINEL_GROUPdata-type: json# 必填的重要字段,指定当前规则类型是"限流"rule-type: flowdegrade-rule: # 唯一名称可自定义nacos:# 设置Nacos的连接地址、命名空间和Group IDserver-addr: ${spring.cloud.nacos.discovery.server-addr}username: ${spring.cloud.nacos.discovery.username}password: ${spring.cloud.nacos.discovery.password}namespace: # 为空,使用默认的public# 设置Nacos中配置文件的命名规则dataId: ${spring.application.name}-degrade-rulesgroupId: SENTINEL_GROUPdata-type: json# 必填的重要字段,指定当前规则类型是"限流"rule-type: degradesystem-rule: # 唯一名称可自定义nacos:# 设置Nacos的连接地址、命名空间和Group IDserver-addr: ${spring.cloud.nacos.discovery.server-addr}username: ${spring.cloud.nacos.discovery.username}password: ${spring.cloud.nacos.discovery.password}namespace: # 为空,使用默认的public# 设置Nacos中配置文件的命名规则dataId: ${spring.application.name}-system-rulesgroupId: SENTINEL_GROUPdata-type: json# 必填的重要字段,指定当前规则类型是"限流"rule-type: systemparam-flow-rule: # 唯一名称可自定义nacos:# 设置Nacos的连接地址、命名空间和Group IDserver-addr: ${spring.cloud.nacos.discovery.server-addr}username: ${spring.cloud.nacos.discovery.username}password: ${spring.cloud.nacos.discovery.password}namespace: # 为空,使用默认的public# 设置Nacos中配置文件的命名规则dataId: ${spring.application.name}-param-flow-rulesgroupId: SENTINEL_GROUPdata-type: json# 必填的重要字段,指定当前规则类型是"限流"rule-type: param-flowauthority-rule: # 唯一名称可自定义nacos:# 设置Nacos的连接地址、命名空间和Group IDserver-addr: ${spring.cloud.nacos.discovery.server-addr}username: ${spring.cloud.nacos.discovery.username}password: ${spring.cloud.nacos.discovery.password}namespace: # 为空,使用默认的public# 设置Nacos中配置文件的命名规则dataId: ${spring.application.name}-authority-rulesgroupId: SENTINEL_GROUPdata-type: json# 必填的重要字段,指定当前规则类型是"限流"rule-type: authorityfeign:sentinel:enabled: true #sentinel是不会对feign进行监控的,需要开启配置

此外,定义Sentinel的异常处理器,使得当被限流、降级时,可以同一返回异常信息

import com.ah.ums.common.constants.ResCodeEnum;
import com.ah.ums.common.utils.R;
import com.alibaba.csp.sentinel.adapter.spring.webmvc.callback.BlockExceptionHandler;
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.alibaba.fastjson.JSON;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.stereotype.Component;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;/* sentinel 限流降级后异常处理* @author jianchen*/
@Component
public class SentinelExceptionHandler implements BlockExceptionHandler {@Overridepublic void handle(HttpServletRequest request, HttpServletResponse response, BlockException ex) throws Exception {ResCodeEnum resCodeEnum = null;if (ex instanceof FlowException) {resCodeEnum = ResCodeEnum.SENTINEL_FLOW_ERROR;} else if (ex instanceof DegradeException) {resCodeEnum = ResCodeEnum.SENTINEL_DEGRADE_ERROR;} else if (ex instanceof ParamFlowException) {resCodeEnum = ResCodeEnum.SENTINEL_PARAM_FLOW_ERROR;} else if (ex instanceof SystemBlockException) {resCodeEnum = ResCodeEnum.SENTINEL_SYSTEM_ERROR;} else if (ex instanceof AuthorityException) {resCodeEnum = ResCodeEnum.SENTINEL_AUTHORITY_ERROR;}// http状态码response.setCharacterEncoding("utf-8");response.setHeader("Content-Type", "application/json;charset=utf-8");response.setContentType("application/json;charset=utf-8");String errJson = JSON.toJSONString(R.error(resCodeEnum.getCode(), resCodeEnum.getMsg()));new ObjectMapper().writeValue(response.getWriter(), errJson);}
}/* 返回码枚举 @author chenjian* @version 1.0* @date 2022/11/11*/
public enum ResCodeEnum {ok(0, "成功"),NO_PARAM(201, "传入参数缺失,请检查"),ERR_PARAM(202, "传入参数异常,请检查"),VALID_FAIL_PARAM(203, "参数校验异常"),SENTINEL_FLOW_ERROR(427, "请求过于频繁,被限流了"),SENTINEL_DEGRADE_ERROR(428, "请求过于频繁,被降级了"),SENTINEL_PARAM_FLOW_ERROR(429, "热点数据请求过于频繁"),SENTINEL_SYSTEM_ERROR(430, "请求过于频繁,触发系统限制"),SENTINEL_AUTHORITY_ERROR(431, "授权规则不通过"),UNKNOWN(999, "操作失败!!");private Integer code;private String msg;ResCodeEnum(Integer code, String msg) {this.code = code;this.msg = msg;}public Integer getCode() {return code;}public String getMsg() {return msg;}public static ResCodeEnum getByCode(Integer code) {if (code == null) {return null;}for (ResCodeEnum resMsgEnum : values()) {if(resMsgEnum.code.equals(code)) {return resMsgEnum;}}return UNKNOWN;}
}

至此,普通服务改造完成,可以使用Sentinel进行监控了

3.测试

1.分别启动 nacos sentinel 以及SpringBoot应用程序。

2.首先访问Springboot的controller 

http://localhost:9803/test/test

 

访问成功后 ,会在Sentinel的控制台看到有api的请求进来了

3.点击流控新增一条流控规则

 

 此时会发现nacos中会自动增肌了一条配置,里面记录了api的流控规则

 

 我们接着测试,在浏览器不停刷新刚刚的链接,会出现限流的提示,此时,我们的url就被限流了

 

 3.熔断规则

假设有两个服务,都是按照第2节配置了Sentinel的依赖、yaml。:

  • API 服务是对外服务,供第三方调研;
  • service 服务是内部服务,供内部调用;

一般的规则时 API服务通过openFeign调用service提供的服务。使用feign注意的是 API 一定要配置  feign.sentinel.enable=true ,否则熔断效果不会生效

在 Feign定义处,加上fallback。

 对应的fallback

 

在Sentinel增加一条熔断规则,2秒内溢出比例达到0.2就进行熔断。

 

 

我们在服务内部加上 int a = 10/0;然后再让API请求查询用户分页数据。就会出发熔断,进入回调方法。

 

4.网关限流

网关限流与第2节中的配置基本类似,不过需要增加网关相关的依赖和配置,以及增加启动参数。

4.1 网关限流的依赖,增加了一个依赖

       <!-- 引入Nacos数据源 --><dependency><groupId>com.alibaba.csp</groupId><artifactId>sentinel-datasource-nacos</artifactId><version>${sentinel.version}</version></dependency><!-- Sentinel流量监控、限流、熔断降级处理 --><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-sentinel</artifactId></dependency><!-- gateway网关整合sentinel进行限流降级 --><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-alibaba-sentinel-gateway</artifactId></dependency>

4.2 网关需要增加网关规则的持久化依赖

网关持久化依赖

spring:cloud:sentinel:datasource:gw-flow-rule: # 唯一名称可自定义nacos:# 设置Nacos的连接地址、命名空间和Group IDserver-addr: ${spring.cloud.nacos.discovery.server-addr}username: ${spring.cloud.nacos.discovery.username}password: ${spring.cloud.nacos.discovery.password}namespace: # 为空,使用默认的public# 设置Nacos中配置文件的命名规则dataId: ${spring.application.name}-gw-flow-rulesgroupId: SENTINEL_GROUPdata-type: json# 必填的重要字段,指定当前规则类型是"限流"rule-type: gw-flowgw-api-group: # 唯一名称可自定义nacos:# 设置Nacos的连接地址、命名空间和Group IDserver-addr: ${spring.cloud.nacos.discovery.server-addr}username: ${spring.cloud.nacos.discovery.username}password: ${spring.cloud.nacos.discovery.password}namespace: # 为空,使用默认的public# 设置Nacos中配置文件的命名规则dataId: ${spring.application.name}-gw-api-groupsgroupId: SENTINEL_GROUPdata-type: json# 必填的重要字段,指定当前规则类型是"限流"rule-type: gw-api-group

完整的网关yaml配置

# Tomcat
server:tomcat:uri-encoding: UTF-8port: 9803servlet:context-path: /spring:application:name: apicloud:gateway:# 路由数组:指当请求满足什么样的断言时,转发到哪个服务上routes:# 配置断言路由到 API 服务中# 路由标识,要求唯一,名称任意- id: apiuri: lb://api # 使用了lb形式,从注册中心负载均衡的获取uri# 设置断言predicates:# 断言,满足 /api/ 路径的请求都会被路由到 API 服务中- Path=/api/filters:- StripPrefix=1 # 去除url的第一个前缀,本规则是去除/apinacos:# nacos.core.auth.enabled=true 开启权限验证discovery:# nacos地址server-addr: 127.0.0.1:8848username: nacospassword: nacosnamespace: group: DEFAULT_GROUPmetadata:management:context-path: ${server.servlet.context-path}/actuatorconfig:server-addr: 127.0.0.1:8848username: nacospassword: nacos#data-id后缀file-extension: yaml# 分组名称namespace: group: DEFAULT_GROUPencode: UTF-8# 修改后自动加载refresh-enabled: true# 允许nacos服务端向本地同步配置enable-remote-sync-config: truesentinel:eager: true  #是否开启网关限流,默认true#配置限流之后的响应内容scg:fallback:# 两种模式:一种是response返回文字提示信息,一种是redirect,重定向跳转mode: response# 响应的状态response-status: 426# 响应体response-body: '{"code": 426,"message": "限流了,稍后重试!"}'transport:# 添加sentinel的控制台地址dashboard: 127.0.0.1:8800#指定应用与Sentinel控制台交互的端口,应用本地会起一个该端口占用的HttpServer# port: 8719web-context-unify: false # 默认将调用链路收敛,导致链路流控效果无效# 持久化配置datasource:flow-rule: # 唯一名称可自定义nacos:# 设置Nacos的连接地址、命名空间和Group IDserver-addr: ${spring.cloud.nacos.discovery.server-addr}username: ${spring.cloud.nacos.discovery.username}password: ${spring.cloud.nacos.discovery.password}namespace: # 为空,使用默认的public# 设置Nacos中配置文件的命名规则dataId: ${spring.application.name}-flow-rulesgroupId: SENTINEL_GROUPdata-type: json# 必填的重要字段,指定当前规则类型是"限流"rule-type: flowdegrade-rule: # 唯一名称可自定义nacos:# 设置Nacos的连接地址、命名空间和Group IDserver-addr: ${spring.cloud.nacos.discovery.server-addr}username: ${spring.cloud.nacos.discovery.username}password: ${spring.cloud.nacos.discovery.password}namespace: # 为空,使用默认的public# 设置Nacos中配置文件的命名规则dataId: ${spring.application.name}-degrade-rulesgroupId: SENTINEL_GROUPdata-type: json# 必填的重要字段,指定当前规则类型是"限流"rule-type: degradesystem-rule: # 唯一名称可自定义nacos:# 设置Nacos的连接地址、命名空间和Group IDserver-addr: ${spring.cloud.nacos.discovery.server-addr}username: ${spring.cloud.nacos.discovery.username}password: ${spring.cloud.nacos.discovery.password}namespace: # 为空,使用默认的public# 设置Nacos中配置文件的命名规则dataId: ${spring.application.name}-system-rulesgroupId: SENTINEL_GROUPdata-type: json# 必填的重要字段,指定当前规则类型是"限流"rule-type: systemparam-flow-rule: # 唯一名称可自定义nacos:# 设置Nacos的连接地址、命名空间和Group IDserver-addr: ${spring.cloud.nacos.discovery.server-addr}username: ${spring.cloud.nacos.discovery.username}password: ${spring.cloud.nacos.discovery.password}namespace: # 为空,使用默认的public# 设置Nacos中配置文件的命名规则dataId: ${spring.application.name}-param-flow-rulesgroupId: SENTINEL_GROUPdata-type: json# 必填的重要字段,指定当前规则类型是"限流"rule-type: param-flowauthority-rule: # 唯一名称可自定义nacos:# 设置Nacos的连接地址、命名空间和Group IDserver-addr: ${spring.cloud.nacos.discovery.server-addr}username: ${spring.cloud.nacos.discovery.username}password: ${spring.cloud.nacos.discovery.password}namespace: # 为空,使用默认的public# 设置Nacos中配置文件的命名规则dataId: ${spring.application.name}-authority-rulesgroupId: SENTINEL_GROUPdata-type: json# 必填的重要字段,指定当前规则类型是"限流"rule-type: authoritygw-flow-rule: # 唯一名称可自定义nacos:# 设置Nacos的连接地址、命名空间和Group IDserver-addr: ${spring.cloud.nacos.discovery.server-addr}username: ${spring.cloud.nacos.discovery.username}password: ${spring.cloud.nacos.discovery.password}namespace: # 为空,使用默认的public# 设置Nacos中配置文件的命名规则dataId: ${spring.application.name}-gw-flow-rulesgroupId: SENTINEL_GROUPdata-type: json# 必填的重要字段,指定当前规则类型是"限流"rule-type: gw-flowgw-api-group: # 唯一名称可自定义nacos:# 设置Nacos的连接地址、命名空间和Group IDserver-addr: ${spring.cloud.nacos.discovery.server-addr}username: ${spring.cloud.nacos.discovery.username}password: ${spring.cloud.nacos.discovery.password}namespace: # 为空,使用默认的public# 设置Nacos中配置文件的命名规则dataId: ${spring.application.name}-gw-api-groupsgroupId: SENTINEL_GROUPdata-type: json# 必填的重要字段,指定当前规则类型是"限流"rule-type: gw-api-group

启动网关应用,访问一次路由地址 http://localhost/api/test/test?params=1

在Sentinel的控制台能到网关的请求。

 点击流控,

  • 根据Route Id (也就是yaml中定义的routes下的id)进行限流
  • 根据API 分组进行限流,可以更细化的管理网关的限流

测试根据Route Id进行限流,先设置限流规则

刷新页面,会提示限流 http://localhost/api/test/test?params=1

 

根据API分组限流,首先需要在API管理中设置

 在流控设置中可以选择对应的分组

 删除原来的网关限流规则,使用新的规则,测试限流