前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Sentinel搭建流程 顶

Sentinel搭建流程 顶

作者头像
算法之名
发布2019-12-16 10:23:38
8280
发布2019-12-16 10:23:38
举报
文章被收录于专栏:算法之名

雪崩效应

雪崩效应如上图所示,我们在微服务中的调用链中,当一个基础微服务的API接口A不可用时,当B调用A的服务会堆积阻塞,因为我们知道我们每一次调用,无论是调用方还是服务提供方,其实都是一个线程,而这些线程一般都是线程池中的线程。一般一个线程池中的线程数是有限的,一直到请求超时的时候,这个线程才会被释放(在正常情况下,任务执行完毕,线程释放,所以要求每个调用的执行时间越短越好,便于线程池中的线程不断重复使用,不出现阻塞)。在高并发的情况下,B的线程池中的线程资源会被瞬间完全占用,在短期内再也无法创建线程来执行任务,于是B停摆,挂掉了。同理,C、D服务在后续调用中也被B搞挂了。我们把基础服务故障,导致上层服务故障,并且这个故障不断放大的过程,称为雪崩效应。

常见容错方案

  • 超时 我们在每次请求中都设置一个较短的超时时间,比如1秒,这样,只要线程释放的速度够快,那么在上图中的B服务就不那么容易被A服务拖死。
  • 限流 我们知道,一个请求会有一个最大的吞吐量qps,我们假设C、D调用B服务的请求达到一个阈值,即便A服务挂了,那么B请求A会在不断正常超时的情况下,依然可以释放线程,那么我们可以给B做这样的限流,不让C、D请求B的并发量过大,使得B可以正常运行。
  • 仓壁模式 给同一个服务不同的Controller设立不同的线程池,彼此不影响,并且每一个Controller的独立线程池的线程数并不大。
  • 断路器模式 给具体的某一个Restful API进行监控,如果在设定的时间内,比如5秒内的错误率,错误次数达到一个阈值,断路器自动切断该API的访问,断路器处于打开状态。在一段时间后,断路器会处于半开状态,会进行一种尝试性的访问,如果此时访问成功,断路器会关闭,允许该接口重新被正常访问。在次模式下,比如上图中的A服务失败,B服务访问A若干次后,失败,断路器关闭B服务的访问,若A服务恢复使用,则B服务无需人工干预,则自己可以恢复访问。

Sentinel的控制台安装

控制台下载地址 https://github.com/alibaba/Sentinel/releases

根据你自己的版本挑选Sentinel的版本号,我这里使用的是1.7.0

下载好后使用

java -jar sentinel-dashboard-1.7.0.jar

使用docker安装控制台

docker pull bladex/sentinel-dashboard:1.7.0

docker run -d --name sentinel -p 8858:8858 bladex/sentinel-dashboard:1.7.0

这里需要注意的是用docker启动的该实例端口号为8858,而不是我们正常启动jar包的8080

启动后,界面如下,访问端口8080(以jar包启动为例)

输入用户名sentinel,密码sentinel后登录后,界面如下,目前控制台中,什么都没有。

项目整合控制台

pom

代码语言:javascript
复制
<dependency>
   <groupId>com.alibaba.cloud</groupId>
   <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>

添加配置

代码语言:javascript
复制
spring:
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848
    sentinel:
      transport:
        dashboard: 127.0.0.1:8080
  application:
    name: nacos

启动该项目,访问该项目的一个依赖于user的接口

代码语言:javascript
复制
@RestController
public class BalanceController {
    @Autowired
    private RestTemplate restTemplate;

    @GetMapping("/trace")
    public String trace() {
        return restTemplate.getForObject("http://user/find", String.class);
    }
}

由于user未启动,访问结果如下

刷新Sentinel控制台(此处的nacos是项目名叫nacos)

我们多刷新几次trace接口的访问,此时再看到实时监控已经有数据了

在簇点链路中,我们可以看到我们访问过的url

我们点流控按钮,可以对该访问API进行流控设置

比如我此处设置QPS为1的时候,设置为访问直接失败,点保存,当我们不断的点浏览器刷新,就会出现访问被限流点情况。

第二个关联,当关联的资源达到阈值,就限流自己。

比方说我们有另外一个接口/trace

现在我用代码一直去访问这个/trace

代码语言:javascript
复制
public class TestFind {
    public static void main(String[] args) throws InterruptedException {
        RestTemplate restTemplate = new RestTemplate();
        for (int i = 0;i < 10000;i++) {
            restTemplate.getForObject("http://127.0.0.1:8081/trace", String.class);
            Thread.sleep(500);
        }
    }
}

此时,这个/trace的QPS肯定是大于1的。我们用浏览器来访问/find,结果如下

此处可以考虑的业务场景为某一个资源的读写优先级,根据业务需求,来进行关联限流。

第三个链路 只记录指定链路上的流量(经测试好像无效,正常配置如下)

现在来写一个测试代码

代码语言:javascript
复制
@Slf4j
@Service
public class CommonService {
    @SentinelResource("common")
    public String common() {
        log.info("Common");
        return "Common";
    }
}
代码语言:javascript
复制
@RestController
public class CommonController {
    @Autowired
    private CommonService commonService;

    @GetMapping("test-a")
    public String testA() {
        return commonService.common();
    }

    @GetMapping("test-b")
    public String testB() {
        return commonService.common();
    }
}

我们访问test-a

我们可以在簇点链路中找到这次访问的情况,我们会发现在/test-a下面有一个common,点击common的流控

进行如下设置

由于该设置无效,就不提供测试结果了。但是对资源点common设置"直接"是有效的。

Warm Up(预热) 根据codeFactor(冷加载因子,默认3)的值,从阈值/codeFactor,经过预热时长,才达到设置的QPS阈值。

这里设置的意思是一开始的QPS阈值为100/3,在10秒内不断上升到100。这里主要使用于一开始有大量突增激发流量到情况下,设置为该模式,不让这种突发到并发访问撑破服务器。

排队等待 匀速排队,让请求以均匀到速度通过,阈值类型必须设置为QPS,否则无效。

这个图的意思是1秒内只有1个请求允许通过,如果这个请求达到超时时间,就丢弃该请求。

用代码来测试

代码语言:javascript
复制
@Slf4j
public class OneByOneTest {
    public static void main(String[] args) {
        RestTemplate restTemplate = new RestTemplate();
        for (int i = 0;i < 10000;i++) {
            long start = System.currentTimeMillis();
            String object = restTemplate.getForObject("http://127.0.0.1:8081/test-a", String.class);
            log.info(object + ",间隔时间:" + (System.currentTimeMillis() - start));
        }
    }
}

运行结果(部分):

15:44:15.463 [main] DEBUG org.springframework.web.client.RestTemplate - HTTP GET http://127.0.0.1:8081/test-a 15:44:15.474 [main] DEBUG org.springframework.web.client.RestTemplate - Accept=[text/plain, application/json, application/*+json, */*] 15:44:15.482 [main] DEBUG org.springframework.web.client.RestTemplate - Response 200 OK 15:44:15.483 [main] DEBUG org.springframework.web.client.RestTemplate - Reading to [java.lang.String] as "text/plain;charset=UTF-8" 15:44:15.485 [main] INFO com.cgc.cloud.nacos.OneByOneTest - test-a,间隔时间:45 15:44:15.486 [main] DEBUG org.springframework.web.client.RestTemplate - HTTP GET http://127.0.0.1:8081/test-a 15:44:15.486 [main] DEBUG org.springframework.web.client.RestTemplate - Accept=[text/plain, application/json, application/*+json, */*] 15:44:16.484 [main] DEBUG org.springframework.web.client.RestTemplate - Response 200 OK 15:44:16.484 [main] DEBUG org.springframework.web.client.RestTemplate - Reading to [java.lang.String] as "text/plain;charset=UTF-8" 15:44:16.484 [main] INFO com.cgc.cloud.nacos.OneByOneTest - test-a,间隔时间:998 15:44:16.485 [main] DEBUG org.springframework.web.client.RestTemplate - HTTP GET http://127.0.0.1:8081/test-a 15:44:16.485 [main] DEBUG org.springframework.web.client.RestTemplate - Accept=[text/plain, application/json, application/*+json, */*] 15:44:17.483 [main] DEBUG org.springframework.web.client.RestTemplate - Response 200 OK 15:44:17.483 [main] DEBUG org.springframework.web.client.RestTemplate - Reading to [java.lang.String] as "text/plain;charset=UTF-8" 15:44:17.483 [main] INFO com.cgc.cloud.nacos.OneByOneTest - test-a,间隔时间:999 15:44:17.484 [main] DEBUG org.springframework.web.client.RestTemplate - HTTP GET http://127.0.0.1:8081/test-a 15:44:17.484 [main] DEBUG org.springframework.web.client.RestTemplate - Accept=[text/plain, application/json, application/*+json, */*] 15:44:18.485 [main] DEBUG org.springframework.web.client.RestTemplate - Response 200 OK 15:44:18.485 [main] DEBUG org.springframework.web.client.RestTemplate - Reading to [java.lang.String] as "text/plain;charset=UTF-8" 15:44:18.485 [main] INFO com.cgc.cloud.nacos.OneByOneTest - test-a,间隔时间:1002 15:44:18.486 [main] DEBUG org.springframework.web.client.RestTemplate - HTTP GET http://127.0.0.1:8081/test-a 15:44:18.486 [main] DEBUG org.springframework.web.client.RestTemplate - Accept=[text/plain, application/json, application/*+json, */*] 15:44:19.481 [main] DEBUG org.springframework.web.client.RestTemplate - Response 200 OK 15:44:19.481 [main] DEBUG org.springframework.web.client.RestTemplate - Reading to [java.lang.String] as "text/plain;charset=UTF-8" 15:44:19.481 [main] INFO com.cgc.cloud.nacos.OneByOneTest - test-a,间隔时间:996 15:44:19.482 [main] DEBUG org.springframework.web.client.RestTemplate - HTTP GET http://127.0.0.1:8081/test-a 15:44:19.482 [main] DEBUG org.springframework.web.client.RestTemplate - Accept=[text/plain, application/json, application/*+json, */*] 15:44:20.485 [main] DEBUG org.springframework.web.client.RestTemplate - Response 200 OK 15:44:20.485 [main] DEBUG org.springframework.web.client.RestTemplate - Reading to [java.lang.String] as "text/plain;charset=UTF-8" 15:44:20.485 [main] INFO com.cgc.cloud.nacos.OneByOneTest - test-a,间隔时间:1004 15:44:20.486 [main] DEBUG org.springframework.web.client.RestTemplate - HTTP GET http://127.0.0.1:8081/test-a 15:44:20.486 [main] DEBUG org.springframework.web.client.RestTemplate - Accept=[text/plain, application/json, application/*+json, */*] 15:44:21.485 [main] DEBUG org.springframework.web.client.RestTemplate - Response 200 OK 15:44:21.485 [main] DEBUG org.springframework.web.client.RestTemplate - Reading to [java.lang.String] as "text/plain;charset=UTF-8" 15:44:21.486 [main] INFO com.cgc.cloud.nacos.OneByOneTest - test-a,间隔时间:1001 15:44:21.486 [main] DEBUG org.springframework.web.client.RestTemplate - HTTP GET http://127.0.0.1:8081/test-a 15:44:21.486 [main] DEBUG org.springframework.web.client.RestTemplate - Accept=[text/plain, application/json, application/*+json, */*] 15:44:22.485 [main] DEBUG org.springframework.web.client.RestTemplate - Response 200 OK 15:44:22.485 [main] DEBUG org.springframework.web.client.RestTemplate - Reading to [java.lang.String] as "text/plain;charset=UTF-8" 15:44:22.485 [main] INFO com.cgc.cloud.nacos.OneByOneTest - test-a,间隔时间:999

我们可以看到,基本上每次请求的间隔为1秒。没有一个请求被丢弃。而之前的两种——快速失败和Worm Up,都会丢弃请求。该方式适用于流量访问不均衡的情况,有激增时段,有空闲时段,当激增时段到来时,不断让流量缓缓通过,用空闲时段来慢慢处理。

降级规则设置

我们继续使用/test-a来说明

点击降级按钮

这里RT为平均响应时间,上面设置为1毫秒,时间窗口5,这里的总体意思表示如下

平均响应时间(秒级统计)超出阈值(此处为大于1毫秒)并且在5秒内通过的请求>=5次——>触发降级(熔断器打开)——>时间窗口5秒结束——>关闭降级

我们访问一次该接口,可以看到单次的相应时间为3毫秒,这个3毫秒是肯定大于1毫秒的。我们不断的刷新该接口,当我们连续刷5次,到第6次的时候

触发降级策略,

那么在5秒内,我们的访问就会一直是限流状态,直到5秒后才可以正常访问。

降级注意点

RT默认最大4900ms

这里就是说这个RT设置的再大,比如100000,它依然还是按4900ms来执行,如果一定要修改这个值,可以通过-Dcsp.sentinel.statistic.max.rt=xxx修改

降级-异常比例

QPS >= 5并且异常比例(秒级统计,0.0-1.0)超过阈值——>触发降级(熔断器打开)——>时间窗口5秒结束——>关闭降级

降级-异常数

异常数(分钟统计)超过阈值——>触发降级(熔断器打开)——>时间窗口65秒结束——>关闭降级。

此处不同于RT和异常比例都是以秒级统计,而异常数是以分钟统计的。

注意点 时间窗口<60秒可能会出问题。比如做如下配置

这里的意思为当1分钟以内的异常数大于10,就会触发降级,直到10秒的时间窗口结束,关闭降级。但异常数的统计是在分钟级别的,可能10秒结束的时候依然在1分钟以内,异常数依然大于10次,那么就会再次进入降级。所以时间窗口建议设置大于等于60的值。

由此可以看出,相对于断路器三态状态,Sentinel的断路器没有半开状态。但可能会在未来增加。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
容器服务
腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档