Dubbo学习记录(二十一)-服务调用【七】- Dubbo补充

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

  1. 调用doInvoke方法, 传入proxy代理实例, 方法名methodName, 参数类型parameterTypes, 方法参数arguments;返回一个Object值;
  2. 将Object返回值value波安装为CompleteFuture实例future;
  3. 创建AsyncRpcResult 实例asyncRpcResult
  4. 当方法执行完后, 设置会调用方法future.whenComplete,回调方法中,创建AppResponse实例, 代表响应的内容, 根据是否发生异常设置响应的内容 ;再调用asyncRpcResult#complete的方法,将响应结果内容设置给asyncRpcResult
  5. 返回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

  1. 创建一个Response对象实例;设置请求ID,版本号;
  2. 调用下一个handler#reply方法处理请求,返回一个CompletionStage实例, 是上面返回的asyncRpcResult实例。
  3. 调用CompletionStage#whenComplete方法,如果请求执行完成, 会触发该回调方法;
    【当(一)的AbstractProxyInvoker#invoke种,调用asyncRpcResult#complete的方法时,就会触发当前的whenComplete方法】
    若执行发生了异常, ,返回状态为服务调用错误SERVICE_ERROR,设置result结果为异常信息;
    若执行没有发生异常,返回状态为Response.OK,设置返回结果为apResult, 代表的请求返回的数据
  4. 调用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,它是服务提供者端的一个过滤器,它主要是在服务提供者执行完服务后会去识别异常:

  1. 如果是需要开发人员捕获的异常,那么忽略,直接把这个异常返回给消费者
  2. 如果在当前所执行的方法签名上有声明,那么忽略,直接把这个异常返回给消费者
  3. 如果抛出的异常不需要开发人员捕获,或者方法上没有申明,那么服务端或记录一个error日志
  4. 异常类和接口类在同一jar包里,那么忽略,直接把这个异常返回给消费者
  5. 如果异常类是JDK自带的异常,那么忽略,直接把这个异常返回给消费者
  6. 如果异常类是Dubbo自带的异常,那么忽略,直接把这个异常返回给消费者
  7. 否则,把异常信息包装成RuntimeException,并覆盖AppResponse对象中的exception属性

总结

消费端

  1. MockClusterInvoker.invoke(new RpcInvocation(method, args)):Mock逻辑
  2. AbstractClusterInvoker.invoke(invocation):把RpcContext中设置的Attachments添加到invocation对象上,调用路由链从服务目录上筛选出适合的服务Invoker,获得服务均衡策略loadbalance
  3. FailoverClusterInvoker.doInvoke(invocation, invokers, loadbalance):根据负载均衡策略选出一个invoker,然后执行
  4. InvokerWrapper.invoke(invocation):没做什么事情
  5. CallbackRegistrationInvoker.invoke(invocation):开始执行Filter链,执行完得到结果后,会获取ListenableFilter中的listener,执行listener的onResponse方法
  6. ConsumerContextFilter.invoke(invocation):设置RpcContext中LocalAddress、RemoteAddress、RemoteApplicationName参数
  7. FutureFilter.invoke(invocation):
  8. MonitorFilter.invoke(invocation):方法的执行次数+1
  9. ListenerInvokerWrapper.invoke(invocation):没做什么事情
  10. AsyncToSyncInvoker.invoke(invocation):异步转同步,会先用下层Invoker去异步执行,然后阻塞Integer.MAX_VALUE时间,直到拿到了结果
  11. AbstractInvoker.invoke(invocation):主要调用DubboInvoker的doInvoke方法,如果doInvoker方法出现了异常,会进行包装,包装成AsyncRpcResult
  12. DubboInvoker.doInvoke(invocation):从clients轮询出一个client进行数据发送,如果配置了不关心结果,则调用ReferenceCountExchangeClient的send方法,否则调用ReferenceCountExchangeClient的request方法
  13. ReferenceCountExchangeClient.request(Object request, int timeout):没做什么事情
  14. HeaderExchangeClient.request(Object request, int timeout):没做什么事情
  15. HeaderExchangeChannel.request(Object request, int timeout):构造一个Request对象,并且会构造一个DefaultFuture对象来阻塞timeout的时间来等待结果,在构造DefaultFuture对象时,会把DefaultFuture对象和req的id存入FUTURES中,FUTURES是一个Map,当HeaderExchangeHandler接收到结果时,会从这个Map中根据id获取到DefaultFuture对象,然后返回Response。
  16. AbstractPeer.send(Object message):从url中获取send参数,默认为false
  17. AbstractClient.send(Object message, boolean sent):没做什么
  18. NettyChannel.send(Object message, boolean sent):调用NioSocketChannel的writeAndFlush发送数据,然后判断send如果是true,那么则阻塞url中指定的timeout时间,因为如果send是false,在HeaderExchangeChannel中会阻塞timeout时间
  19. NioSocketChannel.writeAndFlush(Object msg):最底层的Netty非阻塞式的发送数据

消费端总结

  1. 最外层是Mock逻辑,调用前,调用后进行Mock
  2. 从服务目录中,根据当前调用的方法和路由链,筛选出部分服务Invoker(DubboInvoker)
  3. 对服务Invoker进行负载均衡,选出一个服务Invoker
  4. 执行Filter链
  5. AsyncToSyncInvoker完成异步转同步,因为DubboInvoker的执行是异步非阻塞的,所以如果是同步调用,则会在此处阻塞,知道拿到响应结果
  6. DubboInvoker开始异步非阻塞的调用
  7. HeaderExchangeChannel中会阻塞timeout的时间来等待结果,该timeout就是用户在消费端所配置的timeout

服务端

  1. NettyServerHandler:接收数据
  2. MultiMessageHandler:判断接收到的数据是否是MultiMessage,如果是则获取MultiMessage中的单个Message,传递给HeartbeatHandler进行处理
  3. HeartbeatHandler:判断是不是心跳消息,如果是不是则把Message传递给AllChannelHandler
  4. AllChannelHandler:把接收到的Message封装为一个ChannelEventRunnable对象,扔给线程池进行处理
  5. ChannelEventRunnable:在ChannelEventRunnable的run方法中会调用DecodeHandler处理Message
  6. DecodeHandler:按Dubbo协议的数据格式,解析当前请求的path,versio,方法,方法参数等等,然后把解析好了的请求交给HeaderExchangeHandler
  7. HeaderExchangeHandler:处理Request数据,首先构造一个Response对象,然后调用ExchangeHandlerAdapter得到一个CompletionStage future,然后给future通过whenComplete绑定一个回调函数,当future执行完了之后,就可以从回调函数中得到ExchangeHandlerAdapter的执行结果,并把执行结果设置给Response对象,通过channel发送出去。
  8. ExchangeHandlerAdapter:从本机已经导出的Exporter中根据当前Request所对应的服务key,去寻找Exporter对象,从Exporter中得到Invoker,然后执行invoke方法,此Invoker为ProtocolFilterWrapper$CallbackRegistrationInvoker
  9. ProtocolFilterWrapper$CallbackRegistrationInvoker:负责执行过滤器链,并且在执行完了之后回调每个过滤器的onResponse或onError方法
  10. EchoFilter:判断当前请求是不是一个回升测试,如果是,则不继续执行过滤器链了(服务实现者Invoker也不会调用了)
  11. ClassLoaderFilter:设置当前线程的classloader为当前要执行的服务接口所对应的classloader
  12. GenericFilter:把泛化调用发送过来的信息包装为RpcInvocation对象
  13. ContextFilter:设置RpcContext.getContext()的参数
  14. TraceFilter:先执行下一个invoker的invoke方法,调用成功后录调用信息
  15. TimeoutFilter:调用时没有特别处理,只是记录了一下当前时间,当整个filter链都执行完了之后回调TimeoutFilter的onResponse方法时,会判断本次调用是否超过了timeout
  16. MonitorFilter:记录当前服务的执行次数
  17. ExceptionFilter:调用时没有特别处理,在回调onResponse方法时,对不同的异常进行处理,详解Dubbo的异常处理
  18. DelegateProviderMetaDataInvoker:过滤器链结束,调用下一个Invoker
  19. AbstractProxyInvoker:在服务导出时,根据服务接口,服务实现类对象生成的,它的invoke方法就会执行服务实现类对象的方法,得到结果

猜你喜欢

转载自blog.csdn.net/yaoyaochengxian/article/details/124650104
今日推荐