微服务流量治理组件 Sentinel
分布式架构下的服务雪崩问题
在分布式架构下,往往一个服务会依赖其他服务,如 商品详情服务,依赖于 商品服务、价格服务、商品评论服务。
服务雪崩
当一个用户请求进入后,需要查询商品详情,就会去访问 商品服务、价格服务、评论服务。但是如果此时评论服务宕机或其他原因导致服务不可用时,所有的线程都会被阻塞,从而造成 上游服务资源耗尽导致服务雪崩。
服务雪崩:因为服务提供者服务不可用,导致调用者的线程堵塞并逐渐放大,最终导致整个依赖链路都出现问题。
导致雪崩的原因
- 高并发请求
- 一般在秒杀或者促销的情况下出现,瞬间高并发大量的请求进来,服务提供者没有做缓存预热和服务集群来应对,导致服务不可用,从而导致链上的服务雪崩
- 硬件故障
- 服务提供者硬件故障,导致上游调用者服务不可用,最终整个依赖链路服务雪崩
- 缓存击穿
- 缓存层应用故障或缓存大量同一时间失效导致大量缓存没有命中,请求打到了存储层 数据库上,导致数据库宕机
- 业务层BUG
- 代码BUG导致服务不可用,自己写的BUG自己认,自己挖的坑自己跳
服务雪崩解决方案
超时机制
如果没有超时机制,服务提供者不可用的话会导致消费者的线程一直等待,直到 资源耗尽。
如:消费者通过线程池的线程请求某服务,但是某服务一直没有响应,消费者就一直等待。以此类推,线程池的线程就会被耗尽 。
如果引入了超时机制,服务提供者在规定的时间内没有正确的响应,就会释放该线程。
服务限流
核心思想:为一些服务分配固定的线程,这样即便某个服务挂掉了,也只会消耗固定的线程,也不会导致线程池里的线程都被消耗掉。
如:使用服务限流后,详情服务调用评论服务,此时评论服务不可用导致20个线程等待,就不会再启用线程调用该服务,同时也不会影响其他的服务现成调用。
服务熔断
远程服务不稳定或网络抖动时暂时关闭,就叫服务熔断。
- 当大量的请求进行服务调用时,服务调用失败了,达到阈值就会触发 断路器打开。
- 断路器打开一段时间后,就会进入 半开状态,半开状态只是一个瞬间的状态
- 进入半开状态后 只允许一次请求调用,服务这次请求成功了,就会关闭 断路器,如果请求还是失败,就会打开断路器。过段时间后,再次进入半开状态。
半开状态可以实现 应用的自我恢复,并且通过断路器可以避免资源浪费。
服务降级
有服务熔断,必然要有服务降级。当服务不可用时,我们可以降级 让请求访问另外一个服务或者一个fallback 回调方法,返回一个友好的提示,如 “服务繁忙请稍后再试”。
虽然降级好像看起来没什么作用,但是却能给用户一个友好的提示,比 服务直接宕机返回 未知错误 强。
具体怎么降级要看实际的场景,如果服务不可用,有的场景需要降级到 另外的服务进行补偿措施,有的场景直接返回友好的错误提示。
Sentinel 微服务流量治理组件
什么是Sentinel
随着微服务的流行,服务和服务之间的稳定性变得越来越重要。Sentinel主要以流量为切入点,从流量路由、流量控制、流量整形、熔断降级、系统自适应过载保护、热点流量防护等多个维度来帮助开发者保障微服务的稳定性。
源码地址:https://github.com/alibaba/Sentinel
官方文档:https://github.com/alibaba/Sentinel/wiki https://sentinelguard.io/zh-cn/docs/introduction.html
Sentinel优势:
- 丰富的应用场景: Sentinel 承接了阿里巴巴近 10 年的双十一大促流量的核心场景,例如秒杀(即突发流量控制在系统容量可以承受的范围)、消息削峰填谷、实时熔断下游不可用应用等。
- 完备的实时监控: Sentinel 同时提供实时的监控功能。您可以在控制台中看到接入应用的单台机器秒级数据,甚至 500 台以下规模的集群的汇总运行情况。
- 广泛的开源生态: Sentinel 提供开箱即用的与其它开源框架/库的整合模块,例如与 Spring Cloud、Dubbo、gRPC 的整合。您只需要引入相应的依赖并进行简单的配置即可快速地接入 Sentinel。
- 完善的 SPI 扩展点: Sentinel 提供简单易用、完善的 SPI 扩展点。您可以通过实现扩展点,快速的定制逻辑。例如定制规则管理、适配数据源等。
阿里云除了开源的Sentinel外,还提供了 企业级的 Sentinel 服务,应用高可用服务 AHAS,不过是商业收费的。
Sentinel和Hystrix对比
Hystrix 的关注点在于以 隔离 和 熔断 为主的容错机制,超时或被熔断的调用将会快速失败,并可以提供 fallback 机制。
而 Sentinel 的侧重点在于:
- 多样化的流量控制
- 熔断降级
- 系统负载保护
- 实时监控和控制台
相较Hystrix而言,Sentinel明显支持的功能更多,更加全面,所以我们当前的微服务架构更多的是使用Sentinel来进行流量治理。
Sentinel 工作原理
基本概念
资源
资源是Sentinel的概念,可以是一个应用,也可以是一段代码。我们可以通过Sentinel API把这个资源保护起来,而我们可以通过方法签名,URL,甚至服务名称作为资源名来标示资源。
规则
根据资源的实时状态设定的规则,可以包括流量控制规则、熔断降级规则以及系统保护规则。所有规则可以动态实时调整。
Sentinel 功能和设计理念
流量控制
每台服务节点的处理资源都是有限的,如果流量太大的话,节点承受不住就会变得卡顿甚至内存溢出从而导致宕机,甚至上游服务雪崩。
为此,我们就需要对流量进行控制,比如 某节点最大1秒只能承受1000次请求,那我们就对流量进行控制,让他1秒内接收最大800个请求,留下20%的资源 确保服务有足够的预留空间。
流量控制有以下几个角度:
- 资源的调用关系,例如资源的调用链路,资源和资源之间的关系;
- 运行指标,例如 QPS、线程池、系统负载等;
- 控制的效果,例如直接限流、冷启动、排队等。
Sentinel 的设计理念是让您自由选择控制的角度,并进行灵活组合,从而达到想要的效果。
熔断降级
Sentinel 和 Hystrix 的原则是一致的: 当调用链路中某个资源出现不稳定,例如,表现为 timeout,异常次数达到了阈值,就会对这个资源的调用进行限制,并让请求快速失败,避免影响到其它的资源,最终产生雪崩的效果。
在限制的手段上,Sentinel 和 Hystrix 采取了完全不一样的方法。
Hystrix 通过线程池的方式,来对资源进行了隔离。这样做的好处是资源和资源之间做到了最彻底的隔离。缺点是除了增加了线程切换的成本,还需要预先给各个资源做线程池大小的分配。
Sentinel通过两种方式来进行熔断降级
- 通过并发线程数进行限制
Sentinel通过限制并发线程数,来实现 流量控制。
如:1秒内只允许5个线程,此时5个线程的时间窗口(运行周期,请求处理时间)是2秒,如果有新的请求进来会直接被拒绝,因为在1秒内5个线程都占满了。
- 通过响应时间(QPS)对资源进行降级
除了对并发线程数进行控制以外,Sentinel 还可以通过响应时间来快速降级不稳定的资源。当依赖的资源出现响应时间过长后,所有对该资源的访问都会被直接拒绝,直到过了指定的时间窗口之后才重新恢复。
假设一个请求响应时间是0.2秒,那么1秒的QPS就是5,如果我们设置1秒只允许5个请求的话,新的请求进来就就会被拒绝。
系统过载保护
单台节点的并发IO是有限的,如果大量的请求进入了该节点,该节点资源处理不过来,很可能会宕机。我们可以通过过载保护来解决这个问题,超过阈值的请求都会被拒绝返回。
Sentinel工作主流程
官方文档:https://github.com/alibaba/Sentinel/wiki/Sentinel%E5%B7%A5%E4%BD%9C%E4%B8%BB%E6%B5%81%E7%A8%8B
在 Sentinel 里面,所有的资源(服务)都对应一个资源名称(resourceName),每次资源调用都会创建一个 Entry 对象。
Entry 可以通过对主流框架的适配自动创建,也可以通过注解的方式或调用 SphU API 显式创建。
Slot功能槽
Entry 创建的时候,同时也会创建一系列功能插槽(slot chain),这些插槽有不同的职责:
- NodeSelectorSlot 负责收集资源的路径,并将这些资源的调用路径,以树状结构存储起来,用于根据调用路径来限流降级;
- ClusterBuilderSlot 则用于存储资源的统计信息以及调用者信息,例如该资源的 RT, QPS, thread count 等等,这些信息将用作为多维度限流,降级的依据;
- StatisticSlot 则用于记录、统计不同纬度的 runtime 指标监控信息;
- FlowSlot 则用于根据预设的限流规则以及前面 slot 统计的状态,来进行流量控制;
- AuthoritySlot 则根据配置的黑白名单和调用来源信息,来做黑白名单控制;
- DegradeSlot 则通过统计信息以及预设的规则,来做熔断降级;
- SystemSlot 则通过系统的状态,例如 load1 等,来控制总的入口流量;
当外部请求进来后,sentinel会对每个资源(服务)构建一个链路,后面所有服务都会走这个链路,而在这个链路上会有很多的slot,每个slot都有自己的规则
自定义Slot
Sentinel 将 ProcessorSlot 作为 SPI 接口进行扩展,使得 Slot Chain 具备了扩展的能力。我们可以自行加入自定义的 slot 并编排 slot 间的顺序,从而可以给 Sentinel 添加自定义的功能。
SpringBoot集成Sentinel
在代码中,集成Sentinel的办法有很多种,常用的有两种:
- 代码实现策略限流
- 通过集成SpringCloudSentinel并且在WEB管理端配置策略
本次实验的所有sentinel版本均为:1.8.4。
建议:springBoot的sentinel 依赖和sentinel dashboard的版本要统一,避免一些意料之外的错误。
代码实现策略限流
1.1 引入依赖
1 | <dependency> |
1.2 编写策略和业务逻辑
1 | package com.javaxing.springsentineldemo.controller; |
1秒内访问1次
1秒内访问2次,就会触发流控策略
1.3 缺点
- 业务侵入性很强,需要在java代码里面进行 用sentinel代码去包裹
- 配置不灵活 若需要添加新的受保护资源 需要手动添加在代码里面添加
Spring Cloud Alibaba整合Sentinel
2.1 引入依赖
1 | <dependency> |
2.2 Controller控制器
1 |
|
2.3 application.yaml
1 | spring: |
1、我们需要在application.yaml中指定 sentinel控制台地址,服务会与该IP进行通信
2、SpringBoot默认不会暴露actuator端点,所以我们需要在application.yaml中设置
2.4 安装并运行 Sentinel dashboard
- 我们可以通过Sentinel dashboard配置流控规则和策略
本次实验的下载地址:https://files.javaxing.com/sentinel/sentinel-dashboard-1.8.4.jar
1 | # 运行控制台jar包 |
用户可以通过如下参数进行配置:
- -Dsentinel.dashboard.auth.username=sentinel
- 用于指定控制台的登录用户名为 sentinel;
- -Dsentinel.dashboard.auth.password=123456
- 用于指定控制台的登录密码为 123456;如果省略这两个参数,默认用户和密码均为 sentinel;
- -Dserver.servlet.session.timeout=7200
- 用于指定 Spring Boot 服务端 session 的过期时间,如 7200 表示 7200 秒;60m 表示 60 分钟,默认为 30 分钟;
登录sentinel
访问http://10.211.55.12:9998/#/login,默认用户名密码: sentinel/sentinel
控制台
sentinel刚开始是没数据的,sentinel会在客户端首次调用接口的时候进行初始化,所以我们需要访问一下controller
2.5 访问controller
访问controller接口后,再次查看sentinel dashboard:
2.6 对接口进行流控
- 资源名: 接口的API
- 针对来源: 默认是default,当多个微服务都调用这个资源时,可以配置微服务名来对指定的微服务设置阈值
- 阈值类型: 分为QPS和线程数,假设阈值为1
- QPS类型: 只得是每秒访问接口的次数>1就进行限流
- 线程数: 为接受请求该资源分配的线程数>1就进行限流
2.7 测试QPS流控
1秒内访问1次:
1秒内访问2次,会触发流控策略被限流:
微服务和Sentinel Dashboard通信原理
Sentinel控制台与微服务端之间,实现了一套服务发现机制。
1、微服务启动后,会通过 sentinel-transport-simple-http模块,将自己的注册到 Sentinel Dashboard中
2、当用户访问微服务的controller时,sentinel Dashboard会去微服务中获取相应的元数据,值得注意的是 获取的元数据,是不会存储在数据库的,如果微服务一旦重启,数据就会丢失
缺点:
- 由于数据并不会持久化在Sentinel Dashboard,一旦微服务重启或控制台重启,则数据都会丢失
- 如果只是一个节点我们就配置一个策略,那么在生产环境中存在大量的节点就需要配置很多次,明显这不是一个很合理的方案。
通信源码分析
先下载sentinel commandHandler的源码和HttpEventTask源码
下载后,我们可以通过 查找handle使用的方法,去定位到具体的调用位置:
找到调用位置:
控制台发送规则,服务端接收规则后存储到内存中即时生效
Sentinel 集成Open Feign
sentinel可以对api 接口等资源进行流控外,也可以对open feign的接口进行拦截并进行一系列流控。
1、项目结构介绍
这里一共有2个模块,1、sentinel demo 2、xingmall-order ,我们都注册到了nacos中,并且在sentinel demo上通过open feign调用xingmall-order的controller
sentime demo pom
1 | <dependencies> |
xingmall-order 只是一个很简单的微服务,对外提供了一个controller,所以这里就不贴pom了
2、sentinel demo写feign接口
1 | package com.javaxing.springsentineldemo.api; |
fallback
1 | package com.javaxing.springsentineldemo.api; |
3、sentinel demo调用feign接口
1 |
|
4、sentinel dashboard设置流控策略
如果我们要去对openfeign进行流控的话,链路资源名 和正常的controller不太一样,需要注意, 针对正确的资源名称进行流控
5、访问测试
如图所示,我们1秒访问一次 接口返回正常查询,1秒访问2次就会触发流控并且进入了fallBack的回调(降级)。