限流算法: 计数器、令牌桶、漏斗算法,滑动窗口算法(sentinel)
使用限流和熔断的原因: 访问流量大,资源处理不足
限流:高并发情况下,对流量进行(部分)限制
熔断:全部被动
降级:部分主动①服务响应速度慢一些②服务不稳定,经常出现异常
Sentinel (分布式系统的流量防卫兵)(观察者模式)
下载及启动
下载网址:https://github.com/alibaba/Sentinel/releases
下载文件目录下打开小黑窗口,执行如下命令:
当然也可以通过Idea启动该服务,idea启动sentinel,mysql,nacos常用配置_闪耀太阳de超级piracy的博客-CSDN博客java -Dserver.port=8180 -Dcsp.sentinel.dashboard.server=localhost:8180 -Dproject.name=sentinel-dashboard -jar sentinel-dashboard-1.8.1.jar
-Dserver.port=8180 制定启动服务的端口
-Dcsp.sentinel.dashboard.server=localhost:8180 制定控制台地址
-Dproject.name=sentinel-dashboard 制定控制台名称
-jar sentinel-dashboard-1.8.1.jar 执行jar文件访问测试,密码和用户名都是sentinel
Sentinel流控(限流)使用流程
添加依赖
添加sentinel限流依赖,此时项目中会注入一个拦截器HandlerInterceptor的实现类AbstractSentinelInterceptor(spring mvc)对象,对请求进行拦截,记录请求的单位时间的访问次数.HandlerInterceptor接口有三个默认方法,preHandle()请求在访问controller前执行;postHandle()请求访问完controller时执行,afterCompletion()返回数据给前段时执行
<dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId> </dependency>
配置文件bootstrap.yml,添加sentinel配置
spring: cloud: sentinel: transport: dashboard: localhost:8180 # 指定sentinel控制台地址。
创建一个用于演示限流操作的Controller对象,
@RestController @RequestMapping("/provider") public class ProviderSentinelController { @GetMapping("/sentinel01") public String doSentinel01(){ return "sentinel 01 test ..."; } }
浏览器访问,sentinel设置限流规则,进行测试
反复刷新访问你的服务,检测是否有限流信息输出:
自定义限流异常处理
抛出的异常是BlockException,通过BlockExceptionHandler的实现类DefaultBlockExceptionHandler对异常进行处理,若对异常处理方案不满意,可以自己写实现类对异常进行处理,底层会调用sentinelWebMvcConfig类自动注入该实现类
/** * 自定义BlockException的异常处理对象 */ @Component public class DefaultSentinelExceptionHandler implements BlockExceptionHandler { @Override public void handle(HttpServletRequest request, HttpServletResponse response, BlockException e) throws Exception { //设置响应内容类型,以及告诉高客户端应该按什么编码显示数据 response.setContentType("text/html;charset=utf-8"); //设置响应状态码(将来可以基于这个状态码进行相关处理) response.setStatus(429); //获取响应输出流对象 PrintWriter out = response.getWriter(); String msg="访问太频繁,稍等片刻再访问."; //判断e指向的对象是否为DegradeException类型(降级) if(e instanceof DegradeException){//DegradeException 熔断异常 msg="服务暂时不可用,稍等片刻再访问."; }else if(e instanceof AuthorityException){//AuthorityException 表示授权异常 msg="您无权访问这个资源"; } out.print(msg); out.flush(); out.close(); } }
Sentinel限流流控规则
阈值类型:
QPS(Queries Per Second):当调用相关url对应的资源时,QPS达到单机阈值时,就会限流。
线程数:当调用相关url对应的资源时,线程数达到单机阈值时,就会限流。限流效果:
快速失败:访问不成功直接返回异常处理结果
Warm Up:访问过大,可以在指定时间内处理这些请求
排队等待:访问依次排队处理限流模式:
直接模式: 对某资源的访问进行限流
关联模式: 当核心业务流量非常大时,可以对一些不那么重要的业务进行限流,保证核心业务正常运行
链路模式: 某业务占时还在修复时,占时不对外开放(Aop)
准备一类资源Service类(SentinelResourceAspect切面,对该目标方法进行)package com.jt.provider.service; @Service public class ResourceService{ /**sentinelResource注解描述的方法为一个切入点方法, * 也就是说对此方法进行访问时,进行一些限流操作. *具体的限流动作会放在切面的通知方法中.*/ @SentinelResource("doGetResource") public String doGetResource(){ return "doGetResource"; } }
准备controller的两个不通方法对同一资源进行调用
@Autowired private ResourceService resourceService; @GetMapping("/sentinel03") public String doSentinel03(){ resourceService.doGetResource(); return "sentinel 03 test"; } @GetMapping("/sentinel04") public String doSentinel04(){ resourceService.doGetResource(); return "sentinel 04 test"; }
配置打散链路聚焦 (其他版本可能不需要打散链路聚焦)
spring: cloud: sentinel: web-context-unify: false #打散链路聚焦
访问,sentinel设置流控规则
访问测试,链路限流异常
自定义链路限流处理
HandlerInterceptor接口有三个默认方法,preHandle()请求在访问controller前执行;postHandle()请求访问完controller时执行,afterCompletion()返回数据给前段时执行
/** * 通过此类处理由@SentinelResource注解描述的方法出现的限流异常. */ @Slf4j @Component public class ResourceBlockHandler { /** * 这里的异常处理方法有要求: * 1)修饰符 public static * 2)返回值类型要与@SentinelResource注解描述方法返回值类型相同 * 3)参数列表要与@SentinelResource注解描述方法参数列表相同, * 可以再最后多添加一个BlockException参数 */ public static String doHandle(BlockException ex){ log.error("被限流了.....,{}",ex); return "访问太频繁了...."; } }
@SentinelResource(value="doGetResource", blockHandlerClass = ResourceBlockHandler.class, blockHandler = "doHandle") public String doGetResource(){ return "do get resource"; }
自定义拦截器对象对特定请求进行拦截
自定义拦截器
/** * 自定义spring mvc中的拦截器对象,基于此对象对请求和响应进行拦截 */ public class TimeInterceptor implements HandlerInterceptor { /** * 此方法会在目标controller方法执行之前执行,可以这里对请求进行 * 预处理,然后在交给后端的controller,同时也可以基于此方法的 * 返回值,决定请求是继续放行,还是到此结束. * 在当前应用中,我们基于访问时间通过拦截器对请求进行限制. * @param request * @param response * @param handler * @return true表示放行,false表示拦截. * @throws Exception */ @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { System.out.println("==preHandle=="); //1.获取当前时间 LocalTime now = LocalTime.now(); //2.获取当前时间的对应的小时 int hour=now.getHour(); System.out.println("hour="+hour); //3.判断时间是否在允许范围之内 if(hour<10||hour>=23){ throw new RuntimeException("请在允许时间访问6~23"); } return true; } }
配置拦截器(位于配置类/启动类中)
/** * Spring MVC 配置类 */ @Configuration public class SpringWebConfig implements WebMvcConfigurer { @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new TimeInterceptor()) .addPathPatterns("/provider/sentinel01"); } }
Sentinel降级
降级:部分主动①服务响应速度慢一些②服务不稳定,经常出现异常 ③
访问太频繁,服务就会自动降级,抛出DegradeException异常
可以对该异常处理进行修改/** * 自定义BlockException的异常处理对象 */ @Component public class DefaultSentinelExceptionHandler implements BlockExceptionHandler { @Override public void handle(HttpServletRequest request, HttpServletResponse response, BlockException e) throws Exception { //设置响应内容类型,以及告诉高客户端应该按什么编码显示数据 response.setContentType("text/html;charset=utf-8"); //设置响应状态码(将来可以基于这个状态码进行相关处理) response.setStatus(429); //获取响应输出流对象 PrintWriter out = response.getWriter(); String msg="访问太频繁,稍等片刻再访问."; //判断e指向的对象是否为DegradeException类型(降级) if(e instanceof DegradeException){//DegradeException 熔断异常 msg="服务暂时不可用,稍等片刻再访问."; }else if(e instanceof AuthorityException){//AuthorityException 表示授权异常 msg="您无权访问这个资源"; } out.print(msg); out.flush(); out.close(); } }
Sentinel热点规则(AOP)
@SentinelResource(value="resource", blockHandlerClass = ResourceBlockHandler.class, blockHandler = "doHandle")定义目标方法,切面SentinelResourceAspect
LRU策略:最近最少使用策略(缓存淘汰,热点数据限流),java中LinkedHashMap内置了该算法
簇点链路中设置制定参数进行限流
热点规则中对制定参数的值进行限流
Sentinel系统规则
监控服务器的CPU、内存、IO等的使用率,主要目的就是保证服务器正常的运行,针对于所有的资源
Sentinel授权规则
修改请求解析器,获取请求ip并返回
@Component public class DefaultRequestOriginParser implements RequestOriginParser { /**这个方法中基于业务规则对请求数据进行解析,然后进行授权 * 1)可以对请求参数进行解析 * 2)可以对请求头数据进行解析 * 3).......*/ @Override public String parseOrigin(HttpServletRequest request) { //对请求参数进行解析,并返回参数值,然后将这个值应用在sentinel的授权规则中 //http://ip:port/path?origin=xxx //String origin=request.getParameter("origin");//这里的名字为参数名 //return origin; //对请求头中的数据进行解析,请求头的名字为token,然后基于值进行授权。 //String token=request.getHeader("token"); //return token; //对请求ip地址进行解析,然后获取ip值进行授权。 String ip = request.getRemoteAddr(); int port=request.getRemotePort(); System.out.println("ip="+ip); return ip; } }
在sentinel控制台定义授权规则
Sentinel授权异常如上降级异常中的AuthorityException