写点什么

Spring Cloud 集成 Sentinel

用户头像
Java收录阁
关注
发布于: 2020 年 05 月 10 日

本篇主要介绍 Sentinel 如何实现 Spring Cloud 应用的限流操作。

Sentinel 接入 Spring Cloud


1. 创建一个基于 Spring Boot 的项目,并集成 Greenwich.SR2 版本的 Spring Cloud 依赖。

2. 添加 Sentinel 依赖包

<dependency>	<groupId>com.alibaba.cloud</groupId>	<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>	<version>2.1.1.RELEASE</version></dependency>
复制代码
  1. 创建一个 REST 接口,并通过 @SentinelResource 配置限流保护资源


@RestControllerpublic class SentinelDemoController {
@SentinelResource(value = "hello", blockHandler = "blockHandlerHello") @GetMapping("/hello") public String hello() { return "Hello Sentinel"; }
public String blockHandlerHello(BlockException e) { return "限流了"; }}
复制代码

在上面代码中,配置限流资源有几种情况:

  • Sentinel starter 在默认情况下会为所有 HTTP 服务提供限流埋点,所以如果只想对 HTTP 服务进行限流,只需要添加依赖即可。

  • 如果想要对特定的方法进行限流或者降级,则需要通过 @SentinelResource 注解来实现限流资源的定义

  • 可以通过 SphU.entry()方法来配置资源。


  1. 手动配置流控规则,可以借助 Sentinel 的 InitFunc SPI 扩展接口来实现,只需要实现自己的 InitFunc 接口,并在 init 方法中编写规则加载的逻辑即可。


public class FlowRuleInitFunc implements InitFunc {    @Override    public void init() throws Exception {        List<FlowRule> rules = new ArrayList<>();        FlowRule rule = new FlowRule();        rule.setCount(1);        rule.setResource("hello");        rule.setGrade(RuleConstant.FLOW_GRADE_QPS);        rule.setLimitApp("default");        rules.add(rule);        FlowRuleManager.loadRules(rules);    }}
复制代码

SPI 是扩展点机制,如果需要被 Sentinel 加载,那么还要在 resource 目录下创建 META-INF/services/com.alibaba.csp.sentinel.init.InitFunc 文件,文件内容就是自定义扩展点的全路径

com.sentinel.springcloud.demo.FlowRuleInitFunc
复制代码

按照上述配置好之后,在初次访问任意资源的时候,Sentinel 就会自动加载 hello 资源的流控规则。

  1. 启动服务后,访问 http://localhost:8080/hello 方法,当访问频率超过设定阈值的时候,就会触发限流。


上述配置过程是基于手动配置来加载流控规则的,还有一种方式就是通过 Sentinel Dashboard 来进行配置。


基于 Sentinel Dashboard 来实现流控配置

基于 Sentinel Dashboard 来配置流控规则,可以实现流控规则的动态配置,执行步骤如下:

  1. 启动 Sentinel Dashboard

  1. 在 application.yaml 中增加如下配置:

spring:  application:    name: sentinel-spring-cloud-demo  cloud:    sentinel:      transport:        dashboard: localhost:7777
复制代码

spring.cloud.sentinel.transport.dashboard 指向的是 Sentinel Dashboard 的服务器地址,可以实现流控数据的监控和流控规则的分发。


  1. 提供一个 REST 接口:

@RestControllerpublic class SentinelDashboardController {
@GetMapping("/dash") public String dashboard() { return "Hello Sentinel Dashboard"; }}
复制代码

此处不需要添加任何资源埋点,在默认情况下 Sentinel Starter 会对所有 HTTP 请求进行限流。


  1. 启动服务后,此时访问 http://localhost:8080/dash,不存在任何限流行为。


至此,Spring Cloud 集成 Sentinel 的配置就完成了,接下来就可以进入 Sentinel Dashboard 去实现限流规则的配置。


  1. 访问 local host:7777 进入 Sentinel Dashboard。

  2. 进入 spring.application.name 对应的菜单,访问“簇点链路”,如下图所示,在该列表下可以看到/dash 这个 REST 接口的资源名称。

  1. 针对/dash 这个资源,点击最右边的操作栏中的“流控”按钮设置流控规则,如下图所示:

新增规则中的所有配置信息,实际就是 FlowRule 中对应的属性配置。

  1. 新增完成后,再次访问 localhost:8080/dash,当超过设置的阈值的时候,就可以看到限流的效果,并获得如下输出:

自定义 URL 限流异常

在默认情况下,URL 触发限流后会直接返回 Blocked by Sentinel (flow limiting),但是在实际应用中,大都采用 JSON 格式的数据,所以如果希望修改触发限流之后的返回结果形式,则可以通过自定义限流异常来处理,实现 UrlBlockHandler 并重写 blocked 方法:

@Servicepublic class CustomUrlBlockHandler implements UrlBlockHandler {    @Override    public void blocked(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, BlockException e) throws IOException {        httpServletResponse.setHeader("Content-Type", "application/json;charset=UTF-8");        String message = "{\"code\":999,\"msg\":\"访问人数过多\"}";        httpServletResponse.getWriter().write(message);    }}
复制代码

还有一种场景是:当触发限流之后,我们希望直接跳转到一个降级页面,可以通过下面这个配置来实现:


spring.cloud.sentinel.servlet.block-page={url}
复制代码


URL 资源清洗

Sentinel 中 HTTP 服务的限流默认由 Sentinel-Web-Servlet 包中的 CommonFilter 来实现,从代码中可以看到,这个 Filter 会把每个不同的 URL 都作为不同的资源来处理。


在下面这段代码中,提供一个携带{id}参数的 REST 风格的 API,对于每一个不同的{id},URL 也都不一样,所以在默认情况下 Sentinel 会把所有的 URL 当作资源来进行流控。


@RestControllerpublic class UrlCleanController {
@GetMapping("/clean/{id}") public String clean(@PathVariable("id") int id) { return "Hello URL"; }
}
复制代码

这会导致两个问题:

  1. 限流统计不准确,实际需求是控制 clean 方法总的 QPS,结果统计的是每个 URL 的 QPS。

  2. 导致 Sentinel 中资源数量过多,默认资源数量的阈值是 6000, 对于多出的资源规则将不会生效。


针对这个问题,可以通过 UrlCleaner 接口来实现资源清洗,也就是对/clean/{id}这个 URL,我们可以统一归集到/clean/*资源下,具体配置代码如下:


@Servicepublic class CustomUrlCleaner implements UrlCleaner {    @Override    public String clean(String s) {        if (StringUtils.isEmpty(s)) {            return s;        }        if (s.startsWith("/clean/")) {            return "/clean/*";        }        return s;    }}
复制代码


发布于: 2020 年 05 月 10 日阅读数: 110
用户头像

Java收录阁

关注

士不可以不弘毅,任重而道远 2020.04.30 加入

喜欢收集整理Java相关技术文档的程序员,欢迎关注同名微信公众号 Java收录 阁获取更多文章

评论

发布
暂无评论
Spring Cloud集成Sentinel