• 强强联合:OpenFeign 整合 Sentinel


    书接前文:

    微服务间的远程接口调用:OpenFeign 的使用

    当项目中使用了 OpenFeign 后,可以很方便的进行远程服务调用,现在有个问题,假如远程服务出现故障了,调不了远程的接口,这边又着急等着返回结果,怎么办呢?

    当然是使用 服务降级 ,本篇就使用 OpenFeign 进行远程调用,并结合 Sentinel 对出现的异常、故障等问题进行服务降级

    准备

    仍以前面 open-feign-service 服务为调用方, nacos-provider 服务为提供方来进行操练。

    Jar 包依赖

    Open-feign-service 除了引入 spring-cloud-starter-openfeign 外,再引入 spring-cloud-starter-alibaba-sentinel 组件,另外我们这里使用 nacos 的配置中心做 Sentinel 限流规则的持久化,所以还需要引入 spring-cloud-alibaba-sentinel-datasource 和 sentinel-datasource-nacos :

    1. <!-- 引入二方库 -->
    2. <dependency>
    3. <groupId>cn.chendapeng.springcloud</groupId>
    4. <artifactId>internal-common</artifactId>
    5. <version>0.0.1-SNAPSHOT</version>
    6. </dependency>
    7. <dependency>
    8. <groupId>org.springframework.boot</groupId>
    9. <artifactId>spring-boot-starter-web</artifactId>
    10. </dependency>
    11. <dependency>
    12. <groupId>com.alibaba.cloud</groupId>
    13. <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
    14. </dependency>
    15. <dependency>
    16. <groupId>org.springframework.cloud</groupId>
    17. <artifactId>spring-cloud-starter-loadbalancer</artifactId>
    18. </dependency>
    19. <dependency>
    20. <groupId>org.springframework.cloud</groupId>
    21. <artifactId>spring-cloud-starter-openfeign</artifactId>
    22. </dependency>
    23. <dependency>
    24. <groupId>com.alibaba.cloud</groupId>
    25. <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
    26. </dependency>
    27. <dependency>
    28. <groupId>com.alibaba.cloud</groupId>
    29. <artifactId>spring-cloud-alibaba-sentinel-datasource</artifactId>
    30. </dependency>
    31. <dependency>
    32. <groupId>com.alibaba.csp</groupId>
    33. <artifactId>sentinel-datasource-nacos</artifactId>
    34. </dependency>
    35. 复制代码

    配置文件

    配置文件 application.yml :

    1. spring:
    2. application:
    3. name: open-feign-service
    4. cloud:
    5. nacos:
    6. discovery:
    7. server-addr: 192.168.242.112:81
    8. sentinel:
    9. transport:
    10. dashboard: localhost:8080
    11. port: 8719
    12. # https://github.com/alibaba/Sentinel/issues/1213
    13. web-context-unify: false
    14. # Sentinel 规则持久化到 Nacos
    15. datasource:
    16. rule1:
    17. nacos:
    18. serverAddr: 192.168.242.112:81
    19. groupId: DEFAULT_GROUP
    20. dataId: sentinelFlowRule.json
    21. ruleType: flow
    22. feign:
    23. client:
    24. config:
    25. # 默认的超时时间设置
    26. default:
    27. connectTimeout: 5000
    28. readTimeout: 5000
    29. # 在指定的 FeignClient 设置超时时间,覆盖默认的设置
    30. nacos-provider:
    31. connectTimeout: 1000
    32. readTimeout: 1000
    33. loggerLevel: full
    34. # 激活 Sentinel
    35. sentinel:
    36. enabled: true
    37. 复制代码

    这里增加了 Sentinel 的数据持久化内容,以及激活 OpenFeign 与 Sentinel 联合使用的 feign.sentinel.enabled=true 配置。

    全局统一异常处理

    不管是 Sentinel 限流后返回,还是 OpenFeign 的 fallback 返回,本质上他们都是出现异常了,这里配置一下全局的统一异常处理。

    首先,增加一个业务异常类:

    1. public class BusinessException extends RuntimeException {
    2. private String code;
    3. private String message;
    4. public BusinessException(String code, String message) {
    5. this.code = code;
    6. this.message = message;
    7. }
    8. public String getCode() {
    9. return code;
    10. }
    11. public void setCode(String code) {
    12. this.code = code;
    13. }
    14. @Override
    15. public String getMessage() {
    16. return message;
    17. }
    18. public void setMessage(String message) {
    19. this.message = message;
    20. }
    21. }
    22. 复制代码

    然后使用 Spring 的 @RestControllerAdvice 注解进行全局的异常进行处理:

    1. @RestControllerAdvice
    2. public class GlobalExceptionHandler {
    3. private static final Logger LOGGER = LoggerFactory.getLogger(GlobalExceptionHandler.class);
    4. /**
    5. * 业务异常,统一处理
    6. * @param e 异常对象
    7. * @return ResponseResult 全局异常响应
    8. */
    9. @ExceptionHandler(BusinessException.class)
    10. public ResponseResult<String> businessException(BusinessException e) {
    11. LOGGER.info("code={}, message={}", e.getCode(), e.getMessage());
    12. return ResponseResult.fail(e.getCode(), e.getMessage());
    13. }
    14. // 其他异常...
    15. }
    16. 复制代码

    这样,只要指定了抛出的异常类型,就会返回统一的响应格式。

    操练

    @FeignClient 的 fallback

    在上一篇文章中,我们通过 FeignClient 接口调用远程的服务:

    1. @Service
    2. @FeignClient("nacos-provider")
    3. public interface ProductService {
    4. /**
    5. * 调用远程服务 nacos-provider 的 product/{id} 接口
    6. * @param id 参数 id
    7. * @return 返回
    8. */
    9. @GetMapping("/product/{id}")
    10. String getProductById(@PathVariable("id") Long id);
    11. }
    12. 复制代码

    如果远程接口不通,这里可以在 @FeignClient 注解上增加一个属性 fallback ,该属性定义一个容错的处理类,当调用远程接口失败或超时时,会调用对应接口的容错逻辑,fallback 指定的类必须实现 @FeignClient 标记的接口。

    先来定义一个实现 ProductService 的类:

    1. @Component
    2. @Slf4j
    3. public class ProductServiceImpl implements ProductService {
    4. /**
    5. * 调用远程服务 nacos-provider 的 product/{id} 接口失败后的处理方法
    6. *
    7. * @param id 参数 id
    8. * @return 返回
    9. */
    10. @Override
    11. public String getProductById(Long id) {
    12. log.error("调用接口 getProduct 失败,id={}", id);
    13. //return "OpenFeign 降级";
    14. throw new BusinessException(ResponseCode.RPC_ERROR.getCode(), ResponseCode.RPC_ERROR.getMessage());
    15. }
    16. }
    17. 复制代码

    该类需要被 Spring 识别,所以加个 @Component 。该类的实现方法可以添加实际业务的处理逻辑,本案例只是打印一些信息后直接抛出自定义的异常。

    Tips :ResponseCode.RPC_ERROR 在二方库中有定义。

    给 FeignClient 接口增加 fallback 属性:

    1. @FeignClient(name = "nacos-provider", fallback = ProductServiceImpl.class)
    2. 复制代码

    OK,不启动服务提供方 nacos-provider,直接调用接口测试。

    接口返回:

    控制台打印信息:

    这样就实现了 fallback 的容错处理,即时远程服务不可用,也能进行降级处理。

    @SentinelResource 限流

    在 Controller 层使用 FeignClient 定义的接口进行远程调用服务时,还可以定义 Sentinel 资源,并设置规则对资源进行限流。

    @SentinelResource 的一些使用方法在前几篇文章中已有提及,这里再结合 OpenFeign 使用,本例中有如下定义:

    1. @GetMapping("/product/{id}")
    2. @SentinelResource(value = "getProduct",
    3. blockHandler = "getProductBlock",
    4. fallback = "getProductFallback")
    5. public String getProduct(@PathVariable("id") Long id) {
    6. return productService.getProductById(id);
    7. }
    8. public String getProductBlock(Long id, BlockException e) {
    9. log.error("访问资源 getProduct 被限流,id={}", id);
    10. throw new BusinessException("C0002", "访问资源 getProduct 被限流");
    11. }
    12. public String getProductFallback(Long id) {
    13. log.error("访问资源 getProduct fallback");
    14. return "请稍后重试";
    15. }
    16. 复制代码

    在前面的准备工作中,我们已经配置了 Sentinel 资源限流规则持久化到 Nacos,现在 Nacos 中配置一下资源 getProduct 的限流规则:

    1. [
    2. {
    3. "resource": "getProduct",
    4. "limitApp": "default",
    5. "grade": 1,
    6. "count": 1,
    7. "strategy": 0,
    8. "controlBehavior": 0,
    9. "clusterMode": false
    10. }
    11. ]
    12. 复制代码

    限流规则是 QPS 阈值为1,只要我1秒大于1次请求就会被限流。

    启动远程服务 nacos-provider ,下面来验证一下。

    1秒只发一个请求的结果:

    1秒内快速刷新几次,造成QPS大于1,将会被限流:

    小结

    • OpenFeign 整合 Sentinel 需要引入 Sentinel 相关依赖包;
    • 在配置文件通过 feign.sentinel.enabled=true 来开启 Feign 与 Sentinel的结合使用;
    • 在 @FeignClient 注解中增加 fallback 属性,该属性定义远程接口访问有问题时的容错处理逻辑的类;
    • fallback 定义的类需实现 @FeignClient 定义的接口。

  • 相关阅读:
    Centos 7 部署SVN服务器
    移动通信覆盖自愈的研究与实现
    制造业企业设备管理常见的三个问题及对应的解决方案
    最新Prompt预设词分享,DALL-E3文生图+文档分析
    蓝桥等考Python组别十三级005
    xv6第一章:Operating system interfaces
    【12】c++11新特性 —>forward完美转发
    【.NET】聊聊 IChangeToken 接口
    每天一个数据分析题(三百四十六)
    对话框管理器第三章:创建控件
  • 原文地址:https://blog.csdn.net/BASK2312/article/details/128036578