sentinel工作原理及使用
sentinel 是一个功能全面的、面向分布式服务架构的轻量级高可用流量控制组件,主要以流量为切入点,从流量控制、熔断降级、系统负载保护等多个维度来帮助用户保护服务的稳定性。下面说一下它的工作原理及使用方法。
一、 sentinel工作原理
当sentinel作用在调用的接口上时,会将这个接口抽象成一种资源,调用方需要申请这种资源,使用的方法是SphUentry(),如果能够申请成功,则说明没有被限流,否则会抛出BlockException,表面已经被限流了。
从SphUentry()方法往下执行会进入到Sphentry(),在entry中,会为每一次申请创建一个context,这个context就是记录本次请求的上下文,然后还会为每一种资源创建一组插槽,这一组插槽就是责任链模式,每一个插槽实现一个功能,请求会进行插槽一个一个执行,这些插槽主要有以下几个:
其中,ClusterBuilderSlot会为每一种资源申请一个全局的ClusterNode,这个ClusterNode会在StatistcSlot中记录每个线程对这个接口的调用情况(调用总数、成功次数、失败次数等)。StatistcSlot采用的是滑动窗口统计方法,它有两个重要的参数:windowLength(窗口长度), intervalInSec(时间间隔)。如窗口长度为500ms,时间间隔是1s,这样系统就会申请两个窗口,然后计算当前时间戳为time,进一步确定落在哪个窗口,具体逻辑如下:
可见通过时间窗口,就可以将一次请求对应到一个窗口,进而计算单位时间的流量,进行流控,同时熔断降级也是通过计算单位时间的异常来实现的,所以时间窗口是十分核心的模块。随着统计的不断进行,当一个窗口时间小于当前时间时,会被系统从当前窗口数组中移除,放入到一个list中,本地会启动一个定时任务,周期性的将list中的窗口数据刷新到本地文件中。当dashboard发送获取统计数据的请求后,本地就会返回文件中的数据给dashboard,这样用户就可以看到统计数据。
二、 sentinel的使用
目前使用sentinel主要用4种方式。
1、在代码中直接使用
在代码中直接调用函数SphUentry(),由于这种方式是直接入侵代码,所以不推荐
2、通过注解添加
在调用的接口上面添加注解@SentinelResource,这样当调用该接口时,spring会通过AOP的方式对含有SentinelResource注解的接口进行增强,采用环绕通知@Around进行切片捕捉,在执行目标方法前执行SphUentry()。这是一种比较好的方法,但是不适合大规模配置,如果接口众多,一个一个添加注解很麻烦。
3、在filter中添加
目前我们项目中调用接口时,通过filter时,会获取调用接口的名称,让好通过名称为资源命名,这样每个接口就是一种资源,这样想到于在每个接口上执行SphUentry(),这样就实现了对每个接口对流量监控
4、在dashboard中添加配置
本地代码中加入sentinel的包,相当与client。在服务器上部署了dashboard,可以通过在dashboard中的配置页面来实现sentinel配置的添加。本地的sentinel通过zk来监听最新的配置消息,当配置修改时,zk通过主动推送的方式将配置消息发送给client。
三、 sentinel集群的实现
sentinel中限流、熔断降级可以在本地进行,也可以几个实例组成一个集群来统一控制。在集群模式中,一个实例做service,其余做client,通过netty进行socket通信。client端遇到流量统计、流控、熔断降级都是发送请求到service,由service统一进行流量统计、流控、熔断降级的处理。client的服务发现通过dashboard的动态配置来完成。
四、 sentinel本地与dashboard通信
sentinel本地和dashboard通过http协议通信,实现方式:本地的sentinel-transport-netty-http模块通过netty实现简单的http服务(采用netty提供的http编码和解码方式:HttpRequestDecoder和HttpResponseEncoder),通过SPI获取本地的继承CommandHandler的类,这些类中找到含有注解CommandMapping的类,CommandMapping的name字段就是http中的url,创建一个map,记录url到Handler的映射关系,当本地服务器接收到dashboard的请求时,从里面的url中找到handler,进行请求的处理。其实就是类似于Spring MVC里面的MappingHandler。
五、总结
sentinel的功能强大,使用简单,可帮助大家快速搭建线上监控系统。
服务降级,当服务器压力剧增的情况下,根据当前业务情况及流量对一些服务和页面有策略的降级,以此释放服务器资源以保证核心任务的正常运行。比如电商平台,在针对618、双11等高峰情形下采用部分服务不出现或者延时出现的情形。
二、实现方式
我在spring Cloud项目中,使用了两种方式处理降级操作。
(1)使用feign组件完成降级操作,到内容提供者无法提供服务时, 消费者会调用降级操作,返回服务不可用等信息,或者返回提前准备好的静态页面。 调用的降级处理方法如下:
1@Component
2public class FeignClientFallbackFactory implements FallbackFactory<SchedualServiceHi> {
3 // 打印日志
4 private static final Logger print = LoggerFactorygetLogger(FeignClientFallbackFactoryclass);
5 //降级处理方式
6 @Override
7 public SchedualServiceHi create(Throwable throwable) {
8 return new SchedualServiceHi() {
9 @Override
10 public String mm(@RequestParam("uname") String uname, @RequestParam("upwd") String upwd) {
11 printinfo("fallback; reason was:", throwable);
12 return "服务报错了";
13 }
14 };
15 }
16}
(2)也可以使用zuul网关,在spring Cloud自定义一个类实现ZuulFallbackProvider接口,当出现问题,无法正常调用时 ,为服务提供回退响应。
1@Component
2public class MyfaultFallback implements FallbackProvider {
3 @Override
4 public String getRoute() {
5// 表示为哪个服务提供回退,此处表示所有微服务。
6 return "";
7 }
8
9 @Override
10 public ClientHttpResponse fallbackResponse(String route, Throwable cause) {11 return new ClientHttpResponse() {
12 @Override
13 public HttpStatus getStatusCode() throws IOException {
14// fallback返回的状态码
15 return HttpStatusOK;
16 }
17
18 @Override
19 public int getRawStatusCode() throws IOException {
20 //数字类型的状态码,本例返回的是200
21 return thisgetStatusCode()value();
22 }
23
24 @Override
25 public String getStatusText() throws IOException {
26 //状态文本27 return "OK";
28 }
29
30 @Override
31 public void close() {
32
33 }
34
35 @Override
36 public InputStream getBody() throws IOException {
37// 响应体38 return new ByteArrayInputStream("用户微服务不可用,请稍候再试"getBytes());
39 }
40
41 @Override
42 public HttpHeaders getHeaders() {
43 HttpHeaders headers = new HttpHeaders();
44 headerssetContentType(MediaTypeAPPLICATION_JSON);
45 MediaType mt = new MediaType("application",
46 "json", CharsetforName("UTF-8"));
47 headerssetContentType(mt);
48 return headers;
49 }
50 };
51 }
52}
三、效果展示
当我们访问zuul网关时,服务提供者没有开启,访问不到,就会进行降级处理,显示下面内容。
原文链接:https://zhuanlanzhihucom/p/69635613
1 缓存击穿
缓存击穿是指一个请求要访问的数据,缓存中没有,但数据库中有的情况。这种情况一般都是缓存过期了。
但是这时由于并发访问这个缓存的用户特别多,这是一个热点 key,这么多用户的请求同时过来,在缓存里面没有取到数据,所以又同时去访问数据库取数据,引起数据库流量激增,压力瞬间增大,直接崩溃给你看。
所以一个数据有缓存,每次请求都从缓存中快速的返回了数据,但是某个时间点缓存失效了,某个请求在缓存中没有请求到数据,这时候我们就说这个请求就"击穿"了缓存。
针对这个场景,对应的解决方案一般来说有三种。
借助Redis setNX命令设置一个标志位就行。设置成功的放行,设置失败的就轮询等待。就是在更新缓存时加把锁
后台开一个定时任务,专门主动更新过期数据
比如程序中设置 why 这个热点 key 的时候,同时设置了过期时间为 10 分钟,那后台程序在第 8 分钟的时候,会去数据库查询数据并重新放到缓存中,同时再次设置缓存为 10 分钟。
其实上面的后台续命思想的最终体现是也是永不过期。
只是后台续命的思想,会主动更新缓存,适用于缓存会变的场景。会出现缓存不一致的情况,取决于你的业务场景能接受多长时间的缓存不一致。
2 缓存穿透
缓存穿透是指一个请求要访问的数据,缓存和数据库中都没有,而用户短时间、高密度的发起这样的请求,每次都打到数据库服务上,给数据库造成了压力。一般来说这样的请求属于恶意请求。
解决方案有两种:
就是在数据库即使没有查询到数据,我们也把这次请求当做 key 缓存起来,value 可以是 NULL。下次同样请求就会命中这个 NULL,缓存层就处理了这个请求,不会对数据库产生压力。这样实现起来简单,开发成本很低。
3 缓存雪崩
缓存雪崩是指缓存中大多数的数据在同一时间到达过期时间,而查询数据量巨大,这时候,又是缓存中没有,数据库中有的情况了。
防止雪崩的方案简单来说就是错峰过期。
在设置 key 过期时间的时候,在加上一个短的随机过期时间,这样就能避免大量缓存在同一时间过期,引起的缓存雪崩。
如果发了雪崩,我们可以有服务降级、熔断、限流手段来拒绝一些请求,保证服务的正常。但是,这些对用户体验是有一定影响的。
4 Redis 高可用架构
Redis 高可用架构,大家基本上都能想到主从、哨兵、集群这三种模式。
哨兵模式:
它主要执行三种类型的任务:
哨兵其实也是一个分布式系统,我们可以运行多个哨兵。
然后这些哨兵之间需要相互通气,交流信息,通过投票来决定是否执行自动故障迁移,以及选择哪个从服务器作为新的主服务器。
哨兵之间采用的协议是 gossip,是一种去中心化的协议,达成的是最终一致性。
选举规则:
0条评论