1. 前言
前面的文章分析了Dubbo集群容错层之Cluster和ClusterInvoker,我们已经知道,Directory会提供可调用的服务列表,Cluster会将这一组服务聚合成单个具备集群容错能力的ClusterInvoker,上层服务还是对ClusterInvoker发起invoke调用,透明的实现了集群容错。
本文重点分析下Directory接口,Dubbo目前提供了两种实现,分别是基于注册中心的RegistryDirectory,和静态服务StaticDirectory。可以这么说,Dubbo实现服务的自动注册与发现,RegistryDirectory功不可没。
2. Directory
Directory是服务目录接口,它主要的功能就是在Consumer发起RPC调用时,根据Invocation过滤出可调用的服务给ClusterInvoker做负载均衡。
public interface Directory<T> extends Node {
/**
* 服务接口
* 一个接口对应一个Directory实例
*/
Class<T> getInterface();
/**
* 获取可用的Invoker
* 根据Router过滤掉部分服务
*/
List<Invoker<T>> list(Invocation invocation) throws RpcException;
// 获取所有Invoker
List<Invoker<T>> getAllInvokers();
// 消费者URL
URL getConsumerUrl();
}
2.1 RegistryDirectory
RegistryDirectory是基于注册中心的服务目录,它会去注册中心订阅服务。
public void subscribe(URL url) {
setConsumerUrl(url);
// 注册监听
CONSUMER_CONFIGURATION_LISTENER.addNotifyListener(this);
serviceConfigurationListener = new ReferenceConfigurationListener(this, url);
// 订阅服务
registry.subscribe(url, this);
}
服务变更时会触发notify()
方法,会获取到服务提供者的URL。因为服务往往都是集群部署的,所以获取到的一般都是一组URL,暂且称它为ProviderUrls。只要ProviderUrls有变更,就会将它们自动转换成对应的Invoker,例如Provider提供的是dubbo协议的服务,那么就会自动转换成DubboInvoker。
有了一组Invoker,接下来,当Consumer发起RPC调用时,就会依赖RegistryDirectory提供服务列表了,此时会调用它的doList()
方法,默认情况下会返回所有的服务,但是Dubbo是支持服务路由的,你可以编写自己的路由规则,例如针对有状态的请求尽可能的调用同一台服务器。
Router是Dubbo提供的服务路由接口,Dubbo支持条件路由、文件路由和脚本路由,开发者可以基于Router实现自己的路由规则。Router被设计成链式结构,RegistryDirectory会构建RouterChain,利用SPI的自动激活特性,将激活的Router加入到RouterChain。
private RouterChain(URL url) {
List<RouterFactory> extensionFactories = ExtensionLoader.getExtensionLoader(RouterFactory.class)
.getActivateExtension(url, "router");
List<Router> routers = extensionFactories.stream()
.map(factory -> factory.getRouter(url))
.collect(Collectors.toList());
initWithRouters(routers);
}
Invokers会经过Router的层层过滤,最终返回的Invokers才会被用来做负载均衡。
public List<Invoker<T>> doList(Invocation invocation) {
List<Invoker<T>> invokers = null;
try {
invokers = routerChain.route(getConsumerUrl(), invocation);
} catch (Throwable t) {}
return invokers == null ? Collections.emptyList() : invokers;
}
RegistryDirectory还有一个比较重要的方法是toInvokers()
,它会将ProviderUrls自动转换成对应的Invoker,依赖的其实还是Protocol,以dubbo协议为例,那就是创建DubboInvoker,此时就会和服务端建立网络连接。
invoker = new InvokerDelegate<>(protocol.refer(serviceType, url), url, providerUrl);
2.2 StaticDirectory
StaticDirectory是一个静态的服务目录,实现很简单,不会和注册中心打交道,它内部的Invokers是在创建的时候直接指定的,什么时候会用到StaticDirectory呢? 当消费者想绕过注册中心,直接指定url,和Provider点对点直连时,就会用到StaticDirectory。
3. 总结
Directory接口用于Consumer发起RPC调用时提供服务列表,Dubbo内置了两种实现,分别是基于注册中心的RegistryDirectory,和静态服务StaticDirectory,后者在点对点直连时会用到,使用更多的是前者。 RegistryDirectory+注册中心实现了Dubbo服务自动注册与发现,RegistryDirectory会去注册中心订阅服务,然后将ProviderUrls转换成对应的Invoker,然后构建RouterChain,后续根据RPC调用的Invocation参数进行服务过滤,提供一组可用的Invokers交给LoadBalance做负载均衡。