文章目录
- Sentinel高并发方法论
- Sentinel是什么?
- 基本概念及作用
- 整合SpringBoot
- 引入Sentinel依赖
- 下载Sentinel控制台
- cmd启动Sentinel
- 配置Sentinel控制台地址信息
- 在控制台设置流控规则
- 规则持久化
- 流量监控
- 自定义流控返回数据
- 适配Feign,并加上熔断保护方法
- 自定义受保护的资源
- 网关流控
Sentinel高并发方法论
Sentinel是什么?
Sentinel中文文档:https://github.com/alibaba/Sentinel/wiki/介绍
随着微服务的流行,服务和服务之间的稳定性变得越来越重要。Sentinel 以流量为切入点,从流量控制、熔断降级、系统负载保护等多个维度保护服务的稳定性。
Sentinel 具有以下特征:
- 丰富的应用场景:Sentinel 承接了阿里巴巴近 10 年的双十一大促流量的核心场景,例如秒杀(即突发流量控制在系统容量可以承受的范围)、消息削峰填谷、集群流量控制、实时熔断下游不可用应用等。
- 完备的实时监控:Sentinel 同时提供实时的监控功能。您可以在控制台中看到接入应用的单台机器秒级数据,甚至 500 台以下规模的集群的汇总运行情况。
- 广泛的开源生态:Sentinel 提供开箱即用的与其它开源框架/库的整合模块,例如与 Spring Cloud、Apache Dubbo、gRPC、Quarkus 的整合。您只需要引入相应的依赖并进行简单的配置即可快速地接入 Sentinel。同时 Sentinel 提供 Java/Go/C++ 等多语言的原生实现。
- 完善的 SPI 扩展机制:Sentinel 提供简单易用、完善的 SPI 扩展接口。您可以通过实现扩展接口来快速地定制逻辑。例如定制规则管理、适配动态数据源等。
Sentinel的主要特征:
Sentinel的开源生态:
Sentinel 分为两个部分:
- **核心库(Java 客户端)**不依赖任何框架/库,能够运行于所有 Java 运行时环境,同时对 Dubbo / Spring Cloud 等框架也有较好的支持。
- **控制台(Dashboard)**基于 Spring Boot 开发,打包后可以直接运行,不需要额外的 Tomcat 等应用容器。
基本概念及作用
基本概念:
**资源:**是 Sentinel 的关键概念。它可以是 Java 应用程序中的任何内容,例如,由应用程序提供的服务,或由应用程序调用的其它应用提供的服务,甚至可以是一段代码。在接下来的文档中,我们都会用资源来描述代码块。
只要通过 Sentinel API 定义的代码,就是资源,能够被 Sentinel 保护起来。大部分情况下,可以使用方法签名,URL,甚至服务名称作为资源名来标示资源。
**规则:**围绕资源的实时状态设定的规则,可以包括流量控制规则、熔断降级规则以及系统保护规则。所有规则可以动态实时调整。
主要作用:
- 流量控制
- 熔断降级
- 系统负载保护
我们说的资源,可以是任何东西,服务,服务里的方法,甚至是一段代码。使用 Sentinel 来进行资源保护,主要分为几个步骤:
- 定义资源
- 定义规则
- 检验规则是否生效
先把可能需要保护的资源定义好,之后再配置规则。也可以理解为,只要有了资源,我们就可以在任何时候灵活地定义各种流量控制规则。在编码的时候,只需要考虑这个代码是否需要保护,如果需要保护,就将之定义为一个资源。
整合SpringBoot
引入Sentinel依赖
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
下载Sentinel控制台
https://github.com/alibaba/Sentinel/tree/master/sentinel-dashboard
注意,版本要与项目中的Sentinel版本一致
cmd启动Sentinel
访问http://localhost:8333
默认用户名和密码都是sentinel
配置Sentinel控制台地址信息
appliaction.properties
# Sentinel控制台的端口
spring.cloud.sentinel.transport.dashboard=localhost:8333
# 随便写,只要不被占用就行,一个用来与Sentinel交互的端口
spring.cloud.sentinel.transport.port=8719
启动项目,并在网关处有一个请求通过,就会在Sentinel主页出现网关服务
在控制台设置流控规则
(默认流控规则保存在内存中,项目重启就没了)
规则持久化
无论是通过硬编码的方式来更新规则,还是通过接入 Sentinel Dashboard 后,在页面上操作更新规则,都无法避免一个问题,那就是服务重启后,规则就丢失了,因为默认情况下规则是保存在内存中的。
我们在 Dashboard 上为客户端配置好了规则,并推送给了客户端。这时由于一些因素客户端出现异常,服务不可用了,当客户端恢复正常再次连接上 Dashboard 后,这时所有的规则都丢失了,我们还需要重新配置一遍规则,这肯定不是我们想要的。
持久化配置分以下3步:
- 引入依赖
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-nacos</artifactId>
</dependency>
- 添加配置
# 这里datasource后的consumer是数据源名称,可以随便写,推荐使用服务名
spring.cloud.sentinel.datasource.consumer.nacos.server-addr=localhost:8848
spring.cloud.sentinel.datasource.consumer.nacos.dataId=${spring.application.name}-sentinel-rules
spring.cloud.sentinel.datasource.consumer.nacos.groupId=SENTINEL_GROUP
spring.cloud.sentinel.datasource.consumer.nacos.data-type=json
# 规则类型,取值见:org.springframework.cloud.alibaba.sentinel.datasource.RuleType
spring.cloud.sentinel.datasource.consumer.nacos.rule_type=flow
- nacos中创建流控规则
配置内容如下:
[
{
"resource": "/hello",
"limitApp": "default",
"grade": 1,
"count": 2,
"strategy": 0,
"controlBehavior": 0,
"clusterMode": false
}
]
resource:资源名称
limitApp:限流应用,就是用默认就可以
grade:阈值类型,0表示线程数,1表示qps
count:单机阈值
strategy:流控模式,0-直接,1-关联, 2-链路
controlBehavior:流控效果。0-快速失败,1-warm up 2-排队等待
clusterMode:是否集群
查看sentinel客户端:就有了限流配置了
流量监控
利用SpringBoot提供的审计模块,引入spring-boot-starter-actuator依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
在application.properties中添加配置,让所有服务都暴露给统计模块去统计
management.endpoints.web.exposure.include=*
自定义流控返回数据
@Configuration
public class GulimallSeckillSentinelConfig {
public GulimallSeckillSentinelConfig() {
WebCallbackManager.setUrlBlockHandler(new UrlBlockHandler() {
@Override
public void blocked(HttpServletRequest request, HttpServletResponse response, BlockException ex) throws IOException {
R error = R.error(10003, "请求流量过大,请稍后再试");
response.setCharacterEncoding("UTF-8");
response.setContentType("application/json");
response.getWriter().write(JSON.toJSONString(error));
}
});
}
}
适配Feign,并加上熔断保护方法
application.properties中加入以下代码,开启sentinel对feign的支持
feign.sentinel.enabled=true
写熔断保护的兜底方法
@Component
public class SeckillFeignServiceFallBack implements SeckillFeignService {
@Override
public R getSkuSeckilInfo(Long skuId) {
return R.error(BizCodeEnum.TO_MANY_REQUEST.getCode(),BizCodeEnum.TO_MANY_REQUEST.getMsg());
}
}
与远程调用绑定 fallback = SeckillFeignServiceFallBack.class
@FeignClient(value = "gulimall-seckill",fallback = SeckillFeignServiceFallBack.class)
public interface SeckillFeignService {
@GetMapping(value = "/sku/seckill/{skuId}")
R getSkuSeckilInfo(@PathVariable("skuId") Long skuId);
}
自定义受保护的资源
在要保护的方法上加上如下注解
@SentinelResource(value = "getCurrentSeckillSkusResource",blockHandler = "blockHandler")
blockHandler是方法被限流之后的回调事件,和被限流的方法写在同一个类中,其返回值和原方法一样,传入参数比原方法多了一个异常值参数
public List<SeckillSkuRedisTo> blockHandler(BlockException e) {
log.error("getCurrentSeckillSkusResource被限流了,{}",e.getMessage());
return null;
}
网关流控
引入依赖
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-sentinel-gateway</artifactId>
<version>版本号和网关一致最好</version>
</dependency>
自定义流控返回
@Configuration
public class SentinelGatewayConfig {
public SentinelGatewayConfig() {
GatewayCallbackManager.setBlockHandler(new BlockRequestHandler() {
//网关限流了请求,就会调用此回调
@Override
public Mono<ServerResponse> handleRequest(ServerWebExchange exchange, Throwable t) {
R error = R.error(BizCodeEnum.TO_MANY_REQUEST.getCode(), BizCodeEnum.TO_MANY_REQUEST.getMsg());
String errorJson = JSON.toJSONString(error);
Mono<ServerResponse> body = ServerResponse.ok().body(Mono.just(errorJson), String.class);
return body;
}
});
}
}