함께 만들고 함께 성장하기 위해 함께 노력하십시오! "너겟 데일리 뉴플랜 · 8월 업데이트 챌린지" 참여 22일차 입니다 . 이벤트 상세보기 클릭
머리말
이 책은 다음과 같이 계속됩니다.
마이크로서비스 간 원격 인터페이스 호출: 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
.
<!-- 引入二方库 -->
<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>
复制代码
구성 파일
구성 파일 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
의 데이터 지속성 내용과 활성화 OpenFeign
및 Sentinel
겸용 구성이 여기에 추가됩니다 feign.sentinel.enabled=true
.
글로벌 통합 예외 처리
Sentinel
현재 한도 이후의 리턴이든 , 의 리턴이든 본질적OpenFeign
으로 모두 비정상입니다.여기서 전역 통합 예외 처리를 구성하십시오.fallback
먼저 비즈니스 예외 클래스를 추가합니다.
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
,直接调用接口测试。
接口返回:
控制台打印信息:
이러한 방식으로 폴백의 내결함성 처리 가 실현 되며 원격 서비스를 사용할 수 없는 경우에도 다운그레이드할 수 있습니다.
@SentinelResource 전류 제한
컨트롤러 계층이 FeignClient에서 정의한 인터페이스를 사용하여 원격으로 서비스를 호출하는 경우 Sentinel 리소스를 정의하고 현재 리소스 흐름을 제한하는 규칙을 설정할 수도 있습니다.
@SentinelResource 를 사용하는 몇 가지 방법은 이전 기사에서 언급되었으며 여기에서 조합하여 OpenFeign
사용 .이 예제에서는 다음 정의가 제공됩니다.
@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 "请稍后重试";
}
复制代码
이전 준비에서 Sentinel 리소스 전류 제한 규칙이 Nacos에 지속되도록 구성했지만 이제 Nacos에서 리소스 getProduct
전류 .
[
{
"resource": "getProduct",
"limitApp": "default",
"grade": 1,
"count": 1,
"strategy": 0,
"controlBehavior": 0,
"clusterMode": false
}
]
复制代码
현재 제한 규칙은 QPS 임계값이 1이라는 것입니다. 초당 1개 이상의 요청이 있는 한 제한됩니다.
원격 서비스 nacos-provider
를 시작하고 아래에서 확인하십시오.
1초에 하나의 요청만 보낸 결과:
1초 내에 여러 번 빠르게 새로 고침하여 QPS가 1보다 크면 전류가 제한됩니다.
요약
OpenFeign
통합을Sentinel
위해서는Sentinel
관련 종속성 패키지를 도입해야 합니다.- 구성 파일
feign.sentinel.enabled=true
에서 사용하여 Feign과 Sentinel을 함께 사용할 수 있습니다. - 원격 인터페이스 액세스에 문제가 있을 때 내결함성 처리 논리 클래스를 정의 하는 @FeignClient
fallback
주석 에 속성을 추가합니다. fallback
정의된 클래스는 @FeignClient 에 의해 정의된 인터페이스를 구현해야 합니다 .
엄지손가락을 치켜세우고 가자~