Sentinel的主要作用就是保护微服务。微服务常见的问题就是雪崩问题:
那么什么是雪崩问题呢?
简单来说,就是相互依赖的服务中,有其中一方不可用,导致依赖它的一方,因为请求得不到回应,导致请求一直堵塞,当达到了服务的临界值后,tomcat的资源被耗尽,被请求堵塞的服务就会也变成不可用,同样的道理,就会有越来越多的服务损坏,造成不可挽回的微服务崩塌损坏的现象,和自然界的雪崩道理相同,这就是服务雪崩。
那么遇到这种雪崩问题,我们的微服务集群就会彻底崩塌不可使用,为了避免这种情况的发生,大佬们就写出了Hystrix、Sentinel这种服务保护机制,那么这篇文章就是总结Sentinel这个框架的。
那么究其根本,雪崩问题就是请求的不断堵塞,占用系统资源,因为资源始终有限,所以必定会导致资源耗尽,最终出现资源不可用的现象。
解决雪崩问题的常见方式有四种:
1、超时处理:设定超时时间,请求超过一定时间没有响应就返回错误信息,不会无休止等待、
这种方式只能对雪崩现象有一个缓解作用,并不能解决这个问题,当请求的频率超过设定的超时时间,时间长了仍然会造成雪崩。

2、舱壁模式:船舱都会被隔板分离为多个独立空间,当船体破损时,只会导致部分空间进入,将故障控制在一定范围内,避免整个船体都被淹没。可以限定每个业务能使用的线程数,避免耗尽整个tomcat的资源,因此也叫线程隔离。
具体的做法就是:给每个业务分配资源,避免一颗老鼠屎毁了整锅汤。但是这种方式,会造成一定程度上的资源浪费,明明那个就不能用了,还得分配给它资源。

3、熔断降级:由断路器统计业务的异常比例,如果超出阈值则会熔断该业务,拦**问该业务的一切请求
和日常的保险丝一个道理,如果访问正常比例和失败的比例达到了某个临界值,那么就是熔断,只要是想访问这个失败的服务,断路器就会拦截并使其快速失败,立即释放tomcat资源,这样就避免了资源的浪费。

4、流量控制:限制业务访问的QPS,避免服务因流量的突增而故障
QPS:每秒钟处理的请求的数量
前三种方式都是避免避免雪崩发生的方法,而流量控制属于预防雪崩的 方法,未雨绸缪型。这里就使用到了Sentinel,通过流量控制,保证服务可以接收的QPS大于等于外部来的请求,就可避免因高并发导致服务处理不了而宕机。

雪崩问题总结:

服务保护的技术框架对比:

认识Sentinel
Sentinel是阿里巴巴的一个服务保护的产品,没别的,就是牛。
关于Sentinel客户端的安装,可以在GitHub - alibaba/Sentinel: A powerful flow control component enabling reliability, resilience and monitoring for microservices. (面向云原生微服务的高可用流控防护组件)A powerful flow control component enabling reliability, resilience and monitoring for microservices. (面向云原生微服务的高可用流控防护组件) - GitHub - alibaba/Sentinel: A powerful flow control component enabling reliability, resilience and monitoring for microservices. (面向云原生微服务的高可用流控防护组件)
https://github.com/alibaba/Sentinel上下载jar包,然后,用“java -jar jar包名称 ”的方式直接启动。然后在浏览器访问8080端口,账号密码都是sentinel。然后就可以看到Sentinel的控制台。

下载方式:到github网址之后,点击图中红框框位置:

点击之后,往下稍微翻一翻就能看到这个jar包,点击直接下载就可以啦。

下载完毕,那么就看看能不能修改一下它的访问端口号啊什么的。但是它已经是个jar包了,没法改代码或者什么配置文件了,所以我们只能在启动jar包的时候在后面加参数就可以了,具体如图:

在github上点击wiki,会有更详细的文档。有兴趣的小伙伴可以查看一下。.

整合微服务和Sentinel:
1)引入sentinel依赖
<!--sentinel-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
2)配置控制台
修改application.yaml文件,添加下面内容:
server:
port: 8088
spring:
cloud:
sentinel:
transport:
dashboard: localhost:8080
3)访问order-service的任意端点
需要访问任意一个controller,任意一个controller中的方法就可以理解成一个端点(endpoint)。这样才能触发sentinel对服务的监控。
Sentinel实现了四种解决雪崩方法中的三种,分别是舱壁模式(线程隔离)、降级熔断(断路器)、流量控制。
流量控制(限流):
簇点链路:
当请求进入微服务时,首先会访问DispatcherServlet,然后进入Controller、Service、Mapper,这样的一个调用链就叫做簇点链路。簇点链路中被监控的每一个接口就是一个资源。
默认情况下sentinel会监控SpringMVC的每一个端点(Endpoint,也就是controller中的方法),因此SpringMVC的每一个端点(Endpoint)就是调用链路中的一个资源。
流控、熔断等都是针对簇点链路中的资源来设置的,因此我们可以点击对应资源后面的按钮来设置规则:
- 流控:流量控制
- 降级:降级熔断
- 热点:热点参数限流,是限流的一种
- 授权:请求的权限控制

那么设置好规则之后,就可以利用jmeter压测工具,进行测试。
限流的基本用法:

关于jmeter的安装:查看我的这篇文章吧https://blog.csdn.net/weixin_/article/details/?csdn_share_tail=%7B%22type%22%3A%22blog%22%2C%22rType%22%3A%22article%22%2C%22rId%22%3A%%22%2C%22source%22%3A%22weixin_%22%7D&ctrtid=uVL4F
https://blog.csdn.net/weixin_/article/details/?csdn_share_tail=%7B%22type%22%3A%22blog%22%2C%22rType%22%3A%22article%22%2C%22rId%22%3A%%22%2C%22source%22%3A%22weixin_%22%7D&ctrtid=uVL4F
在高级选项中,流控模式和流控效果都有三种模式:
流控模式:
直接模式
流控模式的直接模式就是我们的默认模式,监控某个服务,达到阈值,就对这个服务做限流。
关联模式
那么关联模式是什么意思呢?如下图,监控写服务,如果写的操作达到了阈值,就对读的操作做限流,避免影响写资源的操作。

链路模式:
那么链路模式又是什么东东呢?简单来说,就是监控规定线路上的请求。
也就是说下图中,只监控从从test2过来的common请求。test1过来的common请求不监控。

但是这里,只有Controller中的方法会被看作成端点被监控,那么如果要用Service中的方法,并且也想让它被当做端点监控有没有办法呢?有:
默认情况下,Service中的方法是不被Sentinel监控的,需要我们自己通过注解来标记要监控的方法。
给Service中的方法添加@SentinelResource注解:eg:
@SentinelResource("goods") public void queryGoods(){ System.err.println("查询商品"); }
讯享网
链路模式中,是对不同来源的两个链路做监控。但是sentinel默认会给进入SpringMVC的所有请求设置同一个root资源,会导致链路模式失效。
我们需要关闭这种对SpringMVC的资源聚合,修改服务的application.yml文件:
讯享网 spring: cloud: sentinel: web-context-unify: false # 关闭context整合
重启服务,访问两个起始端点,可以查看到sentinel的簇点链路规则中,出现了新的资源
流控模式总结

流控效果:
流控效果也有三种:快速失败,预热模式,排队等待。

warm up 预热模式
阈值一般是一个微服务能承担的最大QPS,但是一个服务刚刚启动时,一切资源尚未初始化(冷启动),如果直接将QPS跑到最大值,可能导致服务瞬间宕机。
案例:
排队等待:
排队等待,顾名思义就是让过来的请求进行排队,每过多长时间,就放行一个请求,这样的话,不管你过来多少请求,频率多高,从这里出去的请求始终是稳定的。
当请求超过QPS阈值时,快速失败和warm up 会拒绝新的请求并抛出异常。
而排队等待则是让所有请求进入一个队列中,然后按照阈值允许的时间间隔依次执行。后来的请求必须等待前面执行完成,如果请求预期的等待时间超出最大时长,则会被拒绝。
工作原理
例如:QPS = 5,意味着每200ms处理一个队列中的请求;timeout = 2000,意味着预期等待时长超过2000ms的请求会被拒绝并抛出异常。
那什么叫做预期等待时长呢?
比如现在一下子来了12 个请求,因为每200ms执行一个请求,那么:
- 第6个请求的预期等待时长 = 200 * (6 - 1) = 1000ms
- 第12个请求的预期等待时长 = 200 * (12-1) = 2200ms
请求的并发量就会变得平缓一些

案例:只要QPS超过10个请求,就进入排队等待模式,每5000毫秒也就是5秒释放一个请求。

流控效果总结:

热点参数限流:

那么比如商品当中 ,某几个商品比较火热,访问的比较多,其他商品有的比较冷淡,访问的比较少,那么就需要特殊情况特殊对待了,所以这里还可以针对某个参数进行阈值的单独修改:
那么要注意的一点是,热点参数限流只能应用在使用@SentinelResource的方法上,对SpringMVC的资源是无效的。如果想要在Controller的方法上配置热点参数限流,那么就只能加上@SentinelResource这个注解。配置完之后,这个方法就会有两个资源名称。

并且还要注意的是,我们的参数类型只能在下图的几种当中,如果不在,是没有办法做参数限流的。

在Sentinel当中的热点规则这里配置热点参数限流。
针对隔离和降级,都是对客户端(调用方)的保护,在我们的微服务中,调用使用的就是Feign,所以这两种模式就要整合Feign做服务的保护。
整合Feign:修改调用方的配置文件,并且给FeignClient编写降级策略,避免直接抛异常的情况出现
第一步: 在Feign服务中,定义类,实现FallbackFactory,在泛型中定义好自己是为哪个客户端编写得降级逻辑。例如:这里是给UserClient做降级逻辑,在重写的方法中写逻辑
package cn.itcast.feign.clients.fallback; import cn.itcast.feign.clients.UserClient; import cn.itcast.feign.pojo.User; import feign.hystrix.FallbackFactory; import lombok.extern.slf4j.Slf4j; @Slf4j public class UserClientFallbackFactory implements FallbackFactory<UserClient> { @Override public UserClient create(Throwable throwable) { return new UserClient() { @Override public User findById(Long id) { log.error("查询用户异常", throwable); return new User(); } }; } }
第二步:就是在Feign服务的配置类中,将UserClientFallbackFactory(第一步定义的类的名字)注册成一个Bean。
讯享网@Bean public UserClientFallbackFactory userClientFallbackFactory(){ return new UserClientFallbackFactory(); }
第三步:在UserClient客户端中,通过注解的方式,使用我们定义好的降级逻辑
import cn.itcast.feign.clients.fallback.UserClientFallbackFactory; import cn.itcast.feign.pojo.User; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; @FeignClient(value = "userservice", fallbackFactory = UserClientFallbackFactory.class) public interface UserClient { @GetMapping("/user/{id}") User findById(@PathVariable("id") Long id); }
整合Fegin总结:

隔离(舱壁模式)

两种方式的优缺点:
主动超时:我们给每一个调用都是一个独立的线程,我们可以通过线程池来控制它,我们可以看它耗时有点久就主动终止这个线程,那么线程被终止的了,这个业务也就被终止了。这种就是主动超时。
异步调用:既然每个都是独立线程了,不同调用采用了不同的线程池,彼此之间没有关系,就是异步调用了。
扇出的概念:调用的服务多少,以扇形调用,一个调用多个。
在Sentinel中只需要将QPS改成线程数就变成了舱壁模式。例如:

隔离小结:

熔断(降级熔断)
降级熔断这里有三个状态,就是关闭,打开,和半开。和日常生活中一样,我们保险丝断了之后,会尽快接上。我们的熔断也相同,在熔断之后会尝试连接,释放请求,如果失败了,就再次回到断开的状态。

断路器熔断策略:
1、慢调用:

例如:

2、异常比例:
慢调用看的是响应时长和失败比例是否达到阈值,异常比例看的是请求失败的比例,异常数看的是请求失败的数量

异常比例案例:

异常数案例:

熔断降级总结:

授权规则:
授权规则可以对调用方的来源做控制,有白名单和黑名单两种方式。
- 白名单:来源(origin)在白名单内的调用者允许访问
- 黑名单:来源(origin)在黑名单内的调用者不允许访问
点击左侧菜单的授权,可以看到授权规则:

- 资源名:就是受保护的资源,例如/order/{orderId}
- 流控应用:是来源者的名单,
- 如果是勾选白名单,则名单中的来源被许可访问。
- 如果是勾选黑名单,则名单中的来源被禁止访问。
比如:
我们允许请求从gateway到order-service,不允许浏览器访问order-service,那么白名单中就要填写网关的来源名称(origin)。
但是:默认情况下,sentinel不管请求者从哪里来,返回值永远是default,也就是说一切请求的来源都被认为是一样的值default。
所以我们要自己做一个约定,来判断这个请求到底是从网关来的还是浏览器来的。这里就是在请求头当中添加这个origin,并给它个值。
如何获取origin
Sentinel是通过RequestOriginParser这个接口的parseOrigin来获取请求的来源的。
讯享网public interface RequestOriginParser { / * 从请求request对象中获取origin,获取方式自定义 */ String parseOrigin(HttpServletRequest request); }
这个方法的作用就是从request对象中,获取请求者的origin值并返回。
我们需要自定义这个接口的实现,让不同的请求,返回不同的origin。
在调用者服务当中,我们定义一个RequestOriginParser的实现类:
package cn.itcast.order.sentinel; import com.alibaba.csp.sentinel.adapter.spring.webmvc.callback.RequestOriginParser; import org.springframework.stereotype.Component; import org.springframework.util.StringUtils; import javax.servlet.http.HttpServletRequest; @Component public class HeaderOriginParser implements RequestOriginParser { @Override public String parseOrigin(HttpServletRequest request) { // 1.获取请求头 String origin = request.getHeader("origin"); // 2.非空判断 if (StringUtils.isEmpty(origin)) { origin = "blank"; } return origin; } }
我们会尝试从request-header中获取origin值。
给网关添加请求头:
既然获取请求origin的方式是从reques-header中获取origin值,我们必须让所有从gateway路由到微服务的请求都带上origin头。
这个需要利用之前学习的一个GatewayFilter来实现,AddRequestHeaderGatewayFilter。
修改gateway服务中的application.yml,添加一个defaultFilter:
讯享网spring: cloud: gateway: default-filters: - AddRequestHeader=origin,gateway
这样,从gateway路由的所有请求都会带上origin头,值为gateway。而从其它地方到达微服务的请求则没有这个头。
配置授权规则
接下来,我们添加一个授权规则,放行origin值为gateway的请求。下面的配置中流控应用后面填写的就是我们规定的白名单的origin的值。也就是gateway

这样的话,我们跳过网关访问的话,就会失败,使用网关访问才会通过。
自定义异常结果
默认情况下,发生限流、降级、授权拦截时,都会抛出异常到调用方。异常结果都是flow limmiting(限流)。这样不够友好,无法得知是限流还是降级还是授权拦截。
异常类型
而如果要自定义异常时的返回结果,需要实现BlockExceptionHandler接口:
public interface BlockExceptionHandler { / * 处理请求被限流、降级、授权拦截时抛出的异常:BlockException */ void handle(HttpServletRequest request, HttpServletResponse response, BlockException e) throws Exception; }
这个方法有三个参数:
- HttpServletRequest request:request对象
- HttpServletResponse response:response对象
- BlockException e:被sentinel拦截时抛出的异常
这里的BlockException包含多个不同的子类:
| 异常 | 说明 |
|---|---|
| FlowException | 限流异常 |
| ParamFlowException | 热点参数限流的异常 |
| DegradeException | 降级异常 |
| AuthorityException | 授权规则异常 |
| SystemBlockException | 系统规则异常 |
自定义异常处理
下面,我们就在调用者服务中定义一个自定义异常处理类:
讯享网package cn.itcast.order.sentinel; 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 org.springframework.stereotype.Component; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @Component public class SentinelExceptionHandler implements BlockExceptionHandler { @Override public void handle(HttpServletRequest request, HttpServletResponse response, BlockException e) throws Exception { String msg = "未知异常"; int status = 429; if (e instanceof FlowException) { msg = "请求被限流了"; } else if (e instanceof ParamFlowException) { msg = "请求被热点参数限流"; } else if (e instanceof DegradeException) { msg = "请求被降级了"; } else if (e instanceof AuthorityException) { msg = "没有权限访问"; status = 401; } response.setContentType("application/json;charset=utf-8"); response.setStatus(status); response.getWriter().println("{\"msg\": " + msg + ", \"status\": " + status + "}"); } }
重启测试,在不同场景下,会返回不同的异常消息.
总结:

学到这里我们会发现一个问题,每当我们重启了服务之后,我们配置的规则就会消失,那么怎么让规则持久化呢?就是我们接下来要看的内容了。
规则持久化:
规则是否能持久化,取决于规则管理模式,sentinel支持三种规则管理模式:
- 原始模式:Sentinel的默认模式,将规则保存在内存,重启服务会丢失。
- pull模式
- push模式
pull模式
pull模式:控制台将配置的规则推送到Sentinel客户端,而客户端会将配置规则保存在本地文件或数据库中。以后会定时去本地文件或数据库中查询,更新本地规则。

push模式
push模式:控制台将配置规则推送到远程配置中心,例如Nacos。Sentinel客户端监听Nacos,获取配置变更的推送消息,完成本地配置更新。

Sentinel 规则持久化
具体步骤如下:
1.引入依赖
在order-service中引入sentinel监听nacos的依赖:
<dependency> <groupId>com.alibaba.csp</groupId> <artifactId>sentinel-datasource-nacos</artifactId> </dependency>
2.配置nacos地址
在order-service中的application.yml文件配置nacos地址及监听的配置信息:
讯享网spring: cloud: sentinel: datasource: flow: nacos: server-addr: localhost:8848 dataId: 服务名字-flow-rules groupId: SENTINEL_GROUP rule-type: flow degrade: nacos: server-addr: localhost:8848 dataId: 服务名字-degrade-rules groupId: SENTINEL_GROUP rule-type: degrade system: nacos: server-addr: localhost:8848 dataId: 服务名字-system-rules groupId: SENTINEL_GROUP rule-type: system authority: nacos: server-addr: localhost:8848 dataId: 服务名字-authority-rules groupId: SENTINEL_GROUP rule-type: authority param-flow: nacos: server-addr: localhost:8848 dataId: 服务名字-param-flow-rules groupId: SENTINEL_GROUP rule-type: param-flow
修改sentinel-dashboard源码
SentinelDashboard默认不支持nacos的持久化,需要修改源码。
这里过程比较复杂,就使用改好的吧。
「sentinel-dashboard-魔改版.jar」https://www.aliyundrive.com/s/zesXUmEG4jq 提取码: 10cs 点击链接保存,或者复制本段内容,打开「阿里云盘」APP ,无需下载极速在线查看,视频原画倍速播放。
.启动
启动方式跟官方一样:
java -jar sentinel-dashboard.jar
如果要修改nacos地址,需要添加参数:
讯享网java -jar -Dnacos.server-addr=localhost:8848 sentinel-dashboard.jar
总结:




版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请联系我们,一经查实,本站将立刻删除。
如需转载请保留出处:https://51itzy.com/kjqy/25285.html