Dubbo生产者抛出自定义异常的问题

原创 2016年08月29日 21:51:58

最近开始学习Dubbo框架,在工作中会把之前的业务迁移过来。

        在原来的Spring MVC框架实现中,有使用到自定义异常的场景(自定义异常继承RuntimeException)。而对于异常(包括自定义异常),在业务代码中都不做任何try-catch操作,而是由公用的Controller来处理异常。

        在使用dubbo的过程中,在dubbo的service端定义有自定义异常进行throw的时候,却发现在customer的Controller中无法instanceof,自己自定义的异常类被转成了Runtime异常,有点不理解。在翻看dubbo源码的时候,发现确实如此:

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;
        }
    }

 return new RpcResult(new RuntimeException(StringUtils.toString(exception)));
最后这一段是是把我们的自定义异常给重新封装了一下了,所以我们就捕获不到自定义异常了

官方提供的解决方法:

服务化最佳实践
(+) (#)

分包
建议将服务接口,服务模型,服务异常等均放在API包中,因为服务模型及异常也是API的一部分,
同时,这样做也符合分包原则:重用发布等价原则(REP),共同重用原则(CRP)
如果需要,也可以考虑在API包中放置一份spring的引用配置,这样使用方,只需在Spring加载过程中引用此配置即可,
配置建议放在模块的包目录下,以免冲突,如:com/alibaba/china/xxx/dubbo-reference.xml
粒度
服务接口尽可能大粒度,每个服务方法应代表一个功能,而不是某功能的一个步骤,否则将面临分布式事务问题,Dubbo暂未提供分布式事务支持。
服务接口建议以业务场景为单位划分,并对相近业务做抽象,防止接口数量爆炸
不建议使用过于抽象的通用接口,如:Map query(Map),这样的接口没有明确语义,会给后期维护带来不便。
版本
每个接口都应定义版本号,为后续不兼容升级提供可能,如:<dubbo:service interface="com.xxx.XxxService" version="1.0" />
建议使用两位版本号,因为第三位版本号通常表示兼容升级,只有不兼容时才需要变更服务版本。
当不兼容时,先升级一半提供者为新版本,再将消费者全部升为新版本,然后将剩下的一半提供者升为新版本。
兼容性
服务接口增加方法,或服务模型增加字段,可向后兼容,删除方法或删除字段,将不兼容,枚举类型新增字段也不兼容,需通过变更版本号升级。
各协议的兼容性不同,参见: 服务协议
枚举值
如果是完备集,可以用Enum,比如:ENABLE, DISABLE。
如果是业务种类,以后明显会有类型增加,不建议用Enum,可以用String代替。
如果是在返回值中用了Enum,并新增了Enum值,建议先升级服务消费方,这样服务提供方不会返回新值。
如果是在传入参数中用了Enum,并新增了Enum值,建议先升级服务提供方,这样服务消费方不会传入新值。
序列化
服务参数及返回值建议使用POJO对象,即通过set,get方法表示属性的对象。
服务参数及返回值不建议使用接口,因为数据模型抽象的意义不大,并且序列化需要接口实现类的元信息,并不能起到隐藏实现的意图。
服务参数及返回值都必需是byValue的,而不能是byRef的,消费方和提供方的参数或返回值引用并不是同一个,只是值相同,Dubbo不支持引用远程对象。
异常
建议使用异常汇报错误,而不是返回错误码,异常信息能携带更多信息,以及语义更友好,
如果担心性能问题,在必要时,可以通过override掉异常类的fillInStackTrace()方法为空方法,使其不拷贝栈信息,
查询方法不建议抛出checked异常,否则调用方在查询时将过多的try...catch,并且不能进行有效处理,
服务提供方不应将DAO或SQL等异常抛给消费方,应在服务实现中对消费方不关心的异常进行包装,否则可能出现消费方无法反序列化相应异常。
调用
不要只是因为是Dubbo调用,而把调用Try-Catch起来。Try-Catch应该加上合适的回滚边界上。
对于输入参数的校验逻辑在Provider端要有。如有性能上的考虑,服务实现者可以考虑在API包上加上服务Stub类来完成检验。

所以在dubbo的service端想抛出自定义异常,只能通过在service端的接口方法上声明所要抛出的异常,或者将异常类与接口同包,再或者是接口的实现类再实现dubbo的GenericService接口。

        对于第一种方案没有使用,因为它对代码的入侵比较严重。

        第二种方案可以实现,可对于目前的业务框架,让接口类和异常类同包则变得不太可能。

        所以最后选择了让接口实现类再实现GenericService接口,而对于其需要实现的$invoke方法则没有做任何的方法体处理,直接废弃。


        对于dubbo的service端自定义异常类的处理,有些不理解的就是,为什么dubbo需要对自定义异常类做一次Runtime异常的转化,而不是直接抛出原异常类型。或者有没有对dubbo更了解的朋友,有对自定义异常更好的处理方法。

发现实现这个GenericService接口不是很好用,最后还是每个接口声明所要跑出的异常了例如:

package com.test.test.manager.api;

import com.test.entity.Area;
import com.test.common.exception.BaseRuntimeException;

public interface AreaApiManager {

	Area selectByPrimaryKey1(Integer id) throws BaseRuntimeException;

}

package com.test.test.manager.impl.api;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;

import com.shihai.borrow.dao.api.AreaApiMapper;
import com.test.entity.Area;
import com.test.test.manager.api.AreaApiManager;
import com.test.common.exception.BaseRuntimeException;
@Transactional
public class AreaApiManagerImpl implements AreaApiManager {
	
	@Autowired
	AreaApiMapper areaApiMapper;

	@Override
	public Area selectByPrimaryKey1(Integer id) throws BaseRuntimeException {
		// TODO Auto-generated method stub
		return areaApiMapper.selectByPrimaryKey1(id);
	}

}
通过以上的封装在消费者端就能捕获到异常了

原文:http://my.oschina.net/boltwu/blog/493888?p={{page}}

相关文章推荐

dubbo实践1..异常处理

使用dubbo有几年了,最近开始在博客上分享自己的一些实际工作经验,今天就先说下dubbo的异常处理,请大家多加指正。           dubbo有自己的异常处理机制,当服务端抛出一个dubbo可...
  • wzl18
  • wzl18
  • 2016年04月26日 14:41
  • 4038

浅谈dubbo的ExceptionFilter异常处理

背景 我们的项目使用了dubbo进行不同系统之间的调用。 每个项目都有一个全局的异常处理,对于业务异常,我们会抛出自定义的业务异常(继承RuntimeException)。 全局的异常处理会根据不同的...
  • mj158518
  • mj158518
  • 2016年04月23日 22:47
  • 20658

Dubbo的正确打开方式之基本接口定义及异常处理

为什么要写这篇文章呢?因为公司在使用Dubbo时并没有对Dubbo的用法做深入的了解,而是属于拿来就用,随着自己的想法来使用。这样很不好,就像天龙八部的鸠摩智练习错误的六脉神剑一样,方式不对,就容易走...

关于RPC架构分布式框架dubbo框架对unchecked类型异常的处理

业务场景:consumer需要明确catch provider抛出的自定义异常。 dubbo内部提供ExceptionFilter来处理异常,源码中不难看出dubbo对unchecked异常进行了包...
  • yyit153
  • yyit153
  • 2016年10月02日 13:01
  • 564

使用Dubbo框架,异常处理原则

在Dubbo的Service实现中捕获异常,记录异常日志,将异常进行转移为自定义异常抛出,注意自定义异常需是RuntimeException,否则使用Spring事务时,不会出现回滚。 在dubbo服...

Dubbo抛出自定义异常

问题描述: 在使用Dubbo分布式框架开发时,自定义了异常,但是在测试时发现,该异常没有正常抛出!代码骨架如下: // Controller层 public Object apply(ModelMap...

基于spring aop的dubbo异常统一处理

dubbo统一异常处理,调用方只显示封装后的异常。 1、返回封装后的Exception 2、返回封装后的统一返回信息 import org.aspectj.lang.annotation.Aft...

Dubbo抛出自定义异常

最近开始学习Dubbo框架,在工作中会把之前的业务迁移过来。         在原来的Spring MVC框架实现中,有使用到自定义异常的场景(自定义异常继承RuntimeException)。而对于...

dubbo(四)异常处理

dubbo的异常处理网上描述的文章很多,复制黏贴也不在少数.在这里记录下自己的一些体会. 还是带着问题(目录)来写吧. 1.dubbo官方推荐的异常处理方式是什么? 2.dubbo处理异常的逻辑是...

关于dubbo的提供者(provider)和消费者(custom)异常捕获的问题

一、背景 我们在自己的业务系统中,通常会用到自定义的业务异常类,这个异常会继承extends RuntimeException,当发生业务限制的时候,会throw出来。但是在使用dubbo进行soa...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:Dubbo生产者抛出自定义异常的问题
举报原因:
原因补充:

(最多只允许输入30个字)