Sentinel 是什么?
随着微服务的流行,服务和服务之间的稳定性变得越来越重要。
Sentinel
以流量为切入点,
从流量控制、熔断降级、系统负载保护等多个维度保护服务的稳定性。
Sentinel 核心功能
1.流量控制
2.熔断降级
所谓的熔断降级就是当检测到调用链路中某个资源出现不
稳定的表现,例如请求响应时间长或异常比例升高的时候,则对这个资源的调用进行限
制,让请求快速失败,
避免影响到其它的资源而导致级联故障
3.系统负载保护
-
根据系统能够处理的请求,和允许进来的请求,来做平衡,追求的目标是在系统不被
拖垮的情况下
,
提高系统的吞吐率
4.消息削峰填谷
-
某瞬时来了大流量的请求
,
而如果此时要处理所有请求,很可能会导致系统负载过高,
影响稳定性。但其实可能后面几秒之内都没有消息投递,若直接把多余的消息丢掉则没
有充分利用系统处理消息的能力
- Sentinel
的
Rate Limiter
模式能在某一段时间间隔内以匀速方式处理这样的请求, 充分利
用系统的处理能力, 也就是削峰填谷, 保证资源的稳定性
Sentinel 控制台
1
指令
: java -jar sentinel-dashboard-1.8.0.jar
2
注意
: Sentinel
控制台 默认端口是
8080
1.
浏览器输入
: http://localhost:8080 ,
用户
/
密码都是
sentinel
1
、更改
Sentinel
控制台的端口
java -jar sentinel-dashboard-1.8.0.jar --server.port=9999
将sentinel集成到微服务中(让sentinel对该服务进行实时监控)
当调用了 该
微服务时
,
可以监控到请求的
url/QPS/
响应时间
/
流量
代码/配置实现
1.
修改
微服务
pom.xml,
引入
alibaba-sentinel
<
dependencies
>
<!--
引入
alibaba-sentinel -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
<!--
在微服务模块引入
nacos-discovery starter -->
<
dependency
>
<
groupId
>com.alibaba.cloud</
groupId
>
<
artifactId
>spring-cloud-starter-alibaba-nacos-discovery</
artifactId
>
</
dependency
>
....
2.修改application.yml
cloud
:
nacos
:
discovery
:
server-addr
: localhost:8848
#
配置
Nacos
地址
sentinel:
transport:
dashboard: localhost:8080
#
这个是
sentinel
控制台
(sentinel dashboard)
的地址
#1. spring.cloud.sentinel.transport.port
端口配置会在被监控的微服务
#
对应的机器上启动一个
Http Server
#2.
该
Server
会与
Sentinel
控制台做交互
#3.
比如
Sentinel
控制台添加了
1
个限流规则,会把规则数据
push 给这个
#
Http Server
接收,
Http Server
再将规则注册到
Sentinel
中
#4.
简单的说明
: spring.cloud.sentinel.transport.port
:指定被监控的微服务应用与
#
Sentinel
控制台交互的端口,微服务应用本地会起一个该端口占用的
Http Server
port: 8719
#
默认
8719
,假如被占用了
,
会自动从
8719
开始依次
+1
扫描。
#
直至找到未被占用的端口
注意事项和细节
QPS: Queries Per Second(每秒查询率),是服务器每秒响应的查询次数
Sentinel 采用的是懒加载, 只有调用了某个接口/服务,才能看到监控数据
流量控制实例-关联
当调用
member-service-nacos-provider-10004
的
/t2 API
接口时,如果
QPS
超过
1
,这
时调用
/t1 API
接口 直接接失败,抛异常
.
/t2
是关联的资源
,
限流的资源是
/t1
流量控制
流量控制实例-Warm up
当流量突然增大的时候,我们常常会希望系统从空闲状态到繁忙状态的切换的时间长
一些。即如果系统在此之前长期处于空闲的状态,我们希望处理请求的数量是缓步的
增多,经过预期的时间以后,到达系统处理请求个数的最大值。
Warm Up(
冷启动,
预热
)
模式就是为了实现这个目的的。
这个场景主要用于启动需要额外开销的场景,例如建立数据库连接等
理解机制:默认coldFactor为3,QPS从QPS阈值/3开始,经过预热逐步升至指点的QPS阈值
Warm up
称为 冷启动
/预热
应用场景
:
秒杀在开启瞬间,大流量很容易造成冲垮系统,
Warmup
可慢慢的把流量放入,最
终将阀值增长到设置阀值
注:如果请求停止
(
即
:
一段时间没有达到阈值
), Warm up
过程将重复
,
小伙伴可以理解是一个弹
性过程
流量控制实例-排队
1.
排队方式:这种方式严格控制了请求通过的间隔时间,也即是让请求以均匀的速度通
过,对应的是漏桶算法
2.
这种方式主要用于处理间隔性突发的流量,例如消息队列。比如这样的场景,在某一
秒有大量的请求到来,而接下来的几秒则处于空闲状态,我们希望系统能够在接下来的
空闲期间逐渐处理这些请求,而不是在第一秒直接拒绝多余的请求。
-
类似前面说的 削
峰填谷
3.匀速排队,阈值必须设置为
QPS
4.QPS阈值=2时,每隔500ms才允许通过下一请求
5.
当调用
/API
的
QPS
超过 阈值
时,不拒绝请求,而是排队等待
,
依次执行
6.
当等待时间超过
10
秒,则为等待超时
.
Sentinel 熔断降级
例如:服务A调用服务B ,但是服务B不稳定导致服务A线程堆积导致服务A也崩溃。
解决:对不稳定的服务实现熔断降级,让其迅速返回结果避免线程堆积。
熔断策略:
1.慢调用比例
RT:自己设置值 若请求时间大于RT则为慢调用
比例阈值:0~1.0 慢调用请求比例 和 总请求数比
1.当一定时间内 1.请求数量> 最小请求数 2.慢调用比例 > 阈值
则接下来的时间就会进行熔断
2.当熔断时间过后会进入恢复状态,若下一个请求请求响应时间 < RT则熔断结束,若 > RT则会被再次熔断,
2.异常比例
1.请求数大于最小请求数2.异常比例 > ;比例阈值进行熔断
2.若接下来一个请求没有异常则结束熔断,否则会再次熔断
3.异常数
1.请求数大于最小请求数2.异常数目超过阈值
2.若接下来一个请求没有异常则结束熔断,否则会再次熔断
Sentinel 热点规则
引出:比如新闻热搜,在某段时间内高频访问,为了防止系统雪崩,对该条新闻进行热点限流。
热点参数限流会统计传入参数中的热点参数,对包含热点参数的资源进行限流
需求:
1.对 /news?id=x&type=x API接口进行热点限流
2.假定 id= 10 为热点新闻,当查询新闻时,对普通id (非热点新闻)请求QPS=2,如果id=10则QPS定为100
3.若访问超出QPS触发热点限流规则,调用自定义方法给出提示信息
4.降低访问量QPS达到规定范围,服务恢复
修改业务类:
/**
*
注意
@GetMapping
是
url
带有
/ , @SentinelResource
的
value
是没有
/
的
* @param
id
* @param
type
* @return
*/
@GetMapping
(
"/news"
)
@SentinelResource
(value =
"news"
,blockHandler =
"newsBlockHandler"
)
public
Result
queryNews
(
@RequestParam
(value =
"id"
,required =
false
)
String
id,
@RequestParam
(value =
"type"
,required =
false
)
String
type)
{
log
.info(
"
到
DB
查询数据库
.... "
);
return
Result
.success(
"
返回
id= "
+ id +
"
新闻
fromDB"
);
}
//
热点
key
限制异常处理方法
public
Result
newsBlockHandler
(
String
id,
String
type,
BlockException
exception){
return
Result
.success(
"
查询
id= "
+ id
+
"
新闻 触发热点
key
限制保护
.. sorry"
);}
理解:1.
@SentinelResource value值为自定会体现在Sentinel簇点链路中
2.
blockHandler 指定限流处理方法
"newsBlockHandler"
为同一类中的方法名
系统规则
一个问题引出系统规则
1. 如我们系统最大性能能抗 100QPS, 如何分配 /t1 /t2 的 QPS?
2.
方案
1
: /t1
分配
QPS=50 /t2
分配
QPS=50 ,
问题
,
如果
/t1
当前
QPS
达到
50 ,
而
/t2
的
QPS
才
10,
会造成没有充分利用服务器性能
.
3.
方案
2
: /t1
分配
QPS=100 /t2
分配
QPS=100 ,
问题
,
容易造成 系统没有流量保护,
造成请求线程堆积,形成雪崩
.
4. 有没有对各个 资源请求的 QPS弹性设置, 只要总数不超过系统最大QPS的流量保护规
则? ==> 系统规则
@SentinelResource自定义全局限流处理类
先看之前写过的限流处理类方法
@GetMapping
(
"/news"
)
@SentinelResource
(value =
"news"
,blockHandler =
"newsBlockHandler"
)
public
Result
queryNews
(
@RequestParam
(value =
"id"
,required =
false
)
String
id,
@RequestParam
(value =
"type"
,required =
false
)
String
type) {
log
.info(
"
到
DB
查询数据库
.... "
);
return
Result
.
success
(
"
返回
id= "
+ id +
"
新闻
fromDB"
);
}
//
热点
key
限制异常处理方法
public
Result
newsBlockHandler
(
String
id,
String
type,
BlockException
exception){
return
Result
.
success
(
"
查询
id= "
+ id +
"
新闻 触发热点
key
限制保护
.. sorry"
);
}
说明
:
当配置的资源名
news
触发限流机制时,会调用
newsBlockHandler
方法
2.
上面的处理方案存在一些问题
每个
@SentinelResource
对应一个异常处理方法,会造成方法很多
异常处理方法和资源请求方法在一起,不利于业务逻辑的分离
解决方案
->
自定义全局限流处理类
1.创建限流处理类
public class
CustomGlobalBlockHandler
{
public static
Result
handlerMethod1
(
BlockException
exception){
return
Result
.error(
"400"
,
"
客户自定义异常处理
handlerm1()"
);
}
public static
Result
handlerMethod2
(
BlockException
exception){
return
Result
.error(
"401"
,
"
客户自定义异常处理
handlerm2()"
);
}
}
创建的异常处理方法需要是
static
2.修改controller
@GetMapping
(value =
"/t6"
)
@SentinelResource
(
value =
"t6"
,
//指定限流处理类
blockHandlerClass =
CustomGlobalBlockHandler
.
class
,
//指定处理方法
blockHandler =
"handlerMethod1"
)
public
Result
t6
() {
log
.info(
"
执行
t6()
线程
id= "
+
Thread
.
currentThread
().getId());
return
Result
.
success
(
"200"
,
"t6()
执行成功
"
);
}
理解:与上一种区别将处理方法统一进行编写
fallback(@SentinelResource的一个属性)
fallback负责java异常,业务异常的返回信息
1. 需求: 请编写一个自定义全局 fallback 处理类, 处理 java 异常/业务异常
public class
CustomGlobalFallbackHandler
{
public static
Result
fallBackHandlerMethod1
(
Throwable
e) {
return
Result
.error(
"400"
,
"
java
异常信息
= "
+ e.getMessage());
}
public static
Result
fallBackHandlerMethod2
(
Throwable
e) {
return
Result
.error(
"401"
,
"
java
异常信息
= "
+ e.getMessage());
}
}
2.修改controller
@GetMapping
(value =
"/t6"
)
@SentinelResource
(
value =
"t6"
,
fallbackClass = CustomGlobalFallbackHandler.class,
fallback = "fallBackHandlerMethod1",
blockHandlerClass =
CustomGlobalBlockHandler
.
class
,
blockHandler =
"handlerMethod1"
)
public
Result
t6
() {
if
(++
num
%
5
==
0
) {
//
假定
:
当访问
t6
资源次数是
5
的倍数时,就出现了一个
java
的异
常
throw new
RuntimeException(
"num
的值异常
num= "
+
num
);
}
log
.info(
"
执行
t6()
线程
id= "
+
Thread
.
currentThread
().getId());
return
Result
.
success
(
"200"
,
"t6()
执行成功
"
);
}