我们都知道Spring cloud 作熔断降级的组件 Hystrix,Spring cloud 之熔断机制(实战)一文中,也讲述了如何使用 Hystrix,这是大家一直耳熟能详的。其实阿里的一款神器 Sentinel,也可以提供熔断降级的功能。
Sentinel简介
随着微服务的发展,服务们之间的稳定性变得越来越重要。Sentinel 以流量作为切入点,从流量控制、熔灾降级、系统负载保护等多个维度来保护服务的稳定性。Sentinel 具有以下特征:完备的实时监控、广泛的开源生态、完善的 SPI 扩展点、积累丰富的场景。
首先,我们先简单看看 Sentinel 与 Hystrix 的区别:
功能 | Sentinel | Hystrix |
---|---|---|
隔离策略 | 信号量隔离策略 | 线程池/信号量隔离策略 |
熔断降级 | 基于响应时间、异常次数或异常比例 | 基于异常比例 |
动态规则配置 | 支持多数据源 | 支持多数据源 |
限流 | 基于QPS | 有限的支持 |
系统自适应保护 | 支持 | 不支持 |
控制台管理 | 可配置规则、查看监控、服务发现等 | 简单的链路监控查看 |
基于注解的支持 | 支持 | 支持 |
处理规则 | 支持预热、匀速器、排队模式 | 不支持 |
扩展性 | 多个扩展 | 插件形式 |
上面看到 Sentinel 能实现的功能也很多的,而且在熔断方面跟 Hystrix 差不多,甚至更好,所以接下来我们看看利用 Sentinel 来实现熔断的逻辑。
首先,服务生产者还是按照 Spring Cloud Kubernetes之实战二服务注册与发现 一文来进行,这里利用了简单的k8s作为服务的注册与发现功能。跟
eureka、zk、nacos 等配置都基本类似。只不过这里不用那些了,只用k8s来处理即可。
对于服务消费者,首先配置依赖:
代码语言:javascript复制<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.8.RELEASE</version>
<relativePath/>
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<swagger.version>2.6.1</swagger.version>
<xstream.version>1.4.7</xstream.version>
<pageHelper.version>4.1.6</pageHelper.version>
<fastjson.version>1.2.51</fastjson.version>
<springcloud.version>Greenwich.SR4</springcloud.version>
<!-- <springcloud.version>2.1.8.RELEASE</springcloud.version> -->
<springcloud.kubernetes.version>1.1.1.RELEASE</springcloud.kubernetes.version>
<mysql.version>5.1.46</mysql.version>
<springcloud.alibaba.version>0.2.2.RELEASE</springcloud.alibaba.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>${springcloud.alibaba.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${springcloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-undertow</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-oauth2</artifactId>
</dependency>
<!-- spring-cloud-starter-oauth2包含了 spring-cloud-starter-security -->
<!-- <dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-security</artifactId>
</dependency> -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-actuator-autoconfigure</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-kubernetes-config</artifactId>
</dependency>
<!-- springcloud-k8s-discovery -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-commons</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-kubernetes-core</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-kubernetes-discovery</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-kubernetes-ribbon</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
</dependencies>
接下来就需要配置 Sentinel 的监听:
代码语言:javascript复制spring:
application:
name: operate-git-service
cloud:
sentinel:
enabled: true #Sentinel自动化配置是否生效
eager: true #取消Sentinel控制台懒加载
log:
dir: /data/sentinel/logs #Sentinel日志文件所在的目录
transport:
dashboard: localhost:8080 #sentinel的Dashboard地址
heartbeatIntervalMs: 500 #应用与Sentinel控制台的心跳间隔时间 or heartbeat-interval-ms
port: 8719
以上配置完成后,我们写一个测试类 TestController:
代码语言:javascript复制@GetMapping(value = "/getUser")
//@SentinelResource(value = "hello",blockHandler = "apiBlockHandlerException", blockHandlerClass = MyExceptionUtil.class) //fallback = "testFallBack"
public Response<Object> getUser() {
return gitClientService.getUser();
}
接着写一个Service:
代码语言:javascript复制@SentinelResource(value = "hello",blockHandler = "apiBlockHandlerException", blockHandlerClass = MyExceptionUtil.class, fallback = "testFallBack")
@Override
public Response<Object> getUser() {
return Response.ok(200, 0, "success", "data");
}
public Response<Object> testFallBack() {
return Response.ok(200, -5, "testFallBack ...", null);
}
限流异常处理类:
public class MyExceptionUtil {
/**
*
* @author Damon
* @date 2020年3月24日
*
*/
/**
*
* 基于API函数的限流
* @param ex
* @return
* @author Damon
* @date 2020年3月24日
*
*/
public static Response<Object> apiBlockHandlerException(BlockException ex) {
return Response.ok(200, -4, "block 。。。。", null);
}
}
大家可以发现,controller 与 service 中都可以有注解 @SentinelResource,而且在 Spring cloud 之多种方式限流(实战)一文中,我们知道 Sentinel 默认为所有的 HTTP 服务提供限流埋点,而且有默认的限流提示(对于接口),只需要控制配置限流规则即可。这里,我们加了 @SentinelResource,其提供了属性 blockHandler、blockHandlerClass 限流的处理逻辑,同时,该注解也提供了 fallback 属性可以直接加熔断的处理函数。
同样,我们先打开 Sentinel 的Dashboard,看看其服务实时监控(如果没有则先请求几个接口):
请求的几个簇点链路:
我们可以针对函数本身作任何的规则设置,也可以针对注解@SentinelResource资源来进行设置规则:
发现,对于方法,直接加限流或降级规则设置后,其返回的结果都是:
代码语言:javascript复制Blocked by Sentinel (flow limiting)
而对于注解 @SentinelResource,设置限流规则:
访问其接口时,发现返回:
代码语言:javascript复制{"message":{"status":200,"code":-4,"message":"block 。。。。"},"data":null}
设置熔断规则:
访问后,返回:
代码语言:javascript复制{"message":{"status":200,"code":-5,"message":"testFallBack ..."},"data":null}
从上面的测试结果来看:
- Sentinel 默认给所有的Http服务 设下埋点,只要定义好规则(限流、熔断),其都会按照规则执行,并且返回默认的信息: Blocked by Sentinel (flow limiting)
- Sentinel 中设置的注解 @SentinelResource 中,包括属性 value、blockHandler、blockHandlerClass、fallback,其中中间两个是对限流定义的类与其出路逻辑函数,而 fallback 是针对熔断规则设置的处理函数。
- 设置的规则中,限流的处理函数中,必须要有BlockException ex 这个参数,返回值要与原函数的返回值一致。而熔断的处理函数中,其函数的参数与返回值必须与原函数的都完全一致。
OK,Sentinel 作为熔断机制来处理,操作结束。