在我们日常生活中,经常会在淘宝、天猫、京东、拼多多等平台上参与商品的秒杀、抢购以及一些优惠活动,也会在节假日使用12306 手机APP抢火车票、高铁票,甚至有时候还要帮助同事、朋友为他们家小孩拉投票、刷票,这些场景都无一例外的会引起服务器流量的暴涨,导致网页无法显示、APP反应慢、功能无法正常运转,甚至会引起整个网站的崩溃。
我们如何在这些业务流量变化无常的情况下,保证各种业务安全运营,系统在任何情况下都不会崩溃呢?我们可以在系统负载过高时,采用限流、降级和熔断,三种措施来保护系统,由此一些流量控制中间件诞生。例如Sentinel。
一、概述
Sentinel (分布式系统的流量防卫兵) 是阿里开源的一套用于服务容错的综合性解决方案。它以流量为切入点, 从流量控制、熔断降级、系统负载保护等多个维度来保护服务的稳定性。
Sentinel 承接了阿里巴巴近 10 年的双十一大促流量的核心场景, 例如秒杀(即突发流量控制在系统容量可以承受的范围)、消息削峰填谷、集群流量控制、实时熔断下游不可用应用等。
两大组成:
- Java 客户端:能够运行于所有 Java 运行时环境,同时对Dubbo /Spring Cloud 等框架也有较好的支持。
- 控制台(Dashboard):基于 Spring Boot 开发,打包后可以直接运行。
二、安装
1.Sentinel是一个jar包,我们需要下载并运行这个jar包
2.在jar包目录下用指定命令运行这个jar包
java -Dserver.port=8180 -Dcsp.sentinel.dashboard.server=localhost:8180 -Dproject.name=sentinel-dashboard -jar sentinel-dashboard-1.8.0.jar
3.访问8180端口,账号密码默认为sentinel,出现如下页面表示访问成功
4.这样是不是每次的启动都太麻烦了呢,我们在IDEA里添加脚本快速启动
第一行是jdk里的bin里的java.exe文件目录
第二行是运行时指定的命令,把前面的java前缀去掉即可
第三行是jar文件所在的目录
添加完成后我们即可在IDEA里快速启动了
三、Sentinel限流
顾名思义,限流就是限制流量
目的:防止恶意请求流量、恶意攻击,或者防止流量超过系统峰值导致系统崩溃。
1.导入pom文件
首先需要导入pom依赖,一般在消费端进行导入
//sentinel依赖
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
//健康监控依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
2.配置配置文件
spring:
cloud:
sentinel:
transport:
port: 8099 #跟sentinel控制台交流的端口,随意指定一个未使用的端口即可,可以省略
dashboard: localhost:8180 # 指定sentinel控制台地址。
3.运行查看
启动服务提供方和服务消费方并随便访问一个url进入Sentinel控制面板进行查看
监控成功,接下来我们进行流量的限制
4.限流
1.在簇点链路里找到对应的资源名新增流控规则
QPS:每秒请求的此时
单机阈值:允许每秒请求的次数
2.每秒超过一次的访问进行测试
限流成功,每秒只能访问一次
3.高级限流---直接模式
Senntinel默认的流控处理方式就是直接模式
4.高级限流---关联模式
当关联的资源达到阈值,就限流自己。例如设置了关联资源为/ur2时,假如关联资源/url2的qps阀值超过1时,就限流/url1接口(是不是感觉很霸道,关联资源达到阀值,是本资源接口被限流了)。这种关联模式有什么应用场景呢?我们举个例子,订单服务中会有2个重要的接口,一个是读取订单信息接口,一个是写入订单信息接口。在高并发业务场景中,两个接口都会占用资源,如果读取接口访问过大,就会影响写入接口的性能。业务中如果我们希望写入订单比较重要,要优先考虑写入订单接口。那就可以利用关联模式;在关联资源上面设置写入接口,资源名设置读取接口就行了;这样就起到了优先写入,一旦写入请求多,就限制读的请求。
总结就是一句话,牺牲自己,保全别人
这时我们开启两个端口服务,疯狂访问Echo2,观察Echo1是否被限制访问
可以看到疯狂访问2后再次对1进行访问,1被限制访问了
4.高级限流---链路模式
链路模式只记录指定链路入口的流量。也就是当多个服务对指定资源调用时,假如流量超出了指定阈值,则进行限流。
说明,流控模式为链路模式时,假如是sentinel 1.7.2以后版本,Sentinel Web过滤器默认会聚合所有URL的入口为sentinel_spring_web_context,因此单独对指定链路限流会不生效,需要在application.yml添加如下语句来关闭URL PATH聚合。
sentinel:
web-context-unify: false
对指定链路下的路径进行频繁访问报错出现,不在指定链路下的则没事。
注:
限流算法:计数器、令牌桶、漏斗算法,滑动窗口算法
四、Sentinel降级
除了流量控制以外,对调用链路中不稳定的资源进行熔断降级也是保障高可用的重要措施之一。由于调用关系的复杂性,如果调用链路中的某个资源不稳定,最终会导致请求发生堆积。
Sentinel 熔断降级会在调用链路中某个资源出现不稳定状态时(例如调用超时或异常比例升高),对这个资源的调用进行限制,让请求快速失败,避免影响到其它的资源而导致级联错误。当资源被降级后,在接下来的降级时间窗口之内,对该资源的调用都自动熔断
1.设计慢调用
@Controller
public class Jangji {
private AtomicLong aLong=new AtomicLong(1);
public void jiangji() throws InterruptedException {
long num = aLong.getAndIncrement();
if(num%2==0){
Thread.sleep(200);//模拟耗时操作
}
}
}
代码意思是对百分之50的请求延时200毫秒,这里系统设计认为超过200毫秒为慢调用
在Echo1请求里注入调用jiangji方法
2.降级
这里表示熔断策略为慢调用比例,表示链路请求数超过3时,假如平均响应时间假如超过200毫秒的有30%,则对请求进行熔断,熔断时长为10秒钟,10秒以后恢复正常。
3.异常处理
默认降级熔断是返回默认异常,我们可以自定义异常
//自定义限流异常处理对象
//服务被限流或者降级,sentinel底层提供的拦截器会调用此方法进行异常处理
@Component
public class ServiceBlockExceptionHandler implements BlockExceptionHandler {
@Override
public void handle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, BlockException e) throws Exception {
//1)向客户端输出数据(写数据)用什么?输出流对象
//2)如何获取输出流对象?(标准的JAVAEE规范中,要借助HttpServletResponse对象)
//3)当向客户端输出数据时,除了响应业务数据外,还要做什么?
//设置响应数据的编码
httpServletResponse.setCharacterEncoding("utf-8");
//告诉浏览器服务端向它响应的数据类型,以及以什么编码进行显示.
// httpServletResponse.setContentType("text/html;charset=utf-8");
/*有多种客户端,不一定是浏览器,还有手机汽车等。。。。,为了兼容所以用统一用json串返回*/
httpServletResponse.setContentType("application/json;charset=utf-8");
//响应业务数据
PrintWriter out=httpServletResponse.getWriter();
Map<String,Object> map=new HashMap<>();
map.put("status", 429);
if(e instanceof DegradeException){//DegradeException降级异常
// out.println("<h2>服务暂时不可用</h2>");
map.put("message","服务暂时不可用");
}else{
// out.println("<h2>访问太频繁,稍等片刻再访问</h2>");
map.put("message","访问太频繁,稍等片刻再访问");
}
//将map对象转换为json格式字符串
String jsonStr= new ObjectMapper().writeValueAsString(map);
out.println(jsonStr);//输出json串
out.flush();//所有字符流都要刷新
}
}
五、Sentinel热点规则
热点即经常访问的数据---微博热搜
热点参数限流会统计传入参数中的热点数据,并根据配置的限流阈值与模式,对包含热点参数的资源调用进行限流。热点参数限流可以看做是一种特殊的流量控制,仅对包含热点参数的资源调用生效。其中,Sentinel会利用 LRU 策略统计最近最常访问的热点参数,结合令牌桶算法来进行参数级别的流控。
1.定义热点业务代码
@GetMapping("consumer/findById")
@SentinelResource("res")
public String doFindById(Integer id,String name){
return "Resource id is"+id+name;
}
2.添加要限流的热点链路
参数索引为@SentinelResource注解的方法参数下标,0代表第一个参数,1代表第二个参数。单机阈值以及统计窗口时长表示在此窗口每两秒超过一次访问就限流。
3.热点指定参数限流
热点参数:其实说白了就是特殊的流控,流控设置是针对整个请求的;但是热点参数他可以设置到具体哪个参数,甚至参数针对的值,这样更灵活的进行流控管理。
对于第一个参数值为5的话,每秒到达100次访问就进行限流,对于其他的值则每秒限制访问一次
六、Sentinel授权规则
很多时候,我们需要根据调用方来限制资源是否通过,这时候可以使用 Sentinel 的黑白名单控制的功能。黑白名单根据资源的请求来源(origin)限制资源是否通过,若配置白名单则只有请求来源位于白名单内时才可通过;若配置黑名单则请求来源位于黑名单时不通过,其余的请求通过。
1.基于参数
定义请求解析器
//定义一个请求源解析对象,基于此对象对请求中指定数据进行解析
//然后Sentinel底层会对此接口方法的返回值基于规则的定义进行处理
@Component
public class DefaultRequestOriginParser implements RequestOriginParser {
@Override
public String parseOrigin(HttpServletRequest request) {
//获取请求参数数据,参数名可以自己写
//http://ip:port/path?origin=app1
return request.getParameter("origin");
}
新增授权规则
带黑名单中的参数进行访问
加入黑名单成功,app1app2不可访问,其他访问正常,反之则白名单内正常,其他不可访问
2.基于IP地址
定义请求解析器
@Component
public class DefaultRequestOriginParser implements RequestOriginParser {
@Override
public String parseOrigin(HttpServletRequest request) {
//访问请求中的ip地址,基于地址进行黑白名单的设计
String ip=request.getRemoteAddr();
System.out.println(ip);
return ip;
}
}
新增授权规则
带黑名单中的参数进行访问
其他IP进行访问
测试成功,指定在黑名单中的IP不可访问资源
3.基于请求头
定义请求解析器
@Component
public class DefaultRequestOriginParser implements RequestOriginParser {
@Override
public String parseOrigin(HttpServletRequest request) {
String token=request.getHeader("token");
return token;
}//授权规则种的黑白名单的值,来自此方法的返回值
}
新增授权规则
带黑名单中的参数进行访问
测试成功,携带指定的请求头访问失败,其他则正常
总结
- 为什么要限流?(系统承受不了高并发而导致宕机)
- Sentinel 的限流规则?(阈值类型-QPS&线程数,限流模式-直接,关联,链路)
- Sentinel 的降级(熔断)策略?(慢调用,异常比例,异常数)
- Sentinel 系统规则设计?(了解,全局规则定义,针对所有请求有效)
- Sentinel 授权规则设计?(黑白名单)
- 你了解的那些限流框架?(sentinel)
- 常用的限流算法有那些?(计数,令牌桶-电影票,漏桶-漏斗,滑动窗口)
- Sentinel 的热点规则中的热点数据?(微博热搜)