分布式流量防护组件Sentinel

本文仅做简单使用

背景

随着微服务的流行,服务和服务之间的稳定性变得越来越重要。Sentinel 是面向分布式、多语言异构化服务架构的流量治理组件,主要以流量为切入点,从流量路由、流量控制、流量整形、熔断降级、系统自适应过载保护、热点流量防护等多个维度来帮助开发者保障微服务的稳定性。

** Sentinel是阿里巴巴开源的一款微服务流量控制组件,可以在github上下载(Sentinel下载)**

启动

Sentinel 提供一个轻量级的开源控制台,它提供机器发现以及健康情况管理、监控(单机和集群),规则管理和推送的功能

java -jar sentinel-dashboard-1.8.1.jar -Dserver.port=8090

访问 localhost:8080 默认账号密码都是:sentinel

SpringCloud整合Sentinel

<!--sentinel-->
<dependency>
    <groupId>com.alibaba.cloud</groupId> 
    <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
    # 配置Sentinel流控
    sentinel:
      transport:
        #配置Sentinel dashboard地址
        dashboard: localhost:8080
        #默认8719端口,如果被占用会向上扫描。
        port: 8719

      # sentinel持久化到nacos
      datasource:
        flow:
          nacos:
            # nacos连接地址
            server-addr: ${spring.cloud.nacos.discovery.server-addr}
            # nacos中的配置名称
            data-id: ${spring.application.name}-flow-rules
            group-id: DEFAULT_GROUP
            data-type: json
            rule-type: flow
            username: ${spring.cloud.nacos.username}
            password: ${spring.cloud.nacos.password}

        degrade:
          nacos:
            server-addr: ${spring.cloud.nacos.discovery.server-addr}
            data-id: ${spring.application.name}-degrade-rules
            group-id: DEFAULT_GROUP
            data-type: json
            rule-type: degrade
            username: ${spring.cloud.nacos.username}
            password: ${spring.cloud.nacos.password}

这里Sentinel的部分配置放入Nacos中进行管理

@SentinelResource详解

Sentinel 支持通过 @SentinelResource 注解定义资源并配置 blockHandler 和 fallback 函数来进行限流之后的处理。@SentinelResource 注解是 Sentinel 提供的最重要的注解之一,它还包含了多个属性,如下表:

注:在 Sentinel 1.6.0 之前,fallback 函数只针对降级异常(DegradeException)进行处理,不能处理业务异常。 注意:注解方式埋点不支持 private 方法。 特别地,若 blockHandler 和 fallback 都进行了配置,则被限流降级而抛出 BlockException 时只会进入 blockHandler 处理逻辑。若未配置 blockHandler、fallback 和 defaultFallback,则被限流降级时会将 BlockException 直接抛出。

示例代码:

public class TestService {

    // 对应的 `handleException` 函数需要位于 `ExceptionUtil` 类中,并且必须为 static 函数.
    @SentinelResource(value = "test", blockHandler = "handleException", blockHandlerClass = {ExceptionUtil.class})
    public void test() {
        System.out.println("Test");
    }

    // 原函数
    @SentinelResource(value = "hello", blockHandler = "exceptionHandler", fallback = "helloFallback")
    public String hello(long s) {
        return String.format("Hello at %d", s);
    }
    
    // Fallback 函数,函数签名与原函数一致或加一个 Throwable 类型的参数.
    public String helloFallback(long s) {
        return String.format("Halooooo %d", s);
    }

    // Block 异常处理函数,参数最后多一个 BlockException,其余与原函数一致.
    public String exceptionHandler(long s, BlockException ex) {
        // Do some log here.
        ex.printStackTrace();
        return "Oops, error occurred at " + s;
    }
}

限流实验

新增一体限流规则,QPS阈值为5,这里需要注意的是资源名要和 @SentinelResource的value属性指定的值相同 这里拿某博客写个限流方法举个例子:

@RequestLimit(amount = 200, time = 60000)
    @ApiOperation(value = "通过推荐等级获取博客列表", notes = "通过推荐等级获取博客列表")
    @GetMapping("/getBlogByLevel")
    @SentinelResource(value = "/index/getBlogByLevel" ,/*blockHandler = "test",*/fallback = "test2")
    public String getBlogByLevel(HttpServletRequest request,
                                 @ApiParam(name = "level", value = "推荐等级", required = false) @RequestParam(name = "level", required = false, defaultValue = "0") Integer level,
                                 @ApiParam(name = "currentPage", value = "当前页数", required = false) @RequestParam(name = "currentPage", required = false, defaultValue = "1") Long currentPage,
                                 @ApiParam(name = "useSort", value = "使用排序", required = false) @RequestParam(name = "useSort", required = false, defaultValue = "0") Integer useSort) {

        return ResultUtil.result(SysConf.SUCCESS, blogService.getBlogPageByLevel(level, currentPage, useSort));
    }
    public String test(HttpServletRequest request,Integer level, Long currentPage, Integer useSort, BlockException blockException){
        log.error("错误信息");
        return "请求频繁,请稍后再试!!";
    }
    public String test2(HttpServletRequest request,Integer level, Long currentPage, Integer useSort, Throwable throwable){
        log.error("错误信息2");
        return "请求频繁,请稍后再试!!2";
    }

可以看到我打印的错误信息,这里的fallback与blockhandler两个属性有不同的作用,这一点很重要,博主测试的时候踩过不少坑 fallback是方法出现异常时的回调,可以针对所有异常处理,而blockHander是针对BlockException异常,即限流异常的后续处理

Sentinel与OpenFeign的集成使用(默认拥有SpringCloud环境和Feign)

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
    <version>${openfeign-version}</version>
</dependency>
# 激活Sentinel对OpenFeign的支持
feign:
  sentinel:
    enabled: true

再举个例子

//当没有成功调用/info/{id}接口时会走fallback属性标注的类型的处理方法
@Service
@FeignClient(value = "nacos-provider", configuration = FeignConfiguration.class, fallback = FeignServiceImpl.class)
public interface FeignService {
    /**
     * 远程调用对应方法
     */
    @GetMapping("info/{id}")
    public JsonResult<String> getSql(@PathVariable("id") Long id);
}

实现类必须添加@Component注解,否则无法注入到容器中

@Component
public class FeignServiceImpl implements FeignService {
 
    @Override
    public JsonResult<String> getSql(Long id) {
        return new JsonResult<>(444,"服务降级返回!");
    }
}

若是被调用服务出现异常,则会服务降级返回预先写好的对应实现类中的方法 ####seninel还具有熔断、热点、授权、流控等多种功能