dubbo异常处理
dubbo异常处理
我们的项目使用了dubbo进行不同系统之间的调用。
每个项目都有一个全局的异常处理,对于业务异常,我们会抛出自定义的业务异常(继承RuntimeException)。
全局的异常处理会根据不同的异常类型进行不同的处理。
最近我们发现,某个系统调用dubbo请求,provider端(服务提供方)抛出了自定义的业务异常,但consumer端(服务消费方)拿到的并不是自定义的业务异常。
这是为什么呢?还需要从dubbo的ExceptionFilter说起
我们来看看dubbo官方文档的推荐处理方式是什么
建议使用异常汇报错误,而不是返回错误码,异常信息能携带更多信息,并且语义更友好。
如果担心性能问题,在必要时,可以通过 override 掉异常类的 fillInStackTrace() 方法为空方法,使其不拷贝栈信息。
查询方法不建议抛出 checked 异常,否则调用方在查询时将过多的 try...catch,并且不能进行有效处理。
服务提供方不应将 DAO 或 SQL 等异常抛给消费方,应在服务实现中对消费方不关心的异常进行包装,否则可能出现消费方无法反序列化相应异常。
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对于第一层异常会直接序列化传输(Cause异常会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;
}
// 未在方法签名上定义的异常,在服务器端打印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的处理方式主要是:
- 如果provider实现了GenericService接口,直接抛出
- 如果是checked异常,直接抛出
- 在方法签名上有声明,直接抛出
- 异常类和接口类在同一jar包里,直接抛出
- 是JDK自带的异常,直接抛出
- 是Dubbo本身的异常,直接抛出
- 否则,包装成RuntimeException抛给客户端
接下来我们来测试一下
我们自定义一个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);
}
}
在服务端中抛出一个自定义的异常
public class DemoServiceImpl implements DemoService {
@Override
public 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();
}
}
启动服务对并对服务端进行访问,我们可以发现
服务端的日志
Caused by: com.alibaba.fastjson.JSONException: syntax error, pos 1, json : aaa
at 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 : aaa
at 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)
可以看出, 我们这里并没有抛出自定义的异常, 从而会导致客户端没有捕捉到自定义的异常.
如何正确捕获业务异常
抛出一个自定义异常有这么麻烦吗? 主要原因是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替换成自己的自定义异常!这样就从根本上解决了异常处理的问题.后续有其他问题,也可以直接修改.
dubbo异常处理的更多相关文章
- Dubbo 自定义异常,你是怎么处理的?
前言 记录Dubbo对于自定义异常的处理方式. 实现目标 服务层异常,直接向上层抛出,web层统一捕获处理 如果是系统自定义异常,则返回{"code":xxx,"msg& ...
- Dubbo的异常处理
记一次Dubbo的异常处理过程. 现象:业务团队报送,服务端定义一个BuinessException,继承与RunTimeException,服务端执行时抛出该异常,但是客户端捕捉不到该异常. 记录: ...
- 浅谈dubbo的ExceptionFilter异常处理
背景 我们的项目使用了dubbo进行不同系统之间的调用. 每个项目都有一个全局的异常处理,对于业务异常,我们会抛出自定义的业务异常(继承RuntimeException). 全局的异常处理会根据不同的 ...
- dubbo超时重试和异常处理
dubbo超时重试和异常处理 dubbo超时重试和异常处理 参考: https://www.cnblogs.com/ASPNET2008/p/7292472.html https://www.tuic ...
- 异常处理_Maven多模块web项目整合ssm+dubbo
异常如下: [ERROR][org.springframework.web.context.ContextLoader.initWebApplicationContext(ContextLoader. ...
- 通过单元测试理解spring容器以及dubbo+zookeeper单元测试异常处理
一.先说一个结论:单元测试与主项目的spring容器是隔离的,也就是说,单元测试无法访问主项目spring容器,需要自己加载spring容器. 接下来是代码实例,WEB主项目出于运行状态,单元测试中可 ...
- dubbo could not get local host ip address will use 127.0.0.1 instead 异常处理
dubbo could not get local host ip address will use 127.0.0.1 instead 查看hostname localhost:spring wls ...
- 异构SOA系统架构之Asp.net实现(兼容dubbo)
我们公司技术部门情况比较复杂,分到多个集团,每个集团又可能分为几个部门,每个部门又可能分为多个小组,组织架构比较复杂,开发人员比较多. 使用的编程语言也有点复杂,主流语言有.net(C#).Java. ...
- Spring+Maven+Dubbo+MyBatis+Linner+Handlebars—Web开发环境搭建
本文主要分三部分,分别是:后台核心业务逻辑.桥梁辅助控制和前台显示页面. 本Web开发环境综合了多种工具,包括Maven包管理与编译工具.Dubbo分布式服务框架.MyBatis数据持久化工具.Lin ...
随机推荐
- docker无法启动
docker无法启动 docker启动时报错Error starting daemon: SELinux is not supported with the overlay2 graph driver ...
- Go - Map 集合
目录 概述 声明 Map 生成 JSON 编辑和删除 推荐阅读 概述 Map 集合是无序的 key-value 数据结构. Map 集合中的 key / value 可以是任意类型,但所有的 key ...
- C#中的委托和事件(下篇)
上次以鸿门宴的例子写了一篇博文,旨在帮助C#初学者迈过委托和事件这道坎,能够用最快的速度掌握如何使用它们.如果觉得意犹未尽,或者仍然不知如何在实际应用中使用它们,那么,这篇窗体篇,将在Winform场 ...
- 数字IC前后端设计中的时序收敛(一)前言
本文转自:自己的微信公众号<数字集成电路设计及EDA教程> 里面主要讲解数字IC前端.后端.DFT.低功耗设计以及验证等相关知识,并且讲解了其中用到的各种EDA工具的教程. 为了纪念,同时 ...
- Nginx+Lua+MySQL/Redis实现高性能动态网页展现
Nginx结合Lua脚本,直接绕过Tomcat应用服务器,连接MySQL/Redis直接获取数据,再结合Lua中Template组件,直接写入动态数据,渲染成页面,响应前端,一次请求响应过程结束.最终 ...
- Vue快速学习_第三节
过滤器 局部过滤器(组件内部使用的过滤器,跟django的很像, filters: {过滤器的名字: {function(val, a,b){}}} 全局过滤器(全局过滤器,只要过滤器一创建,在任何组 ...
- [原创]Zabbix3.4_API的python示例
说明: 1.python版本为:python2.7 2.zabbix版本为:zabbix3.4 3.通过python脚本调用zabbix的api接口可以实现批量增删改查主机的信息. 示例如下: #-* ...
- 【CYH-02】NOIp考砸后虐题赛:数学:题解
赛后放上.
- 描述符\get/set/delete,init/new/call,元类
6.23 自我总结 1.描述符__get__,__set__,__delete__ 描述符是什么:描述符本质就是一个新式类,在这个新式类中,至少实现了____get__(),__set__(),__d ...
- Android总结之打开手机相册获取图片
上一篇,总结了如何打开照相机获取图片,详情请看>>>> 这篇将总结如何打开手机存储(相册)来获取手机上的图片. 打开相册 在需要这个功能的类中,我们可以自定义一个方法openA ...