图解Dubbo提供者部分重要的类关系

Dubbo的提供者发布服务是一个比较复杂的过程。概括起来说,主要有以下几步:
1.解析配置
2.导出服务

解析服务的过程略过。本文主要分析导出服务的过程,其中有一些比较重要的类,它们之间通过代理模式和装饰器模式,巧妙组合在一起,实现了Dubbo的提供者的功能,本文通过画图来解释它们。

导出服务又可以分为以下几步:

生成实现类的包裹对象即代理对象

假设我们定义了一个远程调用的接口,那么在提供者端就需要一个实现类来实现这个接口。同时为了实现监控,回调等功能,当请求到达时,不能简单根据类名和方法名去通过反射调用实现类。而是需要在此基础上对类进行包装,同时生成一系列代理对象。此外,Dubbo还通过责任链模式,实现了对请求的拦截处理链。概括起来,这个过程生成的类的关系图如下所示
在这里插入图片描述
最里层的ref代表接口的实现类,它是由用户开发的。从内到外,是Dubbo框架自动进行的一层层的包装。下面介绍一下其中比较重要的类或概念
1)Wrapper,是利用javassist字节码工具生成的ref的动态代理类,它不仅对ref实现的接口的方法进行了包装,同时也包装了ref的实现类的各种属性,方法的元数据等。
2)AbstractProxyInvoker是对Wrapper的包装,它是Invoker接口的默认实现类,在这里实际上是AbstractProxyInvoker类的一个抽象匿名子类。因为Invoker在拦截处理链中代表实现类的执行者,所以需要将Wrapper再包装为一个Invoker。值得注意的是AbstractProxyInvoker的invoke方法和doInvoker方法。
首先看doInvoke方法

    @Override
    public <T> Invoker<T> getInvoker(T proxy, Class<T> type, URL url) {
        // TODO Wrapper cannot handle this scenario correctly: the classname contains '$'
        final Wrapper wrapper = Wrapper.getWrapper(proxy.getClass().getName().indexOf('$') < 0 ? proxy.getClass() : type);
        return new AbstractProxyInvoker<T>(proxy, type, url) {
            @Override
            protected Object doInvoke(T proxy, String methodName,
                                      Class<?>[] parameterTypes,
                                      Object[] arguments) throws Throwable {
                return wrapper.invokeMethod(proxy, methodName, parameterTypes, arguments);
            }
        };
    }

这里的T泛型代表ref的接口,proxy就是ref,可以看出先生成了上文说的Wrapper包裹类,之后生成了AbstractProxyInvoker的抽象子类。这里的doInvoke方法就是通过wrapper去实际调用到ref里的方法实现了。

再看invoke方法

    @Override
    public Result invoke(Invocation invocation) throws RpcException {
        try {
            Object value = doInvoke(proxy, invocation.getMethodName(), invocation.getParameterTypes(), invocation.getArguments());
			CompletableFuture<Object> future = wrapWithFuture(value);
            CompletableFuture<AppResponse> appResponseFuture = future.handle((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);
                }
                return result;
            });
            return new AsyncRpcResult(appResponseFuture, invocation);
        } catch (InvocationTargetException e) {
            if (RpcContext.getContext().isAsyncStarted() && !RpcContext.getContext().stopAsync()) {
                logger.error("Provider async started, but got an exception from the original method, cannot write the exception back to consumer because an async result may have returned the new thread.", e);
            }
            return AsyncRpcResult.newDefaultAsyncResult(null, e.getTargetException(), invocation);
        } catch (Throwable e) {
            throw new RpcException("Failed to invoke remote proxy method " + invocation.getMethodName() + " to " + getUrl() + ", cause: " + e.getMessage(), e);
        }
    }

这个方法的第一行就是调用doInvoker方法。然后获取到结果之后,就将这个结果通过CompletableFuture进行异步化。通过这里我们看出,在框架层面Dubbo已经做到了完全的异步化,但是如果用户的方法是同步方法的话,这里还是会阻塞。如果要做到完全的异步化,可以将远程接口的返回类型定义为CompletableFuture。这样可以做到从框架到应用的完全异步化,在某些场景可以进一步提升性能。
2)DelegateProviderMetaDataInvoker只是简单包装,同时保存了服务的配置元信息。
3)InvokeDelegate是对DelegateProviderMetaDataInvoker的进一步封装,同时保存了注册的服务URL对象。
4)往外的各个Filter就是为了实现各种框架的必需功能或可选功能而嵌套的拦截链了。当请求进来时,会从EchoFilter开始向下穿越各个filter和代理对象,直至进入用户自己的实现类。其中可能开启某些功能时请求会在中途直接返回,而不继续向下。比如如果是回声测试,就会在EchoFilter就直接返回。

服务导出者类

在这里插入图片描述
这些类实现的接口是Exporter,代表了一个被导出的服务(即可以被远程调用的接口服务)。
DubboExporter代表是由Dubbo协议导出;ListenerExporterWrapper的功能是集成了一个ExporterListener列表,在导出时进行回调。

绑定服务端的类

在这里插入图片描述
在完成了形式上的导出服务之后,最重要的一步就是绑定服务端,也就是对外界开放接收网络连接,并且接收请求的能力。Dubbo底层默认是基于Netty的,所以最里层是NettyServer,它封装了通过Netty建立一个服务端的功能,外层是HeaderExchangeServer,它主要是封装了一些端到端连接的功能,比如长连接的空闲检测,在Dubbo框架层面管理连接等。DubboProtocolServer是进一步封装。

连接或消息处理类

在上图中,最里层的MultiMessageHandler并不是代表绑定服务端的类。而是一个连接或消息处理类。因此因此这一组类
在这里插入图片描述
ExchangeHandlerAdapter里层的其实是ExchangeHandlerAdapter这个类的抽象匿名子类,这个类只有一个方法,方法签名为

public CompletableFuture<Object> reply(ExchangeChannel channel, Object msg) throws RemotingException

它的实现类

@Override
        public CompletableFuture<Object> reply(ExchangeChannel channel, Object message) throws RemotingException {

            if (!(message instanceof Invocation)) {
                throw new RemotingException(channel, "Unsupported request: "
                        + (message == null ? null : (message.getClass().getName() + ": " + message))
                        + ", channel: consumer: " + channel.getRemoteAddress() + " --> provider: " + channel.getLocalAddress());
            }

            Invocation inv = (Invocation) message;
            Invoker<?> invoker = getInvoker(channel, inv);
            // need to consider backward-compatibility if it's a callback
            // 省略次要代码
            RpcContext.getContext().setRemoteAddress(channel.getRemoteAddress());
            Result result = invoker.invoke(inv);
            return result.thenApply(Function.identity());
        }

可见这个方法内部就是通过参数去获取Invoker对象,进而调用实现类。这里获取到的实现类,就是上文的包裹了EchoFilter的Invoker实例。同时这里reply方法返回类型是CompletableFuture,这是因为前面介绍的AbstractProxyInvoker的invoke方法对调用结果进行了异步化。

HeaderExchangeHandler主要还是管理连接等。
DecodeHandler主要是对请求进行解码。
AllChannelHandler是主要负责线程管理。
HeartbeatHandler主要负责心跳检测。
MultiMessageHandler主要负责将Dubbo内部定义的多条消息的聚合消息进行拆分处理。

总结

本文简单介绍了Dubbo提供者端发布服务时的一些重要的类及其组合关系。在阅读源码,梳理流程时,抓住这些关键的类,可以有助于理解。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章