Dubbo补充
异步转同步机制
一. 客户端执行 :AsyncToSyncInvoker#invoke
invoke#invoke执行后,返回一个Result 实例 , 在调用asyncResult.get(Integer.MAX_VALUE, TimeUnit.MILLISECONDS)方法, 目的时无限阻塞, 等待响应结果返回;
public class AsyncToSyncInvoker<T> implements Invoker<T> {
private Invoker<T> invoker;
public AsyncToSyncInvoker(Invoker<T> invoker) {
this.invoker = invoker;
}
@Override
public Result invoke(Invocation invocation) throws RpcException {
// 异步转同步
Result asyncResult = invoker.invoke(invocation); // AsyncRpcResult--->CompletableFuture
try {
// 如果invocation指定是同步的,则阻塞等待结果
if (InvokeMode.SYNC == ((RpcInvocation) invocation).getInvokeMode()) {
asyncResult.get(Integer.MAX_VALUE, TimeUnit.MILLISECONDS);
}
} catch (InterruptedException e) {
} catch (Throwable e) {
throw new RpcException(e.getMessage(), e);
}
return asyncResult;
}
这里Future 用AsyncToSyncInvoker#asyncResult表示;
二. 客户端执行:DubboInvoker#invoke
- 创建AsyncRpcResult实例asyncRpcResult;
- 调用Client发送数据, 返回CompleteFuture实例responseFuture;
- asyncRpcResult.subscribeTo(responseFuture), 当responseFuture完成后,会通知asyncRpcResult;
- 返回asyncRpcResult;
即当responseFuture#complete完成后, 就会通知asyncRpcResult#get解阻塞了。
public class DubboInvoker<T> extends AbstractInvoker<T> {
@Override
protected Result doInvoke(final Invocation invocation) throws Throwable {
RpcInvocation inv = (RpcInvocation) invocation;
ExchangeClient currentClient;
if (clients.length == 1) {
currentClient = clients[0];
} else {
// 轮询使用clients
currentClient = clients[index.getAndIncrement() % clients.length];
}
try {
// isOneway为true,表示请求不需要拿结果
boolean isOneway = RpcUtils.isOneway(getUrl(), invocation);
// 拿当前方法的所配置的超时时间,默认为1000,1秒
int timeout = getUrl().getMethodPositiveParameter(methodName, TIMEOUT_KEY, DEFAULT_TIMEOUT);
if (isOneway) {
//省略
} else {
AsyncRpcResult asyncRpcResult = new AsyncRpcResult(inv);
// 异步去请求,得到一个CompletableFuture
CompletableFuture<Object> responseFuture = currentClient.request(inv, timeout);
// responseFuture会完成后会调用asyncRpcResult中的方法,这里并不会阻塞,如果要达到阻塞的效果在外层使用asyncRpcResult去控制
asyncRpcResult.subscribeTo(responseFuture);
// save for 2.6.x compatibility, for example, TraceFilter in Zipkin uses com.alibaba.xxx.FutureAdapter
FutureContext.getContext().setCompatibleFuture(responseFuture);
return asyncRpcResult;
}
} catch (TimeoutException e) {
} catch (RemotingException e) {
}
}
}
这里执行client返回responseFuture 记为DubboInvoker#responseFuture
三. 客户端执行:HeaderExchangeChannel#request
创建一个DefaultFuture#newFuture方法, 创建实例DefaultFuture,并将DefaultFuture为value,以key为请求ID放入FUTURES缓存中;
@Override
public CompletableFuture<Object> request(Object request, int timeout) throws RemotingException {
// create request.
Request req = new Request();
req.setVersion(Version.getProtocolVersion());
req.setTwoWay(true);
req.setData(request);
DefaultFuture future = DefaultFuture.newFuture(channel, req, timeout);
try {
channel.send(req);
} catch (RemotingException e) {
future.cancel();
throw e;
}
return future;
}
DefaultFuture#newFuture
维护一份本地缓FUTURES实例;
public class DefaultFuture extends CompletableFuture<Object> {
private static final Logger logger = LoggerFactory.getLogger(DefaultFuture.class);
private static final Map<Long, Channel> CHANNELS = new ConcurrentHashMap<>();
private static final Map<Long, DefaultFuture> FUTURES = new ConcurrentHashMap<>();
public static final Timer TIME_OUT_TIMER = new HashedWheelTimer(
new NamedThreadFactory("dubbo-future-timeout", true),
30,
TimeUnit.MILLISECONDS);
// invoke id.
private final Long id;
private final Channel channel;
private final Request request;
private final int timeout;
private final long start = System.currentTimeMillis();
private volatile long sent;
private Timeout timeoutCheckTask;
private DefaultFuture(Channel channel, Request request, int timeout) {
this.channel = channel;
this.request = request;
this.id = request.getId();
this.timeout = timeout > 0 ? timeout : channel.getUrl().getPositiveParameter(TIMEOUT_KEY, DEFAULT_TIMEOUT);
// put into waiting map.
FUTURES.put(id, this);
CHANNELS.put(id, channel);
}
}
到这里为止,关系如下: AsyncToSyncInvoker#asyncResult =>订阅=>DubboInvoker#responseResult => 放入缓存=>DEFUTURES#DefaultFuture;
四. 服务端执行:AbstractProxyInvoker#invoke
- 调用doInvoke方法, 传入proxy代理实例, 方法名methodName, 参数类型parameterTypes, 方法参数arguments;返回一个Object值;
- 将Object返回值value波安装为CompleteFuture实例future;
- 创建AsyncRpcResult 实例asyncRpcResult
- 当方法执行完后, 设置会调用方法future.whenComplete,回调方法中,创建AppResponse实例, 代表响应的内容, 根据是否发生异常设置响应的内容 ;再调用asyncRpcResult#complete的方法,将响应结果内容设置给asyncRpcResult
- 返回asyncRpcResult;
public abstract class AbstractProxyInvoker<T> implements Invoker<T> {
@Override
public Result invoke(Invocation invocation) throws RpcException {
try {
// 执行服务,得到一个接口,可能是一个CompletableFuture(表示异步调用),可能是一个正常的服务执行结果(同步调用)
// 如果是同步调用会阻塞,如果是异步调用不会阻塞
Object value = doInvoke(proxy, invocation.getMethodName(), invocation.getParameterTypes(), invocation.getArguments());
// 将同步调用的服务执行结果封装为CompletableFuture类型
CompletableFuture<Object> future = wrapWithFuture(value, invocation);
// 异步RPC结果
AsyncRpcResult asyncRpcResult = new AsyncRpcResult(invocation);
//设置一个回调,如果是异步调用,那么服务执行完成后将执行这里的回调
// 不会阻塞
future.whenComplete((obj, t) -> {
AppResponse result = new AppResponse();
if (t != null) {
if (t instanceof CompletionException) {
result.setException(t.getCause());
} else {
result.setException(t);
}
} else {
result.setValue(obj);
}
// 将服务执行完之后的结果设置到异步RPC结果对象中
asyncRpcResult.complete(result);
});
// 返回异步RPC结果
return asyncRpcResult;
} catch (InvocationTargetException e) {
// 假设抛的NullPointException,那么会把这个异常包装为一个Result对象
return AsyncRpcResult.newDefaultAsyncResult(null, e.getTargetException(), invocation);
} catch (Throwable e) {
//抛出异常
}
private CompletableFuture<Object> wrapWithFuture (Object value, Invocation invocation) {
if (RpcContext.getContext().isAsyncStarted()) {
return ((AsyncContextImpl)(RpcContext.getContext().getAsyncContext())).getInternalFuture();
} else if (value instanceof CompletableFuture) {
return (CompletableFuture<Object>) value;
}
return CompletableFuture.completedFuture(value);
}
protected abstract Object doInvoke(T proxy, String methodName, Class<?>[] parameterTypes, Object[] arguments) throws Throwable;
这里的Future记为AbstractProxyInvoker#future, 而asyncRpcResult返回了上一层的HeaderExchangeHandler#future
二. 服务端执行 HeaderExchangeHandler#hadnleRequest
- 创建一个Response对象实例;设置请求ID,版本号;
- 调用下一个handler#reply方法处理请求,返回一个CompletionStage实例, 是上面返回的asyncRpcResult实例。
- 调用CompletionStage#whenComplete方法,如果请求执行完成, 会触发该回调方法;
【当(一)的AbstractProxyInvoker#invoke种,调用asyncRpcResult#complete的方法时,就会触发当前的whenComplete方法】
若执行发生了异常, ,返回状态为服务调用错误SERVICE_ERROR,设置result结果为异常信息;
若执行没有发生异常,返回状态为Response.OK,设置返回结果为apResult, 代表的请求返回的数据 - 调用channel#send方法发送数据给客户端;
void handleRequest(final ExchangeChannel channel, Request req) throws RemotingException {
// 请求id,请求版本
Response res = new Response(req.getId(), req.getVersion());
// 获取 data 字段值,也就是 RpcInvocation 对象,表示请求内容
// find handler by message class.
Object msg = req.getData();
try {
// 继续向下调用,分异步调用和同步调用,如果是同步则会阻塞,如果是异步则不会阻塞
CompletionStage<Object> future = handler.reply(channel, msg); // 异步执行服务
// 如果是同步调用则直接拿到结果,并发送到channel中去
// 如果是异步调用则会监听,直到拿到服务执行结果,然后发送到channel中去
future.whenComplete((appResult, t) -> {
try {
if (t == null) {
res.setStatus(Response.OK);
res.setResult(appResult);
} else {
// 服务执行过程中出现了异常,则把Throwable转成字符串,发送给channel中,也就是发送给客户端
res.setStatus(Response.SERVICE_ERROR);
res.setErrorMessage(StringUtils.toString(t));
}
channel.send(res);
} catch (RemotingException e) {
logger.warn("Send result to consumer failed, channel is " + channel + ", msg is " + e);
} finally {
// HeaderExchangeChannel.removeChannelIfDisconnected(channel);
}
});
} catch (Throwable e) {
res.setStatus(Response.SERVICE_ERROR);
res.setErrorMessage(StringUtils.toString(e));
channel.send(res);
}
}
这里的服务端Future关系 AbstractProxyInvoker#future#complete => 触发 =>AbstractProxyInvoker#future#whenComplete回调,调用HeaderExchangeHandler#future#complete
=>触发HeaderExchangeHandler#future#whenComplete =>channel#send返回响应数据Response;
三. 客户端执行 HeaderExchangeHandler#hadnleResponse
客户端发送请求后, 服务端返回响应数据, 客户端接收的数据类为Response, 就会调用handleResponse处理响应结果;
static void handleResponse(Channel channel, Response response) throws RemotingException {
if (response != null && !response.isHeartbeat()) {
DefaultFuture.received(channel, response);
}
}
- 客户端请求数据时, Netty的channel#send方法是异步发送数据,发送完立即返回,没有返回值,为了等待返回的数据,AsyncToSyncInvoker异步转同步执行器会无限等待;
- 而客户端在HeaderExchangeChannel中,调用requst方法时,会创建一个DefaultFuture实例放入缓存DEFUTURES中,key为请求ID, value为DefaultFuture;
- 当返回响应结果时, DefaultFuture#received中,会从FUTURES缓存中,根据响应的ID拿到DefaultFuture实例, 如果future为空, 代表已经超时了。如果不为空, 调用future#doreceived方法,处理返回值;
- doReceived中,如果状态为Response.OK, 则通过Future#complete将返回值appResult设置给future,代表future的阻塞后的返回值, 也就是服务器执行结果
- 如果发生超时, 则调用completeExceptionally抛出超时异常;
- 如果发生了业务异常,则将远程调用异常信息作为返回值,在客户端显示;
- 这个也就是Dubbo的异步变同步的机制;Spring与Redis的通道, openFeign的异步转同步机制也是差不多如此;
public class DefaultFuture extends CompletableFuture<Object> {
public static void received(Channel channel, Response response) {
received(channel, response, false);
}
public static void received(Channel channel, Response response, boolean timeout) {
try {
// response的id,
DefaultFuture future = FUTURES.remove(response.getId());
if (future != null) {
Timeout t = future.timeoutCheckTask;
if (!timeout) {
// decrease Time
t.cancel();
}
future.doReceived(response);
} else {
logger.warn("The timeout response finally returned at "
+ (new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(new Date()))
+ ", response " + response
+ (channel == null ? "" : ", channel: " + channel.getLocalAddress()
+ " -> " + channel.getRemoteAddress()));
}
} finally {
CHANNELS.remove(response.getId());
}
}
private void doReceived(Response res) {
if (res.getStatus() == Response.OK) {
this.complete(res.getResult());
} else if (res.getStatus() == Response.CLIENT_TIMEOUT || res.getStatus() == Response.SERVER_TIMEOUT) {
this.completeExceptionally(new TimeoutException(res.getStatus() == Response.SERVER_TIMEOUT, channel, res.getErrorMessage()));
} else {
this.completeExceptionally(new RemotingException(channel, res.getErrorMessage()));
}
}
}
到这, 当服务端给客户端发送Response数据, => DEFUTURES#DefaultFuture#complete方法;
代表DubboInvoker#responseResult 执行完成了,通知了 =》AsyncToSyncInvoker#asyncResult#get解阻塞;
画成图如下:
Dubbo的异常处理
InvokerInvocationHandler#invoke
public class InvokerInvocationHandler implements InvocationHandler {
private static final Logger logger = LoggerFactory.getLogger(InvokerInvocationHandler.class);
private final Invoker<?> invoker;
public InvokerInvocationHandler(Invoker<?> handler) {
this.invoker = handler;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
String methodName = method.getName();
// 这里的recreate方法很重要,他会调用AppResponse的recreate方法,
// 如果AppResponse对象中存在exception信息,则此方法中会throw这个异常
return invoker.invoke(new RpcInvocation(method, args)).recreate();
}
}
invoker.invoke(new RpcInvocation(method, args))返回了一个Response对象实例, 即响应结果;
调用recreate方法, 获取响应内容AppResponse;
recreate#recreate
调用getAppResponse方法,获取响应内容AppResponse;再调用AppResponse#recreate方法;
public class AsyncRpcResult extends AbstractResult {
@Override
public Object recreate() throws Throwable {
RpcInvocation rpcInvocation = (RpcInvocation) invocation;
FutureAdapter future = new FutureAdapter(this);
RpcContext.getContext().setFuture(future);
if (InvokeMode.FUTURE == rpcInvocation.getInvokeMode()) {
return future;
}
return getAppResponse().recreate();
}
}
AppResponse#recreate
判断响应内容的异常是否存在;
存在就获取异常类型;
设置错误栈信息,
@Override
public Object recreate() throws Throwable {
if (exception != null) {
// fix issue#619
try {
// get Throwable class
Class clazz = exception.getClass();
while (!clazz.getName().equals(Throwable.class.getName())) {
clazz = clazz.getSuperclass();
}
// get stackTrace value
Field stackTraceField = clazz.getDeclaredField("stackTrace");
stackTraceField.setAccessible(true);
Object stackTrace = stackTraceField.get(exception);
if (stackTrace == null) {
exception.setStackTrace(new StackTraceElement[0]);
}
} catch (Exception e) {
// ignore
}
throw exception;
}
return result;
}
服务提供者在执行服务时,如果出现了异常,那么框架会把异常捕获,捕获异常的逻辑在AbstractProxyInvoker中,捕获到异常后,会把异常信息包装为正常的AppResponse对象,只是AppResponse的value属性没有值,exception属性有值;
此后,服务提供者会把这个AppResponse对象发送给服务消费端,服务消费端是在InvokerInvocationHandler中调用AppResponse的recreate方法重新得到一个结果,在recreate方法中会去失败AppResponse对象是否正常,也就是是否存在exception信息,如果存在,则直接throw这个exception,从而做到服务执行时出现的异常,在服务消费端抛出。
那么这里存在一个问题,如果服务提供者抛出的异常类,在服务消费者这边不存在,那么服务消费者也就抛不出这个异常了,那么dubbo是怎么处理的呢?
这里就涉及到了ExceptionFilter,它是服务提供者端的一个过滤器,它主要是在服务提供者执行完服务后会去识别异常:
- 如果是需要开发人员捕获的异常,那么忽略,直接把这个异常返回给消费者
- 如果在当前所执行的方法签名上有声明,那么忽略,直接把这个异常返回给消费者
- 如果抛出的异常不需要开发人员捕获,或者方法上没有申明,那么服务端或记录一个error日志
- 异常类和接口类在同一jar包里,那么忽略,直接把这个异常返回给消费者
- 如果异常类是JDK自带的异常,那么忽略,直接把这个异常返回给消费者
- 如果异常类是Dubbo自带的异常,那么忽略,直接把这个异常返回给消费者
- 否则,把异常信息包装成RuntimeException,并覆盖AppResponse对象中的exception属性
总结
消费端
- MockClusterInvoker.invoke(new RpcInvocation(method, args)):Mock逻辑
- AbstractClusterInvoker.invoke(invocation):把RpcContext中设置的Attachments添加到invocation对象上,调用路由链从服务目录上筛选出适合的服务Invoker,获得服务均衡策略loadbalance
- FailoverClusterInvoker.doInvoke(invocation, invokers, loadbalance):根据负载均衡策略选出一个invoker,然后执行
- InvokerWrapper.invoke(invocation):没做什么事情
- CallbackRegistrationInvoker.invoke(invocation):开始执行Filter链,执行完得到结果后,会获取ListenableFilter中的listener,执行listener的onResponse方法
- ConsumerContextFilter.invoke(invocation):设置RpcContext中LocalAddress、RemoteAddress、RemoteApplicationName参数
- FutureFilter.invoke(invocation):
- MonitorFilter.invoke(invocation):方法的执行次数+1
- ListenerInvokerWrapper.invoke(invocation):没做什么事情
- AsyncToSyncInvoker.invoke(invocation):异步转同步,会先用下层Invoker去异步执行,然后阻塞Integer.MAX_VALUE时间,直到拿到了结果
- AbstractInvoker.invoke(invocation):主要调用DubboInvoker的doInvoke方法,如果doInvoker方法出现了异常,会进行包装,包装成AsyncRpcResult
- DubboInvoker.doInvoke(invocation):从clients轮询出一个client进行数据发送,如果配置了不关心结果,则调用ReferenceCountExchangeClient的send方法,否则调用ReferenceCountExchangeClient的request方法
- ReferenceCountExchangeClient.request(Object request, int timeout):没做什么事情
- HeaderExchangeClient.request(Object request, int timeout):没做什么事情
- HeaderExchangeChannel.request(Object request, int timeout):构造一个Request对象,并且会构造一个DefaultFuture对象来阻塞timeout的时间来等待结果,在构造DefaultFuture对象时,会把DefaultFuture对象和req的id存入FUTURES中,FUTURES是一个Map,当HeaderExchangeHandler接收到结果时,会从这个Map中根据id获取到DefaultFuture对象,然后返回Response。
- AbstractPeer.send(Object message):从url中获取send参数,默认为false
- AbstractClient.send(Object message, boolean sent):没做什么
- NettyChannel.send(Object message, boolean sent):调用NioSocketChannel的writeAndFlush发送数据,然后判断send如果是true,那么则阻塞url中指定的timeout时间,因为如果send是false,在HeaderExchangeChannel中会阻塞timeout时间
- NioSocketChannel.writeAndFlush(Object msg):最底层的Netty非阻塞式的发送数据
消费端总结
- 最外层是Mock逻辑,调用前,调用后进行Mock
- 从服务目录中,根据当前调用的方法和路由链,筛选出部分服务Invoker(DubboInvoker)
- 对服务Invoker进行负载均衡,选出一个服务Invoker
- 执行Filter链
- AsyncToSyncInvoker完成异步转同步,因为DubboInvoker的执行是异步非阻塞的,所以如果是同步调用,则会在此处阻塞,知道拿到响应结果
- DubboInvoker开始异步非阻塞的调用
- HeaderExchangeChannel中会阻塞timeout的时间来等待结果,该timeout就是用户在消费端所配置的timeout
服务端
- NettyServerHandler:接收数据
- MultiMessageHandler:判断接收到的数据是否是MultiMessage,如果是则获取MultiMessage中的单个Message,传递给HeartbeatHandler进行处理
- HeartbeatHandler:判断是不是心跳消息,如果是不是则把Message传递给AllChannelHandler
- AllChannelHandler:把接收到的Message封装为一个ChannelEventRunnable对象,扔给线程池进行处理
- ChannelEventRunnable:在ChannelEventRunnable的run方法中会调用DecodeHandler处理Message
- DecodeHandler:按Dubbo协议的数据格式,解析当前请求的path,versio,方法,方法参数等等,然后把解析好了的请求交给HeaderExchangeHandler
- HeaderExchangeHandler:处理Request数据,首先构造一个Response对象,然后调用ExchangeHandlerAdapter得到一个CompletionStage future,然后给future通过whenComplete绑定一个回调函数,当future执行完了之后,就可以从回调函数中得到ExchangeHandlerAdapter的执行结果,并把执行结果设置给Response对象,通过channel发送出去。
- ExchangeHandlerAdapter:从本机已经导出的Exporter中根据当前Request所对应的服务key,去寻找Exporter对象,从Exporter中得到Invoker,然后执行invoke方法,此Invoker为ProtocolFilterWrapper$CallbackRegistrationInvoker
- ProtocolFilterWrapper$CallbackRegistrationInvoker:负责执行过滤器链,并且在执行完了之后回调每个过滤器的onResponse或onError方法
- EchoFilter:判断当前请求是不是一个回升测试,如果是,则不继续执行过滤器链了(服务实现者Invoker也不会调用了)
- ClassLoaderFilter:设置当前线程的classloader为当前要执行的服务接口所对应的classloader
- GenericFilter:把泛化调用发送过来的信息包装为RpcInvocation对象
- ContextFilter:设置RpcContext.getContext()的参数
- TraceFilter:先执行下一个invoker的invoke方法,调用成功后录调用信息
- TimeoutFilter:调用时没有特别处理,只是记录了一下当前时间,当整个filter链都执行完了之后回调TimeoutFilter的onResponse方法时,会判断本次调用是否超过了timeout
- MonitorFilter:记录当前服务的执行次数
- ExceptionFilter:调用时没有特别处理,在回调onResponse方法时,对不同的异常进行处理,详解Dubbo的异常处理
- DelegateProviderMetaDataInvoker:过滤器链结束,调用下一个Invoker
- AbstractProxyInvoker:在服务导出时,根据服务接口,服务实现类对象生成的,它的invoke方法就会执行服务实现类对象的方法,得到结果