Sentinel学习笔记

安装

Sentinel 提供一个轻量级的控制台, 它提供机器发现、单机资源实时监控以及规则管理等功能,其控制台

  • 安装步骤如下:
  1. 打开sentinel下载网址
    https://github.com/alibaba/Sentinel/releases
  2. 下载jar
    在这里插入图片描述
  3. 运行控制台
java -Dserver.port=8180 -Dcsp.sentinel.dashboard.server=localhost:8180 -Dproject.name=sentinel-dashboard -jar sentinel-dashboard-1.8.1.jar
  1. 登陆控制台
    sentinel/sentinel
    http://localhost:8180/
    在这里插入图片描述
    能看到这个界面说明sentinel启动起来了

  2. 在项目里面添加依赖
    此依赖会在服务中添加一个拦截器对象,这个对象会对前端的请求进行拦截,分析请求是否在sentinel控制台规则的允许范围之内,假如在范围之内就放行,不在则拒绝访问

<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
  1. 要连接控制台,需要在bootstrap.yml里面添加连接sentinel控制台的地址
spring:
  application:
    name: sca-provider
  cloud:
    sentinel:
      transport:
        dashboard: localhost:8180

此时启动项目后,可以在sentinel控制台里面看到如下效果
在这里插入图片描述
图里面的sca-provider就是我们的项目yml配置文件里面的name属性

限流方式

QPS

直接模式

在这里插入图片描述
限定/se/te路径的请求频率为每秒1次,就是1QPS,超过了就根据流控模式流控效果确定的方式进行处理
访问过于频繁时就会出现如下提示
在这里插入图片描述
这个提示也是可以修改的。

修改限流提示

查源码,这个部分不重要

下面说如何找到可以修改提示的那个接口

  1. 从SpringBoot启动时打印的信息里面可以看到这么一条,就是注册了一个拦截器SentinelWebInterceptor
2021-12-24 14:47:37.519  INFO 20120 --- [           main] c.a.c.s.SentinelWebAutoConfiguration     : [Sentinel Starter] register SentinelWebInterceptor with urlPatterns: [/**].
  1. 找到这个类,发现它继承了AbstractSentinelInterceptor
package com.alibaba.csp.sentinel.adapter.spring.webmvc;
public class SentinelWebInterceptor extends AbstractSentinelInterceptor {}
  1. 找到AbstractSentinelInterceptor这个类,里面有一个preHandle方法
 @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
        throws Exception {
        try {
            String resourceName = getResourceName(request);

            if (StringUtil.isEmpty(resourceName)) {
                return true;
            }
            
            if (increaseReferece(request, this.baseWebMvcConfig.getRequestRefName(), 1) != 1) {
                return true;
            }
            
            // Parse the request origin using registered origin parser.
            String origin = parseOrigin(request);
            String contextName = getContextName(request);
            ContextUtil.enter(contextName, origin);
            Entry entry = SphU.entry(resourceName, ResourceTypeConstants.COMMON_WEB, EntryType.IN);
            request.setAttribute(baseWebMvcConfig.getRequestAttributeName(), entry);
            return true;
        } catch (BlockException e) {
            try {
            	// 被限流后交由这个方法来处理
                handleBlockException(request, response, e);
            } finally {
                ContextUtil.exit();
            }
            return false;
        }
    }

再点开 这个handleBlockException的定义,看到它调用了handle方法来处理,我们点中这个handleCtrl + Alt + B组合键打开实现类的方法

protected void handleBlockException(HttpServletRequest request, HttpServletResponse response, BlockException e)
        throws Exception {
        if (baseWebMvcConfig.getBlockExceptionHandler() != null) {
            baseWebMvcConfig.getBlockExceptionHandler().handle(request, response, e);
        } else {
            // Throw BlockException directly. Users need to handle it in Spring global exception handler.
            throw e;
        }
    }

就可以看到这个类了

package com.alibaba.csp.sentinel.adapter.spring.webmvc.callback;

import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.alibaba.csp.sentinel.util.StringUtil;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.PrintWriter;

/**
 * 被限流的请求的默认处理类.
 *
 * @author kaizi2009
 */
public class DefaultBlockExceptionHandler implements BlockExceptionHandler {

    @Override
    public void handle(HttpServletRequest request, HttpServletResponse response, BlockException e) throws Exception {
        // Return 429 (Too Many Requests) by default.
        response.setStatus(429);

        PrintWriter out = response.getWriter();
        out.print("Blocked by Sentinel (flow limiting)");
        out.flush();
        out.close();
    }
}

可以看到这个类实现了一个接口,实现了里面的handle方法,就是我们刚才在页面上看到的提示了,所以我们也写一个类,实现这个接口,并交由Spring容器进行管理,就可以了。
4. 比如我们创建这么一个类

package vip.sjxq.provider.test;

import com.alibaba.csp.sentinel.adapter.spring.webmvc.callback.BlockExceptionHandler;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;


@Component
public class MyBlocakExceptionHandler implements BlockExceptionHandler {
    
    @Override
    public void handle(HttpServletRequest request, HttpServletResponse response, BlockException e) throws Exception {
        response.setStatus(425);
        response.setContentType("text/html;charset=UTF-8");
        response.getWriter().println("你访问的太频繁了");
        response.getWriter().close();
    }
}

重启动项目后再被限流时,提示就已经换成我们的了
在这里插入图片描述

关联模式

需要保证/provider/sentinel02的访问时,可以这样设置,就是02的访问超过阈值时,会限制01的访问
在这里插入图片描述
如图所示,如果02每秒访问次数超过一次,02仍然可以正常访问,但01无论每秒访问几次都是显示被限流。

链路模式(针对SentinelResource标识的方法的限流)

使用@SentinelResource注解标识一个方法,表示对此方法进行流控管理,可以对各种不同的访问路径分别做流控。这里所说的不同的访问路径,可以简单的理解为被@RequestMapping@GetMapping@PostMapping等注解标识的方法、且方法里面有调用被@SentinelResource注解的方法时,此方法上面的诸如@RequestMapping的注解里面的请求路径值,就是上面说的链路的路径。

比如创建一个类,定义获取资源的方法,打上@SentinelResource注解,并把该类交由Spring容器进行管理

package vip.sjxq.provider.service;

import com.alibaba.csp.sentinel.annotation.SentinelResource;
import org.springframework.stereotype.Service;
import vip.sjxq.provider.pojo.User;

@Service
public class UserService {
	//名字可以随便取,但是一定要有,会显示在sentinel控制台里面
    @SentinelResource("queryUser")
    public User getUser(){
        User user  = new User();
        user.setName("张三").setAge(18).setAddress("山东济南");
        return user;
    }
}

然后定义一个UserController定义两个请求处理方法,调用这个userService.getUser()方法 ,模拟访问资源

package vip.sjxq.provider.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import vip.sjxq.provider.pojo.User;
import vip.sjxq.provider.service.UserService;

@RestController
@RequestMapping("/user")
public class ProviderUserController {
    @Autowired
    private UserService userService;
	//该请求路径会访问资源,则/user/one就是一条链路
    @RequestMapping("/one")
    public User showUser() {
        return userService.getUser();
    }
	//该请求路径会访问资源,则/user/two也是一条链路
    @RequestMapping("/two")
    public User showUser2(){
        return userService.getUser();
    }
}

重启服务,并分别访问一次两个链路后,就可以在sentinel-簇点链路中看到如下的情况
在这里插入图片描述
默认情况下会把这两个链路显示到同一个 sentinel_spring_web_context
如果要对/user/one/user/two这两个链路都进行限流,则
在这里插入图片描述
在这里插入图片描述
这样就是把所有当前项目的请求都做了限流。

  • 如果想要分别对各个链路做不同的限流的话,需要在配置里面
spring:
  application:
    name: sca-provider
  cloud:
    sentinel:
      transport:
        dashboard: localhost:8180
      web-context-unify: false
      # 代表不再自动合并链路

这样再次重启,并分别访问两个链路后,就可以看到两个链路下都有queryUser这个资源了
在这里插入图片描述
此时就可以分别对指定的链路进行限流了
在这里插入图片描述

但是限流后显示的页面都是500页面,如何自定义这个页面呢

自定义限流后的页面
  1. 创建一个BlockHandler类,并创建被限流时的处理方法
  • 要求:

该方法的返回值类型要与被处理的路径的那个方法的返回值类型一致
该方法必须是静态的
该方法的参数必须是BlockException异常

package cn.tedu.handler;

import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

@Slf4j
@Component
public class BlockHandler {
	//方法必须为静态的,方法名随便取
	//异常类型必须为BlockException
   public static User call(BlockException e){
       log.error("限流了{}",e.getMessage());
       User user = new User();
       user.setName("你访问的太频繁了");
       return user;
   }
}
  1. 修改UserService里面的@SentinelResource注解内部的值
参数意义
value资源的名称,会显示在sentinel控制台的簇点里面
blockHandlerClass用于指定处理链路限流异常的类型
blockHandler 用于指定blockHandlerClass指定的类里面的用来处理指定异常的方法
@SentinelResource(value ="userResource",blockHandlerClass = BlockHandler.class,blockHandler = "call")
public User getUser(){
    User user =new User();
    user.setName("张三").setAge(18).setAddr("山东济南");
    return user;
}
  1. 重启项目、在sentinel簇点链路里面对userResource资源进行限流后,再执行高频刷新页面后就可以看到我们修改的被限流后的结果信息了
{
  "name": "你访问的太频繁了",
  "age": null,
  "addr": null
}

自定义拦截器,实现仅限定时间段内可访问

  1. 实现HandlerInterceptor接口,里面有很多默认方法,我们可以根据需要对其进行重写
package cn.tedu.config;

import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.time.LocalTime;

public class MyTimeInterceptor implements HandlerInterceptor {

	/**
	* 此方法会在实际方法执行之前执行,返回为:true 放行;false 拦截掉此请求
	*/
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        LocalTime lt = LocalTime.now();
        System.out.println("进入了时间拦截器");
        final int hour = lt.getHour();
        if (hour < 8 || hour > 21) {
            throw  new RuntimeException("请在 8:00 ~ 21:00 期间访问");
        }
        return true;
    }
}

如果请求被拦截器拦截掉,则客户端的请求会被立即响应掉,Content-Length:0,此请求不再向后执行

  1. 把拦截器注册到Spring中
package cn.tedu.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class MyConfig implements WebMvcConfigurer {
    /**
     * 注册拦截器,并定义拦截规则,负责注册拦截器的对象
     * @param registry
     */
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new MyTimeInterceptor()).addPathPatterns("/user/*");
    }
}

降级

规则设置

慢调用比例

在指定的统计时长内,有超过最小请求数的请求,其中有比例阈值点比的请求数的响应时间大于或等于最大RT,则在接下来的熔断时长的时间内,请求会被
在这里插入图片描述
例如下面这个规则的意思是:1000ms内,5个请求的30%的请求的响应时间超过了200ms,则熔断5s
在这里插入图片描述
通过断点可以看到被降级时得到的异常是DegradeException,而限流的话是FlowException

在这里插入图片描述
在这里插入图片描述
因此可以使用instanceof来判断是异常产生后,是被限流了还是被降级了。然后根据不同的异常来对应的向前端返回信息

package cn.tedu.config;

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.degrade.DegradeException;
import com.alibaba.csp.sentinel.slots.block.flow.FlowException;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.PrintWriter;

@Component
public class MyBlockExceptionHandler implements BlockExceptionHandler{

    @Override
    public void handle(HttpServletRequest request, HttpServletResponse response, BlockException e) throws Exception {
        response.setStatus(429);
        response.setCharacterEncoding("utf-8");
        PrintWriter out = response.getWriter();
        String reson = "限流";
        if(e instanceof DegradeException){
            reson = "降级";
        } else if (e instanceof FlowException){
            reson = "限流";
        }
        out.print(String.format("该服务已经被:%s",reson));
        out.flush();
        out.close();
    }
}

热点

在这里插入图片描述
可以设置该热点的参数位置,这里使用索引来指示
在这里插入图片描述
热点规则 里面点击编辑
在这里插入图片描述
在这里插入图片描述

总结:

  1. 流控有直接、关联和链路三种模式;直接流控是指对指定的API或路径进行流量访问频率的控制;关联是指为了保障另外一个API流量的正常访问,而限制本身API的访问量。链路是针对@SentinelResource注解标识的资源访问的来源进行流控,当然了,此注解标识的资源也可以使用前两种流控方式。可能
  2. 降级是某API或路径资源的访问极其耗时,或响应时间不稳定时,为保证不会出现调用堆积的情况而进行的一种流控。通过监控指定时间窗口内、请求数达到了最小请求数且响应超出指定时间的请求数占比超过了阀值,则进行控制。
  3. 热点更加精细一些,可以针对API或是路径资源的某个参数存在否或是其参数的值来进行流控,就是都是同一个API,但是给其传的值可能并不全是热点数据,热点只存在于一小部分值中,就可以使用热点流控的方式来保障热点数据的访问。
  4. 系统流控,就更加的粗犷了,可以说是从系统环境层面来进行的流控,关注的是服务器的性能,比如load(没搞懂是啥)、CPU占用率(太高时可以限流)、线程数等
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

水晶心泉

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值