目录
高并发下的微服务容错方案?
限流、熔断、降级
1:限流
2:熔断
3:降级
分布式系统的流量防卫兵-Sentinel
什么是 Sentinel ?
Sentinel 的主要概念
资源
规则
上手 Sentinel
部署Sentinel控制台
从 Github 上下载 Sentinel
启动 Sentinel 服务
SpringCloud Alibaba 整合 Sentinel
配置流控规则
流控控制面板介绍
流控效果介绍
快速失败
Warm Up
排队等待
熔断降级介绍
慢调用比例
异常比例
异常数
权限介绍
自定义流控熔断返回的JSON信息
Feign 整合 Sentinel 返回兜底数据
在高并发系统中一定要用,高并发的所有请求进来,不是让每个请求都打到后台集群的,后台集群有它的消费能力,应该在它消费能力之内放行请求,这个就是限流
比如服务集群的处理能力是每秒1w,那么从网关处放过来的请求就是1w,超过1w的请求将直接丢弃,返回 “服务繁忙,稍后重试” 等字样。
一个服务常常会调用别的模块,可能是另外的一个远程服务、数据库,或者第三方 API 等。例如,支付的时候,可能需要远程调用银联提供的 API;查询某个商品的价格,可能需要进行数据库查询。然而,这个被依赖服务的稳定性是不能保证的。如果依赖的服务出现了不稳定的情况,请求的响应时间变长,那么调用服务的方法的响应时间也会变长,线程会产生堆积,最终可能耗尽业务自身的线程池,服务本身也变得不可用。
现代微服务架构都是分布式的,由非常多的服务组成。不同服务之间相互调用,组成复杂的调用链路。以上的问题在链路调用中会产生放大的效果。复杂链路上的某一环不稳定,就可能会层层级联,最终导致整个链路都不可用。因此我们需要对不稳定的弱依赖服务调用进行熔断降级,暂时切断不稳定调用,避免局部不稳定因素导致整体的雪崩。熔断降级作为保护自身的手段.
熔断就是在规定的时间内,下游服务还没有返回数据,上游服务在指定的时间内比如10秒内不调用下游服务,会在10秒后重新尝试调用下游服务,看下游服务是否正常响应了
我们知道在一套完整的业务流程中是有主要的核心业务,和非核心业务的,比如电商系统中的商品、订单、支付是核心业务。推荐、优惠、附加业务(如小游戏)等是非核心业务。当服务流量很大的时候,可以降低非核心业务的可用性或者直接停掉。保证核心业务的可用。
官网:https://sentinelguard.io/zh-cn
github:https://github.com/alibaba/spring-cloud-alibaba/wiki/Sentinel
随着微服务的流行,服务和服务之间的稳定性变得越来越重要。Sentinel 是面向分布式、多语言异构化服务架构的流量治理组件,主要以流量为切入点,从流量路由、流量控制、流量整形、熔断降级、系统自适应过载保护、热点流量防护等多个维度来帮助开发者保障微服务的稳定性。
Sentinel 具有以下特征:
资源是 Sentinel 的关键概念。它可以是 Java 应用程序中的任何内容,例如,由应用程序提供的服务,或由应用程序调用的其它应用提供的服务,甚至可以是一段代码。在接下来的文档中,我们都会用资源来描述代码块。
只要通过 Sentinel API 定义的代码,就是资源,能够被 Sentinel 保护起来。大部分情况下,可以使用方法签名,URL,甚至服务名称作为资源名来标示资源。
围绕资源的实时状态设定的规则,可以包括流量控制规则、熔断降级规则以及系统保护规则。所有规则可以动态实时调整。
https://github.com/alibaba/Sentinel
注意:启动 Sentinel 控制台需要 JDK 版本为 1.8 及以上版本
注意:Sentinel 控制台目前仅支持单机部署。
使用如下命令启动控制台:
java -Dserver.port=8080 -Dcsp.sentinel.dashboard.server=localhost:8080 -Dproject.name=sentinel-dashboard -jar sentinel-dashboard.jar
其中 -Dserver.port=8080
用于指定 Sentinel 控制台端口为 8080
访问地址:http://ip:8080
默认账号密码:sentinel/sentinel
maven版本管理如下
17
17
17
17
UTF-8
UTF-8
2.7.4
2021.0.4
2021.0.4.0
org.springframework.boot
spring-boot-dependencies
${spring.boot.version}
pom
import
org.springframework.cloud
spring-cloud-dependencies
${spring.cloud.version}
pom
import
com.alibaba.cloud
spring-cloud-alibaba-dependencies
${spring.cloud.alibaba.version}
pom
import
子服务 pom 中添加依赖
com.alibaba.cloud
spring-cloud-starter-alibaba-sentinel
application.yml 文件配置 sentinel
spring:
cloud:
sentinel:
transport:
port: 8719
# sentinel 控制台地址
dashboard: localhost:8080
这里的
spring.cloud.sentinel.transport.port
端口配置会在应用对应的机器上启动一个 Http Server,该 Server 会与 Sentinel 控制台做交互。比如 Sentinel 控制台添加了一个限流规则,会把规则数据 push 给这个 Http Server 接收,Http Server 再将规则注册到 Sentinel 中。这个端口不能被占用,多个微服务配置不同的port。若被占用,则端口自动+1
确保客户端有访问量,Sentinel 会在客户端首次调用的时候进行初始化,开始向控制台发送心跳包。微服务接口被请求后,就会在控制台显示出微服务
注意:这里的配置规则是通过 调用 sentinel 提供的 HTTP API 接口来改变的,这也意味着这些规则仅在内存态生效,应用重启之后,该规则会丢失。关于持久化规则后面会讲到,请查看官网:在生产环境中使用 Sentinel · alibaba/Sentinel Wiki · GitHub
正常的请求拿到的数据如下
当 一秒 时间里 请求次数超过 2 次,返回流控限制信息
流量控制 · alibaba/Sentinel Wiki · GitHub
资源名:默认是请求路径,可自定义,要保证唯一
针对来源:对哪个微服务进行限流,默认是不区分来源,全部限流,这个是针对 区分上游服务进行限流, 比如 视频服务 被 订单服务、用户服务调用,就可以针对订单服务来源进行限流,对用户服务来源不限流
阈值类型:
- 流量控制
- 原理是监控应用流量的 QPS 或并发线程数等指标,当达到指定的阈值时对流量进行控制,以避免被瞬时的流量高峰冲垮,从而保障应用的高可用性。
- 两种规则
- 基于统计QPS的流量控制:当 QPS 超过某个阈值的时候,则采取措施进行流量控制
- 基于统计并发线程数的流量控制:并发数控制用于保护业务线程池不被慢调用耗尽。Sentinel 并发控制不负责创建和管理线程池,而是简单统计当前请求上下文的线程数目(正在执行的调用数目)如果超出阈值,新的请求会被立即拒绝,效果类似于信号量隔离。
默认的流量控制方式,当QPS超过任意规则的阈值后,新的请求就会被立即拒绝
即预热/冷启动方式。当系统长期处于低水位的情况下,当流量突然增加时,直接把系统拉升到高水位可能瞬间把系统压垮。通过"冷启动",让通过的流量缓慢增加,在一定时间内逐渐增加到阈值上限,给冷系统一个预热的时间,避免冷系统被压垮。
严格控制请求通过的间隔时间,也即是让请求以均匀的速度通过,对应的是漏桶算法,主要用于处理间隔性突发的流量,如消息队列,想象一下这样的场景,在某一秒有大量的请求到来,而接下来的几秒则处于空闲状态,我们希望系统能够在接下来的空闲期间逐渐处理这些请求,而不是在第一秒直接拒绝多余的请求。
- 匀速排队等待策略是 Leaky Bucket 算法结合虚拟队列等待机制实现的。
- 注意:匀速排队模式暂时不支持 QPS > 1000 的场景,不能让用户无限的排对,不然等到啥时候去了?
选择以慢调用比例作为阈值,需要设置允许的慢调用 RT(即最大的响应时间),请求的响应时间大于该值则统计为慢调用。当单位统计时长(
statIntervalMs
)内请求数目大于设置的最小请求数目,并且慢调用的比例大于阈值,则接下来的熔断时长内请求会自动被熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求响应时间小于设置的慢调用 RT 则结束熔断,若大于设置的慢调用 RT 则会再次被熔断。
示例:
同下面的异常比例、异常数类似,就不贴示例了
当单位统计时长(
statIntervalMs
)内请求数目大于设置的最小请求数目,并且异常的比例大于阈值,则接下来的熔断时长内请求会自动被熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求成功完成(没有错误)则结束熔断,否则会再次被熔断。异常比率的阈值范围是[0.0, 1.0]
,代表 0% - 100%。
当单位统计时长内的异常数目超过阈值之后会自动进行熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求成功完成(没有错误)则结束熔断,否则会再次被熔断。
很多时候,我们需要根据调用来源来判断该次请求是否允许放行,这时候可以使用 Sentinel 的来源访问控制(黑白名单控制)的功能。来源访问控制根据资源的请求来源(
origin
)限制资源是否通过,若配置白名单则只有请求来源位于白名单内时才可通过;若配置黑名单则请求来源位于黑名单时不通过,其余的请求通过。
import com.alibaba.csp.sentinel.adapter.spring.webmvc.callback.BlockExceptionHandler;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.alibaba.csp.sentinel.slots.block.authority.AuthorityException;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeException;
import com.alibaba.csp.sentinel.slots.block.flow.FlowException;
import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowException;
import com.alibaba.csp.sentinel.slots.system.SystemBlockException;
import com.alibaba.fastjson.JSON;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.Map;
@Component
public class CustomUrlBlockHandler implements BlockExceptionHandler {
@Override
public void handle(HttpServletRequest request, HttpServletResponse response, BlockException e) throws Exception {
Map backMap = new HashMap<>(2);
if (e instanceof FlowException) {
backMap.put("code", -1);
backMap.put("msg", "限流-异常啦");
} else if (e instanceof DegradeException) {
backMap.put("code", -2);
backMap.put("msg", "降级-异常啦");
} else if (e instanceof ParamFlowException) {
backMap.put("code", -3);
backMap.put("msg", "热点-异常啦");
} else if (e instanceof SystemBlockException) {
backMap.put("code", -4);
backMap.put("msg", "系统规则-异常啦");
} else if (e instanceof AuthorityException) {
backMap.put("code", -5);
backMap.put("msg", "认证-异常啦");
}
response.setHeader("Access-Control-Allow-Origin", "*");
response.setHeader("Cache-Control", "no-cache");
response.setStatus(200);
response.setCharacterEncoding("UTF-8");
response.setContentType("application/json");
response.getWriter().println(JSON.toJSONString(backMap));
response.getWriter().flush();
}
}
子服务 pom 中添加依赖
org.springframework.cloud
spring-cloud-starter-openfeign
com.alibaba.cloud
spring-cloud-starter-alibaba-sentinel
application.yml 文件配置 打开Sentinel对Feign的支持
feign:
sentinel:
enabled: true
当使用 feigin 调用下游服务的时候,下游服务异常,可以使用 @FeignClient 注解的 fallback 属性指定返回兜底数据
// fallback:指定兜底数据是哪个实现类(兜底实现类,要实现本接口类)
@FeignClient(name = "order-service", fallback = OrderServiceFallback.class)
public interface OrderService {
@GetMapping("/order/one")
OrderDO getOne(@RequestParam("orderId") Long orderId);
}
@Service
public class OrderServiceFallback implements OrderService {
@Override
public OrderDO getOne(Long orderId) {
// 返回兜底数据
OrderDO orderDO = new OrderDO();
orderDO.setId(1L);
return orderDO;
}
}
返回了兜底数据