五Dubbo服务引用源码分析--2创建远程调用的代理-2.3zookeeperRegistry.subscribe订阅
五Dubbo服务引用源码分析--2创建远程调用的代理-2.3zookeeperRegistry.subscribe订阅
/**…………………订阅-通知…………………… */
//REFER2.2.3.1 zookeeperRegistry.subscribe-订阅SUB
listener参数是registryDirectory。
FailbackRegistry
@Override
public void subscribe(URL url, NotifyListener listener) {
//SUB1 abstractRegistry.subscribe---将listener加入缓存
super.subscribe(url, listener);
removeFailedSubscribed(url, listener);
try {
//SUB2 doSubscribe
// Sending a subscription request to the server side
doSubscribe(url, listener);
} catch (Exception e) {
Throwable t = e;
List<URL> urls = getCacheUrls(url);
if (urls != null && !urls.isEmpty()) {
notify(url, listener, urls);
logger.error("Failed to subscribe " + url + ", Using cached list: " + urls + " from cache file: " + getUrl().getParameter(Constants.FILE_KEY, System.getProperty("user.home") + "/dubbo-registry-" + url.getHost() + ".cache") + ", cause: " + t.getMessage(), t);
} else {
// 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);
boolean skipFailback = t instanceof SkipFailbackWrapperException;
if (check || skipFailback) {
if (skipFailback) {
t = t.getCause();
}
throw new IllegalStateException("Failed to subscribe " + url + ", cause: " + t.getMessage(), t);
} else {
logger.error("Failed to subscribe " + url + ", waiting for retry, cause: " + t.getMessage(), t);
}
}
//将失败的订阅请求添加到failedlist,并定时的重试
addFailedSubscribed(url, listener);
}
}
//SUB1 abstractRegistry.subscribe
abstractRegistry.subscribe
//该缓存中,存储消费者url以及其对应的NotifyListener集合--即registryDirectory集合,在订阅变化的时候,进行通知
private final ConcurrentMap<URL, Set<NotifyListener>> subscribed = new ConcurrentHashMap<URL, Set<NotifyListener>>();
@Override
public void subscribe(URL url, NotifyListener listener) {
if (url == null) {
throw new IllegalArgumentException("subscribe url == null");
}
if (listener == null) {
throw new IllegalArgumentException("subscribe listener == null");
}
if (logger.isInfoEnabled()) {
logger.info("Subscribe: " + url);
}
Set<NotifyListener> listeners = subscribed.get(url);
if (listeners == null) {
subscribed.putIfAbsent(url, new ConcurrentHashSet<NotifyListener>());
listeners = subscribed.get(url);
}
//将registryDirectory加入subscribed缓存中
listeners.add(listener);
}
subscribed存储的关系,key为消费者consumerURL,value为该消费者的registryDirectory(其也是一个NotifyListener对象),该directory中存储consumer需要引用的invoker的列表。此外,还具有订阅-通知的更新操作,在监听到provider端有服务变化时候,会进行通知,并修改invokers列表。
//SUB2 ZookeeperRegistry.doSubscribe
传入的url参数如下:
ZookeeperRegistry
@Override
protected void doSubscribe(final URL url, final NotifyListener listener) {
try {
/**…………………………………………………………………………消费者消费服务接口为anyValue-*的情况…………………………………………………………………………………………………………………… */
if (Constants.ANY_VALUE.equals(url.getServiceInterface())) {//DemoService
String root = toRootPath();
ConcurrentMap<NotifyListener, ChildListener> listeners = zkListeners.get(url);
if (listeners == null) {
zkListeners.putIfAbsent(url, new ConcurrentHashMap<NotifyListener, ChildListener>());
listeners = zkListeners.get(url);
}
ChildListener zkListener = listeners.get(listener);
if (zkListener == null) {
listeners.putIfAbsent(listener, new ChildListener() {
@Override
public void childChanged(String parentPath, List<String> currentChilds) {
for (String child : currentChilds) {
child = URL.decode(child);
if (!anyServices.contains(child)) {
anyServices.add(child);
subscribe(url.setPath(child).addParameters(Constants.INTERFACE_KEY, child,
Constants.CHECK_KEY, String.valueOf(false)), listener);
}
}
}
});
zkListener = listeners.get(listener);
}
zkClient.create(root, false);
List<String> services = zkClient.addChildListener(root, zkListener);
if (services != null && !services.isEmpty()) {
for (String service : services) {
service = URL.decode(service);
anyServices.add(service);
subscribe(url.setPath(service).addParameters(Constants.INTERFACE_KEY, service,
Constants.CHECK_KEY, String.valueOf(false)), listener);
}
}
}
/**…………………………………………………………………………………………………………处理consume特定接口服务情况………………………………………………………………………………………………………… */
else { //0
List<URL> urls = new ArrayList<URL>();
//此时,消费者订阅服务,category设置表示需要订阅providers/configurators/routers节点下的数据,因此,解析后:
///dubbo/com.alibaba.dubbo.demo.DemoService/providers
///dubbo/com.alibaba.dubbo.demo.DemoService/configurators
///dubbo/com.alibaba.dubbo.demo.DemoService/routers
for (String path : toCategoriesPath(url)) { //1
// private final ConcurrentMap<URL, ConcurrentMap<NotifyListener, ChildListener>> zkListeners = new ConcurrentHashMap<URL, ConcurrentMap<NotifyListener, ChildListener>>();
//存储zookeeper存储路径上的对应的两个监听器,一个NotifyListener对应一个ChildListener
ConcurrentMap<NotifyListener, ChildListener> listeners = zkListeners.get(url);
if (listeners == null) {
zkListeners.putIfAbsent(url, new ConcurrentHashMap<NotifyListener, ChildListener>());
listeners = zkListeners.get(url);
}
ChildListener zkListener = listeners.get(listener);
if (zkListener == null) { //2
listeners.putIfAbsent(listener, new ChildListener() {//3
@Override
public void childChanged(String parentPath, List<String> currentChilds) {
/**……………………………………………………………………………………zookeeper上节点监听、并通知的原理…………………………………………………………………………………………………… */
//创建child节点监听,在节点发生变化时,执行本地方法的notify通知
ZookeeperRegistry.this.notify(url, listener, toUrlsWithEmpty(url, parentPath, currentChilds));
}
});//3
zkListener = listeners.get(listener); //获取registryDirectory对应的zk监听器
}//2
zkClient.create(path, false); //如果providers/configurators/routers节点不存在,创建永久节点
/**……………………………………………………………………………………为providers/configurators/routers节点增加child监听器…………………………………………………………………… */
//向zk对应节点添加监听器,然后交给zkclient、curator等不同的客户端实现,对应框架的watcherImpl包裹实现监听
//返回child节点的信息,对于providers节点,监听其子节点并返回子节点服务
//dubbo%3A%2F%2F192.168.0.101%3A20880%2Fcom.alibaba.dubbo.demo.DemoService%3Fanyhost%3Dtrue%26application%3Ddemotest-provider%26bean.name%3Dcom.alibaba.dubbo.demo.DemoService%26dubbo%3D2.0.2%26generic%3Dfalse%26interface%3Dcom.alibaba.dubbo.demo.DemoService%26methods%3DgetPermissions%26organization%3Ddubbox%26owner%3Dprogrammer%26pid%3D77622%26side%3Dprovider%26timestamp%3D1671863715420
List<String> children = zkClient.addChildListener(path, zkListener);
if (children != null) {
//SUB2.1 toUrlsWithEmpty(url, path, children) 获取消费端所需要消费的url集合
urls.addAll(toUrlsWithEmpty(url, path, children));
}
}//1
//SUB2.2 notify(url, listener, urls)
notify(url, listener, urls);
}//0
} catch (Throwable e) {
throw new RpcException("Failed to subscribe " + url + " to zookeeper " + getUrl() + ", cause: " + e.getMessage(), e);
}
}
这里,subscribe订阅的逻辑如下:
1对于一个consumerURL消费者,需要关注routers、configurators、providers的节点上的数据;
2 ConcurrentMap<NotifyListener, ChildListener> listeners = zkListeners.get(url);为一个registryDirectory的dubbo监听器和zookeeper上的子节点监听器ChildListener的一对一关系。分别在zookeeper上的routers、configurators、providers节点注册子节点监听器childListener,然后,如果这三个路径中,有节点的子节点发生变化,就会执行ZookeeperRegistry.this.notify(url, listener, toUrlsWithEmpty(url, parentPath, currentChilds))通知;
3
//SUB2.1 toUrlsWithEmpty(url, path, children)
这里,是将zookeeper上注册的providers服务,首先与consumer所需要引用的条件进行匹配,如果匹配成功,返回;
如果zookeeper上注册的服务,没有consumer所需要引用的,那么将consumerURL更改协议头为empty,然后添加category到url中。
返回的list
ZookeeperRegistry
private List<URL> toUrlsWithEmpty(URL consumer, String path, List<String> providers) {
//返回providers提供的服务,与消费者需要的服务向匹配的url
List<URL> urls = toUrlsWithoutEmpty(consumer, providers);
if (urls == null || urls.isEmpty()) {
int i = path.lastIndexOf('/');
String category = i < 0 ? path : path.substring(i + 1);
//如果zookeeper上providers上注册的服务,不是consumer所需要引用的,就将consumer://设置为empty,并添加category参数,然后加入zookeeperRegistry上的urls结果中(urls结果表示消费者需要引用的服务url的集合)
URL empty = consumer.setProtocol(Constants.EMPTY_PROTOCOL).addParameter(Constants.CATEGORY_KEY, category);
urls.add(empty);
}
return urls;
}
private List<URL> toUrlsWithoutEmpty(URL consumer, List<String> providers) {
List<URL> urls = new ArrayList<URL>();
if (providers != null && !providers.isEmpty()) {
for (String provider : providers) {
//将zookeeper上注册的providers服务,解码
//dubbo://192.168.0.101:20880/com.alibaba.dubbo.demo.DemoService?anyhost=true&application=demotest-provider&bean.name=com.alibaba.dubbo.demo.DemoService&dubbo=2.0.2&generic=false&interface=com.alibaba.dubbo.demo.DemoService&methods=getPermissions&organization=dubbox&owner=programmer&pid=77622&side=provider×tamp=1671863715420
provider = URL.decode(provider);
if (provider.contains("://")) {
URL url = URL.valueOf(provider);
//判断providerurl是否是consumer需要消费的服务(分别从服务接口、category、group等进行判断)
if (UrlUtils.isMatch(consumer, url)) {
urls.add(url);
}
}
}
}
return urls;
}
//SUB2.2 notify(url, listener, urls)
在执行notify通知前,urls中的服务url为:
FailbackRegistry
protected void notify(URL url, NotifyListener listener, List<URL> urls) {
if (url == null) {
throw new IllegalArgumentException("notify url == null");
}
if (listener == null) {
throw new IllegalArgumentException("notify listener == null");
}
try {
//子类实现通知
doNotify(url, listener, urls);
} catch (Exception t) {
//记录通知失败的url,存入失败集合failedNotified。然后定时的重试
Map<NotifyListener, List<URL>> listeners = failedNotified.get(url);
if (listeners == null) {
failedNotified.putIfAbsent(url, new ConcurrentHashMap<NotifyListener, List<URL>>());
listeners = failedNotified.get(url);
}
listeners.put(listener, urls);
logger.error("Failed to notify for subscribe " + url + ", waiting for retry, cause: " + t.getMessage(), t);
}
}
protected void doNotify(URL url, NotifyListener listener, List<URL> urls) {
super.notify(url, listener, urls);
}
AbstractRegistry
protected void notify(URL url, NotifyListener listener, List<URL> urls) {
if (url == null) {
throw new IllegalArgumentException("notify url == null");
}
if (listener == null) {
throw new IllegalArgumentException("notify listener == null");
}
if ((urls == null || urls.isEmpty())
&& !Constants.ANY_VALUE.equals(url.getServiceInterface())) {
logger.warn("Ignore empty notify urls for subscribe url " + url);
return;
}
if (logger.isInfoEnabled()) {
logger.info("Notify urls for subscribe url " + url + ", urls: " + urls);
}
Map<String, List<URL>> result = new HashMap<String, List<URL>>();
for (URL u : urls) { //1
//判断providerurl是否是consumer需要消费的服务(分别从服务接口、category、group等进行判断)
//对于empty://协议,其从consumer转换过来,因此会匹配
if (UrlUtils.isMatch(url, u)) { //2
String category = u.getParameter(Constants.CATEGORY_KEY, Constants.DEFAULT_CATEGORY);
List<URL> categoryList = result.get(category);
if (categoryList == null) {
categoryList = new ArrayList<URL>();
result.put(category, categoryList);
}
categoryList.add(u);
} //2
} //1
if (result.size() == 0) {
return;
}
//notified为AbstractRegistry上的内存缓存,存储了zookeeper上的全量信息。
Map<String, List<URL>> categoryNotified = notified.get(url);
if (categoryNotified == null) {
notified.putIfAbsent(url, new ConcurrentHashMap<String, List<URL>>());
categoryNotified = notified.get(url);
}
//遍历result中configurators、routers、providers上对应的url地址
for (Map.Entry<String, List<URL>> entry : result.entrySet()) {
String category = entry.getKey();
List<URL> categoryList = entry.getValue(); //rusult的category对应的url的list
categoryNotified.put(category, categoryList);
//SUB2.2.1 saveProperties 文件缓存存储
saveProperties(url);
//SUB2.2.2 listener.notify(categoryList)
listener.notify(categoryList);
}
}
result为:
此时,notified为:
//SUB2.2.1 saveProperties 文件缓存存储
文件缓存中,把notified这一内存缓存上的所有url全部添加到properties上,是全量增加并更新,然后将其写入file文件存储中去。
private void saveProperties(URL url) {
if (file == null) {
return;
}
try {
StringBuilder buf = new StringBuilder();
Map<String, List<URL>> categoryNotified = notified.get(url);
//可知文件缓存是全量增加,每次把notified上的notified.get(url).values全量追加,并增加到properties上
if (categoryNotified != null) {
for (List<URL> us : categoryNotified.values()) {
for (URL u : us) {
if (buf.length() > 0) {
buf.append(URL_SEPARATOR);
}
buf.append(u.toFullString());
}
}
}
//将demoservice和对应的所有url,存入properties属性对上
properties.setProperty(url.getServiceKey(), buf.toString());
long version = lastCacheChanged.incrementAndGet();//提供版本更新的保存,如果版本过低,直接抛弃
/**……………………………………………………………………………………………………文件存储,提供同步保存和异步保存…………………………………………………………………………………………………… */
if (syncSaveFile) {
doSaveProperties(version);
} else {
//异步执行,通过executor框架实现
registryCacheExecutor.execute(new SaveProperties(version));
}
} catch (Throwable t) {
logger.warn(t.getMessage(), t);
}
}
public void doSaveProperties(long version) {
if (version < lastCacheChanged.get()) {
return;//版本过低,直接抛弃
}
if (file == null) {
return;
}
// Save
try {
//创建同名的文件所lock
File lockfile = new File(file.getAbsolutePath() + ".lock");
if (!lockfile.exists()) {
lockfile.createNewFile();
}
RandomAccessFile raf = new RandomAccessFile(lockfile, "rw");
try {
FileChannel channel = raf.getChannel();
try {
FileLock lock = channel.tryLock();
if (lock == null) {
throw new IOException("Can not lock the registry cache file " + file.getAbsolutePath() + ", ignore and retry later, maybe multi java process use the file, please config: dubbo.registry.file=xxx.properties");
}
// Save
try {
if (!file.exists()) {
file.createNewFile();
}
FileOutputStream outputFile = new FileOutputStream(file);
try {
//将properties属性存入文件流
properties.store(outputFile, "Dubbo Registry Cache");
} finally {
outputFile.close();
}
} finally {
lock.release();
}
} finally {
channel.close();
}
} finally {
raf.close();
}
} catch (Throwable e) {
if (version < lastCacheChanged.get()) {
return;
} else {
registryCacheExecutor.execute(new SaveProperties(lastCacheChanged.incrementAndGet()));
}
logger.warn("Failed to save registry store file, cause: " + e.getMessage(), e);
}
}
//SUB2.2.2 listener(即RegistryDirectory).notify(categoryList)
RegistryDirectory
public synchronized void notify(List<URL> urls) {
/**……………………………………………………………………………………………………将consumer订阅的数据,分类存储……………………………………………………………………………………………………… */
//consumer订阅zookeeper中providers/routers/configurators节点下数据,在通知这里,分别对应如下的list<URL>集合
List<URL> invokerUrls = new ArrayList<URL>(); //providers节点下数据
List<URL> routerUrls = new ArrayList<URL>();
List<URL> configuratorUrls = new ArrayList<URL>();
for (URL url : urls) {
String protocol = url.getProtocol();
String category = url.getParameter(Constants.CATEGORY_KEY, Constants.DEFAULT_CATEGORY);
if (Constants.ROUTERS_CATEGORY.equals(category)
|| Constants.ROUTE_PROTOCOL.equals(protocol)) {
routerUrls.add(url);
} else if (Constants.CONFIGURATORS_CATEGORY.equals(category)
|| Constants.OVERRIDE_PROTOCOL.equals(protocol)) {
configuratorUrls.add(url);
} else if (Constants.PROVIDERS_CATEGORY.equals(category)) {
invokerUrls.add(url);
} else {
logger.warn("Unsupported category " + category + " in notified url: " + url + " from registry " + getUrl().getAddress() + " to consumer " + NetUtils.getLocalHost());
}
}
// configurators的处理
if (configuratorUrls != null && !configuratorUrls.isEmpty()) {
/**在调用时候,将override urls转换的协议:
* </br>1.override://0.0.0.0/...( or override://ip:port...?anyhost=true)¶1=value1... means global rules (all of the providers take effect)
* </br>2.override://ip:port...?anyhost=false Special rules (only for a certain provider)
* </br>3.override:// rule is not supported... ,needs to be calculated by registry itself.
* </br>4.override://0.0.0.0/ without parameters means clearing the override */
this.configurators = toConfigurators(configuratorUrls);//empty://不处理
}
// routers的处理
if (routerUrls != null && !routerUrls.isEmpty()) {
List<Router> routers = toRouters(routerUrls);//empty://不处理
if (routers != null) { // null - do nothing
setRouters(routers);
}
}
List<Configurator> localConfigurators = this.configurators; // local reference
// merge override parameters
this.overrideDirectoryUrl = directoryUrl;
if (localConfigurators != null && !localConfigurators.isEmpty()) {
for (Configurator configurator : localConfigurators) {
this.overrideDirectoryUrl = configurator.configure(overrideDirectoryUrl);
}
}
//SUB2.2.2.1 refreshInvoker
refreshInvoker(invokerUrls);
}
//SUB2.2.2.1 refreshInvoker(invokerUrls)--刷新invoker
方法把invokerUrls转换为invoker的map集合。转换规则如下:
1 如果invokerUrls已经转换位invoker,那么不再重新引用,并直接从cache获取(url任何参数更改,都需要重新引用,即构建invoker);
2 如果invokerUrls不为空,表示为最新的invokerUrls;
3 如果传入的invokerUrls为空,表示该规则为override覆盖规则或者route路由规则。需要重新对比,以决定是否重新引用。
private void refreshInvoker(List<URL> invokerUrls) {
//如果invokerURL为empty://,则关闭invoker情况
if (invokerUrls != null && invokerUrls.size() == 1 && invokerUrls.get(0) != null
&& Constants.EMPTY_PROTOCOL.equals(invokerUrls.get(0).getProtocol())) {
this.forbidden = true; // Forbid to access
this.methodInvokerMap = null; // Set the method invoker map to null
destroyAllInvokers(); // Close all invokers
} else {
this.forbidden = false; // Allow to access
Map<String, Invoker<T>> oldUrlInvokerMap = this.urlInvokerMap; // local reference
if (invokerUrls.isEmpty() && this.cachedInvokerUrls != null) {
invokerUrls.addAll(this.cachedInvokerUrls);
} else {
this.cachedInvokerUrls = new HashSet<URL>();
this.cachedInvokerUrls.addAll(invokerUrls);//如果invokersUrls不为null,加入缓存(方便对比)
}
if (invokerUrls.isEmpty()) {//直接返回
return;
}
//SUB2.2.2.1.1 toInvokers(invokerUrls)
Map<String, Invoker<T>> newUrlInvokerMap = toInvokers(invokerUrls);// Translate url list to Invoker map
//SUB2.2.2.1.2 toMethodInvokers(newUrlInvokerMap)
Map<String, List<Invoker<T>>> newMethodInvokerMap = toMethodInvokers(newUrlInvokerMap); // Change method name to map Invoker Map
// state change
// If the calculation is wrong, it is not processed.
if (newUrlInvokerMap == null || newUrlInvokerMap.size() == 0) {
logger.error(new IllegalStateException("urls to invokers error .invokerUrls.size :" + invokerUrls.size() + ", invoker.size :0. urls :" + invokerUrls.toString()));
return;
}
this.methodInvokerMap = multiGroup ? toMergeMethodInvokerMap(newMethodInvokerMap) : newMethodInvokerMap;
this.urlInvokerMap = newUrlInvokerMap;
try {
destroyUnusedInvokers(oldUrlInvokerMap, newUrlInvokerMap); // Close the unused Invoker
} catch (Exception e) {
logger.warn("destroyUnusedInvokers error. ", e);
}
}
}
//SUB2.2.2.1.1 toInvokers(invokerUrls)
当invokerURLs不为null时,dubbo://192.168.0.100:20880/com.alibaba.dubbo.demo.DemoService?anyhost=true&application=demotest-provider&bean.name=com.alibaba.dubbo.demo.DemoService&dubbo=2.0.2&generic=false&interface=com.alibaba.dubbo.demo.DemoService&methods=getPermissions&organization=dubbox&owner=programmer&pid=73971&side=provider×tamp=1672381137107
RegistryDirectory
private Map<String, Invoker<T>> toInvokers(List<URL> urls) {
Map<String, Invoker<T>> newUrlInvokerMap = new HashMap<String, Invoker<T>>();
if (urls == null || urls.isEmpty()) {
return newUrlInvokerMap;
}
Set<String> keys = new HashSet<String>();
String queryProtocols = this.queryMap.get(Constants.PROTOCOL_KEY);
for (URL providerUrl : urls) {
// If protocol is configured at the reference side, only the matching protocol is selected
if (queryProtocols != null && queryProtocols.length() > 0) {
boolean accept = false;
String[] acceptProtocols = queryProtocols.split(",");
for (String acceptProtocol : acceptProtocols) {
if (providerUrl.getProtocol().equals(acceptProtocol)) {
accept = true;
break;
}
}
if (!accept) {
continue;
}
}
if (Constants.EMPTY_PROTOCOL.equals(providerUrl.getProtocol())) {
continue;
}
if (!ExtensionLoader.getExtensionLoader(Protocol.class).hasExtension(providerUrl.getProtocol())) {
logger.error(new IllegalStateException("Unsupported protocol " + providerUrl.getProtocol() + " in notified url: " + providerUrl + " from registry " + getUrl().getAddress() + " to consumer " + NetUtils.getLocalHost()
+ ", supported protocol: " + ExtensionLoader.getExtensionLoader(Protocol.class).getSupportedExtensions()));
continue;
}
/**…………………………………………………………………………构造cache key……………………………………………………………… */
URL url = mergeUrl(providerUrl);//将参数对,合并加入providerurl中
String key = url.toFullString(); // The parameter urls are sorted
if (keys.contains(key)) { // keys保存了是否引用过
continue;
}
keys.add(key);
// Cache key is url that does not merge with consumer side parameters, regardless of how the consumer combines parameters, if the server url changes, then refer again
Map<String, Invoker<T>> localUrlInvokerMap = this.urlInvokerMap; // local reference
Invoker<T> invoker = localUrlInvokerMap == null ? null : localUrlInvokerMap.get(key);
if (invoker == null) { // 如果缓存中没有,重新refer引用
try {
boolean enabled = true;
if (url.hasParameter(Constants.DISABLED_KEY)) {
enabled = !url.getParameter(Constants.DISABLED_KEY, false);
} else {
enabled = url.getParameter(Constants.ENABLED_KEY, true);
}
if (enabled) {
//Invoker InvokerDelegate<T>(protocol.refer(serviceType, url), url, providerUrl) 构造invoker
invoker = new InvokerDelegate<T>(protocol.refer(serviceType, url), url, providerUrl);
}
} catch (Throwable t) {
logger.error("Failed to refer invoker for interface:" + serviceType + ",url:(" + url + ")" + t.getMessage(), t);
}
if (invoker != null) { // Put new invoker in cache
newUrlInvokerMap.put(key, invoker);
}
} else {
newUrlInvokerMap.put(key, invoker);
}
}
keys.clear();
return newUrlInvokerMap;
}
在toInvokers(invokerUrls)方法中,主要是构造invoker
private static class InvokerDelegate<T> extends InvokerWrapper<T> {
private URL providerUrl;
//dubbo://192.168.0.100:20880/com.alibaba.dubbo.demo.DemoService?anyhost=true&application=demotest-consumer&bean.name=com.alibaba.dubbo.demo.DemoService&check=false&dubbo=2.0.2&generic=false&interface=com.alibaba.dubbo.demo.DemoService&methods=getPermissions&organization=dubbox&owner=programmer&pid=75508&qos.enable=false®ister.ip=192.168.0.100&remote.timestamp=1672381137107&side=consumer×tamp=1672387055607
public InvokerDelegate(Invoker<T> invoker, URL url, URL providerUrl) {
super(invoker, url);
//dubbo://192.168.0.100:20880/com.alibaba.dubbo.demo.DemoService?anyhost=true&application=demotest-provider&bean.name=com.alibaba.dubbo.demo.DemoService&dubbo=2.0.2&generic=false&interface=com.alibaba.dubbo.demo.DemoService&methods=getPermissions&organization=dubbox&owner=programmer&pid=73971&side=provider×tamp=1672381137107
this.providerUrl = providerUrl;
}
public InvokerWrapper(Invoker<T> invoker, URL url) {
this.invoker = invoker;
this.url = url;
}