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

一、背景

我们在自己的业务系统中,通常会用到自定义的业务异常类,这个异常会继承extends RuntimeException,当发生业务限制的时候,会throw出来。但是在使用dubbo进行soa治理的时候,会发现provider抛出的异常,在custom端并不能正确的捕获。即便我们在provider和custom都有导入相同framework.jar下面的BusinessException异常,并且抛出这个异常。下面是出错情况

(1)、provider代码

@Service
public class UserServiceImpl implements UserService{
	@Autowired
	private GeneralDAO dao;

	/**
	 * 登录验证
	 *
	 */
	@Override
	public void loginValid(UserVO userVO) {
		if (dao.queryObject("userMap.getUserList",userVO)==null)
			throw new BusinessException("账户或者密码错误1...");
	}
}
(2)、控制台错误

11:39:19.041 [http-nio-8002-exec-9] ERROR com.chentian610.framework.BaseController - Failed to invoke the method loginValid in the service com.chentian610.user.service.UserService. Tried 3 times of the providers [192.168.3.243:20880] (1/1) from the registry 127.0.0.1:2181 on the consumer 192.168.3.243 using the dubbo version 2.8.4a. Last error is: Invoke remote method timeout. method: loginValid, provider: dubbo://192.168.3.243:20880/com.chentian610.user.service.UserService?anyhost=true&application=web-custom&check=false&dubbo=2.8.4a&generic=false&interface=com.chentian610.user.service.UserService&logger=slf4j&methods=loginValid&owner=chentian610&pid=8732&side=consumer×tamp=1481858649300, cause: Waiting server-side response timeout. start time: 2016-12-16 11:39:18.039, end time: 2016-12-16 11:39:19.040, client elapsed: 0 ms, server elapsed: 1001 ms, timeout: 1000 ms, request: Request [id=9, version=2.0.0, twoway=true, event=false, broken=false, data=RpcInvocation [methodName=loginValid, parameterTypes=[class com.chentian610.user.vo.UserVO], arguments=[com.chentian610.user.vo.UserVO@20705b10], attachments={path=com.chentian610.user.service.UserService, interface=com.chentian610.user.service.UserService, version=0.0.0}]], channel: /192.168.3.243:13378 -> /192.168.3.243:20880
com.alibaba.dubbo.rpc.RpcException: Failed to invoke the method loginValid in the service com.chentian610.user.service.UserService. Tried 3 times of the providers [192.168.3.243:20880] (1/1) from the registry 127.0.0.1:2181 on the consumer 192.168.3.243 using the dubbo version 2.8.4a. Last error is: Invoke remote method timeout. method: loginValid, provider: dubbo://192.168.3.243:20880/com.chentian610.user.service.UserService?anyhost=true&application=web-custom&check=false&dubbo=2.8.4a&generic=false&interface=com.chentian610.user.service.UserService&logger=slf4j&methods=loginValid&owner=chentian610&pid=8732&side=consumer×tamp=1481858649300, cause: Waiting server-side response timeout. start time: 2016-12-16 11:39:18.039, end time: 2016-12-16 11:39:19.040, client elapsed: 0 ms, server elapsed: 1001 ms, timeout: 1000 ms, request: Request [id=9, version=2.0.0, twoway=true, event=false, broken=false, data=RpcInvocation [methodName=loginValid, parameterTypes=[class com.chentian610.user.vo.UserVO], arguments=[com.chentian610.user.vo.UserVO@20705b10], attachments={path=com.chentian610.user.service.UserService, interface=com.chentian610.user.service.UserService, version=0.0.0}]], channel: /192.168.3.243:13378 -> /192.168.3.243:20880
	at com.alibaba.dubbo.rpc.cluster.support.FailoverClusterInvoker.doInvoke(FailoverClusterInvoker.java:108) ~[dubbo-2.8.4a.jar:2.8.4a]
	at com.alibaba.dubbo.rpc.cluster.support.AbstractClusterInvoker.invoke(AbstractClusterInvoker.java:227) ~[dubbo-2.8.4a.jar:2.8.4a]
	at com.alibaba.dubbo.rpc.cluster.support.wrapper.MockClusterInvoker.invoke(MockClusterInvoker.java:72) ~[dubbo-2.8.4a.jar:2.8.4a]
	at com.alibaba.dubbo.rpc.proxy.InvokerInvocationHandler.invoke(InvokerInvocationHandler.java:52) ~[dubbo-2.8.4a.jar:2.8.4a]
	at com.alibaba.dubbo.common.bytecode.proxy0.loginValid(proxy0.java) ~[dubbo-2.8.4a.jar:2.8.4a]
(3)、页面直接报错提示

关于dubbo的提供者(provider)和消费者(custom)异常捕获的问题_第1张图片
二、我们查看dubbo中异常处理代码

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;  
               }  
           } 
发现,当异常类和接口类在一个jar下面的时候,会使用自定义的异常类抛出,否则dubbo会自定义封装。

三、解决方案

那么就好办了,既然不想provider和custom有耦合,只需要在接口类中再定义自己的模块异常类来集成公共的BusinessException,比如UserException,

(1)、自定义业务异常类,继承公共业务异常类

/**
 * 用户模块自定义异常,防止消费端捕获不到异常
 * Created by chentian610 on 2016-12-16 .
 */
public class UserExcepiton extends BusinessException {
    public UserExcepiton(String message) {
        super(message);
    }
}
(2)、业务代码中抛出异常改成自定义异常

@Service
public class UserServiceImpl implements UserService{
	@Autowired
	private GeneralDAO dao;

	/**
	 * 登录验证
	 *
	 */
	@Override
	public void loginValid(UserVO userVO) {
		if (dao.queryObject("userMap.getUserList",userVO)==null)
			throw new UserExcepiton("账户或者密码错误,处理后...");
	}
}
(3)、这个时候登录返回的就是正常的JSON提示了

五、最后,这个是项目的结构图

关于dubbo的提供者(provider)和消费者(custom)异常捕获的问题_第2张图片




你可能感兴趣的:(关于dubbo的提供者(provider)和消费者(custom)异常捕获的问题)