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
OpenFeign
After 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 OpenFeign
to make remote calls, and combine with Sentinel
to carry out service downgrade for problems such as exceptions and failures.
Prepare
Still use the previous open-feign-service
service as the caller and the nacos-provider
service as the provider to perform the drill.
Jar package dependencies
open-feign-service
In addition to introducing spring-cloud-starter-openfeign
, and then introducing spring-cloud-starter-alibaba-sentinel
components, and nacos
the Sentinel
persist the current limiting rules, so we also need to introduce spring-cloud-alibaba-sentinel-datasource
and 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>
复制代码
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
复制代码
Sentinel
The data persistence content of , and the configuration of activation OpenFeign
and Sentinel
combined use are added here feign.sentinel.enabled=true
.
Global unified exception handling
Whether it is the return after the Sentinel
current limit , or OpenFeign
the fallback
return 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 。该类的实现方法可以添加实际业务的处理逻辑,本案例只是打印一些信息后直接抛出自定义的异常。
Tips :ResponseCode.RPC_ERROR 在二方库中有定义。
给 FeignClient 接口增加 fallback 属性:
@FeignClient(name = "nacos-provider", fallback = ProductServiceImpl.class)
复制代码
OK,不启动服务提供方 nacos-provider
,直接调用接口测试。
接口返回:
控制台打印信息:
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 OpenFeign
used . 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 getProduct
current :
[
{
"resource": "getProduct",
"limitApp": "default",
"grade": 1,
"count": 1,
"strategy": 0,
"controlBehavior": 0,
"clusterMode": false
}
]
复制代码
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-provider
and verify it below.
The result of sending only one request in 1 second:
Quickly refresh several times within 1 second, resulting in a QPS greater than 1, and the current will be limited:
summary
OpenFeign
IntegrationSentinel
requires the introduction ofSentinel
relevant dependency packages;- Use in the configuration file
feign.sentinel.enabled=true
to enable the combined use of Feign and Sentinel; - Add the attribute to the @FeignClient
fallback
annotation , which defines the class of fault-tolerant processing logic when remote interface access is problematic; fallback
The defined class needs to implement the interface defined by @FeignClient .
Give it a thumbs up and let's go~