Sentinel 提供一个轻量级的控制台, 它提供机器发现、单机资源实时监控以及规则管理等功能,其控制台
- 安装步骤如下:
- 打开sentinel下载网址
https://github.com/alibaba/Sentinel/releases
- 下载jar包
- 运行控制台
java -Dserver.port=8180 -Dcsp.sentinel.dashboard.server=localhost:8180 -Dproject.name=sentinel-dashboard -jar sentinel-dashboard-1.8.1.jar
- 登陆控制台
sentinel/sentinel
http://localhost:8180/
能看到这个界面说明sentinel启动起来了
- 在项目里面添加依赖
此依赖会在服务中添加一个拦截器对象,这个对象会对前端的请求进行拦截,分析请求是否在sentinel控制台规则的允许范围之内,假如在范围之内就放行,不在则拒绝访问
com.alibaba.cloud spring-cloud-starter-alibaba-sentinel
- 要连接控制台,需要在bootstrap.yml里面添加连接sentinel控制台的地址
spring:
application:
name: sca-provider
cloud:
sentinel:
transport:
dashboard: localhost:8180
此时启动项目后,可以在sentinel控制台里面看到如下效果
图里面的sca-provider就是我们的项目yml配置文件里面的name属性
限定/se/te路径的请求频率为每秒1次,就是1QPS,超过了就根据流控模式和流控效果确定的方式进行处理
访问过于频繁时就会出现如下提示
这个提示也是可以修改的。
下面说如何找到可以修改提示的那个接口
- 从SpringBoot启动时打印的信息里面可以看到这么一条,就是注册了一个拦截器SentinelWebInterceptor
2021-12-24 14:47:37.519 INFO 20120 --- [ main] c.a.c.s.SentinelWebAutoConfiguration : [Sentinel Starter] register SentinelWebInterceptor with urlPatterns: [/**].
- 找到这个类,发现它继承了AbstractSentinelInterceptor
package com.alibaba.csp.sentinel.adapter.spring.webmvc;
public class SentinelWebInterceptor extends AbstractSentinelInterceptor {}
- 找到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方法来处理,我们点中这个handle 按Ctrl + 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;
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注解标识一个方法,表示对此方法进行流控管理,可以对各种不同的访问路径分别做流控。这里所说的不同的访问路径,可以简单的理解为被@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页面,如何自定义这个页面呢
自定义限流后的页面- 创建一个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;
}
}
- 修改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;
}
- 重启项目、在sentinel簇点链路里面对userResource资源进行限流后,再执行高频刷新页面后就可以看到我们修改的被限流后的结果信息了
{
"name": "你访问的太频繁了",
"age": null,
"addr": null
}


![Sentinel学习笔记[持续更新中...] Sentinel学习笔记[持续更新中...]](http://www.mshxw.com/aiimages/31/678657.png)
