五Dubbo服务引用源码分析--2创建远程调用的代理-2.2RegistryProtocol.refer
五Dubbo服务引用源码分析--2创建远程调用的代理-2.2RegistryProtocol.refer
/**…………RegistryProtocol.refer………………*/
REFER1 qos/filter/listener.refer
QosProtocolWrapper
@Override
public <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException {
if (Constants.REGISTRY_PROTOCOL.equals(url.getProtocol())) {
//开启心跳服务器
startQosServer(url);
//protocolFilterWrapper.refer
//type为DemoService,url=registry://127.0.0.1:2181/com.alibaba.dubbo.registry.RegistryService?application=demotest-consumer&dubbo=2.0.2&organization=dubbox&owner=programmer&pid=46377&qos.enable=false&refer=application%3Ddemotest-consumer%26dubbo%3D2.0.2%26interface%3Dcom.alibaba.dubbo.demo.DemoService%26methods%3DgetPermissions%26organization%3Ddubbox%26owner%3Dprogrammer%26pid%3D46377%26qos.enable%3Dfalse%26register.ip%3D192.168.0.101%26side%3Dconsumer%26timestamp%3D1672209755972®istry=zookeeper×tamp=1672209756422
return protocol.refer(type, url);
}
return protocol.refer(type, url);
}
ProtocolFilterWrapper
public <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException {
//url为registry://,直接引用服务
if (Constants.REGISTRY_PROTOCOL.equals(url.getProtocol())) {
return protocol.refer(type, url);
}
return buildInvokerChain(protocol.refer(type, url), Constants.REFERENCE_FILTER_KEY, Constants.CONSUMER);
}
ProtocolFilterWrapper.refer根据服务类型、以及url来引用服务,构造invoker时:
1 如果URL 为registry://的注册中心,则直接protocol.refer;
2 如果URL不为registry://,则会在protocol.refer引用服务后,针对获取的invoker构造filter拦截器链。
这里,url为注册中心,直接走前者。
ProtocolListenerWrapper
@Override
public <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException {
if (Constants.REGISTRY_PROTOCOL.equals(url.getProtocol())) {
//
return protocol.refer(type, url);
}
return new ListenerInvokerWrapper<T>(protocol.refer(type, url),
Collections.unmodifiableList(
ExtensionLoader.getExtensionLoader(InvokerListener.class)
.getActivateExtension(url, Constants.INVOKER_LISTENER_KEY)));
}
ProtocolListenerWrapper情况同filter,
1 如果url为注册中心registry://,则直接protocol.refer,传递给包裹的内部protocol;
2 如果不是,首先引用服务构造invoker,然后创建监听;(这里监听过程,和provider端相同,可以参阅)
REFER2 RegistryProtocol.refer--todo
RegistryProtocol.refer
public <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException {
//设置url协议头为zookeeper
//zookeeper://127.0.0.1:2181/com.alibaba.dubbo.registry.RegistryService?application=demotest-consumer&dubbo=2.0.2&organization=dubbox&owner=programmer&pid=46377&qos.enable=false&refer=application%3Ddemotest-consumer%26dubbo%3D2.0.2%26interface%3Dcom.alibaba.dubbo.demo.DemoService%26methods%3DgetPermissions%26organization%3Ddubbox%26owner%3Dprogrammer%26pid%3D46377%26qos.enable%3Dfalse%26register.ip%3D192.168.0.101%26side%3Dconsumer%26timestamp%3D1672209755972×tamp=1672209756422
url = url.setProtocol(url.getParameter(Constants.REGISTRY_KEY, Constants.DEFAULT_REGISTRY)).removeParameter(Constants.REGISTRY_KEY);
//REFER2.1 registryFactory.getRegistry(url)
Registry registry = registryFactory.getRegistry(url);
if (RegistryService.class.equals(type)) {
return proxyFactory.getInvoker((T) registry, type, url);
}
// group="a,b" or group="*"
//解码refer引用的参数对
Map<String, String> qs = StringUtils.parseQueryString(url.getParameterAndDecoded(Constants.REFER_KEY));
String group = qs.get(Constants.GROUP_KEY);
if (group != null && group.length() > 0) {
if ((Constants.COMMA_SPLIT_PATTERN.split(group)).length > 1
|| "*".equals(group)) {
return doRefer(getMergeableCluster(), registry, type, url);
}
}
//REFER2.2. doRefer
return doRefer(cluster, registry, type, url);
}
解析后的refer引用服务的参数对,如下:
RegistryProtocol.refer逻辑:
1 将registry://协议头更换为zookeeper://,然后REFER2.1 registryFactory.getRegistry(url)获取注册registry;
2 然后doRefer创建invoker;
REFER2.1 registryFactory.getRegistry(url)
public class RegistryFactory$Adaptive implements com.alibaba.dubbo.registry.RegistryFactory {
public com.alibaba.dubbo.registry.Registry getRegistry(com.alibaba.dubbo.common.URL arg0) {
if (arg0 == null) throw new IllegalArgumentException("url == null");
com.alibaba.dubbo.common.URL url = arg0;
//zookeeper
String extName = ( url.getProtocol() == null ? "dubbo" : url.getProtocol() );
if(extName == null) throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.registry.RegistryFactory) name from url(" + url.toString() + ") use keys([protocol])");
com.alibaba.dubbo.registry.RegistryFactory extension = (com.alibaba.dubbo.registry.RegistryFactory)ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.registry.RegistryFactory.class).getExtension(extName);
//zookeeperRegistryFactory
return extension.getRegistry(arg0);
}
}
AbstractRegistryFactory
@Override
public Registry getRegistry(URL url) {
//zookeeper://127.0.0.1:2181/com.alibaba.dubbo.registry.RegistryService?application=demotest-consumer&dubbo=2.0.2&interface=com.alibaba.dubbo.registry.RegistryService&organization=dubbox&owner=programmer&pid=46377&qos.enable=false×tamp=1672209756422
//这里是构建注册中心的URL,设置path为RegistryService,并且移除refer引用的配置,并增加接口服务为RegistryService
url = url.setPath(RegistryService.class.getName())
.addParameter(Constants.INTERFACE_KEY, RegistryService.class.getName())
.removeParameters(Constants.EXPORT_KEY, Constants.REFER_KEY);
//zookeeper://127.0.0.1:2181/com.alibaba.dubbo.registry.RegistryService
String key = url.toServiceString();
// Lock the registry access process to ensure a single instance of the registry
LOCK.lock();
try {
Registry registry = REGISTRIES.get(key);
if (registry != null) {
return registry;
}
//创建注册服务类
registry = createRegistry(url);
if (registry == null) {
throw new IllegalStateException("Can not create registry " + url);
}
REGISTRIES.put(key, registry);//放入缓存
return registry;
} finally {
// Release the lock
LOCK.unlock();
}
}
public class ZookeeperRegistryFactory extends AbstractRegistryFactory {
//zookeeperTransporter$Adaptive spi依赖注入
private ZookeeperTransporter zookeeperTransporter;
public void setZookeeperTransporter(ZookeeperTransporter zookeeperTransporter) {
this.zookeeperTransporter = zookeeperTransporter;
}
@Override
public Registry createRegistry(URL url) {
//REFER2.1.1 ZookeeperRegistry(url, zookeeperTransporter) 创建zookeeper注册中心
return new ZookeeperRegistry(url, zookeeperTransporter);
}
创建ZookeeperRegistry注册中心。
//REFER2.1.1 ZookeeperRegistry(url, zookeeperTransporter)初始化--同provider
ZookeeperRegistry实现与zookeeper注册中心的交互逻辑。
此时url=zookeeper://127.0.0.1:2181/com.alibaba.dubbo.registry.RegistryService?application=demotest-consumer&dubbo=2.0.2&interface=com.alibaba.dubbo.registry.RegistryService&organization=dubbox&owner=programmer&pid=46377&qos.enable=false×tamp=1672209756422。
这里zookeeperRegistry的初始化过程,和provider的过程相同,可以参考。此处略
初始化后zookeeperRegistry内的属性如下:
在AbstractRegistry中提供了连个缓存的服务------内存缓存服务、磁盘文件缓存(注册中心重启,会是内存缓存消失,因此再增加文件缓存)。
/**……………………………………………………………………………………………………磁盘文件缓存…………………………………………………………………………………………………………………………………… */
// 本地磁盘缓存文件
private File file;
// 本地磁盘缓存文件的缓存对象,在初始化时,file中数据读入properties中,后续写入,也先写入该properties中,在定时写入file文件中
//key(服务接口名称):value(服务url列表)
private final Properties properties = new Properties();
/**……………………………………………………………………………………………………内存服务缓存…………………………………………………………………………………………………………………………………… */
private final ConcurrentMap<URL, Map<String, List<URL>>> notified = new ConcurrentHashMap<URL, Map<String, List<URL>>>();
//存储的内容为(zookeeper://,map(providers/routers/configurators/consumers,List(服务URL) ) )
//URL为注册中心的地址
REFER2.2 doRefer
return doRefer(cluster, registry, type, url);
方法的参数如下:
cluster:cluster$Adaptive,type:DemoService,
url=zookeeper://127.0.0.1:2181/com.alibaba.dubbo.registry.RegistryService?application=demotest-consumer&dubbo=2.0.2&organization=dubbox&owner=programmer&pid=57858&qos.enable=false&refer=application%3Ddemotest-consumer%26dubbo%3D2.0.2%26interface%3Dcom.alibaba.dubbo.demo.DemoService%26methods%3DgetPermissions%26organization%3Ddubbox%26owner%3Dprogrammer%26pid%3D57858%26qos.enable%3Dfalse%26register.ip%3D192.168.0.101%26side%3Dconsumer%26timestamp%3D1672287700727×tamp=1672287701224
RegistryProtocol
private <T> Invoker<T> doRefer(Cluster cluster, Registry registry, Class<T> type, URL url) {
//REFER2.2.1 RegistryDirectory
RegistryDirectory<T> directory = new RegistryDirectory<T>(type, url);
directory.setRegistry(registry);//zookeeperRegistry
directory.setProtocol(protocol);//Protocol$Adaptive
// REFER_KEY的全部属性
Map<String, String> parameters = new HashMap<String, String>(directory.getUrl().getParameters());
//构造consumerURL(订阅注册中心的url)--协议头为consumer,路径名称为demoService,参数对为refer服务的参数
//consumer://192.168.0.101/com.alibaba.dubbo.demo.DemoService?application=demotest-consumer&dubbo=2.0.2&interface=com.alibaba.dubbo.demo.DemoService&methods=getPermissions&organization=dubbox&owner=programmer&pid=57858&qos.enable=false&side=consumer×tamp=1672287700727
URL subscribeUrl = new URL(Constants.CONSUMER_PROTOCOL, parameters.remove(Constants.REGISTER_IP_KEY), 0, type.getName(), parameters);
if (!Constants.ANY_VALUE.equals(url.getServiceInterface())
&& url.getParameter(Constants.REGISTER_KEY, true)) {
//REFER2.2.2 zookeeperRegistry.register
registry.register(subscribeUrl.addParameters(Constants.CATEGORY_KEY, Constants.CONSUMERS_CATEGORY,
Constants.CHECK_KEY, String.valueOf(false)));
}
//REFER2.2.3 registryDirectory.subscribe
directory.subscribe(subscribeUrl.addParameter(Constants.CATEGORY_KEY,
Constants.PROVIDERS_CATEGORY
+ "," + Constants.CONFIGURATORS_CATEGORY
+ "," + Constants.ROUTERS_CATEGORY));
//REFER2.2.4 cluster$Adaptive.join(directory)
Invoker invoker = cluster.join(directory);
ProviderConsumerRegTable.registerConsumer(invoker, url, subscribeUrl, directory);
return invoker;
}
doRefer完成如下任务:
1 创建registryDirectory,并设置registry、protocol;
2 构造consumer消费者的url(consumer://);
3 向zookeeperRegistry上的consumer_category路径下注册消费者url;//REFER2.2.2 zookeeperRegistry.register
4 由directory.subscribe()订阅zookeeper结构树上provider、configuration、router的category路径下的节点;//REFER2.2.3 registryDirectory.subscribe
5 最后,由cluster集群,cluster.join(directory)返回一个invoker。//REFER2.2.4 cluster$Adaptive.join(directory)
REFER2.2.1 RegistryDirectory初始化
初始化registryDirectory,并设置zookeeperRegistry、Protocol$Adaptive。
RegistryProtocol
private <T> Invoker<T> doRefer(Cluster cluster, Registry registry, Class<T> type, URL url) {
//REFER2.2.1 RegistryDirectory
RegistryDirectory<T> directory = new RegistryDirectory<T>(type, url);
directory.setRegistry(registry);//zookeeperRegistry
directory.setProtocol(protocol);//Protocol$Adaptive
然后,执行registryDirectory的初始化。
RegistryDirectory
//$adaptive类
private static final Cluster cluster = ExtensionLoader.getExtensionLoader(Cluster.class).getAdaptiveExtension();
private static final RouterFactory routerFactory = ExtensionLoader.getExtensionLoader(RouterFactory.class).getAdaptiveExtension();
private static final ConfiguratorFactory configuratorFactory = ExtensionLoader.getExtensionLoader(ConfiguratorFactory.class).getAdaptiveExtension();
public RegistryDirectory(Class<T> serviceType, URL url) {
//REFER2.2.1.1 AbstractDirectory
//初始化AbstractDirectory,以及其内router属性
super(url);
if (serviceType == null)
throw new IllegalArgumentException("service type is null.");
if (url.getServiceKey() == null || url.getServiceKey().length() == 0)
throw new IllegalArgumentException("registry serviceKey is null.");
this.serviceType = serviceType; //DemoService
this.serviceKey = url.getServiceKey();//com.alibaba.dubbo.registry.RegistryService
//是url中refer引用服务的参数对
this.queryMap = StringUtils.parseQueryString(url.getParameterAndDecoded(Constants.REFER_KEY));
//zookeeper://127.0.0.1:2181/com.alibaba.dubbo.registry.RegistryService?application=demotest-consumer&dubbo=2.0.2&interface=com.alibaba.dubbo.demo.DemoService&methods=getPermissions&organization=dubbox&owner=programmer&pid=57858&qos.enable=false®ister.ip=192.168.0.101&side=consumer×tamp=1672287700727
//将原来注册中心url内参数全部删除,把refer服务的参数对map存入
this.overrideDirectoryUrl = this.directoryUrl = url.setPath(url.getServiceInterface()).clearParameters().addParameters(queryMap).removeParameter(Constants.MONITOR_KEY);
String group = directoryUrl.getParameter(Constants.GROUP_KEY, "");//group=""
this.multiGroup = group != null && ("*".equals(group) || group.contains(",")); //false
String methods = queryMap.get(Constants.METHODS_KEY);//getPermissions
this.serviceMethods = methods == null ? null : Constants.COMMA_SPLIT_PATTERN.split(methods);//getPermissions
}
//REFER2.2.1.1 AbstractDirectory
AbstractDirectory//目录directory的抽象实现:从此directory的list方法返回的list<> invokers,都是已经被router过滤过的。所以AbstractDirectory重要的属性是router路由器。
public AbstractDirectory(URL url) {
this(url, null);
}
public AbstractDirectory(URL url, List<Router> routers) {
this(url, url, routers);
}
public AbstractDirectory(URL url, URL consumerUrl, List<Router> routers) {
if (url == null)
throw new IllegalArgumentException("url == null");
//此时,url、consumerUrl同为zookeeper://127.0.0.1:2181/com.alibaba.dubbo.registry.RegistryService?application=demotest-consumer&dubbo=2.0.2&organization=dubbox&owner=programmer&pid=57858&qos.enable=false&refer=application%3Ddemotest-consumer%26dubbo%3D2.0.2%26interface%3Dcom.alibaba.dubbo.demo.DemoService%26methods%3DgetPermissions%26organization%3Ddubbox%26owner%3Dprogrammer%26pid%3D57858%26qos.enable%3Dfalse%26register.ip%3D192.168.0.101%26side%3Dconsumer%26timestamp%3D1672287700727×tamp=1672287701224
this.url = url;
this.consumerUrl = consumerUrl;
setRouters(routers);
}
protected void setRouters(List<Router> routers) {
// copy list
routers = routers == null ? new ArrayList<Router>() : new ArrayList<Router>(routers);
// append url router
String routerkey = url.getParameter(Constants.ROUTER_KEY);
if (routerkey != null && routerkey.length() > 0) {
RouterFactory routerFactory = ExtensionLoader.getExtensionLoader(RouterFactory.class).getExtension(routerkey);
routers.add(routerFactory.getRouter(url));
}
// 在routers路由列表中,追加 mock invoker selector
routers.add(new MockInvokersSelector());
Collections.sort(routers);
this.routers = routers;
}
//AbstractDirectory.list方法,会返回经过router过滤过的可用的invoker的list集合
public List<Invoker<T>> list(Invocation invocation) throws RpcException {
//略
MockInvokersSelector是Router的实现类,其是个旨在实现mock模拟功能的特定的路由器。(例如,如果request请求配置使用mock,那么mockInvokersSelector保证只有protocol协议为mock的invoker,会最终出现在List
REFER2.2.2 zookeeperRegistry.register
registry.register(subscribeUrl.addParameters(Constants.CATEGORY_KEY, Constants.CONSUMERS_CATEGORY,
Constants.CHECK_KEY, String.valueOf(false)));
其中,subscribeurl的参数对中添加category:consumer,check=false的参数对。
FailbackRegistry
public void register(URL url) {
super.register(url);
//failback中保存了失败的订阅、取消订阅的url
failedRegistered.remove(url);
failedUnregistered.remove(url);
try {
// Sending a registration request to the server side
//REFER2.2.2.1 doRegister 由子类实现
doRegister(url);
} catch (Exception e) {
Throwable t = e;
// If the startup detection is opened, the Exception is thrown directly.
boolean check = getUrl().getParameter(Constants.CHECK_KEY, true)
&& url.getParameter(Constants.CHECK_KEY, true)
&& !Constants.CONSUMER_PROTOCOL.equals(url.getProtocol());
boolean skipFailback = t instanceof SkipFailbackWrapperException;
if (check || skipFailback) {
if (skipFailback) {
t = t.getCause();
}
throw new IllegalStateException("Failed to register " + url + " to registry " + getUrl().getAddress() + ", cause: " + t.getMessage(), t);
} else {
logger.error("Failed to register " + url + ", waiting for retry, cause: " + t.getMessage(), t);
}
// Record a failed registration request to a failed list, retry regularly
failedRegistered.add(url);
}
}
AbstractRegistry
@Override
public void register(URL url) {
if (url == null) {
throw new IllegalArgumentException("register url == null");
}
if (logger.isInfoEnabled()) {
logger.info("Register: " + url);
}
registered.add(url);//将consumer消费者的url,添加到abstractRegistry的registered上
}
//REFER2.2.2.1 ZookeeperRegistry.doRegister
ZookeeperRegistry
protected void doRegister(URL url) {
try {
zkClient.create(toUrlPath(url), url.getParameter(Constants.DYNAMIC_KEY, true));//默认为true,临时节点
} catch (Throwable e) {
throw new RpcException("Failed to register " + url + " to zookeeper " + getUrl() + ", cause: " + e.getMessage(), e);
}
}
其中,toUrlPath(url)转换为zookeeper存储上的路径
private String toUrlPath(URL url) {
return toCategoryPath(url) + Constants.PATH_SEPARATOR + URL.encode(url.toFullString());
}
/dubbo/com.alibaba.dubbo.demo.DemoService/consumers/consumer%3A%2F%2F192.168.0.101%2Fcom.alibaba.dubbo.demo.DemoService%3Fapplication%3Ddemotest-consumer%26category%3Dconsumers%26check%3Dfalse%26dubbo%3D2.0.2%26interface%3Dcom.alibaba.dubbo.demo.DemoService%26methods%3DgetPermissions%26organization%3Ddubbox%26owner%3Dprogrammer%26pid%3D57858%26qos.enable%3Dfalse%26side%3Dconsumer%26timestamp%3D1672287700727
这里是通过zkclient,向zookeeper上添加临时节点,完成consumer者注册。
zookeeperClient添加节点过程略:
AbstractZookeeperClient
@Override
public void create(String path, boolean ephemeral) {
if (!ephemeral) {
if (checkExists(path)) {
return;
}
}
int i = path.lastIndexOf('/');
if (i > 0) {
create(path.substring(0, i), false);
}
if (ephemeral) {
createEphemeral(path);
} else {
createPersistent(path);
}
}
CuratorZookeeperClient
public void createEphemeral(String path) {
try {
client.create().withMode(CreateMode.EPHEMERAL).forPath(path);
} catch (NodeExistsException e) {
} catch (Exception e) {
throw new IllegalStateException(e.getMessage(), e);
}
}
REFER2.2.3 registryDirectory.subscribe
RegistryProtocol
//REFER2.2.3 registryDirectory.subscribe
directory.subscribe(subscribeUrl.addParameter(Constants.CATEGORY_KEY,
Constants.PROVIDERS_CATEGORY
+ "," + Constants.CONFIGURATORS_CATEGORY
+ "," + Constants.ROUTERS_CATEGORY));
在订阅时候,会向url中添加category=providers、configurators、routers的参数对。
RegistryDirectory
public void subscribe(URL url) {
//consumer://192.168.0.101/com.alibaba.dubbo.demo.DemoService?application=demotest-consumer&category=providers,configurators,routers&dubbo=2.0.2&interface=com.alibaba.dubbo.demo.DemoService&methods=getPermissions&organization=dubbox&owner=programmer&pid=57858&qos.enable=false&side=consumer×tamp=1672287700727
setConsumerUrl(url);
//REFER2.2.3.1 zookeeperRegistry.subscribe 订阅
registry.subscribe(url, this);
}
在registry.subscribe(url,this)方法中,第二个参数是NotifyListener,即传入RegistryDirectory,可知directory是注册中心订阅--通知机制的关键部分。