如果你有仔细的研读 dubbo 的开发文档,大部分问题都是有答案的,为了进一步了解细节就有必要对源码进行了解。
使用 dubbo 时间也有快两年时间了,虽然官方的 dubbo 已经停止维护,但也有其它组织在继续维护:
一直想看看 dubbo 的源码,但没有集中时间来学习。最近利用了一部分时间所以将我的学习经历记录下来,主要从这几步来展开,只想搞清楚大体流程,对于其它一些细节我目前并不太关注,比如序列化,线程池,集群,上下文,异步回调等。
为了更加清楚的看清楚 RPC 的结构,主要看以下几个核心组件即可,其余的组件都是围绕它们来完成。
从这开始来实际看下 dubbo 的工作流程。RPC 的亮点在于将远程调用的细节隐藏起来,使得调用远程服务像调用本地服务一样简单,而实现上面的功能就是代理。
RPC 框架隐藏了具体的实现细节,客户端通过调用特定的代理类来访问远程服务,下面是一个 dubbo 远程接口的本地引用。
- <dubbo:reference check="false"
- timeout="200000"
- interface="com.product.core.service.ProductFacadeService"
- id="productFacadeService">
ReferenceConfig 这个类的 createProxy 是用来生成远程服务的本地代理,最终交给 RegistryProtocol 来处理。两处核心代码:
- private T createProxy(Map<String, String> map) {
- //...
- if (isJvmRefer) {
- //...
- } else {
- //...
- if (urls.size() == 1) {
- invoker = refprotocol.refer(interfaceClass, urls.get(0));
- } else {
- //...
- }
- }
- //...
- // 创建服务代理
- return (T) proxyFactory.getProxy(invoker);
- }
由于服务注册到 ZK,所以调用端要想调用服务端需要取得服务的注册信息然后建立网络连接。
- private < T > Invoker < T > doRefer(Cluster cluster, Registry registry, Class < T > type, URL url) {
- RegistryDirectory directory = new RegistryDirectory(type, url);
- directory.setRegistry(registry);
- directory.setProtocol(this.protocol);
- URL subscribeUrl = new URL("consumer", NetUtils.getLocalHost(), 0, type.getName(), directory.getUrl().getParameters());
- if (!"*".equals(url.getServiceInterface()) && url.getParameter("register", true)) {
- registry.register(subscribeUrl.addParameters(new String[] {
- "category",
- "consumers",
- "check",
- String.valueOf(false)
- }));
- }
- directory.subscribe(subscribeUrl.addParameter("category", "providers,configurators,routers"));
- return cluster.join(directory);
- }
上面代码两个作用:
上面的 subscribe 方法会监听注册中心的变化,当获取到服务注册信息后会触发 ProtocolListenerWrapper 的 refer 方法:
- public <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException {
- return (Invoker)("registry".equals(url.getProtocol())?this.protocol.refer(type, url):
- new ListenerInvokerWrapper(this.protocol.refer(type, url),
- Collections.unmodifiableList(ExtensionLoader.getExtensionLoader(InvokerListener.class).getActivateExtension(url, "invoker.listener"))));
- }
上面的 this.protocol 就是 DubboProtocol 这个类,此类的其它方法暂时先不关注,只看 refer。
- public <T> Invoker<T> refer(Class<T> serviceType, URL url) throws RpcException {
- this.optimizeSerialization(url);
- DubboInvoker invoker = new DubboInvoker(serviceType, url, this.getClients(url), this.invokers);
- this.invokers.add(invoker);
- return invoker;
- }
getClients 负责创建链接以供调用端调用服务端方法时使用。
- private ExchangeClient[] getClients(URL url) {
- boolean service_share_connect = false;
- int connections = url.getParameter("connections", 0);
- if(connections == 0) {
- service_share_connect = true;
- connections = 1;
- }
- ExchangeClient[] clients = new ExchangeClient[connections];
- for(int i = 0; i < clients.length; ++i) {
- if(service_share_connect) {
- clients[i] = this.getSharedClient(url);
- } else {
- clients[i] = this.initClient(url);
- }
- }
- return clients;
- }
回到 ReferenceConfig 的 createProxy 方法,最后会调用 proxyFactory.getProxy,此方法最终会调用 JavassistProxyFactory 的 getProxy,一看 javassist 就知道是利用字节码来实现代理功能。
- public <T> T getProxy(Invoker<T> invoker, Class<?>[] interfaces) {
- return Proxy.getProxy(interfaces).newInstance(new InvokerInvocationHandler(invoker));
- }
Proxy,这个类的实现比较复杂就不详细分析了,得到了 proxy 将它放在容器中,当调用端调用服务端代码时就有了实例跟本地的实例没什么区别。
- public static Proxy getProxy(Class... ics) {
- return getProxy(ClassHelper.getCallerClassLoader(Proxy.class), ics);
- }
下图中的 callback 是指监听注册中心后回调产生的调用关系。
本文提到了分析框架源码的作用,RPC 简单结构,并制定了一个源码分析的计划。最后以调用端在启动时执行的 dubbo 流程为起点开始了 dubbo 流程的源码分析。
来源: http://www.cnblogs.com/ASPNET2008/p/6491427.html