Dubbo服务的导出和引入,这部分在Dubbo官方文档的源码导读有很详细的讲解。这边分别给出连接。
导出的入口
每个需要导出的服务都要配置这么一条xml元素。信息包括interface和class(具体实现类)
<dubbo:service interface="com.test.service.DemoService" class="com.test.service.impl.DemoServiceImpl" />
每一条dubbo:service的配置在初始化的时候,都会实例化一个ServiceBean对象。可以认为ServiceBean是多例的。利用Spring的观察者监听机制,在Spring加载过程中会调用onApplicationEvent方法。后续在官方文档中都有解说。
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
if (isDelay() && !isExported() && !isUnexported()) {
if (logger.isInfoEnabled()) {
logger.info("The service ready on spring started. service: " + getInterface());
}
export();// 做导出动作
}
}
引入的入口
每个需要导出的服务都要配置这么一条xml元素。信息包括interface和class(具体实现类)
<dubbo:reference id="demoService" interface="com.test.service.DemoService"/>
每一条dubbo:reference的配置在初始化的时候,都会实例化一个ReferenceBean对象。可以认为ReferenceBean是多例的。当系统中对该对象做getBean或者@Autowired时,会调用ReferenceBean.getObject方法获取代理对象。后续在官方文档中都有解说。
@Override
public Object getObject() throws Exception {
return get();
}
服务调用过程
在看源码的过程中会发现各种Invoker,Proxy,Filter等等,这些抽象都是Dubbo的核型,为了把整个过程搞的比较清晰,我画了个图,仔细来过一下。
举个例子,Provider提供了服务接口com.xxx.XxxService。并且有实现类com.xxx.XxxServiceImpl。其中服务方法名声明为 int demoService(int a, int b);
从上往下说
1.Consumer 的业务逻辑中有对com.xxx.XxxService的调用
2.首先会调用到Proxy(Consumer在初始化时为com.xxx.XxxService生成的动态代理)
3.调用InvokerInvocationHandler.invoke ,invoker.invoke被执行过程中首先会执行所有被包装好的filter过滤器链。(关于filter的包装不理解的可以看下文章dubbo的filter)
4.过滤器链执行完后会执行真实的invoker.invoke(Consumer端的invoke执行),这个方法会组织参数RpcInvocation并且编码 + 序列化之后通过RPC协议发送到Provider端
5.Consumer发送完请求之后会执行同步等待方法ResponseFuture.get来等待返回的Response信息。
6.Provider端接收到请求之后,通过各种handler调用到Exporter(服务导出时用来包装Invoker的)
7.Exporter自然会拿到它所包装好的Invoker,并且调用invoker.invoke(Provider端的invoke执行)
8.同样在调用真实Invoker之前会先调用事先包装好的所有filter过滤器链。
9.过滤器链执行完后会执行真实的invoker.invoke(Provider端的invoke执行)这个方法会调用到服务导出时准备好的Proxy。
10.这边的Proxy有两种:
① 直接使用Java的反射完成com.xxx.XxxServiceImpl服务调用
② 使用Javassist在服务导出时,会动态生成对com.xxx.XxxServiceImpl方法调用的代码,并且构造出Proxy(这个算是静态代理),在执行invoker.invoke时,就直接调用方法。
Dubbo默认使用Javassist在服务导出时,生成直接调用方法的静态代理。我认为这个地方这么处理最主要的目的是性能。毕竟每次都反射,不太爽哦。
11.通过上面第10条描述的,对com.xxx.XxxServiceImpl用户服务的执行。
12.com.xxx.XxxServiceImpl执行完成后,把Response原路返回。出Provider端的时候还是TCP发送信息到Consumer端。
13.Consumer端的ResponseFuture.get得到结果。
14.Consumer端执行后续业务行为。
通过上述详细流程的描述和解释,再对照官方文档关于服务导出和引入的源码导读。应该可以很好的理解这个过程了。
关于Protocol
看下Protocol的类结构
子类有2大块(其实有3块,有一块包装类我这边先给去掉了)分别是RegistryProtocol和AbstractProtocol。
RegistryProtocol很特殊,这边实际上用了适配器模式,RegistryProtocol就是适配器。所有服务导出都会首先创建RegistryProtocol实例,然后由RegistryProtocol来做导出过程(包括服务暴露,注册,监听)的适配。这个过程服务引入也是一样的。
看下Protocol的接口行为定义。比较简单,不做太多解释。
public interface Protocol {
int getDefaultPort();
// 导出
@Adaptive
<T> Exporter<T> export(Invoker<T> invoker) throws RpcException;
// 引入
@Adaptive
<T> Invoker<T> refer(Class<T> type, URL url) throws RpcException;
void destroy();
}
总结
关于服务导出和引入,服务调用过程的图解是重点。其他的代码细节可以重点关注