dubbo异常处理
dubbo異常處理
我們的項(xiàng)目使用了dubbo進(jìn)行不同系統(tǒng)之間的調(diào)用。
每個(gè)項(xiàng)目都有一個(gè)全局的異常處理,對(duì)于業(yè)務(wù)異常,我們會(huì)拋出自定義的業(yè)務(wù)異常(繼承RuntimeException)。
全局的異常處理會(huì)根據(jù)不同的異常類型進(jìn)行不同的處理。
最近我們發(fā)現(xiàn),某個(gè)系統(tǒng)調(diào)用dubbo請(qǐng)求,provider端(服務(wù)提供方)拋出了自定義的業(yè)務(wù)異常,但consumer端(服務(wù)消費(fèi)方)拿到的并不是自定義的業(yè)務(wù)異常。
這是為什么呢?還需要從dubbo的ExceptionFilter說起
我們來看看dubbo官方文檔的推薦處理方式是什么
建議使用異常匯報(bào)錯(cuò)誤,而不是返回錯(cuò)誤碼,異常信息能攜帶更多信息,并且語(yǔ)義更友好。如果擔(dān)心性能問題,在必要時(shí),可以通過 override 掉異常類的 fillInStackTrace() 方法為空方法,使其不拷貝棧信息。查詢方法不建議拋出 checked 異常,否則調(diào)用方在查詢時(shí)將過多的 try...catch,并且不能進(jìn)行有效處理。服務(wù)提供方不應(yīng)將 DAO 或 SQL 等異常拋給消費(fèi)方,應(yīng)在服務(wù)實(shí)現(xiàn)中對(duì)消費(fèi)方不關(guān)心的異常進(jìn)行包裝,否則可能出現(xiàn)消費(fèi)方無法反序列化相應(yīng)異常。dubbo在代碼中的處理方式是什么?
/** Copyright 1999-2011 Alibaba Group.* * Licensed under the Apache License, Version 2.0 (the "License");* you may not use this file except in compliance with the License.* You may obtain a copy of the License at* * http://www.apache.org/licenses/LICENSE-2.0* * Unless required by applicable law or agreed to in writing, software* distributed under the License is distributed on an "AS IS" BASIS,* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.* See the License for the specific language governing permissions and* limitations under the License.*/ package com.alibaba.dubbo.rpc.filter;import java.lang.reflect.Method;import com.alibaba.dubbo.common.Constants; import com.alibaba.dubbo.common.extension.Activate; import com.alibaba.dubbo.common.logger.Logger; import com.alibaba.dubbo.common.logger.LoggerFactory; import com.alibaba.dubbo.common.utils.ReflectUtils; import com.alibaba.dubbo.common.utils.StringUtils; import com.alibaba.dubbo.rpc.Filter; import com.alibaba.dubbo.rpc.Invocation; import com.alibaba.dubbo.rpc.Invoker; import com.alibaba.dubbo.rpc.Result; import com.alibaba.dubbo.rpc.RpcContext; import com.alibaba.dubbo.rpc.RpcException; import com.alibaba.dubbo.rpc.RpcResult; import com.alibaba.dubbo.rpc.service.GenericService;/*** ExceptionInvokerFilter* <p>* 功能:* <ol>* <li>不期望的異常打ERROR日志(Provider端)<br>* 不期望的日志即是,沒有的接口上聲明的Unchecked異常。* <li>異常不在API包中,則Wrap一層RuntimeException。<br>* RPC對(duì)于第一層異常會(huì)直接序列化傳輸(Cause異常會(huì)String化),避免異常在Client出不能反序列化問題。* </ol>* * @author william.liangf* @author ding.lid*/ @Activate(group = Constants.PROVIDER) public class ExceptionFilter implements Filter {private final Logger logger;public ExceptionFilter() {this(LoggerFactory.getLogger(ExceptionFilter.class));}public ExceptionFilter(Logger logger) {this.logger = logger;}public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {try {Result result = invoker.invoke(invocation);if (result.hasException() && GenericService.class != invoker.getInterface()) {try {Throwable exception = result.getException();// 如果是checked異常,直接拋出if (! (exception instanceof RuntimeException) && (exception instanceof Exception)) {return result;}// 在方法簽名上有聲明,直接拋出try {Method method = invoker.getInterface().getMethod(invocation.getMethodName(), invocation.getParameterTypes());Class<?>[] exceptionClassses = method.getExceptionTypes();for (Class<?> exceptionClass : exceptionClassses) {if (exception.getClass().equals(exceptionClass)) {return result;}}} catch (NoSuchMethodException e) {return result;}// 未在方法簽名上定義的異常,在服務(wù)器端打印ERROR日志logger.error("Got unchecked and undeclared exception which called by " + RpcContext.getContext().getRemoteHost()+ ". service: " + invoker.getInterface().getName() + ", method: " + invocation.getMethodName()+ ", exception: " + exception.getClass().getName() + ": " + exception.getMessage(), exception);// 異常類和接口類在同一jar包里,直接拋出String serviceFile = ReflectUtils.getCodeBase(invoker.getInterface());String exceptionFile = ReflectUtils.getCodeBase(exception.getClass());if (serviceFile == null || exceptionFile == null || serviceFile.equals(exceptionFile)){return result;}// 是JDK自帶的異常,直接拋出String className = exception.getClass().getName();if (className.startsWith("java.") || className.startsWith("javax.")) {return result;}// 是Dubbo本身的異常,直接拋出if (exception instanceof RpcException) {return result;}// 否則,包裝成RuntimeException拋給客戶端return new RpcResult(new RuntimeException(StringUtils.toString(exception)));} catch (Throwable e) {logger.warn("Fail to ExceptionFilter when called by " + RpcContext.getContext().getRemoteHost()+ ". service: " + invoker.getInterface().getName() + ", method: " + invocation.getMethodName()+ ", exception: " + e.getClass().getName() + ": " + e.getMessage(), e);return result;}}return result;} catch (RuntimeException e) {logger.error("Got unchecked and undeclared exception which called by " + RpcContext.getContext().getRemoteHost()+ ". service: " + invoker.getInterface().getName() + ", method: " + invocation.getMethodName()+ ", exception: " + e.getClass().getName() + ": " + e.getMessage(), e);throw e;}}}從上面我們可以看出,dubbo的處理方式主要是:
接下來我們來測(cè)試一下
我們自定義一個(gè)SelfException
public class SelfException extends RuntimeException {public SelfException() {}public SelfException(String message, Throwable cause) {super(message, cause);}public SelfException(String message) {super(message);}public SelfException(Throwable cause) {super(cause);} }在服務(wù)端中拋出一個(gè)自定義的異常
public class DemoServiceImpl implements DemoService {@Overridepublic String sayHello(String name) {System.out.println("[" + new SimpleDateFormat("HH:mm:ss").format(new Date()) + "] Hello " + name + ", request from consumer: " + RpcContext.getContext().getRemoteAddress());try {JSONObject.parse("aaa");}catch (Exception e){throw new SelfException(e);}return "Hello " + name + ", response from provider: " + RpcContext.getContext().getLocalAddress();}}啟動(dòng)服務(wù)對(duì)并對(duì)服務(wù)端進(jìn)行訪問,我們可以發(fā)現(xiàn)
服務(wù)端的日志
Caused by: com.alibaba.fastjson.JSONException: syntax error, pos 1, json : aaaat com.alibaba.fastjson.parser.DefaultJSONParser.parse(DefaultJSONParser.java:1436)at com.alibaba.fastjson.parser.DefaultJSONParser.parse(DefaultJSONParser.java:1322)at com.alibaba.fastjson.JSON.parse(JSON.java:152)at com.alibaba.fastjson.JSON.parse(JSON.java:162)at com.alibaba.fastjson.JSON.parse(JSON.java:131)at org.apache.dubbo.demo.provider.DemoServiceImpl.sayHello(DemoServiceImpl.java:34)... 29 more客戶端的日志
java.lang.RuntimeException: org.apache.dubbo.demo.provider.SelfException: com.alibaba.fastjson.JSONException: syntax error, pos 1, json : aaa org.apache.dubbo.demo.provider.SelfException: com.alibaba.fastjson.JSONException: syntax error, pos 1, json : aaaat org.apache.dubbo.demo.provider.DemoServiceImpl.sayHello(DemoServiceImpl.java:36)at org.apache.dubbo.common.bytecode.Wrapper1.invokeMethod(Wrapper1.java)at org.apache.dubbo.rpc.proxy.javassist.JavassistProxyFactory$1.doInvoke(JavassistProxyFactory.java:47)at org.apache.dubbo.rpc.proxy.AbstractProxyInvoker.invoke(AbstractProxyInvoker.java:85)at org.apache.dubbo.config.invoker.DelegateProviderMetaDataInvoker.invoke(DelegateProviderMetaDataInvoker.java:52)at org.apache.dubbo.rpc.protocol.InvokerWrapper.invoke(InvokerWrapper.java:56)at org.apache.dubbo.rpc.filter.ExceptionFilter.invoke(ExceptionFilter.java:57)at org.apache.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:72)at org.apache.dubbo.monitor.support.MonitorFilter.invoke(MonitorFilter.java:75)at org.apache.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:72)at org.apache.dubbo.rpc.filter.TimeoutFilter.invoke(TimeoutFilter.java:42)at org.apache.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:72)at org.apache.dubbo.rpc.protocol.dubbo.filter.TraceFilter.invoke(TraceFilter.java:79)at org.apache.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:72)at org.apache.dubbo.rpc.filter.ContextFilter.invoke(ContextFilter.java:77)at org.apache.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:72)at org.apache.dubbo.rpc.filter.GenericFilter.invoke(GenericFilter.java:138)at org.apache.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:72)at org.apache.dubbo.rpc.filter.ClassLoaderFilter.invoke(ClassLoaderFilter.java:38)at org.apache.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:72)at org.apache.dubbo.rpc.filter.EchoFilter.invoke(EchoFilter.java:39)at org.apache.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:72)at org.apache.dubbo.rpc.protocol.dubbo.DubboProtocol$1.reply(DubboProtocol.java:114)at org.apache.dubbo.remoting.exchange.support.header.HeaderExchangeHandler.handleRequest(HeaderExchangeHandler.java:103)at org.apache.dubbo.remoting.exchange.support.header.HeaderExchangeHandler.received(HeaderExchangeHandler.java:200)at org.apache.dubbo.remoting.transport.DecodeHandler.received(DecodeHandler.java:51)at org.apache.dubbo.remoting.transport.dispatcher.ChannelEventRunnable.run(ChannelEventRunnable.java:57)at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)at java.lang.Thread.run(Thread.java:745)可以看出, 我們這里并沒有拋出自定義的異常, 從而會(huì)導(dǎo)致客戶端沒有捕捉到自定義的異常.
如何正確捕獲業(yè)務(wù)異常
拋出一個(gè)自定義異常有這么麻煩嗎? 主要原因是dubbo沒有支持的原因.
既然這樣,我們把dubbo變的支持不就可以了?
是的.把源碼改一下就OK了.如下:
String className = exception.getClass().getName(); if (className.startsWith("java.") || className.startsWith("javax.")) {return result; } // directly throw if it's dubbo exception if (exception instanceof RpcException) {return result; } //在這里添加我自己定義的異常類 if (exception instanceof SelfException) { return result; }或者直接將117行的RuntimeException替換成自己的自定義異常!這樣就從根本上解決了異常處理的問題.后續(xù)有其他問題,也可以直接修改.
轉(zhuǎn)載于:https://www.cnblogs.com/luozhiyun/p/10251585.html
總結(jié)
- 上一篇: kbmMW功能 - kbmMWProce
- 下一篇: 过滤器和标签