sentinel+dubbo如何实现dubbo远程调用异常时全局的降级或熔断
sentinel+dubbo如何实现dubbo远程调用异常时全局的降级或熔断,尝试过sentinel-demo-dubbo无法实现全局fallback,测试代码如下
测试环境
- springcloud:Hoxton.SR12
- springcloudalibaba:2.2.7.RELEASE
- dubbo:2.7.13
- Sentinel:1.8.4
消费者pom.xml
<dependencies>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-dubbo</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-apache-dubbo-adapter</artifactId>
</dependency>
<!--Snetinel 依赖-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-nacos</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-spring-cloud-gateway-adapter</artifactId>
</dependency>
</dependencies>
消费者ConsumerImpl
@Service
public class ConsumerServiceImpl implements IConsumerService {
@DubboReference
private IProductService productService;
@Override
@SentinelResource(value = "testHello")
public String hello(Long id) {
Random random = new Random();
productService.hello(id.toString());
String serialNum = String.valueOf(random.nextInt(1000));
return Thread.currentThread().getName() + "\t" + "调用成功,流水号为:" + serialNum;
}
}
消费者DubboAdapterConfig
全局fallback配置参考Sentinel+dubbo配置,文档中说明用户只需要实现自定义的 DubboFallback 接口,并通过 DubboFallbackRegistry 注册即可,我按如下代码注册结果不生效,注册方式有误还请大佬指正🙈
@Configuration
public class DubboAdapterConfig {
private static final Logger logger = LoggerFactory.getLogger(DubboAdapterConfig.class);
@Bean
private static void registerFallback() {
DubboFallbackRegistry.setProviderFallback(new DubboFallback() {
@Override
public Result handle(Invoker<?> invoker, Invocation invocation, BlockException ex) {
return new AppResponse("错误: " + ex.getClass().getTypeName());
}
});
}
}
服务者pom.xml
<dependencies>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-dubbo</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-apache-dubbo-adapter</artifactId>
</dependency>
<!--Snetinel 依赖-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-nacos</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
</dependencies>
服务者ProductServiceImpl
@DubboService
public class ProductServiceImpl implements IProductService {
@Override
public String hello(String str) {
return "结果:" + str;
}
}
分别启动消费者服务和服务者服务时,可以正常远程调用。当停掉服务者时,再次请求消费者时,控制台抛异常如下:
org.apache.dubbo.rpc.RpcException: No provider available from registry 127.0.0.1:8858 for service com.ran.member.api.service.IProductService on consumer 172.17.192.1 use dubbo version 2.7.13, please check status of providers(disabled, not registered or in blacklist).
at org.apache.dubbo.registry.integration.DynamicDirectory.doList(DynamicDirectory.java:168) ~[dubbo-2.7.13.jar:2.7.13]
Suppressed: reactor.core.publisher.FluxOnAssembly$OnAssemblyException:
Error has been observed at the following site(s):
|_ checkpoint ⇢ org.springframework.cloud.gateway.filter.WeightCalculatorWebFilter [DefaultWebFilterChain]
|_ checkpoint ⇢ org.springframework.boot.actuate.metrics.web.reactive.server.MetricsWebFilter [DefaultWebFilterChain]
|_ checkpoint ⇢ HTTP GET "/index?id=-1" [ExceptionHandlingWebHandler]
Stack trace:
请问要如何实现当服务者停机时,消费者再次请求Sentinel可以做全局降级/限流🤔
消费者 setProviderFallback 你不觉得奇怪么~~
消费者调用失败 抛出 BlockException 的时候 会调用 DubboAdapterGlobalConfig.getConsumerFallback().handle(invoker, invocation, e);(所以在消费端调用需要注册的是 consumerFallack) rpc 异常的时候 没做处理
所以你需要在 调用的时候 rpc 异常了也行调用回调 或者 返回结果中有异常时按需也可调用 可以参考框架的 filter自己实现 在 rpcexception的时候也调用ConsumerFallback 。
全局降级 这个可以配置内置的断路器配合使用 添加对应的DegradeRule即可
@liufeiyu1002 感谢大佬指点😊。实现方式:全局异常捕获BlockException ,其中ConsumerFallack中异常继承于RuntimeException,发生限流时抛出异常为:RuntimeException:SentinelBlockException: FlowException,所以还需对RuntimeException做判断:
消费者DubboAdapterConfig调整
@Configuration
public class DubboAdapterConfig {
@Bean
private static void registerFallback() {
DubboFallbackRegistry.setConsumerFallback(new DubboFallback() {
@Override
public Result handle(Invoker<?> invoker, Invocation invocation, BlockException ex) {
//AsyncRpcResult
CompletableFuture<AppResponse> cf = new CompletableFuture<>();
AsyncRpcResult asyncRpcResult = new AsyncRpcResult(cf, invocation);
asyncRpcResult.setException(ex);
return asyncRpcResult;
}
});
}
}
消费者全局异常捕获 GlobalExceptionHandler
@RestControllerAdvice
public class GlobalExceptionHandler {
private static final Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);
/**
* 限流器异常捕获
*
* @param e BlockException
* @return
*/
@ExceptionHandler(value = {BlockException.class})
public JSONResult handler(BlockException e) {
return getBlockExceptionInfo(e);
}
/**
* dubbo远程调用
*
* @param e RpcException
* @return
*/
@ExceptionHandler(value = {RpcException.class})
public JSONResult handler(RpcException e) {
logger.error("远程调用失败,{}", e.getMessage());
return JSONResult.failure(e.getCode(), e.getMessage());
}
/**
* 运行时异常
*
* @param e RuntimeException
* @return
*/
@ExceptionHandler(value = RuntimeException.class)
public JSONResult handler(RuntimeException e) {
if (e.getCause() instanceof BlockException) {
BlockException bk = (BlockException) e.getCause();
return getBlockExceptionInfo(bk);
}
return JSONResult.failure(e.getMessage());
}
private JSONResult getBlockExceptionInfo(BlockException e) {
String msg = "";
if (e instanceof FlowException) {
msg = "请求限流";
} else if (e instanceof ParamFlowException) {
msg = "请求热点参数限流";
} else if (e instanceof DegradeException) {
msg = "请求降级";
} else if (e instanceof AuthorityException) {
msg = "无访问权限";
} else {
msg = "捕获异常失败";
}
logger.warn(e.getRuleLimitApp()+ "," + msg);
return JSONResult.failure(msg);
}
}