Dubbo
最主要干2个事情。第一个,对服务的导出和引入。第二个,维护注册中心的服务。对于第一个,尽量参考Dubbo
官方文档,有详细解释。对于第二个,这边详细说说。
首先找到服务注册和引用的入口RegistryProtocol
,这部分不清楚的话,可以参考文档Dubbo官方文档-服务导出
public void register(URL registryUrl, URL registedProviderUrl) {
Registry registry = registryFactory.getRegistry(registryUrl);
registry.register(registedProviderUrl);
}
@Override
public <T> Exporter<T> export(final Invoker<T> originInvoker) throws RpcException {
// ----------------------此处省略一堆代码------------------------
registry.subscribe(overrideSubscribeUrl, overrideSubscribeListener);
// ----------------------此处省略一堆代码------------------------
}
private <T> Invoker<T> doRefer(Cluster cluster, Registry registry, Class<T> type, URL url) {
// ----------------------此处省略一堆代码------------------------
registry.register(subscribeUrl.addParameters(Constants.CATEGORY_KEY, Constants.CONSUMERS_CATEGORY,
Constants.CHECK_KEY, String.valueOf(false)));
// ----------------------此处省略一堆代码------------------------
}
上面省略了大量的代码,只是把关于registry
的几个调用点贴出来了。可以看到有register
,subscribe
两个行为。然后看看RegistryService
接口中,总共有多少行为。
public interface RegistryService {
// 向注册中心注册url
void register(URL url);
// 从注册中心删除(卸载)url
void unregister(URL url);
// 向注册中心注册对某个目录的监听
void subscribe(URL url, NotifyListener listener);
// 从注册中心删除(卸载)对某个目录的监听
void unsubscribe(URL url, NotifyListener listener);
// 从注册中心查询服务
List<URL> lookup(URL url);
}
从RegistryService
可以看到除了上面出现的2个行为,还有2个反向的。还有一个查询。很容易理解。把RegistryService
中的5个行为抽象出来之后,就可以有很好的扩展性。可以适配redis
,zookeeper
,数据库,或者开发者自定义实现方式。
注册过程分为2步
1.向注册中心发送注册(Register
)请求(具体注册的内容和存储形式要看使用的是什么注册中心)
2.向注册中心发起订阅请求(订阅的具体内容,和订阅形式要看使用的是什么注册中心)
删除注册过程分为2步
1.向注册中心发送删除注册(UnRegister
)请求
2.向注册中心发起取消订阅(Unsubscribe
)请求
看下类结构图:
结构很清晰。Dubbo
给出了4种实现。先从Zookeeper
开始说。看下示意图。
zookeeper作为注册中心
服务启动并且注册完成后,每个服务都会在zookeeper
上建立4个永久目录。可以从上图中看到。找到代码ZookeeperRegistry.toCategoriesPath
。可以看到4个目录的建立是在这个地方设置的。
private String[] toCategoriesPath(URL url) {
// ----------------------此处省略一堆代码------------------------
categories = new String[]{
Constants.PROVIDERS_CATEGORY, Constants.CONSUMERS_CATEGORY,
Constants.ROUTERS_CATEGORY, Constants.CONFIGURATORS_CATEGORY};
// ----------------------此处省略一堆代码------------------------
}
其中configurators
和routers
并不常用,主要在配置和服务治理的时候才会用到。常用的是providers
和consumers
。如果一个服务有N个Provider
,那在providers
下面会产生N个子节点。同样的,如果一个服务有N个Consumer
,那在consumers
下面会产生N个子节点。关于子节点中对服务的描述会比较长,这边给出一个示意图,调试代码的可以直接登录到Zookeeper
查看自己的服务情况。
注册过程是在zookeeper
上建立目录并且把服务的描述信息写进去。除此之外还需要订阅它关注的服务。服务也就是目录(Dubbo
把它抽象为Directory
)。Provider
和Consumer
都有个自己关心的几个目录。
Provider
只关心configurators
,因为它不关心有多少Consumer
,或者Consumer
的路由策略。所以Provider
会监视configurators
。
Consumer
会关心configurators
,routers
,providers
,它需要关心各个provider
的动态,和自己的路由策略。所以Consumer
会分别监视configurators
,routers
,providers
。
对于所监视的目录,只要children
发生任何变化都会触发childChanged
事件。然后做出相应处理。
比如:Consumer
监视providers
的动态,当/dubbo/com.xxx.XxxService/providers
目录下减少服务时。会触发Consumer
重新搜有这个目录下所有的服务,并且重新维护Consumer
的服务列表(具体对服务列表的维护需要关注Dubbo Directory
模块的代码),客户代码再次调用这个服务时,被删除的服务节点不会再收到服务请求。
Dubbo
为了兼容多个zk客户端,还搞了一个Zookeeper
的Transprter
层。官方给出了2种实现,分别是curator
和zkClient
。看下类图结构。
还是比较清晰的。看下ZookeeperTransporter
的代码。
@SPI("curator")// 默认使用curator
public interface ZookeeperTransporter {
// 建立连接,并且返回一个ZookeeperClient,后续的一些列对zookeeper的操作都需要以ZookeeperClient为委托。
ZookeeperClient connect(URL url);
}
public class CuratorZookeeperTransporter implements ZookeeperTransporter {
// 与zookeeper建立连接的过程就是new CuratorZookeeperClient,连接的过程在构造方法中。
@Override
public ZookeeperClient connect(URL url) {
return new CuratorZookeeperClient(url);
}
}
与Zookeeper
建立连接的过程就是new CuratorZookeeperClient
,连接的过程在构造方法中。
public class CuratorZookeeperClient extends