1.Sentinel工作原理
学习Sentinel关注3点
- 1.如何定义资源
- 2.如何定义规则
- 3.如何处理规则

2.下载Sentinel客户端
home | Sentinel
下载连接:https://github.com/alibaba/Sentinel/releases/tag/1.8.8
1
|
java -jar sentinel-dashboard-1.8.8.jar
|
启动后访问127.0.0.1:8080端口,默认账户密码为sentinel:sentinel

3.Sentinel结合业务
导入Sentinel依赖
1
2
3
4
|
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
|
编辑配置文件连接Sentinel服务器地址
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
spring:
cloud:
openfeign:
client:
config:
product-service: # 只针对product-service服务的配置
connect-timeout: 5000 # 连接超时时间
read-timeout: 5000 # 读取超时时间
logger-level: full # 日志级别
default: # 所有其它服务的默认配置
connect-timeout: 5000 # 连接超时时间
read-timeout: 10000 # 读取超时时间
logger-level: full # 日志级别
Sentinel:
transport:
dashboard: localhost:8080 # sentinel控制台地址
eager: true # 启动时就初始化sentinel规则
|
此时启动项目就连接成功

那么如何通过Sentinel实现监控呢,在你需要监控的资源上加上@SentinelResource注解表示这个方法是Sentinel监控的资源

此时如果我们请求这个路由,Sentinel就会接收到这个请求,根据我们在Sentinel上定义的规则对请求进行拦截或放行

通过Sentinel定义流控规则限制并发数,此时我限制每次发送的请求最多为1次

当我访问过快时,业务端直接报错

4.Sentinel异常处理
Web异常自定义处理异常
由于默认的异常处理是Sentinel的默认错误页,如果我们想对这个异常处理进行优化该怎么操作呢
想要异常处理为自己定义的就要自己写一个BlockExceptionHandler

在模型层定义一个处理异常的JavaBean,给上构造方法

定义一个处理异常的类MyExceptionHandler实现BlockExceptionHandler方法,重写里面的handle方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
package com.example.exception;
import com.alibaba.csp.sentinel.adapter.spring.webmvc_v6x.callback.BlockExceptionHandler;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.example.bean.common.R;
import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Component;
import java.io.PrintWriter;
@Component
public class MyExceptionHandler implements BlockExceptionHandler {
private ObjectMapper objectMapper = new ObjectMapper();
@Override
public void handle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, String s, BlockException e) throws Exception {
httpServletResponse.setCharacterEncoding("UTF-8");
httpServletResponse.setContentType("application/json");
PrintWriter writer = httpServletResponse.getWriter();
R error = R.error(500, "系统繁忙,请稍后再试");
String json = objectMapper.writeValueAsString(error);
writer.write(json);
}
}
|

4.4.2.@SentinelResource异常处理器
一句话总结:@SentinelResource如果标注了blockhandler注解,那就是由blockhandler处理异常,如果没有标注blockhandler属性标注了fallback属性,那么就是由fallback来处理异常,如果一个属性都没有标只取了个名字,那么异常就会没人管向上抛出,最好由SpringBoot来进行处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
//当请求符合Sentinel的要求时,直接走CreateOrder方法并返回真实数据
//当请求不符合Sentinel的规则时,会走blockHandler中的CreateOrderBlockHandler方法,并调用兜底回调
@Override
@SentinelResource(value = "createOrder", blockHandler = "CreateOrderBlockHandler")
public order CreateOrder(Long userId, Long productId) {
order order = new order();
product product = productFeignClient.getProductById(productId);
//product product = getProductFromRemote2(productId);
order.setId(1L);
//计算订单总金额
order.setTotalAmount(product.getPrice().multiply(new BigDecimal("10")));
order.setUserId(userId);
order.setNikeName("zhangsan");
order.setAddress("beijing");
//远程查询商品信息,并设置到订单中
order.setProductList(Arrays.asList(product));
return order;
}
public order CreateOrderBlockHandler(Long userId, Long productId, BlockException e) {
order order = new order();
order.setId(0L);
//计算订单总金额
order.setTotalAmount(new BigDecimal("0"));
order.setUserId(userId);
order.setNikeName("未知用户");
order.setAddress("异常信息"+e.getMessage());
return order;
}
|

4.4.3.OpenFeign调用
可以从链路数据中看出链路一直到达OpenFeign远程调用地址,如果我们在远程调用地址上添加流控会发生什么呢

当请求过快时会看到页面返回OpenFeign兜底回调数据,原因是我们之前在OpenFeign接口上写过兜底回调fallback,如果请求失败并且项目中有兜底数据,那么就会显示兜底数据,如果没有写兜底回调,那么就会往上抛,知道SpringBoot来进行处理

4.4.4.Sphu硬编码方式处理异常
1
2
3
4
5
6
7
|
try {
SphU.entry("createOrder");
//项目原生代码
} catch (BlockException e) {
//进行异常处理逻辑,比如返回默认值或者抛出异常等
throw new RuntimeException(e);
}
|
5.Sentinel流控规则

阈值类型:QPS和并发线程数的区别
QPS:指每秒允许通过的请求数量,基于时间窗口统计,底层采用计数器来计数
并发线程数:适用于代码中使用了线程池,比较复杂,也是每秒运行通过的请求数量

集群阈值模式:
单机均摊:假设总请求为30,有3个集群,那么每个集群均摊10个请求
总体阈值:假设总体阈值请求为10,有3个集群,那么每个集群处理3个请求
5.1.流控模式(直接/关联/链路)


链路规则
比如有这样一个场景:订单创建时分为普通创建订单和秒杀创建订单两种情况,我在createOrder资源中流控模式选择链路模式,入口资源选择秒杀创建订单资源名,那么当请求过快时,普通创建订单不受影响,秒杀创建订单会加载兜底数据

关联规则
当系统中两个资源存在竞争关系时通常使用关联规则
当writedb的流量特别大时,readdb才会被限制

5.2.流控效果(快速失败/Warm up/排队等待)
快速失败
当请求流量超出阈值规则时,多余的请求直接会抛出异常
Warm Up(预热/冷启动)

匀速排队
以前超过的请求都是直接丢掉,匀速排队中多出来的请求一个一个排队,当排队市场超过预期时间,请求就会被丢弃
6.熔断规则

断路器工作原理

6.1.慢调用比例

解释一下这张图:
5秒内所有响应超过1秒的请求都为慢调用请求,如果慢调用请求超过总请求的80%,那么这个请求是不可靠的,那么断路器会熔断30S,这30S期间所有请求均返回错误

6.2.异常比例
5秒内向远程发送请求,如果有80%请求出现异常,那么断路器会打开熔断20秒,这20秒不会在给远程发送请求

6.3.异常数

5秒内不管你发送多少请求,只要你请求中有10个存在异常,那么就直接触发熔断规则
7.热点规则
parameter-flow-control | Sentinel
何为热点?热点即经常访问的数据。很多时候我们希望统计某个热点数据中访问频次最高的 Top K 数据,并对其访问进行限制。比如:
- 商品 ID 为参数,统计一段时间内最常购买的商品 ID 并进行限制
- 用户 ID 为参数,针对一段时间内频繁访问的用户 ID 进行限制
热点参数限流会统计传入参数中的热点参数,并根据配置的限流阈值与模式,对包含热点参数的资源调用进行限流。热点参数限流可以看做是一种特殊的流量控制,仅对包含热点参数的资源调用生效。
1
2
3
|
普通用户秒杀QPS不超过1
vip用户秒杀QPS不限制
666号是下架商品,不允许访问
|
参数索引这第一个0方法签名的表示第一个参数,单机阈值表示参数的个数
高级选项中参数值为6表示userId为6的用户不限流

当服务重启后Sentinel中的所有规则都会失效,如果想要持久化,可以结合Nacos和Mysql数据库实现持久化