在微服务中当面对大流量冲击时,通常会引入熔断降级等机制。如何有效防止服务被冲垮是微服务必须要考虑的一个方面。熔断降级的方式有许多种,可以用Hystrix或者手动嵌入代码完成,本文将介绍如何在spring cloud的最新版本Hoxton中用阿里的Sentinel来实现优雅熔断降级。Sentinel有非常友好的界面来进行流量管理。本文的方案将用Nacos来存储规则配置项,以符合生产级别的Push要求。关于安装nacos高可用方案,集成spring cloud及接入nacos注册中心和配置中心,在之前的文章已说明。如需看上一篇文章可以看我的主页,或者从下面链接看:
https://www.toutiao.com/i6936063678223598110/
https://www.toutiao.com/i6934971763508036133/
Sentinel的官方文档链接为:
https://sentinelguard.io/zh-cn/index.html
引用官方的解释简单说明Sentinel:
Sentinel 是面向分布式服务架构的高可用防护组件,主要以流量为切入点,从流量控制、熔断降级、系统自适应保护等多个维度来帮助用户保障微服务的稳定性。
由于本文说的是针对生产的实践,所以也是按照生产的方式来进行,其它的方式不在本文所讨论范围内。如需要看所有的方式可以用如下链接查看:
https://github.com/alibaba/Sentinel/wiki/%E5%9C%A8%E7%94%9F%E4%BA%A7%E7%8E%AF%E5%A2%83%E4%B8%AD%E4%BD%BF%E7%94%A8-Sentinel
简述所有的实现方式区别如下:
一般来说,规则的推送有下面三种模式:
推送模式 | 说明 | 优点 | 缺点 |
原始模式 | API 将规则推送至客户端并直接更新到内存中,扩展写数据源(WritableDataSource) | 简单,无任何依赖 | 不保证一致性;规则保存在内存中,重启即消失。严重不建议用于生产环境 |
Pull 模式 | 扩展写数据源(WritableDataSource), 客户端主动向某个规则管理中心定期轮询拉取规则,这个规则中心可以是 RDBMS、文件 等 | 简单,无任何依赖;规则持久化 | 不保证一致性;实时性不保证,拉取过于频繁也可能会有性能问题。 |
Push 模式 | 扩展读数据源(ReadableDataSource),规则中心统一推送,客户端通过注册监听器的方式时刻监听变化,比如使用 Nacos、Zookeeper 等配置中心。这种方式有更好的实时性和一致性保证。生产环境下一般采用 push 模式的数据源。 | 规则持久化;一致性;快速 | 引入第三方依赖 |
我们将采用Push模式,用Nacos保证实时和一致性。
一、生产Push方式
本文还没有实现完整的Push方式。本文的具体的实现方式如下:
配置中心Nacos在配置好限流的规则后,应用服务A、B、C在启动的时候会向远程配置中心Nacos拉取配置规则,放到应用本地的内存中。Sentinel控制台中修改规则也会直接同步到应用服务中,但是不会同步到Nacos上。
修改规则如下:
1、Sentinel控制台中修改规则:仅存在于服务的内存中,不会修改Nacos中的配置值,重启后恢复原来的值。
2、Nacos控制台中修改规则:服务的内存中规则会更新,Nacos中持久化规则也会更新,重启后依然保持。
二、项目实现
本文是在spring cloud中接入Sentinel并用Nacos保存规则。
1、在父pom.xml中添加spring cloud和alibaba组件
依然是要先在父pom.xml中引入 spring cloud和alibaba 组件具体如下:
版本<properties>中:
<spring.cloud-version>Hoxton.SR8</spring.cloud-version>
<spring-cloud-alibaba.version>2.2.5.RELEASE</spring-cloud-alibaba.version>
依赖<dependencies>中:
<!-- 引入 spring cloud-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring.cloud-version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!-- 引入 alibaba 组件-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>${spring-cloud-alibaba.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
2、添加相关的Sentinel的依赖
在具体的项目中如本项目zizai-wxwork-api中添加依赖:
<!--sentinel -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-nacos</artifactId>
</dependency>
spring-cloud-starter-alibaba-sentinel为接入Sentinel组件,sentinel-datasource-nacos为用Nacos作为存储组件。
3、在application.yml配置dashboard的URL和Nacos地址
服务要接入dashborad的规则命令,拉取Nacos的规则数据,需要配置对应的地址。
spring:
cloud:
sentinel:
transport:
dashboard: localhost:8080
datasource:
ds:
nacos:
server-addr: nacos-web.test.thinkinpower.net:80
dataId: ${spring.application.name}-sentinel
groupId: DEFAULT_GROUP
namespace: sentinel-config
data-type: json
rule-type: flow
具体的参数说明如下:
spring.cloud.sentinel.transport.dashboard:sentinel dashboard的访问地址,根据上面准备工作中启动的实例配置
spring.cloud.sentinel.datasource.ds.nacos.server-addr:nacos的访问地址,,根据上面准备工作中启动的实例配置
spring.cloud.sentinel.datasource.ds.nacos.groupId:nacos中存储规则的groupId
spring.cloud.sentinel.datasource.ds.nacos.dataId:nacos中存储规则的dataId
spring.cloud.sentinel.datasource.ds.nacos.namespace:nacos中存储规则的namespace
spring.cloud.sentinel.datasource.ds.nacos.data-type nacos中存储规则的数据类型为设置为json
spring.cloud.sentinel.datasource.ds.nacos.rule-type 该参数是用来定义存储的规则类型,设置为flow。所有的规则类型可查看枚举类:com.alibaba.cloud.sentinel.datasource.RuleType
相关RuleType的枚举类有如下:
FLOW("flow", FlowRule.class),
/**
* degrade.
*/
DEGRADE("degrade", DegradeRule.class),
/**
* param flow.
*/
PARAM_FLOW("param-flow", ParamFlowRule.class),
/**
* system.
*/
SYSTEM("system", SystemRule.class),
/**
* authority.
*/
AUTHORITY("authority", AuthorityRule.class),
/**
* gateway flow.
*/
GW_FLOW("gw-flow",
"com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayFlowRule"),
/**
* api.
*/
GW_API_GROUP("gw-api-group",
"com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiDefinition");
4、创建一个测试方法
如下用hi作测试方法,访问url为:http://localhost:11880/k8s/hi
@RestController
@RequestMapping(value = "/k8s")
public class K8SLiveController {
@GetMapping("/hi")
public String hi() {
return "yaokj";
}
}
5、配置Nacos规则
nacos的规则是应用在启动和重启时会拉取配置。
先创建命名空间sentinel-config:
在命名空间sentinel-config下创建配置:
配置的详情如下:
详情里的说明如下:
dataI为:zizai-wxwork-api-sentinel
组为默认的组。
配置内容:
[
{
"resource": "/k8s/hi",
"limitApp": "default",
"grade": 1,
"count": 1,
"strategy": 0,
"controlBehavior": 0,
"clusterMode": false
}
]
这里我们为了测试设置QPS为1。
内容说明:
上面配置规则是一个数组类型,数组中的每个对象是针对每一个保护资源的配置对象,每个对象中的属性解释如下:
resource:资源名,即限流规则的作用对象
limitApp:流控针对的调用来源,若为 default 则不区分调用来源
grade:限流阈值类型(QPS 或并发线程数);0代表根据并发数量来限流,1代表根据QPS来进行流量控制
count:限流阈值
strategy:调用关系限流策略
controlBehavior:流量控制效果(直接拒绝、Warm Up、匀速排队)
clusterMode:是否为集群模式
6、自定义Sentinel异常的返回数据结构
在发生了降级限制后,默认的返回结果只是一个字符串。为了统一返回的数据结构,我们自己定义一个类MyBlockHandler,MyBlockHandler要继承BlockExceptionHandler。具体代码如下:
/**
* 自定义Sentinel异常实现
*
* @author yaokj
*
*/
@Component
public class MyBlockHandler implements BlockExceptionHandler {
@Override
public void handle(HttpServletRequest request, HttpServletResponse response, BlockException ex) throws Exception {
String msg = null;
if (ex instanceof FlowException) {
msg = "限流了";
} else if (ex instanceof DegradeException) {
msg = "降级了";
} else if (ex instanceof ParamFlowException) {
msg = "热点参数限流";
} else if (ex instanceof SystemBlockException) {
msg = "系统规则(负载/...不满足要求)";
} else if (ex instanceof AuthorityException) {
msg = "授权规则不通过";
}
Result<?> result = Result.fail(msg);
// http状态码
response.setStatus(500);
response.setCharacterEncoding("utf-8");
response.setHeader("Content-Type", "application/json;charset=utf-8");
response.setContentType("application/json;charset=utf-8");
// msg可以是对象
new ObjectMapper().writeValue(response.getWriter(), result);
}
}
7、启动项目测试
启动spring boot项目,并多次发起测试连接请求:http://localhost:11880/k8s/hi。因为配置了QPS为1,超过2即看到结果返回:
{"status":100,"code":"1000","message":"限流了","data":null}
说明已经生效。
三、安装Sentinel dashboard 控制台
Sentinel 控制台是流量控制、熔断降级规则统一配置和管理的入口,它为用户提供了机器自发现、簇点链路自发现、监控、规则配置等功能。在 Sentinel 控制台上,我们可以配置规则并实时查看流量控制效果。
为了使可视化效果更加好,可以安装dashboard。安装dashborad的方法参考官方:
https://github.com/alibaba/Sentinel/tree/master/sentinel-dashboard
或者https://sentinelguard.io/zh-cn/docs/dashboard.html
1. 编译和启动
进入
https://github.com/alibaba/Sentinel/tree/master/sentinel-dashboard
把代码拉下来。
1.1 如何编译
使用如下命令将代码打包成一个 fat jar:
mvn clean package
1.2 如何启动
使用如下命令启动编译后的控制台:
java -Dserver.port=8080 \
-Dcsp.sentinel.dashboard.server=localhost:8080 \
-Dproject.name=sentinel-dashboard \
-jar target/sentinel-dashboard.jar
上述命令中我们指定几个 JVM 参数,其中 -Dserver.port=8080 是 Spring Boot 的参数, 用于指定 Spring Boot 服务端启动端口为 8080。其余几个是 Sentinel 客户端的参数。
为便于演示,我们对控制台本身加入了流量控制功能,具体做法是引入 Sentinel 提供的 CommonFilter 这个 Servlet Filter。 上述 JVM 参数的含义是:
参数 | 作用 |
-Dcsp.sentinel.dashboard.server=localhost:8080 | 向 Sentinel 接入端指定控制台的地址 |
-Dproject.name=sentinel-dashboard | 向 Sentinel 指定应用名称,比如上面对应的应用名称就为 sentinel-dashboard |
全部的配置项可以参考 启动配置项文档。
经过上述配置,控制台启动后会自动向自己发送心跳。程序启动后浏览器访问 localhost:8080 即可访问 Sentinel 控制台。
从 Sentinel 1.6.0 开始,Sentinel 控制台支持简单的登录功能,默认用户名和密码都是 sentinel。用户可以通过如下参数进行配置:
- -Dsentinel.dashboard.auth.username=sentinel 用于指定控制台的登录用户名为 sentinel;
- -Dsentinel.dashboard.auth.password=123456 用于指定控制台的登录密码为 123456;如果省略这两个参数,默认用户和密码均为 sentinel;
- -Dserver.servlet.session.timeout=7200 用于指定 Spring Boot 服务端 session 的过期时间,如 7200 表示 7200 秒;60m 表示 60 分钟,默认为 30 分钟;
2. 客户端接入
选择合适的方式接入 Sentinel,然后在应用启动时加入 JVM 参数 -Dcsp.sentinel.dashboard.server=consoleIp:port 指定控制台地址和端口。 确保客户端有访问量,Sentinel 会在客户端首次调用的时候进行初始化,开始向控制台发送心跳包,将客户端纳入到控制台的管辖之下。
3. 验证是否接入成功
客户端正确配置并启动后,会在初次调用后主动向控制台发送心跳包,汇报自己的存在; 控制台收到客户端心跳包之后,会在左侧导航栏中显示该客户端信息。如果控制台能够看到客户端的机器信息,则表明客户端接入成功了。
具体的效果如下:
可以看到正常通过和拒绝的量。
若要看配置的规则可以看:
四、Sentinel 和 Hystrix 区别
说到限流Sentinel 和常用的熔断降级库Netflix Hystrix 有什么异同,看Sentinel官网有一个对比:
Hystrix 的关注点在于以 隔离 和 熔断 为主的容错机制,超时或被熔断的调用将会快速失败,并可以提供 fallback 机制。
而 Sentinel 的侧重点在于:
- 多样化的流量控制
- 熔断降级
- 系统负载保护
- 实时监控和控制台
可以看到两者解决的问题还是有比较大的不同的,下面我们来分别对比一下。
Sentinel | Hystrix | |
隔离策略 | 信号量隔离 | 线程池隔离/信号量隔离 |
熔断降级策略 | 基于慢调用比例或异常比例 | 基于失败比率 |
实时指标实现 | 滑动窗口 | 滑动窗口(基于 RxJava) |
规则配置 | 支持多种数据源 | 支持多种数据源 |
扩展性 | 多个扩展点 | 插件的形式 |
基于注解的支持 | 支持 | 支持 |
限流 | 基于 QPS,支持基于调用关系的限流 | 有限的支持 |
流量整形 | 支持慢启动、匀速排队模式 | 不支持 |
系统自适应保护 | 支持 | 不支持 |
控制台 | 开箱即用,可配置规则、查看秒级监控、机器发现等 | 不完善 |
常见框架的适配 | Servlet、Spring Cloud、Dubbo、gRPC 等 | Servlet、Spring Cloud Netflix |
喜欢的朋友评论、点赞、转发、收藏本文。有疑问的在评论区留言。谢谢!