/* * 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; } } }
if (result.hasException() && GenericService.class != invoker.getInterface()) { //... } return result;调用结果有异常且未实现GenericService接口,进入后续判断逻辑,否则直接返回结果。
/** * 通用服务接口 * * @author william.liangf * @export */ public interface GenericService { /** * 泛化调用 * * @param method 方法名,如:findPerson,如果有重载方法,需带上参数列表,如:findPerson(java.lang.String) * @param parameterTypes 参数类型 * @param args 参数列表 * @return 返回值 * @throws Throwable 方法抛出的异常 */ Object $invoke(String method, String[] parameterTypes, Object[] args) throws GenericException; }泛接口实现方式主要用于服务器端没有API接口及模型类元的情况,参数及返回值中的所有POJO均用Map表示,通常用于框架集成,比如:实现一个通用的远程服务Mock框架,可通过实现GenericService接口处理所有服务请求。
// 如果是checked异常,直接抛出 if (! (exception instanceof RuntimeException) && (exception instanceof Exception)) { return result; }不是RuntimeException类型的异常,并且是受检异常(继承Exception),直接抛出。
try { decode(channel, inputStream); } catch (Throwable e) { if (log.isWarnEnabled()) { log.warn("Decode rpc result failed: " + e.getMessage(), e); } response.setStatus(Response.CLIENT_ERROR); response.setErrorMessage(StringUtils.toString(e)); } finally { hasDecoded = true; }
private Object returnFromResponse() throws RemotingException { Response res = response; if (res == null) { throw new IllegalStateException("response cannot be null"); } if (res.getStatus() == Response.OK) { return res.getResult(); } if (res.getStatus() == Response.CLIENT_TIMEOUT || res.getStatus() == Response.SERVER_TIMEOUT) { throw new TimeoutException(res.getStatus() == Response.SERVER_TIMEOUT, channel, res.getErrorMessage()); } throw new RemotingException(channel, res.getErrorMessage()); }
try { boolean isAsync = RpcUtils.isAsync(getUrl(), invocation); boolean isOneway = RpcUtils.isOneway(getUrl(), invocation); int timeout = getUrl().getMethodParameter(methodName, Constants.TIMEOUT_KEY,Constants.DEFAULT_TIMEOUT); if (isOneway) { boolean isSent = getUrl().getMethodParameter(methodName, Constants.SENT_KEY, false); currentClient.send(inv, isSent); RpcContext.getContext().setFuture(null); return new RpcResult(); } else if (isAsync) { ResponseFuture future = currentClient.request(inv, timeout) ; RpcContext.getContext().setFuture(new FutureAdapter<Object>(future)); return new RpcResult(); } else { RpcContext.getContext().setFuture(null); return (Result) currentClient.request(inv, timeout).get(); } } catch (TimeoutException e) { throw new RpcException(RpcException.TIMEOUT_EXCEPTION, "Invoke remote method timeout. method: " + invocation.getMethodName() + ", provider: " + getUrl() + ", cause: " + e.getMessage(), e); } catch (RemotingException e) { throw new RpcException(RpcException.NETWORK_EXCEPTION, "Failed to invoke remote method: " + invocation.getMethodName() + ", provider: " + getUrl() + ", cause: " + e.getMessage(), e); }
com.alibaba.dubbo.rpc.RpcException: Failed to invoke the method triggerCheckedException in the service com.xxx.api.DemoService. Tried 1 times of the providers [192.168.1.101:20880] (1/1) from the registry 127.0.0.1:2181 on the consumer 192.168.1.101 using the dubbo version 3.1.9. Last error is: Failed to invoke remote method: triggerCheckedException, provider: dubbo://192.168.1.101:20880/com.xxx.api.DemoService?xxx, cause: java.io.IOException: Response data error, expect Throwable, but get {cause=(this Map), detailMessage=null, suppressedExceptions=[], stackTrace=[Ljava.lang.StackTraceElement;@23b84919} java.io.IOException: Response data error, expect Throwable, but get {cause=(this Map), detailMessage=null, suppressedExceptions=[], stackTrace=[Ljava.lang.StackTraceElement;@23b84919} at com.alibaba.dubbo.rpc.protocol.dubbo.DecodeableRpcResult.decode(DecodeableRpcResult.java:94)
// 在方法签名上有声明,直接抛出 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; }如果在provider端的api明确写明抛出运行时异常,则会直接被抛出。
// 异常类和接口类在同一jar包里,直接抛出 String serviceFile = ReflectUtils.getCodeBase(invoker.getInterface()); String exceptionFile = ReflectUtils.getCodeBase(exception.getClass()); if (serviceFile == null || exceptionFile == null || serviceFile.equals(exceptionFile)){ return result; }如果异常类和接口类在同一个jar包中,直接抛出。
// 是JDK自带的异常,直接抛出 String className = exception.getClass().getName(); if (className.startsWith("java.") || className.startsWith("javax.")) { return result; }以java.或javax.开头的异常直接抛出。
// 是Dubbo本身的异常,直接抛出 if (exception instanceof RpcException) { return result; }dubbo自身的异常,直接抛出。
// 否则,包装成RuntimeException抛给客户端 return new RpcResult(new RuntimeException(StringUtils.toString(exception)));不满足上述条件,会做toString处理并被封装成RuntimeException抛出。