博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
关于dubbo的提供者(provider)和消费者(custom)异常捕获的问题
阅读量:2394 次
发布时间:2019-05-10

本文共 7282 字,大约阅读时间需要 24 分钟。

一、背景

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

(1)、provider代码

@Servicepublic 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:20880com.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中异常处理代码

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)、业务代码中抛出异常改成自定义异常

@Servicepublic 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提示了

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

你可能感兴趣的文章
What does “warning: not all control paths return a value” mean? (C++)
查看>>
C++ 运算符优先级
查看>>
Savitzky-Golay smoothing
查看>>
IDL get variable size in bytes
查看>>
high-frequency emphasis filter matlab
查看>>
cat -n
查看>>
使用 ftrace 调试 Linux 内核,第 2 部分
查看>>
使用 ftrace 调试 Linux 内核,第 3 部分
查看>>
内存储器管理概述、内存分配与释放、地址映射机制(mm_struct, vm_area_struct)、malloc/free 的实现
查看>>
glibc-printf
查看>>
Web--JavaWeb应用中文乱码问题原理及解决方法
查看>>
Servlet--HttpServletRequest获取请求信息(请求头、请求行、参数)详解
查看>>
Web--request解决表单参数的中文乱码问题(GET方式和POST方式)
查看>>
UML--类图详解
查看>>
Servlet--关于RequestDispatcher(forward、include)的原理
查看>>
Servlet--Cookie原理及API使用详解
查看>>
Servlet--Session原理及API的使用
查看>>
Servlet--三个作用域(Request、Session、ServletContext)总结
查看>>
Listener--监听器的分类、功能及API详解
查看>>
Listener--ServletContextListener接口的使用详解
查看>>