Strong alliance: OpenFeign integrates Sentinel

Work together to create and grow together! This is the 22nd day of my participation in the "Nuggets Daily New Plan · August Update Challenge", click to view the details of the event

foreword

The book continues:

Remote interface calls between microservices: the use of OpenFeign

OpenFeignAfter using in the project , it is very convenient to call the remote service. Now there is a problem. If the remote service fails, the remote interface cannot be adjusted, and I am anxiously waiting for the return result. What should I do?

Of course, it is to use service downgrade . In this article, we will use OpenFeignto make remote calls, and combine with Sentinelto carry out service downgrade for problems such as exceptions and failures.

Prepare

Still use the previous open-feign-serviceservice as the caller and the nacos-providerservice as the provider to perform the drill.

Jar package dependencies

open-feign-serviceIn addition to introducing spring-cloud-starter-openfeign, and then introducing spring-cloud-starter-alibaba-sentinelcomponents, and nacosthe Sentinelpersist the current limiting rules, so we also need to introduce spring-cloud-alibaba-sentinel-datasourceand sentinel-datasource-nacos:

<!-- 引入二方库 -->
<dependency>
    <groupId>cn.chendapeng.springcloud</groupId>
    <artifactId>internal-common</artifactId>
    <version>0.0.1-SNAPSHOT</version>
</dependency>

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>

<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-alibaba-sentinel-datasource</artifactId>
</dependency>
<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-datasource-nacos</artifactId>
</dependency>
复制代码

image-20220819160608694.png

configuration file

Configuration file application.yml:

spring:
  application:
    name: open-feign-service

  cloud:
    nacos:
      discovery:
        server-addr: 192.168.242.112:81
    sentinel:
      transport:
        dashboard: localhost:8080
        port: 8719
      # https://github.com/alibaba/Sentinel/issues/1213
      web-context-unify: false
      # Sentinel 规则持久化到 Nacos
      datasource:
        rule1:
          nacos:
            serverAddr: 192.168.242.112:81
            groupId: DEFAULT_GROUP
            dataId: sentinelFlowRule.json
            ruleType: flow
            
feign:
  client:
    config:
      # 默认的超时时间设置
      default:
        connectTimeout: 5000
        readTimeout: 5000
      # 在指定的 FeignClient 设置超时时间,覆盖默认的设置
      nacos-provider:
        connectTimeout: 1000
        readTimeout: 1000
        loggerLevel: full
  # 激活 Sentinel
  sentinel:
    enabled: true
复制代码

SentinelThe data persistence content of , and the configuration of activation OpenFeignand Sentinelcombined use are added here feign.sentinel.enabled=true.

Global unified exception handling

Whether it is the return after the Sentinelcurrent limit , or OpenFeignthe fallbackreturn of , they are all abnormal in essence. Configure the global unified exception handling here.

First, add a business exception class:

public class BusinessException extends RuntimeException {

    private String code;

    private String message;

    public BusinessException(String code, String message) {
        this.code = code;
        this.message = message;
    }

    public String getCode() {
        return code;
    }

    public void setCode(String code) {
        this.code = code;
    }

    @Override
    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }
}
复制代码

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

@RestControllerAdvice
public class GlobalExceptionHandler {

    private static final Logger LOGGER = LoggerFactory.getLogger(GlobalExceptionHandler.class);

    /**
     * 业务异常,统一处理
     * @param e 异常对象
     * @return ResponseResult 全局异常响应
     */
    @ExceptionHandler(BusinessException.class)
    public ResponseResult<String> businessException(BusinessException e) {
        LOGGER.info("code={}, message={}", e.getCode(), e.getMessage());
        return ResponseResult.fail(e.getCode(), e.getMessage());
    }

    // 其他异常...
}
复制代码

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

操练

@FeignClient 的 fallback

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

@Service
@FeignClient("nacos-provider")
public interface ProductService {

    /**
     * 调用远程服务 nacos-provider 的 product/{id} 接口
     * @param id 参数 id
     * @return 返回
     */
    @GetMapping("/product/{id}")
    String getProductById(@PathVariable("id") Long id);
}
复制代码

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

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

@Component
@Slf4j
public class ProductServiceImpl implements ProductService {

    /**
     * 调用远程服务 nacos-provider 的 product/{id} 接口失败后的处理方法
     *
     * @param id 参数 id
     * @return 返回
     */
    @Override
    public String getProductById(Long id) {
        log.error("调用接口 getProduct 失败,id={}", id);
        //return "OpenFeign 降级";
        throw new BusinessException(ResponseCode.RPC_ERROR.getCode(), ResponseCode.RPC_ERROR.getMessage());
    }
}
复制代码

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

TipsResponseCode.RPC_ERROR 在二方库中有定义。

FeignClient 接口增加 fallback 属性:

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

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

接口返回:

image-20220819163044024.png

控制台打印信息:

image-20220819162946588.png

In this way, the fault-tolerant processing of fallback is realized , and even if the remote service is unavailable, it can also be downgraded.

@SentinelResource current limiting

When the Controller layer uses the interface defined by FeignClient to call services remotely, you can also define Sentinel resources, and set rules to limit the flow of resources.

Some of the usage methods of @SentinelResource have been mentioned in the previous articles, and they are OpenFeignused . In this example, the following definitions are provided:

@GetMapping("/product/{id}")
@SentinelResource(value = "getProduct",
                  blockHandler = "getProductBlock",
                  fallback = "getProductFallback")
public String getProduct(@PathVariable("id") Long id) {
    return productService.getProductById(id);
}

public String getProductBlock(Long id, BlockException e) {
    log.error("访问资源 getProduct 被限流,id={}", id);
    throw new BusinessException("C0002", "访问资源 getProduct 被限流");
}

public String getProductFallback(Long id) {
    log.error("访问资源 getProduct fallback");
    return "请稍后重试";
}
复制代码

In the previous preparations, we have configured Sentinel resource current limiting rules to persist to Nacos. Now configure the resource getProductcurrent :

[
  {
    "resource": "getProduct",
    "limitApp": "default",
    "grade": 1,
    "count": 1,
    "strategy": 0,
    "controlBehavior": 0,
    "clusterMode": false
  }
]
复制代码

image-20220819164001324.png

The current throttling rule is that the QPS threshold is 1, as long as I have more than 1 request per second, I will be throttled.

Start the remote service nacos-providerand verify it below.

The result of sending only one request in 1 second:

image-20220819164448540.png

Quickly refresh several times within 1 second, resulting in a QPS greater than 1, and the current will be limited:

image-20220819164343886.png

summary

  • OpenFeignIntegration Sentinelrequires the introduction of Sentinelrelevant dependency packages;
  • Use in the configuration file feign.sentinel.enabled=trueto enable the combined use of Feign and Sentinel;
  • Add the attribute to the @FeignClientfallback annotation , which defines the class of fault-tolerant processing logic when remote interface access is problematic;
  • fallbackThe defined class needs to implement the interface defined by @FeignClient .

Give it a thumbs up and let's go~

Guess you like

Origin juejin.im/post/7133509818425278471