spring cloud之集成sentinel

写在前面

源码

本文一起看下spring cloud的sentinel组件的使用。

1:准备

1.1:理论

对于一个系统来说,最重要的就是高可用,那么如何实现高可用呢?你可能会说,集群部署不就可以了,但事实并非这么简单,假定一个不那么靠谱的开发,写了一个不那么靠谱的sql语句,上线之后,DB资源很快耗尽,那么所有需要查询DB的服务都无法处理请求,进而导致这些服务挂掉,而上游的服务因为无法快速得到响应,也会很快的挂掉,进而整个系统都会无法对外提供服务,这其实就是发生了非常可怕的服务雪崩,这个过程可以简单参考下图:

假定那个不那么靠谱的开发,是在服务D开发了那个不那么靠谱的sql语句,则上线后的后果就是D导致B和C不可用,B和C的不可用又会导致A的不可用,最终整个系统不可用。所以想要真正的实现高可用,除了多节点的集群部署外,我们还需要预防服务雪崩,而本文要学习的sentinel正是这样的一个组件,我们可以叫做容错组件

基本上,导致服务雪崩发生的原因在2个方面,第一个方面是高并发导致的请求量增大,第二方面就是应用内部的异常和错误(就像那个不那么靠谱的sql)。其实sentinel也正是从这两方面来预防服务雪崩的,对于高并发,sentinel可以从外部来限制并发量,对于应用内部异常和错误,sentinel可以进行降级和熔断。我们也会从这方面来展开sentinel的实战环节。

在java中,万物皆对象,在Linux中,万物皆文件,而在sentinel中,万物皆资源,而这里的资源我们可以认为就是接口地址,而对这些资源作用的过程是通过其内部的一个责任链,可以参考下图:

比如有用来收集数据的StaticSlot,构建调用链的NodeSelectorSlot(形成一个树状的调用关系图),如果业务有需要,我们也可以增加自定义的额slot到这个调用链上。

1.2:安装sentinel

首先在这里 下载可运行的jar包,接着如下操作:

$ java -Dserver.port=8080 -Dcsp.sentinel.dashboard.server=localhost:8080 -Dproject.name=sentinel-dashboard -jar sentinel-dashboard-1.8.2.jar
INFO: Sentinel log output type is: file
INFO: Sentinel log charset is: utf-8
INFO: Sentinel log base directory is: C:\Users\dell9020\logs\csp\
INFO: Sentinel log name use pid is: false

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v2.0.5.RELEASE)
 ...

成功后访问http://localhost:8080/#/login,进入登陆页面:

账号sentinel/sentinel,成功后进入如下页面:

2:限流实战

限流,或者叫流量整形,是sentinel从外部预防服务雪崩的重要手段。

2.1:基础配置

这部分我们直接来使用sentiniel对我们项目做流量整形,首先,在custom和template模块引入依赖:

xml 复制代码
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>

然后在custom和template模块的application.yml中配置sentinel信息:

yml 复制代码
spring:
  jpa:
    ...
  cloud:
    sentinel:
      eager: true 
      transport:
        port: 8719
        dashboard: localhost:8080

接着使用@SentinelResource注解标记要进行流量整形的接口,如下:

  • custom模块
java 复制代码
dongshi.daddy.sentinel.controller.CouponCustomerController#requestCoupon
@PostMapping("requestCoupon")
@SentinelResource(value = "requestCoupon")
public Coupon requestCoupon(@Valid @RequestBody RequestCoupon request) {
    ...
}
@PostMapping("findCoupon")
@SentinelResource(value = "customer-findCoupon")
public List<CouponInfo> findCoupon(@Valid @RequestBody SearchCoupon request) {
    return customerService.findCoupon(request);
}
  • template模块
java 复制代码
// 读取优惠券
@GetMapping("/getTemplate")
@SentinelResource(value = "getTemplate")
public CouponTemplateInfo getTemplate(@RequestParam("id") Long id){
    log.info("Load template, id={}", id);
    /*try {
        // 休眠二十秒模拟超时
        TimeUnit.SECONDS.sleep(20);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }*/
    return couponTemplateService.loadTemplateInfo(id);
}

// 批量获取
@GetMapping("/getBatch")
// 降级要执行的方法
@SentinelResource(value = "getTemplateInBatch", blockHandler = "getTemplateInBatch_block")
public Map<Long, CouponTemplateInfo> getTemplateInBatch(@RequestParam("ids") Collection<Long> ids) {
    log.info("getTemplateInBatch: {}", JSON.toJSONString(ids));
    return couponTemplateService.getTemplateInfoMap(ids);
}
// 降级要执行的方法
public Map<Long, CouponTemplateInfo> getTemplateInBatch_block(
        Collection<Long> ids, BlockException exception) {
    log.info("接口被限流");
    return Maps.newHashMap();
}

接着我们启动服务,如果一切正常的话,就可以看到custom和template这两个模块注册到sentinel上了:

2.2:通过流控规则进行限流

sentinel支持通过三种方式来进行流量整形:

直接流控:
    针对特定接口(sentinel称为资源)限流
关联流控:
    当某接口(sentinel称为资源)达到限流条件时对关联的接口(sentinel称为资源)进行限流
链路流控:

2.2.1:直接流控

我们对资源getTemplateInBatch进行流控,在sentinel选择template对应的微服务,点击子菜单流控规则,然后点击右上角 添加流控规则,如下:

为了测试我们使用jmeter来进行测试,jmeter配置可以从这里 下载,配置如下:

通过按钮 启动,之后查看template模块日志输出如下:

2024-01-08 15:34:44.411  INFO 26504 --- [io-20006-exec-7] d.d.s.c.CouponTemplateController         : getTemplateInBatch: [2,3]
Hibernate: select coupontemp0_.id as id1_0_, coupontemp0_.available as availabl2_0_, coupontemp0_.type as type3_0_, coupontemp0_.created_time as created_4_0_, coupontemp0_.description as descript5_0_, coupontemp0_.name as name6_0_, coupontemp0_.rule as rule7_0_, coupontemp0_.shop_id as shop_id8_0_ from coupon_template coupontemp0_ where coupontemp0_.id in (? , ?)
2024-01-08 15:34:44.904  INFO 26504 --- [io-20006-exec-5] d.d.s.c.CouponTemplateController         : 接口被限流

可以看到第二次执行了熔断逻辑被限流了。

2.2.2:关联流控

jmeter配置下载

我们来设置当getTempalte接口qps超过1时限制getBatch接口,配置如下:

首先我们使用jmeter模拟qps 2不间断访问getTemplate接口,如下:

启动后,这样,肯定就会触发限流规则了,这样,我们先来启动jmeter,

之后访问getBatch接口,如下:

http://localhost:20006/template/getBatch?ids=2,3

查看后台输出:

然后我们来停止jmeter,再来请求接口,就会恢复正常了:

2.2.3:链路流控

jmeter配置下载

对于同一个资源访问可以来自于不同的链路,针对特定的链路进行流控,就是链路流控了,如下图,上方链路就是被流控的:

这里我们模拟从custom模块访问template模块的getBatch接口,为了测试,需要首先在custom模块中增加如下接口:

java 复制代码
// 批量获取
@GetMapping("/getBatchFromCustomer")
public Map<Long, CouponTemplateInfo> getTemplateInBatch(@RequestParam("ids") Collection<Long> ids) {
    log.info("getTemplateInBatch111: {}", JSON.toJSONString(ids));
    return templateService.getTemplateInBatch(ids);
}

因为需要进行限流的资源需要知道当前的调用者是谁,才能知道是否需要触发,所以,首先需要在custom模块添加openfeign的拦截器,在openfeign的请求中添加来源头

java 复制代码
@Configuration
public class OpenfeignSentinelInterceptor implements RequestInterceptor {

    @Override
    public void apply(RequestTemplate template) {
        template.header("SentinelSource", "coupon-customer-serv");
    }
}

接着在tempalte模块中获取这个来源的信息:

java 复制代码
@Component
@Slf4j
public class SentinelOriginParser implements RequestOriginParser {

    @Override
    public String parseOrigin(HttpServletRequest request) {
        log.info("request {}, header={}", request.getParameterMap(), request.getHeaderNames());
        return request.getHeader("SentinelSource");
    }
}

接着我们来配置sentinel针对来源coupon-customer-serv进行限流,如下:

接着开启jmeter,正常的话会看到被限流了:

为了测试来源不匹配的情况不限流,我们将sentinel的目标资源名称随便改为其他的,如下:

此时再开启jmeter测试就正常了:

2.3:三种流控效果

sentinel支持三种流控效果,快速失败,warm up,排队等待,在上述的实战环节我们使用就是快速失败,这也是sentinel默认的流控效果。

  • 快速失败
    直接失败。
  • warm up
    这是一种从低水位逐渐拉高的一种限流方式,比如如下的配置:

    上图设置在5秒内拉高到10进行限流,而非开始就是10,开始的限流值需要除以冷却因子,这里冷却因子是3,所以就是10/3=3,也就是当qps到达3时开始限流,然后在5秒内将这个限流值拉高到最大值10,

使用的场景,如采用缓存+DB读的场景,如果接口一段时间内都处于很低的水位,导致大量的缓存都失效了,此时突然发生了突发流量(某明星出轨了,某漂亮country被原子弹攻击了),此时缓存还没有完全构建起来,为了避免突发流量全部打到DB,把DB打穿,就可以考虑使用warm up,在一段时间内从低水位逐渐拉到高水位,同时在这段时间内完成缓存的构建工作。

  • 排队等待
    被限流的请求在一个任务队列中排队等待,并按照超时等待时间从队列中删除任务,如下500内还没有处理则移除任务的配置:

3:降级、熔断实战

jmeter配置

降级、熔断,是sentinel从服务内部预防服务雪崩的重要手段。这部分我们一起来看下如何使用sentinel来实现降级和熔断。为了方便测试我们首先在template模块添加一个新的接口并设置为sentinel的资源:

java 复制代码
// 批量获取
@GetMapping("/getBatchV1")
// 降级要执行的方法
@SentinelResource(value = "getTemplateInBatchV1", fallback = "getTemplateInBatch_fallback")
public Map<Long, CouponTemplateInfo> getTemplateInBatchV1(@RequestParam("ids") Collection<Long> ids) {
    log.info("getTemplateInBatch: {}", JSON.toJSONString(ids));
    int i = 1 / 0;
    return couponTemplateService.getTemplateInfoMap(ids);
}

public Map<Long, CouponTemplateInfo> getTemplateInBatch_fallback(Collection<Long> ids) {
    log.info("接口被fallback");
    return Maps.newHashMap();
}

这里注意指定了fallback = "getTemplateInBatch_fallback",则当抛出了RuntimeException时就会进入执行降级逻辑,接着我们就需要在sentinel中配置熔断逻辑了,如下:

降级熔断的判断过程可以参考下图(注意时示例)

接着执行jemter,日志输出如下:

2024-01-10 14:01:47.813  INFO 13112 --- [io-20006-exec-2] d.d.s.c.CouponTemplateController         : getTemplateInBatch: [2,3] #
2024-01-10 14:01:47.813  INFO 13112 --- [io-20006-exec-2] d.d.s.c.CouponTemplateController         : 接口被fallback 
2024-01-10 14:01:47.813  INFO 13112 --- [o-20006-exec-10] d.daddy.sentinel.SentinelOriginParser    : request org.apache.catalina.util.ParameterMap@39d7b9e0, header=org.apache.tomcat.util.http.NamesEnumerator@7d50964c
2024-01-10 14:01:47.814  INFO 13112 --- [o-20006-exec-10] d.d.s.c.CouponTemplateController         : 接口被fallback
2024-01-10 14:01:47.815  INFO 13112 --- [io-20006-exec-9] d.daddy.sentinel.SentinelOriginParser    : request org.apache.catalina.util.ParameterMap@39d7b9e0, header=org.apache.tomcat.util.http.NamesEnumerator@1f47105b
2024-01-10 14:01:47.816  INFO 13112 --- [io-20006-exec-9] d.d.s.c.CouponTemplateController         : 接口被fallback
....
2024-01-10 14:01:47.813  INFO 13112 --- [io-20006-exec-2] d.d.s.c.CouponTemplateController         : getTemplateInBatch: [2,3] # 1s后半开启,后又重新进入熔断
2024-01-10 14:01:47.813  INFO 13112 --- [io-20006-exec-2] d.d.s.c.CouponTemplateController         : 接口被fallback 
2024-01-10 14:01:47.813  INFO 13112 --- [o-20006-exec-10] d.daddy.sentinel.SentinelOriginParser    : request org.apache.catalina.util.ParameterMap@39d7b9e0, header=org.apache.tomcat.util.http.NamesEnumerator@7d50964c
2024-01-10 14:01:47.814  INFO 13112 --- [o-20006-exec-10] d.d.s.c.CouponTemplateController         : 接口被fallback
2024-01-10 14:01:47.815  INFO 13112 --- [io-20006-exec-9] d.daddy.sentinel.SentinelOriginParser    : request org.apache.catalina.util.ParameterMap@39d7b9e0, header=org.apache.tomcat.util.http.NamesEnumerator@1f47105b
2024-01-10 14:01:47.816  INFO 13112 --- [io-20006-exec-9] d.d.s.c.CouponTemplateController         : 接口被fallback
...

可以看到降级后,满足条件就会进入熔断。一段时间后会尝试恢复,这个状态叫做半熔断,完整的状态转换参考下图:

写在后面

参考文章列表

jmeter之简单使用

相关推荐
小钟不想敲代码16 小时前
Docker部署Sentinel
docker·容器·sentinel
蓝天星空17 小时前
spring cloud gateway 3
java·spring cloud
微扬嘴角18 小时前
springcloud篇1(微服务技术栈、服务拆分与远程调用、Eureka、Nacos)
spring cloud·微服务·eureka
荆州克莱1 天前
Golang的性能监控指标
spring boot·spring·spring cloud·css3·技术
大熊程序猿2 天前
docker 搭建集群
spring cloud·docker·微服务
Q_19284999062 天前
基于Springcloud的智能社区服务系统
后端·spring·spring cloud
程序猿零零漆2 天前
SpringCloud 系列教程:微服务的未来(二)Mybatis-Plus的条件构造器、自定义SQL、Service接口基本用法
java·spring cloud·mybatis-plus
荆州克莱3 天前
mysql中局部变量_MySQL中变量的总结
spring boot·spring·spring cloud·css3·技术
bohu834 天前
sentinel学习笔记7-熔断降级
笔记·sentinel·熔断降级·degradeslot·circuitbreaker