版本
spring boot: 3.4.1
dubbo:3.3.1
java17
1,过滤器代码
import jakarta.validation.ValidationException;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.common.constants.CommonConstants;
import org.apache.dubbo.common.extension.Activate;
import org.apache.dubbo.rpc.*;
import org.springframework.validation.BindException;
/**
* dubbo统一异常处理过滤器
*/
@Slf4j
@Activate(group = {CommonConstants.PROVIDER}, order = 1)
public class ExceptionFilter implements Filter, Filter.Listener {
@Override
public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
Result appResponse;
try {
appResponse = invoker.invoke(invocation);
} catch (Throwable e) {
appResponse = AsyncRpcResult.newDefaultAsyncResult(e, invocation);
onResponse(appResponse, invoker, invocation);
}
return appResponse;
}
@Override
public void onResponse(Result appResponse, Invoker<?> invoker, Invocation invocation) {
Throwable exception = appResponse.getException();
if (exception != null) {
log.info("Dubbo统一异常处理过滤器");
// 取得底层真实异常
// 异步线程异常 是包装过的
// 在dubbo自带ExceptionFilter 包装过某些异常
while (exception != null) {
if (exception.getCause() != null) {
exception = exception.getCause();
} else {
break;
}
}
appResponse.setException(null);
if(exception instanceof ValidationException ve) {
// 参数校验异常
appResponse.setValue(GlobalExceptionHandler.getValidExceptionMsg(ve));
} else if (exception instanceof BindException e) {
// 参数校验异常
appResponse.setValue(GlobalExceptionHandler.getBindExceptionMsg(e));
} else if (exception instanceof BizException e) {
// 业务自定义异常
appResponse.setValue(e.getRpcResult());
} else if (exception instanceof Exception e) {
appResponse.setValue(GlobalExceptionHandler.exceptionHandler(e));
}
}
}
@Override
public void onError(Throwable t, Invoker<?> invoker, Invocation invocation) {
}
}
说明:
- 1,@Activate(group = {CommonConstants.PROVIDER}, order = 1) 这个一定要有,是激活该过滤器用的。配置文件里是加载。order 尽量小一点,越小的越靠前执行
- 2,业务逻辑不要写在invoke方法里,要写在onResponse方法里,因为onResponse是dubbo异步调用的回调方法
- 3,如果你想返回错误信息就appResponse.setException(null);appResponse.setValue(错误信息)
- 4,如果你想继续抛出异常就appResponse.setException(你的异常)
2,配置文件
dubbo:
protocol:
name: tri
port: 10003
provider:
export: true
filter:
- "loginAuthFilter,exceptionFilter"
#- "-exception"
validation: true
consumer:
#关闭服务检查 如果依赖的服务挂了 不影响调用
check: false
timeout: 300
# 负载均衡策略 默认random(加权随机) 修改成轮询
loadbalance: roundrobin
# 重试次数 根据服务provider端服务器数量决定 用于查询场景
# 事务场景要设置为0
retries: 0
validation: true
# 集群容错模式 立即失败 用于事务模式
cluster: failfast
说明
- 1,filter配置,这里一定要写 - "loginAuthFilter,exceptionFilter" 说明我加载了两个自定义的过滤器
- "-exception" 过滤器名前面带个【-】是关闭这个过滤器
- 2,dubbo自带的异常过滤器不要关掉,它会帮你做一些事情
3,org.apache.dubbo.rpc.Filter 文件
位置
src
|-main
|-java
|-com
|-xxx
|-XxxFilter.java (实现Filter接口)
|-resources
|-META-INF
|-dubbo
|-org.apache.dubbo.rpc.Filter (纯文本文件,内容为:xxx=com.xxx.XxxFilter)
内容
loginAuthFilter=com.zx.frame.filter.LoginAuthFilter
exceptionFilter=com.zx.frame.filter.ExceptionFilter
至此,自定义过滤器完成
4,问题与注意点
- 不能在onResponse方法里打断点,一打断点就会出现死循环,不知道什么原因
解决办法(虽然不知道为什么)
是因为dubbo 在3.1.6 开始,开启一个序列化检查模式,这个检查模式认定EncodeException 为非法的序列化类,所以报错
但为什么会死循环还没没有搞清楚。明明EncodeException 是dubbo自带的异常,也不是我定义的,不知道为什么就非法了。
具体请参照官方文档(有解决方案):https://cn.dubbo.apache.org/zh-cn/overview/mannual/java-sdk/tasks/security/class-check/
和 https://cn.dubbo.apache.org/zh-cn/overview/mannual/java-sdk/reference-manual/serialization/serialization/
我采用的是比较极端的解决方案:关掉这个检查
dubbo.application.serialize-check-status=DISABLE
- validation 一点要provider端与consumer端都开启,可以提升性能减少麻烦