版本
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統(tǒng)一異常處理過濾器
*/
@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統(tǒng)一異常處理過濾器");
// 取得底層真實異常
// 異步線程異常 是包裝過的
// 在dubbo自帶ExceptionFilter 包裝過某些異常
while (exception != null) {
if (exception.getCause() != null) {
exception = exception.getCause();
} else {
break;
}
}
appResponse.setException(null);
if(exception instanceof ValidationException ve) {
// 參數(shù)校驗異常
appResponse.setValue(GlobalExceptionHandler.getValidExceptionMsg(ve));
} else if (exception instanceof BindException e) {
// 參數(shù)校驗異常
appResponse.setValue(GlobalExceptionHandler.getBindExceptionMsg(e));
} else if (exception instanceof BizException e) {
// 業(yè)務(wù)自定義異常
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 盡量小一點(diǎn),越小的越靠前執(zhí)行
- 2怔毛,業(yè)務(wù)邏輯不要寫在invoke方法里员萍,要寫在onResponse方法里,因為onResponse是dubbo異步調(diào)用的回調(diào)方法
- 3拣度,如果你想返回錯誤信息就appResponse.setException(null);appResponse.setValue(錯誤信息)
- 4碎绎,如果你想繼續(xù)拋出異常就appResponse.setException(你的異常)
2,配置文件
dubbo:
protocol:
name: tri
port: 10003
provider:
export: true
filter:
- "loginAuthFilter,exceptionFilter"
#- "-exception"
validation: true
consumer:
#關(guān)閉服務(wù)檢查 如果依賴的服務(wù)掛了 不影響調(diào)用
check: false
timeout: 300
# 負(fù)載均衡策略 默認(rèn)random(加權(quán)隨機(jī)) 修改成輪詢
loadbalance: roundrobin
# 重試次數(shù) 根據(jù)服務(wù)provider端服務(wù)器數(shù)量決定 用于查詢場景
# 事務(wù)場景要設(shè)置為0
retries: 0
validation: true
# 集群容錯模式 立即失敗 用于事務(wù)模式
cluster: failfast
說明
- 1抗果,filter配置筋帖,這里一定要寫 - "loginAuthFilter,exceptionFilter" 說明我加載了兩個自定義的過濾器
- "-exception" 過濾器名前面帶個【-】是關(guān)閉這個過濾器
- 2,dubbo自帶的異常過濾器不要關(guān)掉冤馏,它會幫你做一些事情
3日麸,org.apache.dubbo.rpc.Filter 文件
位置
src
|-main
|-java
|-com
|-xxx
|-XxxFilter.java (實現(xiàn)Filter接口)
|-resources
|-META-INF
|-dubbo
|-org.apache.dubbo.rpc.Filter (純文本文件,內(nèi)容為:xxx=com.xxx.XxxFilter)
內(nèi)容
loginAuthFilter=com.zx.frame.filter.LoginAuthFilter
exceptionFilter=com.zx.frame.filter.ExceptionFilter
至此逮光,自定義過濾器完成
4代箭,問題與注意點(diǎn)
- 不能在onResponse方法里打斷點(diǎn),一打斷點(diǎn)就會出現(xiàn)死循環(huán)涕刚,不知道什么原因
解決辦法(雖然不知道為什么)
是因為dubbo 在3.1.6 開始嗡综,開啟一個序列化檢查模式,這個檢查模式認(rèn)定EncodeException 為非法的序列化類杜漠,所以報錯
但為什么會死循環(huán)還沒沒有搞清楚极景。明明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/
我采用的是比較極端的解決方案:關(guān)掉這個檢查
dubbo.application.serialize-check-status=DISABLE
- validation 一點(diǎn)要provider端與consumer端都開啟,可以提升性能減少麻煩