文章目录
限流
限流的目的防止恶意请求流量、恶意攻击,或者防止流量超过系统峰值
sentinel使用环境搭建
- 添加pom依赖
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
- 添加yml文件sentinel配置
spring:
cloud:
sentinel: # 位置在cloud元素下, 与nacos是并列关系
transport:
dashboard: localhost:8180 # 指定sentinel控制台地址。
- 创建一个用于演示限流操作的Controller对象
package com.jt.provider.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/provider")
public class ProviderSentinelController {
@GetMapping("/sentinel01")
public String doSentinel01(){
return "sentinel 01 test ...";
}
}
- 启动sca-provider服务,然后对指定服务进行访问
- 刷新sentinel 控制台,实时监控信息
设置限流,默认直接模式
-
选择要限流的链路
-
设置限流策略
- 反复刷新访问消费端端服务,检测是否有限流信息输出
关联模式
当关联的资源达到阈值,就限流自己
比如: 一个是读取订单信息接口,一个是写入订单信息接口, 可利用关联模式, 一旦写入请求多,就限制读的请求
- 添加方法二
@GetMapping("/sentinel02")
public String doSentinel02(){
return "sentinel 02 test ...";
}
- 在sentinel中设置关联模式限流设计
- 打开两个测试窗口,对/provider/sentinel02进行访问,检查/provider/sentinel01的状态
链路模式
只记录指定链路入口的流量
- 创建一个ResourceService类
package com.jt.provider.service;
@Service
public class ResourceService{
@SentinelResource("doGetResource")
public String doGetResource(){
return "doGetResource";
}
}
- 在Controller里添加方法三, 并获取service实例
@Autowired
private ResourceService resourceService;
@GetMapping("/sentinel03")
public String doSentinel03() throws InterruptedException {
resourceService.doGetResource();
return "sentinel 03 test";
}
- 在sentinel中配置链路模式限流规则
- 设置链路流控规则后,再频繁对限流链路进行访问,检测是否会出现500异常
关闭URL PATH聚合
流控模式为链路模式时,sentinel 1.7.2以后版本,Sentinel Web过滤器默认会聚合所有URL的入口为sentinel_spring_web_context,
因此单独对指定链路限流会不生效,需要在application.yml添加如下语句来关闭URL PATH聚合
sentinel:
web-context-unify: false # 默认为true, 会将所有的请求链路聚合到sentinel_spring_web_context 链路下
熔断/降级
设置模拟环境
- ProviderController 类中添加doSentinel04方法
- 此方法中设置休眠,目的是为了演示慢调用(响应时间比较长)
//AtomicLong 类支持线程安全的自增自减操作
private AtomicLong atomicLong=new AtomicLong(1);
@GetMapping("/sentinel04")
public String doSentinel04() throws InterruptedException {
//获取自增对象的值,然后再加1
long num=atomicLong.getAndIncrement();
if(num%2==0){//模拟50%的慢调用比例
Thread.sleep(200);
}
return "sentinel 04 test";
}
满调用比例规则
- 服务启动后,选择要降级的链路
- 设置熔断/降级的值
- 表示熔断策略选择"慢调用比例",表示请求数超过3时,假如平均响应时间超过200毫秒的有30%,则对请求进行熔断,熔断时长为10秒钟,10秒以后恢复正常
- 对指定链路进行刷新,多次访问测试,检测熔断效果
- 熔断时间过后会自动启用服务
Sentinel 异常处理模式
系统提供了默认的异常处理机制,假如默认处理机制不满足我们需求,我们可以自己进行定义。定义方式上可以直接或间接实现BlockExceptionHandler接口,并将对象交给spring管理
- 在方法四上进行更改, 模拟会有50%的几率报出异常
//AtomicLong 类支持线程安全的自增自减操作
private AtomicLong atomicLong=new AtomicLong(1);
@GetMapping("/sentinel04")
public String doSentinel04() throws InterruptedException {
//获取自增对象的值,然后再加1
long num = atomicLong.getAndIncrement();
if(num%2 == 0){//模拟50%的慢调用比例
// Thread.sleep(200);
throw new RuntimeException("出现异常");
}
return "sentinel 04 test";
}
- 设置异常比例规则
- 当一秒内请求数超过两次并且报出异常的概率为30%以上, 就会进行熔断10秒
- 对指定链路进行刷新,多次访问测试,检测熔断效果
异常处理
系统提供了默认的异常处理机制,假如默认处理机制不满足我们需求,我们可以自己进行定义。
定义方式上可以直接或间接实现BlockExceptionHandler接口,并将对象交给spring管理。
自定义异常处理
- 新建Controller
package com.jt.provider.controller;
import com.alibaba.csp.sentinel.adapter.spring.webmvc.callback.BlockExceptionHandler;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.Map;
/*自定义降级异常处理对象*/
@Slf4j
@Component
public class ServiceBlockExceptionHandler implements BlockExceptionHandler {
/*处理BlockException类型以及子类类型的异常
*
* */
@Override
public void handle(HttpServletRequest request,
HttpServletResponse response,
BlockException e) throws Exception {
//设置响应数据编码
response.setCharacterEncoding("utf-8");
//告诉客户端响应数据的类型,以及客户端显示内容的编码
response.setContentType("text/html;charset=utf-8");
//向客户端响应一个json格式的字符串
//String str="{\"status\":429,\"message\":\"访问太频繁了\"}";
Map<String,Object> map=new HashMap<>();
map.put("status", 429);
map.put("message","访问太频繁了");
String jsonStr=new ObjectMapper().writeValueAsString(map);
PrintWriter out = response.getWriter();
out.print(jsonStr);
out.flush();
out.close();
}
}
- 新建service
package com.jt.provider.service;
import com.alibaba.csp.sentinel.annotation.SentinelResource;
import org.springframework.stereotype.Service;
@Service
public class ResourceService {
/*
* @SentinelResource使用此注解描述的方法,
* 在此方法被访问时,会在sentinel的簇点链路中显示,
* 此注解中指定的名字就是资源名,我们可以对这个资源
* 的访问,按照指定的链路进行限流设计.
*
* 此注解中的blockHandlerClass用于指定,出现限流异常时的异常处理类,
* blockHandler属性用于指定异常处理类中的方法(此方法的返回类型,参数
* 要与 @SentinelResource注解描述的方法参数一致,可以加BlockException异常
* 类型参数,而且方法必须是静态.)
* fallbackClass 用于指定业务异常处理类,fallback用于指向业务处理类
* 中的异常处理方法(此方法的返回类型,参数要与@SentinelResource注解描
* 述的方法参数一致,可以加Throwable异常类型参数)
* @return
*/
@SentinelResource(value="doGetResource",
blockHandlerClass = ResourceBlockHandler.class,
blockHandler = "call",
fallback = "call",
fallbackClass = ResourceFallbackHandler.class)
public String doGetResource(){
// int a=100,b=0;
// a=a/b;
return "do get resource";
}
}
package com.jt.provider.service;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
@Slf4j
@Component
public class ResourceBlockHandler {
/**
* 注意此方法中的异常类型必须为BlockException (它是所有限流,降级等异常的父类类型)
* @param ex
* @return
*/
public static String call(BlockException ex){
log.error("block exception {}", ex.getMessage());
return "访问太频繁了,稍等片刻再访问";
}
}
- 设置异常比例降级策略
- 当我们访问有异常的方法4时, 就会使用自定义的异常信息
- 注意: 如果打印的异常信息是中文
- 要设置相应数据编码
- 要告诉浏览器响应数据的类型, 和客户端显示内容的编码
热点
热点参数限流会统计传入参数中的热点数据,并根据配置的限流阈值与模式,对包含热点参数的资源调用进行限流。
热点参数限流可以看做是一种特殊的流量控制,仅对包含热点参数的资源调用生效。
其中,Sentinel会利用 LRU 策略统计最近最常访问的热点参数,结合令牌桶算法来进行参数级别的流控。
测试热点环境搭建
- Controller添加方法
//http://localhost:8082/provider/sentinel/findById?id=10
@GetMapping("/sentinel/findById")
@SentinelResource("resource")
public String doFindById(@RequestParam("id") Integer id){
return "resource id is "+id;
}
- 服务启动后,选择要限流的热点链路
- 设置要限流的热点
- 热点规则的限流模式只有QPS模式(这才叫热点)。
- 参数索引为@SentinelResource注解的方法参数下标,0代表第一个参数,1代表第二个参数。
- 单机阈值以及统计窗口时长表示在此窗口时间超过阈值就限流。
- 多次访问热点参数方法,会进行限流
- 特定参数设置, 编辑热点规则
- 添加参数类型,比如int
- 参数值为5, 阈值为100
- 当前端访问参数ID=5时, 会使用特定参数阈值为100的规则
- 参数为其他值时, 会使用统一的阈值为1的规则
- 可以添加多种类型的多种参数
系统规则
Sentinel的系统保护规则是从应用级别的入口流量进行控制,从单台机器的总体 Load(负载)、RT(响应时间)、入口 QPS 、线程数和CPU使用率五个维度监控应用数据,
让系统尽可能跑在最大吞吐量的同时保证系统整体的稳定性
- 新增系统保护规则
- 系统规则是一种全局设计规则,其中
- Load(仅对 Linux/Unix-like 机器生效):当系统 load1 超过阈值,且系统当前的并发线程数超过系统容量时才会触发系统保护。系统容量由系统的 maxQps * minRt 计算得出。设定参考值一般是 CPU cores * 2.5。
- CPU使用率:当系统 CPU 使用率超过阈值即触发系统保护(取值范围 0.0-1.0)
- RT:当单台机器上所有入口流量的平均 RT 达到阈值即触发系统保护,单位是毫秒。
- 线程数:当单台机器上所有入口流量的并发线程数达到阈值即触发系统保护。
- 入口 QPS:当单台机器上所有入口流量的 QPS 达到阈值即触发系统保护。
- 使用CPU保护规则
- 当这个服务CPU使用率超过0.01, 就会限制所有的访问
- 前端访问方法一
- 修改CPU值为0.1测试
- 前端访问方法一
- 没有超过设置的CPU使用率范围, 不会触发系统保护规则
授权规则
根据调用方来限制资源是否通过, 使用 Sentinel 的授权规则的黑白名单控制的功能
- 黑白名单根据资源的请求来源(origin)限制资源是否通过
- 若配置白名单则只有请求来源位于白名单内时才可通过;
- 若配置黑名单则请求来源位于黑名单时不通过,其余的请求通过。
测试授权规则的环境搭建
新建Controller类实现RequestOriginParser
package com.jt.provider.controller;
import com.alibaba.csp.sentinel.adapter.spring.webmvc.callback.RequestOriginParser;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
/*构建DefaultRequestOriginParser对象, 对请求数据进行解析
* 1.请求头
* 2.请求行
* 3.请求体 */
@Component
public class DefaultRequestOriginParser implements RequestOriginParser {
/*当设置了授权规则后, 系统底层拦截到请求, 会调用此方法, 对请求数据进行解析
* http://ip;port/path?参数名origin=xxx*/
@Override
public String parseOrigin(HttpServletRequest request) {
String origin = request.getParameter("origin");
return origin;
}
}
设置授权规则
- 添加授权规则
- 设置授权规则
- 前端访问方法一,并传入参数
- 当传入origin参数的值为app1和app2时,都会被拦截, 进行数据解析, 为黑名单数据拒绝通行
- 当传入其他值时, 也会被底层RequestOriginParser拦截解析, 但是会放行