目录
1.在调用服务中自定义方法继承RequestOriginParser
前言:本文是maven工程下的微服务与sentinel进行整合,讲解Sentine在微服务保护中扮演的角色,能够帮助我们解决什么问题,怎么解决,以及将不同的解决方式进行比较分析出他们合适的应用场景;
在进行Sentinel安装之前我们先了解为什么需要sentinel这样的微服务保护机制:
雪崩问题:我们需要微服务保护的首要原因就是微服务的雪崩问题——微服务调用链路中的某个服务故障,引起整个链路中的所有微服务都不可用,这就是雪崩。
解决雪崩问题常见的四种:
- 超时处理:设定超时时间,请求超过一定时间没有响应就返回错误信息,不会无休止等待,这样就解决了因为某个服务调用另一个服务而进行无休止等待并且占用相关资源而无法得到释放,当大量处理累积就会导致服务器雪崩的问题。
- 舱壁模式:限定每个业务能使用的线程数,避免耗尽整个tomcat的资源,因此也叫线程隔离,设置服务的处理请求的线程数,到某个服务的线程数达到上限后,就会拒绝之后的访问请求,避免了因为某个服务(如服务C)的故障导致被调用者(如服务A)累计大量的处理请求,进而自身的其他服务无法正常进行导致的雪崩问题
- 熔断降级:由断路器统计业务执行的异常比例,如果超出阈值则会熔断该业务,拦截访问该业务的一切请求。和舱壁模式的处理相似。当某个服务节点故障时,或者响应结果异常时,发生异常的次数会被记录下来,当比例达到预设值,就会将所有访问此节点的请求进行拦截做出降级处理或者返回异常信息
- 流量控制:限制业务访问的QPS(一秒内处理访问请求的数量),避免服务因流量的突增(瞬间高并发流量)而导致服务程序的崩溃,引起的雪崩问题
那么什么又是sentinel呢?它的框架和技术又有哪些呢?
Sentinel
隔离策略
信号量隔离
熔断降级策略
基于慢调用比例或异常比例
实时指标实现
滑动窗口
规则配置
支持多种数据源
扩展性
多个扩展点
基于注解的支持
支持
限流
基于 QPS,支持基于调用关系的限流
流量整形
支持慢启动、匀速排队模式
系统自适应保护
支持
控制台
开箱即用,可配置规则、查看秒级监控、机器发现等
常见框架的适配
Servlet、Spring Cloud、Dubbo、gRPC 等
Sentinel是阿里巴巴开源的一款微服务流量控制组件。官网地址:https://sentinelguard.io/zh-cn/index.htmlhttps://sentinelguard.io/zh-cn/index.html
Sentinel 具有以下特征:
- 丰富的应用场景:Sentinel 承接了阿里巴巴近 10 年的双十一大促流量的核心场景,例如秒杀(即突发流量控制在系统容量可以承受的范围)、消息削峰填谷、集群流量控制、实时熔断下游不可用应用等。
- 完备的实时监控:Sentinel 同时提供实时的监控功能。您可以在控制台中看到接入应用的单台机器秒级数据,甚至 500 台以下规模的集群的汇总运行情况。
- 广泛的开源生态:Sentinel 提供开箱即用的与其它开源框架/库的整合模块,例如与 Spring Cloud、Dubbo、gRPC 的整合。您只需要引入相应的依赖并进行简单的配置即可快速地接入 Sentinel。
- 完善的 SPI 扩展点:Sentinel 提供简单易用、完善的 SPI 扩展接口。您可以通过实现扩展接口来快速地定制逻辑。例如定制规则管理、适配动态数据源等。
一、 安装Sentinel、配置默认端口,账户以及密码
1.下载官方提供的jar包并存放在无中文目录下
2.启动Sentinel并配置默认端口以及用户名和密码
- 2.1在当前目录下打开cmd启动jar包
java -jar -Dserver.port=8090 -Dsentinel.dashboard.auth.username=cool -Dsentinel.dashboard.auth.password=1234 sentinel-dashboard-1.8.8.jar
配置项
默认值
说明
server.port
8080
服务端口
sentinel.dashboard.auth.username
sentinel
默认用户名
sentinel.dashboard.auth.password
sentinel
默认密码
2.2访问自定义端口(如果不进行修改默认端口为8080),输入账户和密码进行登录
3.在Maven工程下整合Sentinel
前言:这里我的Maven工程主要只有两个服务(order和service),通过order服务的订单查询接口远程调用service获取订单的用户信息,所以我的maven工程只有一个服务消费者和一个服务提供者
3.1导入Maven依赖触发Sentinel监控
(1)引入Sensinel依赖
<!--Sentinel--> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId> </dependency>
(2)配置控制台地址用于注册和发现
spring:
cloud:
sentinel:
transport:
dashboard: localhost:8080注意:注意层级递进关系和设置自己的端口
(3)启动服务,并访问服务任意端点,触发监控
3.2流控规则的使用和讲解
前言:在进行设置流控规则之前我们需要了解,流控的规则有哪些,这些规则设置在谁身上,以及设置的作用是什么。
Sentinel提供的流控规则有下面三种流控模式及其作用:
- 直接:统计当前资源的请求,触发阈值时对当前资源直接限流,也是默认的模式
- 关联:统计与当前资源相关的另一个资源,触发阈值时,对当前资源限流
- 链路:统计从指定链路访问到本资源的请求,触发阈值时,对指定链路限流
上述的三种流控规则主要设置簇点链路中
簇点链路:就是项目内的调用链路,链路中被监控的每个接口就是一个资源。默认情况下sentinel会监控SpringMVC的每一个端点(Endpoint),因此SpringMVC的每一个端点(Endpoint)就是调用链路中的一个资源。流控、熔断等都是针对簇点链路中的资源来设置的,因此我们可以点击对应资源后面的按钮来设置规则
3.3设置直接流控规则
对 /order/{orderId}这个资源设置流控规则,QPS不能超过 5。然后利用jemeter测试。
1.流控规则设置
2.jemeter测试(设置发送请求访问资源端口,同时发送的QPS设置为10)
注意:因为我们设置的流控规则规定的直接规则,当超过我们设置的QPS后,多余的请求就会直接被拒绝
3.测试结果查询
观测结果如上图所示我们就完成了对order服务的查询订单接口的直接流控规则设置,实现了直接对当前服务进行限流设置
3.4设置关联流控规则
前言:
关联规则的应用场景:主要是针对两个服务要对同一个资源进行操作,进而产生竞争,这是我们需要对相对来优先级不高的服务进行限流,保证另一个能够顺利和高时效的完成
这里,我在order服务中新建立两个端点,一个是 /order/query(订单查询)一个是/order/update(订单修改),两个端点都需要的数据库的数据进行操作(一个读,一个写),配置关联流控规则,当/order/ update资源被访问的QPS超过5时,对/order/query请求限流,也就是说,当有更多的数据修改操作时,先进行数据修改,完毕后再进行数据查询
1.设置关联流规则
注意:这里我们是对 /order/query 进行限流所以我们需要在该端点加上限流规则,且设置QPS为5,当访问/order/update的QPS超过5之后对/query端点进行限流
2.设置jmeter规则
3.观测结果
访问/query端点,此时因为/update的QPS是10,已经超过了预定的5所以对query端点进行限流处理,但是update的请求处理仍然正常
3.5设置链路流控规则
前言:
链路流控规则的应用场景:当两个服务都需要调用另外一个服务时,针对调用服务的来源进行限流,限流其中一个优先度较低的服务保证另一个服务的正常调用,比如,我创建了两个服务 /query 和 /update 都需要调用商品查询接口 /quryGoods(这里我将其写为一个service方法,另外另个端口进行调用此service方法)
注意:
1、Sentinel默认只标记Controller中的方法为资源,如果要标记其它方法,需要利用@SentinelResource注解
2、Sentinel默认会将Controller方法做context整合,导致链路模式的流控失效,需要修改application.yml
spring:
cloud:
sentinel:
web-context-unify: false
1.设置链路规则(按上述配置好服务后,重启服务,并访问两个服务端口,触发监听,检测到自定义资源,以及链路)
2.为goods设置链路规则(上面的两个中任意一个) ,当对goods资源访问的QPS超过2之后会对/query端点进行限流处理,而/update没有影响
3.jmeter配置,发出两个请求,分别请求/query和/update,请求的QPS为4
4.结果分析
这是/update的访问结果,很明显,没有受到任何影响,完成了测试
而/query 的访问结果就出现了因为限流而导致的请求错误,符合测试预期
4.流控效果的设置以及作用
前言:
在前面的测试中,某些服务因为达到阈值而被限流处理,使用的其实是快速失败的方式进行处理,实际上还有其他两种方式预热模式和排队等待
- 快速失败:达到阈值后,新的请求会被立即拒绝并抛出FlowException异常。是默认的处理方式。
- warm up:预热模式,对超出阈值的请求同样是拒绝并抛出异常。但这种模式阈值会动态变化,从一个较小值逐渐增加到最大阈值。它是应对服务冷启动的一种方案。请求阈值初始值是 threshold / coldFactor,持续指定时长后,逐渐提高到threshold值。而coldFactor的默认值是3.
例如,我设置QPS的threshold为10,预热时间为5秒,那么初始阈值就是 10 / 3 ,也就是3,然后在5秒后逐渐增长到10- 排队等待:让所有的请求按照先后次序排队执行,两个请求的间隔不能小于指定时长。
当请求超过QPS阈值时,快速失败和warm up 会拒绝新的请求并抛出异常。而排队等待则是让所有请求进入一个队列中,然后按照阈值允许的时间间隔依次执行。后来的请求必须等待前面执行完成,如果请求预期的等待时间超出最大时长,则会被拒绝。
例如:QPS = 5,意味着每200ms处理一个队列中的请求;timeout = 2000,意味着预期等待超过2000ms的请求会被拒绝并抛出异常
5.热点参数限流设置
前言:
之前的限流是统计访问某个资源的所有请求,判断是否超过QPS阈值。而热点参数限流是分别统计参数值相同的请求,判断是否超过QPS阈值。比如我进行订单查询,需要提交具体的订单id这个参数,我们可以对这个参数的值进行限流设置,针对某些特定的值我可以将QPS调高或者调低,适用于某些订单(商品)的查询频率要高一些,而有些订单的查询频率要低一些,就设置不同的QPS。
注意:热点参数限流对默认的SpringMVC资源无效,所以我们需要在需要进行热点参数限流的方法上加上@SentinelResource完成热点参数配置
1.完成上述注释添加后,重启服务,访问端口,触发监控
2.在热点规则·栏目中,新增热点规则
六、Sentinel的线程隔离和熔断降级
虽然限流可以尽量避免因高并发而引起的服务故障,但服务还会因为其它原因而故障。而要将这些故障控制在一定范围,避免雪崩,就要靠线程隔离(舱壁模式)和熔断降级手段了。
不管是线程隔离还是熔断降级,都是对客户端(调用方)的保护。
6.1Feign和Sentinel的整合
SpringCloud中,微服务调用都是通过Feign来实现的,因此做客户端保护必须整合Feign和Sentinel
1.修改OrderService的application.yml文件,开启Feign的Sentinel功能
feign: sentinel: enabled: true2.通过FallbackFactory给FeignClient编写失败后的降级逻辑,日志显示查询异常,并返回空的user,注意加上@Component注解,不然启动时无法找到实体,以及注意导包
3.在UserClient接口中使用UserClientFallbackFactory
5.重启服务,触发监控,完成整合以及配置降级方法
6.2实现线程隔离
1.对UserClient的查询用户接口设置流控规则,线程数不能超过2,当访问请求超过线程数后,会调用我们设置的降级处理
2.jmeter测试,这里我设置的并发线程数为10
3.查看jmeter的测试结果,有结果可知,当并发线程超过规定上线后,采用了我们的降级处理返回空的user对象,而不是直接返回错误结果
6.3 熔断降级
熔断降级的思路是由断路器统计服务调用的异常比例、慢请求比例,如果超出阈值则会熔断该服务。即拦截访问该服务的一切请求;而当服务恢复时,断路器会放行访问该服务的请求
断路器一共有三种状态:最开始的状态时Closed,为关闭状态,被调用的服务抛出异常的次数达到阈值,断路器就会进入open状态,拦截调用服务的访问并直接返回异常信息,在open状态持续一段时间后,就会进入Half-Open状态,会尝试放行一次访问请求,如果请求的结果成功断路器的状态更改为Closed否则依旧保持为Open。
熔断降级的三种策略:
- 慢调用:业务的响应时长(RT)大于指定时长的请求认定为慢调用请求。在指定时间内,如果请求数量超过设定的最小数量,慢调用比例大于设定的阈值,则触发熔断。例如:RT超过500ms的调用是慢调用,统计最近10000ms内的请求,如果请求量超过10次,并且慢调用比例不低于0.5,则触发熔断,熔断时长为5秒。然后进入half-open状态,放行一次请求做测试。
- 异常比例或异常数:统计指定时间内的调用,如果调用次数超过指定请求数,并且出现异常的比例达到设定的比例阈值(或超过指定异常数),则触发熔断。例如:
- 统计最近1000ms内的请求,如果请求量超过10次,并且异常比例不低于0.5,则触发熔断,熔断时长为5秒。然后进入half-open状态,放行一次请求做测试。
七、自定义异常结果
默认情况下,发生限流、降级、授权拦截时,都会抛出异常到调用方。如果要自定义异常时的返回结果,需要实现BlockExceptionHandler接口:
public interface BlockExceptionHandler {
/**
* 处理请求被限流、降级、授权拦截时抛出的异常:BlockException
*/
void handle(HttpServletRequest request, HttpServletResponse response, BlockException e) throws Exception;
}而BlockException包含很多个子类,分别对应不同的场景
异常
说明
FlowException
限流异常
ParamFlowException
热点参数限流的异常
DegradeException
降级异常
AuthorityException
授权规则异常
SystemBlockException
系统规则异常
所以我们需要在order-service中定义类,实现BlockExceptionHandler接口:
@Component public class SentinelBlockHandler implements BlockExceptionHandler { @Override public void handle( HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, BlockException e) throws Exception { String msg = "未知异常"; int status = 429; if (e instanceof FlowException) { msg = "请求被限流了!"; } else if (e instanceof DegradeException) { msg = "请求被降级了!"; } else if (e instanceof ParamFlowException) { msg = "热点参数限流!"; } else if (e instanceof AuthorityException) { msg = "请求没有权限!"; status = 401; } httpServletResponse.setContentType("application/json;charset=utf-8"); httpServletResponse.setStatus(status); httpServletResponse.getWriter().println("{\"message\": \"" + msg + "\", \"status\": " + status + "}"); } }当达到阈值就会抛出对应异常
八、访问授权
前言:
什么是访问授权呢?为什么需要访问授权?
访问授权就是只限定允许的请求进行访问服务,这里我将指定我的访问服务必须是通过网关发送的请求才允许通过,其他的请求一律拦截,这样就算自己的微服务端口被意外暴露,外界也无法直接通过端口进行数据操作。规避了一些不安全的风险,让所有请求都必须通过网关做身份认证。
授权规则主要有两种:
- 白名单:来源(origin)在白名单内的调用者允许访问
- 黑名单:来源(origin)在黑名单内的调用者不允许访问
这里我将通过网关发送的请求的请求头中添加一个为"Qiuqiu"的请求头,值为"fighting",作为标识,然后再通过Sentinel做校验判断发送来的请求是否携带"qiuqiu"的请求头,有则放行,无则拦截
1.在调用服务中自定义方法继承RequestOriginParser
Sentinel是通过RequestOriginParser这个接口的parseOrigin来获取请求的来源的。所以我们需要自定义方法和处理逻辑并继承该接口同时将其加上@Component注解交给SpringBoot进行管理
@Component public class RequestHandler implements RequestOriginParser { @Override public String parseOrigin(HttpServletRequest request) { String origin = request.getHeader("qiuqiu"); if (origin == null) { return "blank"; } return origin; } }这里我的代码逻辑是获取请求头为”Qiuqiu“的数据有的话直接俄返回数据,没有则返回"blank"字符串,sentinel接收到返回的数据后再进行定义的规则进行判断处理,拦截还是放行
2.编写Sentinel授权规则
编写白名单规则:
编写黑名单规则:
3.使用不同的端口进行查询返回结果
浏览器直接访问
网关访问