Java Dubbo泛化调用之自定义异常处理

Dubbo 肯定是家常便饭使用了,最近因为项目需要,对一个特定的异常,进行相关处理,并且这个异常不是checked异常,所以就没有在方法上加上异常签名,想通过消费方来直接进行异常捕获。

TODO 接下来是遇到的坑

  • 泛化调用和非泛化调用的区别
  • Dubbo调用Filter责任链
  • 异常包装

泛化调用和非泛化调用在provider上的区别提现,GenericFilter 实现?

 

问题:Java Dubbo 泛化调用不会返回自定义异常

解决:

自定义两个Filter:

DubboExceptionFilter:替换原生dubbo的Exception,抛出自定义异常,用于常规调用

@Activate(group={ Constants.PROVIDER})
public class DubboExceptionFilter implements Filter {
    private static final Logger LOGGER = LoggerFactory.getLogger(DubboExceptionFilter.class);

    @Override
    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();

                    // directly throw if it's checked exception
                    if (!(exception instanceof RuntimeException) && (exception instanceof Exception)) {
                        return result;
                    }
                    // directly throw if the exception appears in the signature
                    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;
                    }
                    // for the exception not found in method's signature, print ERROR message in server's log.
                    // 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);
                    // directly throw if exception class and interface class are in the same jar file.
                    String serviceFile = ReflectUtils.getCodeBase(invoker.getInterface());
                    String exceptionFile = ReflectUtils.getCodeBase(exception.getClass());
                    if (serviceFile == null || exceptionFile == null || serviceFile.equals(exceptionFile)) {
                        return result;
                    }
                    // directly throw if it's JDK exception
                    String className = exception.getClass().getName();
                    if (className.startsWith("java.") || className.startsWith("javax.")) {
                        return result;
                    }
                    // 修改为自定义异常
                    if (className.startsWith("XXXX") || exception instanceof CustomException) {
                        return result;
                    }
                    // directly throw if it's dubbo exception
                    if (exception instanceof RpcException) {
                        return result;
                    }

                    // otherwise, wrap with RuntimeException and throw back to the client
                    return new RpcResult(new RuntimeException(StringUtils.toString(exception)));
                } catch (Throwable e) {
                    return result;
                }
            } else {
                return result;
            }
        } catch (Exception e) {
            throw e;
        }

    }
}

CustomGenericFilter:替换原生GenericFilter,用于泛化调用


/**
 * 这里order的源码是-20000,因为我们要执行在原逻辑之前
 * 因此我们这里改成了-25000
 **/
@Activate(group = Constants.PROVIDER, order = -25000)
public class CustomGenericFilter implements Filter {

    @Override
    public Result invoke(Invoker invoker, Invocation inv) throws RpcException {
        if (inv.getMethodName().equals(Constants.$INVOKE)
                && inv.getArguments() != null
                && inv.getArguments().length == 3
                && !invoker.getUrl().getParameter(Constants.GENERIC_KEY, false)) {
            String name = ((String) inv.getArguments()[0]).trim();
            String[] types = (String[]) inv.getArguments()[1];
            Object[] args = (Object[]) inv.getArguments()[2];
            try {
                Method method = ReflectUtils.findMethodByMethodSignature(invoker.getInterface(), name, types);
                Class[] params = method.getParameterTypes();
                if (args == null) {
                    args = new Object[params.length];
                }
                String generic = inv.getAttachment(Constants.GENERIC_KEY);
                if (StringUtils.isEmpty(generic)
                        || ProtocolUtils.isDefaultGenericSerialization(generic)) {
                    args = PojoUtils.realize(args, params, method.getGenericParameterTypes());
                } else if (ProtocolUtils.isJavaGenericSerialization(generic)) {
                    for (int i = 0; i < args.length; i++) {
                        if (byte[].class == args[i].getClass()) {
                            try {
                                UnsafeByteArrayInputStream is = new UnsafeByteArrayInputStream((byte[]) args[i]);
                                args[i] = ExtensionLoader.getExtensionLoader(Serialization.class)
                                        .getExtension(Constants.GENERIC_SERIALIZATION_NATIVE_JAVA)
                                        .deserialize(null, is).readObject();
                            } catch (Exception e) {
                                throw new RpcException("Deserialize argument [" + (i + 1) + "] failed.", e);
                            }
                        } else {
                            throw new RpcException(
                                    new StringBuilder(32).append("Generic serialization [")
                                            .append(Constants.GENERIC_SERIALIZATION_NATIVE_JAVA)
                                            .append("] only support message type ")
                                            .append(byte[].class)
                                            .append(" and your message type is ")
                                            .append(args[i].getClass()).toString());
                        }
                    }
                }
                Result result = invoker.invoke(new RpcInvocation(method, args, inv.getAttachments()));
                if (result.hasException()
                        && !(result.getException() instanceof GenericException)) {
                    /*********************和源码不一样的地方**********************/
                    if (result.getException() instanceof IllegalImplementsException) {
                        return new RpcResult(result.getException());
                    } else {
                        return new RpcResult(new GenericException(result.getException()));
                    }
                    /*********************和源码不一样的地方_结束**********************/
                }
                if (ProtocolUtils.isJavaGenericSerialization(generic)) {
                    try {
                        UnsafeByteArrayOutputStream os = new UnsafeByteArrayOutputStream(512);
                        ExtensionLoader.getExtensionLoader(Serialization.class)
                                .getExtension(Constants.GENERIC_SERIALIZATION_NATIVE_JAVA)
                                .serialize(null, os).writeObject(result.getValue());
                        return new RpcResult(os.toByteArray());
                    } catch (IOException e) {
                        throw new RpcException("Serialize result failed.", e);
                    }
                } else {
                    return new RpcResult(PojoUtils.generalize(result.getValue()));
                }
            } catch (NoSuchMethodException e) {
                throw new RpcException(e.getMessage(), e);
            } catch (ClassNotFoundException e) {
                throw new RpcException(e.getMessage(), e);
            }
        }
        return invoker.invoke(inv);
    }

}

添加SPI引入文件

META-INF/dubbo/com.alibaba.dubbo.rpc.Filter

dubboExceptionFilter=xxx.DubboExceptionFilter // 完整包
customGenericFilter=xxx.CustomGenericFilter // 完整包

添加dubbo配置

重点!!!Attention!!

filter中逗号隔开,代表filter责任链执行顺序,如上执行顺序为:customGenericFilter,原生Filter,dubboExceptionFilter,并去除exception(dubbo自带ExcptionFilter)

It's Over 

现在你可以在消费方,使用泛化调用并且捕获自定义异常了。类似
 

try {
    Object ret = genericService.$invoke(method, new String[]{Req.class.getCanonicalName()}, new Object[]{BeanUtil.javabean2map(req)});
    logger.info("泛化调用Dubbo服务返回:{}", ret);
    return SUCCESS(ret);
} catch (CustomException e) {
    return ERROR(e);
} catch (Exception e) {
    return ERROR(e);
}

 

感谢:
泛化调用GenericService自定义Exception变成GenericException的问题

Filter链调用顺序

dubbo filter 扩展 以及 重写 dubbo ExceptionFilter

你可能感兴趣的:(java,dubbo,java,dubbo,exception,filter)