概述
Sentinel: 分布式系统的流量防卫兵
下载sentinel-dashboard-1.7.2.jar
,地址:https://github.com/alibaba/Sentinel/tags
运行jar包,访问localhost:8080
,来到sentinel界面,账号密码都是sentinel
微服务整合sentinel
- 建模块
cloudalibaba-sentinel-service8401
- 导依赖
<!-- SpringCloud ailibaba nacos-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!-- SpringCloud ailibaba sentinel-datasource-nacos 持久化需要用到-->
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-nacos</artifactId>
</dependency>
<!-- SpringCloud ailibaba sentinel-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
- 写yml文件
server:
port: 8401
spring:
application:
name: cloudalibaba-sentinel-service
cloud:
nacos:
discovery:
server-addr: localhost:8848 #Nacos服务注册中心地址
sentinel:
transport:
dashboard: localhost:8080 #配置sentinel dashboard地址
port: 8719 #默认8719端口,假如被占用会自动从8719开始依次+1扫描,直至找到未被占用的端口
#sentinel会启动一个http server与dashboard进行通信,这个http server要占用8719这个端口
management:
endpoints :
web:
exposure:
include: "*"
- 主启动类
@EnableDiscoveryClient
@SpringBootApplication
public class MainApp8401 {
public static void main(String[] args) {
SpringApplication.run(MainApp8401.class, args);
}
}
- controller
@RestController
public class FlowLimitController {
@GetMapping("/testA")
public String testA() {
return "-----testA";
}
@GetMapping("/testB")
public String testB() {
return "-----testB";
}
}
- 启动8401
此时我们到sentinel中查,发现并8401的任何信息,是因为sentinel是懒加载,需要我们执行一次访问才会有信息,访问localhost:8401/testA,再刷新sentinel的dashboard页面,可以看到已经开始监听
sentinel的流控规则和流控模式
- 资源名:唯一名称,默认请求路径
- 针对来源:Sentine可以针对调用者进行限流,填写微服务名,默认default(不区分来源)
- 阈值类型/单机阈值:
- QPS(每秒钟的请求数量):当调用该api的SPs达到阈值值的时候,进行限流
- 线程数:当调用该api的线程数达到阈值的时候,进行限流
- 是否集群:不需要集群
- 流控模式:
- 直接:api达到限流条件时,直接限流
- 关联:设置关联资源B,当关联的资源B达到阈值时,就限流A自己
- 链路:只记录指定链路上的流量(指定资源从入口资源进来的流量,如果达到阈值,就进行限流)【api级别的针对来源】
- 流控效果:
- 快速失败:直接失败,抛异常
- Warm Up:根据codeFactor(冷加载因子,默认3)的值,从阈值/codeFactor,经过预热时长,才达到设置的QPS阈值
- 排队等待:让请求以均匀的速度通过,阈值类型必须设成QPS,否则无效。
sentinel的降级规则
Sentinel 降级会在调用链路中某个资源出现不稳定状态时(例如调用超时或异常比例升高),对这个资源的调用进行限制,让请求快速失败,避免影响到其它的资源而导致级联错误。
当资源被降级后,在接下来的降级时间窗口之内,对该资源的调用都自动熔断(默认行为是抛出DegradeException)。
- RT(平均响应时间,秒级)
在时间窗口内通过的请求>=5
且平均响应时间超出阈值
,两个条件同时满足后触发降级,在实践窗口内开启断路器,窗口期过后关闭断路器
RT最大4900ms(更大的需要通过-Dcsp.sentinel.statistic.max.rt=XXXX才能生效) - 异常比列(秒级)
QPS>=5
且异常比例(秒级统计)超过阈值
时,触发降级;时间窗口结束后,关闭降级 - 异常数(分钟级)
异常数(分钟统计)超过阈值
时,触发降级;时间窗口结束后,关闭降级
sentinel的热点规则
如何自定义降级方法,而不是默认的抛出异常?
使用@SentinelResource直接实现降级方法,它等同Hystrix的@HystrixCommand
定义一个测试方法
@GetMapping("/testHotKey")
// value = "testHotKey"定义一个资源名,blockHandler指定降级方法
@SentinelResource(value = "testHotKey", blockHandler = "deal_testHotKey")
public String testHotKey(@RequestParam(value = "p1", required = false) String p1,
@RequestParam(value = "p2", required = false) String p2) {
return "------testHotKey";
}
public String deal_testHotKey(String p1, String p2, BlockException exception) {
//sentinel系统默认的提示:BLocked by Sentin
return "------deal_testHotKey,o(T-T)o";
}
测试:localhost:8401/testHotKey?p1=1
一秒钟内多次访问会发生降级,走的是我们自己定义的降级方法
有一点需要注意,如果是带第二个请求参数的请求,热点规则不会限制,因为我们配置的是第一个参数
sentinel的热点规则的高级选项
参数例外项,可以进行一些高级设置,比如p1为某个特殊值的时候,有单独的限流规则
当参数值为5的时候,有特殊的热点规则
注意
上面的配置中如果程序出现异常,是不会走blockHander的降级方法的
,因为这个@SentinelResource注解的方法只配置了热点规则,这里配置的降级方法是sentinel针对热点规则配置的,只有触发热点规则才会降级
那如何配置异常的降级方法呢,也很简单,在@SentinelResource
注解的注解后加上fallback参数进行定义
sentinel的系统规则
Sentinel系统自适应限流从整体维度对应用入口流量进行控制,结合应用的Load、CPU使用率、总体平
均RT、入口QPS和并发线程数等几个维度的监控指标,通过自适应的流控策略,让系统的入口流量和
系统的负载达到一个平衡,让系统尽可能跑在最大吞吐量的同时保证系统整体的稳定性。
系统规则支持以下的模式:
- Load自适应(仅对Linux/Unix-like机器生效):系统的load1作为启发指标,进行自适应系统保护。当系统load1超过设定的启发值,且系统当前的并发线程数超过估算的系统容量时才会触发系统保护(BBR阶段)。系统容量由系统的
maxQps*minRt
估算得出。设定参考值一般是CPU cores*2.5
。 - CPU usage(1.5.d版本):当系统CPU使用率超过阈值即触发系统保护(取值范围0.0-1.0),比较灵敏。
- 平均RT:当单台机器上所有入口流量的平均RT达到阈值即触发系统保护,单位是毫秒。
- 并发线程数:当单台机器上所有入口流量的并发线程数达到阈值即触发系统保护。
- 入口QPS:当单台机器上所有入口流量的QPS达到阈值即触发系统保护。
@SentinelResource注解
- 在模块
cloudalibaba-sentinel-service8401
中引入自定义的common依赖
<dependency><!-- 引用自己定义的api通用包,可以使用Payment支付Entity -->
<groupId>com.kk.springcloud</groupId>
<artifactId>cloud-api-commons</artifactId>
<version>${project.version}</version>
</dependency>
- 额外创建一个controller类
@RestController
public class RateLimitcontroller{
@GetMapping("/byResource")
@SentinelResource(value="byResource",blockHandler="handleException")
public CommonResult byResource(){
return new CommonResult(200, "按资源名称限流测试ok", new Payment(2020L, "serial001"));
}
public CommonResult handleException(BlockException exception) {
return new CommonResult(444, exception.getClass().getCanonicalName() + "t服务不可用");
}
}
- 启动
- 配置限流规则,这里资源名可以是@SentinelSource的value属性值,也可以是@GetMapping的value值
- 访问测试,可看到超过限流规则,会进入我们定义的限流方法
现在我们关闭8401服务,会发现我们配置的限流规则也消失了,关闭服务,规则就没有了。
出现了Hystrix中出现的问题,降级方法与业务方法耦合,一个服务需要配置一个降级方法,并且配置没有实现持久化
自定义限流处理逻辑
- 单独创建一个类,用于处理限流,里面定义了
handlerException1
和handlerException2
public class CustomerBlockHandler {
public static CommonResult handlerException1(BlockException exception) {
return new CommonResult(4444, "按客户自定义,global handlerException---1");
}
public static CommonResult handlerException2(BlockException exception) {
return new CommonResult(4444, "按客户自定义,global handlerException----2");
}
}
- 在controller中,指定使用自定义类中的方法作为降级方法
关键点:
blockHandlerClass = CustomerBlockHandler.class
,指定处理类
blockHandler = "handlerException2"
,指定降级方法
@GetMapping("/rateLimit/customerBlockHandler")
@SentinelResource(value = "customerBlockHandler", blockHandlerClass = CustomerBlockHandler.class, blockHandler = "handlerException2")
public CommonResult customerBlockHandler() {
return new commonResult(code:200, message:"按客户自定义", new Payment(id:2020L, serial:"serial003"));
}
- 启动服务,配置限流规则,然后测试方法是否会按我们配置的那样,由我们指定的处理类和方法进行降级
- 测试成功
注意:降级方法必须与原方法参数一致,并且多一个BlockException exception参数
sentinel的熔断
- 建模块
cloudalibaba-provider-payment9003
和cloudalibaba-provider-payment9004
- 导依赖
<!-- SpringCloud ailibaba nacos-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!-- SpringCloud ailibaba sentinel-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
- 改yml文件
server:
port: 9003
spring:
application:
name: nacos-payment-provider
cloud:
nacos:
discovery:
server-addr: localhost:8848 #配置Nacos地址
management:
endpoints:
web:
exposure:
include: "*"
- 主启动类加
@EnableDiscoveryClient
注解 - controller
@RestController
public class PaymentController{
@Value("${server.port}")
private String serverPort;
public static HashMap<Long, Payment> hashMap = new HashMap<>();
static{
hashMap.put(1L,new Payment(1L,"28a8cle3bc2742d8848569891fb42181"));
hashMap.put(2L,new Payment(2L,"bba8cle3bc2742d8848569891ac32182"));
hashMap.put(3L,new Payment(3L,"6ua8c1e3bc2742d8848569891xt92183"));
}
@GetMapping(value="/paymentSQL/{id}")
public CommonResult<Payment> paymentSQL(@PathVariable("id") Long id){
Payment payment = hashMap.get(id);
CommonResult<Payment>result = new CommonResult(200,"frommysql,serverPort:"+serverPort,payment);
return result;
}
}
- 新建一个
cloudalibaba-consumer-nacos-order84
消费者模块 - 导依赖,(nacos-discovery中自动集成了Ribbon)
<!-- SpringCloud ailibaba nacos-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
- 写yml文件
server:
port: 84
spring:
application:
name: nacos-order-consumer
cloud:
nacos:
discovery :
server-addr: localhost:8848
sentinel:
transport:
dashboard: localhost:8080
port: 8719
#消费者将要去访问的微服务名称(注册成功进nacos的微服务提供者)
service-url:
nacos-user-service: http://nacos-payment-provider
- 主启动类加
@EnableDiscoveryClient
- 添加RestTemplate配置类
@Configuration
public class ApplicationContextConfig{
@Bean
@LoadBalanced
public RestTemplate getRestTemplate(){
return new RestTemplate();
}
}
- controller,为业务方法添加
fallback
来指定降级方法
@RestController
public class CircleBreakerController {
public static final String SERVICE_URL = "http://nacos-payment-provider";
@Resource
private RestTemplate restTemplate;
@RequestMapping("/consumer/fallback/{id}")
@SentinelResource(value = "fallback", fallback = "handlerFallback")
public CommonResult<Payment> fallback(@PathVariable Long id) {
CommonResult<Payment> result = restTemplate.getForObject(SERVICE_URL + "/paymentsQL/" + id, CommonResult.class, id);
if (id == 4) {
throw new IllegalArgumentException("rllegalArgumentException,非法参数异常......");
} else if (result.getData() == null) {
throw new NullPointerException("NullpointerException,该ID没有对应记录,空指针异常");
}
return result;
}
public CommonResult handlerFallback(@PathVariable Long id, Throwable e) {
Payment payment = new Payment(id, "null");
return new CommonResult<>(444, "兜底异常handlerFallback,exception内容" + e.getMessage(), Payment.class);
}
}
- 启动
cloudalibaba-consumer-nacos-order84
服务
- 访问
http://localhost:84/consumer/fallback/1
,发现调到了9003和9004服务,并且实现了轮询。 - 访问
http://localhost:84/consumer/fallback/4
,会报我们定义的运行时异常,此时会走我们自定义的fallback降级方法。
如果去掉fallback方法,为业务方法添加blockHandler降级方法,看看是什么效果?
①定义blockHandler
方法
public CommonResult blockHandler(@PathVariable Long id, BlockException blockException) {
Payment payment = new Payment(id, " null ");
return new CommonResult<>(445, "blockHandler-sentinel限流,无此流水:blockException" + blockException.getStackTrace());
}
②在@SentinelSource
注解后加上blockHandler = "blockHandler"
③重新启动84服务,并且定义限流规则或者降级规则
④访问http://localhost:84/consumer/fallback/4
,发现由于去掉了fallback方法,运行时异常没人管了,直接Error Page显示给用户了。但是如果快速访问,QPS达到我们设置的限流规则的话,就能跳转到blockHandler的降级方法
如果是fallback和blockHandler都定义的话会如何呢
测试可以看到,若blockHandler和fallback都进行了配置,快速点击进行访问导致错误的,会由blockhandler
降级方法处理
fallback管运行异常、blockHandler管配置违规
sentinel整合ribbon+openFeign+fallback
- 84模块中导入OpenFeign的依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
- 配置yml文件,激活sentinel对feign的支持
#激活sentinel对feign的支持
feign :
sentinel:
enabled: true
- 主启动类上加
@EnableFeignClients
注解 - 创建远程调用pay模块的接口
@FeignClient(value = "nacos-payment-provider")
public interface PaymentService {
@GetMapping(value = "/paymentSQL/{id}")
public CommonResult<Payment> paymentSQL(@PathVariable("id") Long id);
}
- 创建这个接口的实现类,用于降级
@Component
public class PaymentFallbackService implements PaymentService {
@Override
public CommonResult<Payment> paymentSQL(Long id) {
return new CommonResult<>(444, "服务降级返回,--PaymentFallbackservice");
}
}
- 再次修改接口,指定降级类,就是
@FeignClient
注解后添加fallback= PaymentFallbackService.class
属性 - controller添加远程调用
@Resource
private PaymentService paymentservice;
@GetMapping(value="/consumer/paymentSQL/{id}")
public CommonResult<Payment> paymentSQL(@PathVariable("id") Long id){
return paymentservice. paymentSQL(id);
}
- 启动9003和84,关闭9003,测试84服务是否会降级,测试成功
sentinel的规则持久化
将限流配置规则持久化进Nacos保存
,只要刷新8401某个rest地址,sentinel控制台的流控规则就能看到,只要Nacos里面的配置不删除,针对8401上sentinel上的流控规则持续有效
- 在pom文件中导入相关依赖
<!-- SpringCloud ailibaba sentinel-datasource-nacos 持久化需要用到-->
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-nacos</artifactId>
</dependency>
- yml配置文件
实际上就是指定,我们的规则要保证在哪个名称空间的哪个分组下,这里没有指定namespace,但是是可以指定的,注意这里的dataid要与服务名一致
spring:
cloud:
sentinel:
datasource:
ds1:
nacos:
server-addr: localhost:8848
dataId: cloudalibaba-sentinel-service(服务名)
groupId: DEFAULT_GROUP
data-type: json
rule-type: flow
- 在nacos中创建一个配置文件,dataId就是上面配置文件中配置的dataId:
cloudalibaba-sentinel-service
(不用加yaml),将规则配入,以json格式
[{
"resource": "访问的路径",
"limitApp": "default",
"grade": 1,
"count": 1,
"strategy": 0,
"controlBehavior": 0,
"clusterMode": false
}]
resource:资源名称;(说白了就是ip:端口
后面的那一串controller定义的访问uri)
limitApp:来源应用;
grade:阈值类型,0表示线程数,1表示QPS;
count:单机阈值;
strategy:流控模式,0表示直接,1表示关联,2表示链路;
controlBehavior:流控效果,0表示快速失败,1表示Warm Up,2表示排队等待;
clusterMode:是否集群。
这样配置后,服务重启,可看到所配置的规则会恢复,从而实现规则配置的持久化
最后
以上就是哭泣蜻蜓为你收集整理的SpringCloud Alibaba服务熔断降级与限流之【Sentinel】Sentinel: 分布式系统的流量防卫兵的全部内容,希望文章能够帮你解决SpringCloud Alibaba服务熔断降级与限流之【Sentinel】Sentinel: 分布式系统的流量防卫兵所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复