转载自 http://dubbo.apache.org/books/dubbo-dev-book/implementation.html
实现细节
初始化过程细节
解析服务
基于 dubbo.jar 内的META-INF/spring.handlers配置,Spring 在遇到 dubbo 名称空间时,会回调DubboNamespaceHandler。
所有 dubbo 的标签,都统一用DubboBeanDefinitionParser进行解析,基于一对一属性映射,将 XML 标签解析为 Bean 对象。
在ServiceConfig.export()或ReferenceConfig.get()初始化时,将 Bean 对象转换 URL 格式,所有 Bean 属性转成 URL 的参数。
然后将 URL 传给协议扩展点,基于扩展点的扩展点自适应机制,根据 URL 的协议头,进行不同协议的服务暴露或引用。
暴露服务
1. 只暴露服务端口:
在没有注册中心,直接暴露提供者的情况下1,ServiceConfig解析出的 URL 的格式为:dubbo://service-host/com.foo.FooService?version=1.0.0。
基于扩展点自适应机制,通过 URL 的dubbo://协议头识别,直接调用DubboProtocol的export()方法,打开服务端口。
2. 向注册中心暴露服务:
在有注册中心,需要注册提供者地址的情况下2,ServiceConfig解析出的 URL 的格式为:
registry://registry-host/com.alibaba.dubbo.registry.RegistryService?export=URL.encode("dubbo://service-host/com.foo.FooService?version=1.0.0"),
基于扩展点自适应机制,通过 URL 的registry://协议头识别,就会调用RegistryProtocol的export()方法,将export参数中的提供者 URL,先注册到注册中心。
再重新传给Protocol扩展点进行暴露:dubbo://service-host/com.foo.FooService?version=1.0.0,然后基于扩展点自适应机制,通过提供者 URL 的dubbo://协议头识别,就会调用DubboProtocol的export()方法,打开服务端口。
引用服务
1. 直连引用服务:
在没有注册中心,直连提供者的情况下3,ReferenceConfig解析出的 URL 的格式为:
dubbo://service-host/com.foo.FooService?version=1.0.0。
基于扩展点自适应机制,通过 URL 的dubbo://协议头识别,直接调用DubboProtocol的refer()方法,返回提供者引用。
2. 从注册中心发现引用服务:
在有注册中心,通过注册中心发现提供者地址的情况下4,ReferenceConfig解析出的 URL 的格式为:
registry://registry-host/com.alibaba.dubbo.registry.RegistryService?refer=URL.encode("consumer://consumer-host/com.foo.FooService?version=1.0.0")。
基于扩展点自适应机制,通过 URL 的registry://协议头识别,就会调用RegistryProtocol的refer()方法,基于refer参数中的条件,查询提供者 URL,如:dubbo://service-host/com.foo.FooService?version=1.0.0。
基于扩展点自适应机制,通过提供者 URL 的dubbo://协议头识别,就会调用DubboProtocol的refer()方法,得到提供者引用。
然后RegistryProtocol将多个提供者引用,通过Cluster扩展点,伪装成单个提供者引用返回。
拦截服务
基于扩展点自适应机制,所有的Protocol扩展点都会自动套上Wrapper类。
基于ProtocolFilterWrapper类,将所有Filter组装成链,在链的最后一节调用真实的引用。
基于ProtocolListenerWrapper类,将所有InvokerListener和ExporterListener组装集合,在暴露和引用前后,进行回调。
包括监控在内,所有附加功能,全部通过Filter拦截实现。
远程调用细节
服务提供者暴露一个服务的详细过程
上图是服务提供者暴露服务的主过程:
首先ServiceConfig类拿到对外提供服务的实际类 ref(如:HelloWorldImpl),然后通过ProxyFactory类的getInvoker方法使用 ref 生成一个AbstractProxyInvoker实例,到这一步就完成具体服务到Invoker的转化。接下来就是Invoker转换到Exporter的过程。
Dubbo 处理服务暴露的关键就在Invoker转换到Exporter的过程,上图中的红色部分。下面我们以 Dubbo 和 RMI 这两种典型协议的实现来进行说明:
Dubbo 的实现
Dubbo 协议的Invoker转为Exporter发生在DubboProtocol类的export方法,它主要是打开 socket 侦听服务,并接收客户端发来的各种请求,通讯细节由 Dubbo 自己实现。
RMI 的实现
RMI 协议的Invoker转为Exporter发生在RmiProtocol类的export方法,它通过 Spring 或 Dubbo 或 JDK 来实现 RMI 服务,通讯细节这一块由 JDK 底层来实现,这就省了不少工作量。
服务消费者消费一个服务的详细过程
上图是服务消费的主过程:
首先ReferenceConfig类的init方法调用Protocol的refer方法生成Invoker实例(如上图中的红色部分),这是服务消费的关键。接下来把Invoker转换为客户端需要的接口(如:HelloWorld)。
关于每种协议如 RMI/Dubbo/Web service 等它们在调用refer方法生成Invoker实例的细节和上一章节所描述的类似。
满眼都是 Invoker
由于Invoker是 Dubbo 领域模型中非常重要的一个概念,很多设计思路都是向它靠拢。这就使得Invoker渗透在整个实现代码里,对于刚开始接触 Dubbo 的人,确实容易给搞混了。下面我们用一个精简的图来说明最重要的两种Invoker:服务提供Invoker和服务消费Invoker:
为了更好的解释上面这张图,我们结合服务消费和提供者的代码示例来进行说明:
服务消费者代码:
public class DemoClientAction{
private DemoService demoService;
public void setDemoService(DemoService demoService){
this.demoService = demoService;
}
public void start(){
String hello = demoService.sayHello("world"+ i);
}
}
上面代码中的DemoService就是上图中服务消费端的 proxy,用户代码通过这个 proxy 调用其对应的Invoker5,而该Invoker实现了真正的远程服务调用。
服务提供者代码:
public class DemoService Implimplements DemoService{
public String sayHello(String name)throws RemoteException{
return"Hello "+ name;
}
}
上面这个类会被封装成为一个AbstractProxyInvoker实例,并新生成一个Exporter实例。这样当网络通讯层收到一个请求后,会找到对应的Exporter实例,并调用它所对应的AbstractProxyInvoker实例,从而真正调用了服务提供者的代码。Dubbo 里还有一些其他的Invoker类,但上面两种是最重要的。
远程通讯细节
协议头约定
线程派发模型
Dispather:all,direct,message,execution,connection
ThreadPool:fixed,cached