0
点赞
收藏
分享

微信扫一扫

Spring Cloud Alibaba(7) - 服务容错组件-Sentinel

1. Sentinel介绍

Github: Sentinel

随着微服务的流行,服务和服务之间的稳定性变得越来越重要。 Sentinel以流量为切入点,从流量控制、熔断降级、系统负载保护等多个维度保护服务的稳定性。

Sentinel 具有以下特征:

  • 丰富的应用场景: Sentinel 承接了阿里巴巴近 10 年的双十一大促流量的核心场景,例如秒杀(即突发流量控制在系统容量可以承受的范围)、消息削峰填谷、实时熔断下游不可用应用等。
  • 完备的实时监控: Sentinel 同时提供实时的监控功能。您可以在控制台中看到接入应用的单台机器秒级数据,甚至 500 台以下规模的集群的汇总运行情况。
  • 广泛的开源生态: Sentinel 提供开箱即用的与其它开源框架/库的整合模块,例如与 Spring Cloud、Dubbo、gRPC 的整合。您只需要引入相应的依赖并进行简单的配置即可快速地接入 Sentinel。
  • 完善的 SPI 扩展点: Sentinel 提供简单易用、完善的 SPI 扩展点。您可以通过实现扩展点,快速的定制逻辑。例如定制规则管理、适配数据源等。

2. 搭建Sentinel控制台

sentinel控制台介绍文档:https://github.com/alibaba/Sentinel/wiki/控制台

2.1 下载控制台

下载地址:https://github.com/alibaba/Sentinel/releases

也可以使用源码进行编译打包

2.2 启动控制台

java -Dserver.port=8840 \
-Dcsp.sentinel.dashboard.server=localhost:8840 \
-Dproject.name=sentinel-dashboard \
-jar sentinel-dashboard.jar

2.3 使用控制台

控制台地址:http://localhost:8840

默认账号密码:sentinel / sentinel

3. 为项目整合sentinel

3.1 引入依赖

<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>

3.2 配置属性

spring:
    cloud:
        sentinel:
      transport:
        # 指定sentinel控制台地址
        dashboard: 127.0.0.1:8840

3.3 启动项目并查看sentinel控制台

启动后,访问几次项目的url。可以在sentinel控制台看到如下页面。

4. 限流规则

4.1 阈值类型

流量控制主要有两种统计类型:

  • 线程数:通过统计当前资源的并发线程数来进行流控
  • QPS:通过统计当前资源的QPS来进行流控

4.2 流控模式

  • 直接:当前资源达到阈值,就限流自己
  • 关联:当关联的资源达到阈值,就限流自己
  • 链路:只记录指定链路上的流量

4.3 配置示例

/echo-feign/hehe 资源的QPS达到1时,进行流控。如下图所示:

关联资源限流,当/sleep资源的QPS达到1时,对/echo-feign/hehe 资源进行流控

5. SentinelResource注解

5.1 注解属性

基于Sentinel 1.6.3

属性 作用 是否必须
value 资源名称
entryType entry类型,标记流量的方向,取值IN/OUT,默认是OUT
blockHandler 处理BlockException的函数名称。函数要求: 1. 必须是 public 2.返回类型与原方法一致 3. 参数类型需要和原方法相匹配,并在最后加 BlockException 类型的参数。 4. 默认需和原方法在同一个类中。若希望使用其他类的函数,可配置 blockHandlerClass ,并指定blockHandlerClass里面的方法。
blockHandlerClass 存放blockHandler的类。对应的处理函数必须static修饰,否则无法解析,其他要求:同blockHandler。
fallback 用于在抛出异常的时候提供fallback处理逻辑。fallback函数可以针对所有类型的异常(除了 exceptionsToIgnore 里面排除掉的异常类型)进行处理。函数要求: 1. 返回类型与原方法一致 2. 参数类型需要和原方法相匹配,Sentinel 1.6开始,也可在方法最后Throwable 类型的参数。 3.默认需和原方法在同一个类中。若希望使用其他类的函数,可配置 fallbackClass ,并指定fallbackClass里面的方法。
fallbackClass【1.6】 存放fallback的类。对应的处理函数必须static修饰,否则无法解析,其他要求:同fallback。
defaultFallback【1.6】 用于通用的 fallback 逻辑。默认fallback函数可以针对所有类型的异常(除了 exceptionsToIgnore 里面排除掉的异常类型)进行处理。若同时配置了 fallback 和 defaultFallback,以fallback为准。函数要求: 1. 返回类型与原方法一致 2. 方法参数列表为空,或者有一个 Throwable 类型的参数。 3. 默认需要和原方法在同一个类中。若希望使用其他类的函数,可配置 fallbackClass ,并指定 fallbackClass 里面的方法。
exceptionsToIgnore【1.6】 指定排除掉哪些异常。排除的异常不会计入异常统计,也不会进入fallback逻辑,而是原样抛出。
exceptionsToTrace 需要trace的异常 Throwable

注:

  • 1.6.0 之前的版本 fallback 函数只针对降级异常(DegradeException)进行处理,不能针对业务异常进行处理

  • 若 blockHandler 和 fallback 都进行了配置,则被限流降级而抛出 BlockException 时只会进入 blockHandler 处理逻辑。若未配置 blockHandlerfallbackdefaultFallback,则被限流降级时会将 BlockException 直接抛出

  • 从 1.4.0 版本开始,注解方式定义资源支持自动统计业务异常,无需手动调用 Tracer.trace(ex) 来记录业务异常。Sentinel 1.4.0 以前的版本需要自行调用 Tracer.trace(ex) 来记录业务异常。

5.2 SentinelResource示例:

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;
    }
}

5.3 参考文档

6. RestTemplate整合Sentinel

给RestTemplate加上@SentinelRestTemplate注解即可完成整合

代码示例如下:

@Configuration
@Slf4j
public class ContentCenterConfig {

    @LoadBalanced
    @Bean
    @SentinelRestTemplate(blockHandlerClass = RestTemplateExceptionUtil.class, blockHandler = "block", //
        fallbackClass = RestTemplateExceptionUtil.class, fallback = "fallback")
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }

    public static class RestTemplateExceptionUtil {
        public static SentinelClientHttpResponse block(HttpRequest request, byte[] body,
            ClientHttpRequestExecution execution, BlockException ex) {
            log.warn("Oops: " + ex.getClass().getCanonicalName());
            return new SentinelClientHttpResponse("restTemplate block info");
        }

        public static SentinelClientHttpResponse fallback(HttpRequest request, byte[] body,
            ClientHttpRequestExecution execution, BlockException ex) {
            log.warn("fallback: " + ex.getClass().getCanonicalName());
            return new SentinelClientHttpResponse("restTemplate fallback info");
        }
    }
}

@SentinelRestTemplate注解属性:

  • blockHandlerClass:限流后处理的类
  • blockHandler: 限流后处理的方法
  • fallbackClsss:熔断后处理的类
  • fallback:熔断后处理的方法

开启或关闭@SentinelRestTemplate注解

resttemplate:
  sentinel:
    # 为false时表示关闭@SentinelRestTemplate注解
    enabled: true

7. Feign整合Sentinel

7.1 配置属性

feign:
  sentinel:
    enabled: true

7.2 使用fallback自定义流控处理

  1. 实现FeignClient
  2. @FeignClient注解的属性设置为实现类的class
@Component
public class EchoFeignClientFallback implements EchoFeignClient {
    @Override
    public String echo(String str) {
        // 处理流控返回结果
        return "feign-fallback-echo";
    }

    @Override
    public String divide(Integer a, Integer b) {
        // 处理流控返回结果
        return "feign-fallback-divide";
    }
}

@FeignClient(name = "user-center", fallback = EchoFeignClientFallback.class)
public interface EchoFeignClient {

    /**
     * 请求http://user-center/echo/{str}服务
     *
     * @param str
     * @return
     */
    @GetMapping("/echo/{str}")
    String echo(@PathVariable String str);

    /**
     * 请求http://user-center/divide服务
     *
     * @param a
     * @param b
     * @return
     */
    @GetMapping("/divide")
    String divide(@RequestParam("a") Integer a, @RequestParam Integer b);

}

7.3 使用fallbackFactory自定义流控处理

@Component
@Slf4j
public class EchoFeignClientFallbackFactory implements FallbackFactory<EchoFeignClient> {
    @Override
    public EchoFeignClient create(Throwable throwable) {
        log.warn("远程调用被限流或降级了", throwable);
        return new EchoFeignClient() {
            @Override
            public String echo(String str) {
                // 处理流控返回结果
                return "feign-fallback-echo-1";
            }

            @Override
            public String divide(Integer a, Integer b) {
                // 处理流控返回结果
                return "feign-fallback-divide-1";
            }
        };
    }
}

@FeignClient(name = "user-center", fallbackFactory = EchoFeignClientFallbackFactory.class)
public interface EchoFeignClient {

    /**
     * 请求http://user-center/echo/{str}服务
     *
     * @param str
     * @return
     */
    @GetMapping("/echo/{str}")
    String echo(@PathVariable String str);

    /**
     * 请求http://user-center/divide服务
     *
     * @param a
     * @param b
     * @return
     */
    @GetMapping("/divide")
    String divide(@RequestParam("a") Integer a, @RequestParam Integer b);

}

8. Sentinel使用总结

整合方式 使用方式 使用方法
编码方式 API try...catch...finally...
注解方式 @SentinelResource blockHandler/fallback
RestTemplate @SentinelRestTemplate blockHandler/fallback
Feign feign.sentinel.enabled = true fallback/fallbackFactory
举报

相关推荐

0 条评论