工厂模式的Registry

本想按照服务发布和服务引用来写,但是感觉先理解Registry,后面发布和服务引用里面可以省略掉这里的东西,更方便关注主流程的东西。

工厂模式创建Registry

Registry提供服务的注册,订阅功能,采用工厂模式创建,看图:

registry

左边Registry代表产品体系,右边RegistryFactory工厂生成Registry,每个具体factory生成具体的Registry,分离产品的创建。后期只需要增加具体的工厂生成具体的产品。

RegistryFactory

RegistryFactory也是采用dubbo的扩展点机制加载,默认dubbo,我例子用的是zk,后面讲解也以zk为主。

@SPI("dubbo")
public interface RegistryFactory {

@Adaptive({"protocol"})
Registry getRegistry(URL url);

}

>

AbstractRegistryFactory

public Registry getRegistry(URL url) {
    url = url.setPath(RegistryService.class.getName())
            .addParameter(Constants.INTERFACE_KEY, RegistryService.class.getName())
            .removeParameters(Constants.EXPORT_KEY, Constants.REFER_KEY);
    String key = url.toServiceString();
// 锁定注册中心获取过程,保证注册中心单一实例
    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 {
// 释放锁
        LOCK.unlock();
    }
}
//模板模式,createRegistry有具体的工厂生成
protected abstract Registry createRegistry(URL url);

 ZookeeperRegistryFactory

public class ZookeeperRegistryFactory extends AbstractRegistryFactory {

private ZookeeperTransporter zookeeperTransporter;
//ZookeeperTransporter通过扩展点加载injectExtension
public void setZookeeperTransporter(ZookeeperTransporter zookeeperTransporter) {
this.zookeeperTransporter = zookeeperTransporter;
}
//创建ZookeeperRegistry
public Registry createRegistry(URL url) {
return new ZookeeperRegistry(url, zookeeperTransporter);
}
}

这样就生成了具体的产品ZookeeperRegistry。

Registry

AbstractRegistry

RegistryService接口定义了注册的几个接口,包括注册、订阅、取消注册、取消订阅接口和服务lookup查找接口,在其继承体系中AbstractRegistry主要提供服务的本地文件缓存功能,其线程安全通过:

  1. 异步情况下通过线程池的newFixedThreadPool(1),只有一个核心线程处理来保障;
  2. 建立临时文件.lcok,通过FileLock加锁;
  3. 通过version。

对RegistryService接口的实现,主要是入参的存储,例如

public void register(URL url) {
if (url == null) {
throw new IllegalArgumentException("register url == null");
    }
if (logger.isInfoEnabled()){
        logger.info("Register: " + url);
    }
//服务的本地存储
    registered.add(url);
}

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);
}
//监听存储
listeners.add(listener);
}

其他类似。

FailbackRegistry

继承体系中FailbackRegistry,主要提供服务注册、订阅失败情况的5秒定时重试机制,并且通过模板模式,定义处理注册订阅流程,具体实现由具体子类实现。

例如

@Override
public void register(URL url) {
super.register(url);
        failedRegistered.remove(url);
        failedUnregistered.remove(url);
try {
// 向服务器端发送注册请求
            doRegister(url);
        } catch (Exception e) {
            Throwable t = e;

// 如果开启了启动时检测,则直接抛出异常
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);
}

// 将失败的注册请求记录到失败列表,定时重试
failedRegistered.add(url);
}
}

// ==== 模板方法 ====
protected abstract void doRegister(URL url);

其他订阅等接口类似处理。

ZookeeperRegistry

我配置的注册是zk,所以看下ZookeeperRegistry,主要看下注册和订阅接口的实现。

构造

//构造,这里的url是注册中心的地址
public ZookeeperRegistry(URL url, ZookeeperTransporter zookeeperTransporter) {
    super(url);
if (url.isAnyHost()) {
throw new IllegalStateException("registry address == null");
    }
//group主要用在后面的注册的服务路径里面,如果服务本身没有group使用默认dubbo
    String group = url.getParameter(Constants.GROUP_KEY, DEFAULT_ROOT);
if (! group.startsWith(Constants.PATH_SEPARATOR)) {
group = Constants.PATH_SEPARATOR + group;
    }
this.root = group;
//取得zk的连接
    zkClient = zookeeperTransporter.connect(url);
//zk的监听,zk状态变换,需要将本地失败的注册和订阅重新注册和订阅
    zkClient.addStateListener(new StateListener() {
public void stateChanged(int state) {
if (state == RECONNECTED) {
try {
                    recover();
                } catch (Exception e) {
                    logger.error(e.getMessage(), e);
                }
            }
        }
    });
}

doRegister

//注册
protected void doRegister(URL url) {
try {
//通过zkClient实现,了解zk的应该知道zk类似于目录结构,这里也是建立服务的目录结构
//toUrlPath方法转将注册的服务转换为路径,/group(没有就是dubbo)/接口名/provider(默认provider,根据url中category参数决定,现在基本这几种:consumer、routers、configurators)/服务信息(url形式)
//第二个主要表示创建的临时节点还是持久化的节点,一般路径都是持久的,只有最后服务信息是临时的
        zkClient.create(toUrlPath(url), url.getParameter(Constants.DYNAMIC_KEY, true));
    } catch (Throwable e) {
throw new RpcException("Failed to register " + url + " to zookeeper " + getUrl() + ", cause: " + e.getMessage(), e);
    }
}

doSubscribe

//订阅服务,类似zk的watch吧,入参NotifyListener为回调接口
protected void doSubscribe(final URL url, final NotifyListener listener) {
try {
//这个if暂时不关心,最后还是到else那里
if (Constants.ANY_VALUE.equals(url.getServiceInterface())) {
            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() {
public void childChanged(String parentPath, List<String> currentChilds) {
for (String child : currentChilds) {
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.size() > 0) {
                anyServices.addAll(services);
for (String service : services) {
                    subscribe(url.setPath(service).addParameters(Constants.INTERFACE_KEY, service, 
                            Constants.CHECK_KEY, String.valueOf(false)), listener);
                }
            }
        } else {
            List<URL> urls = new ArrayList<URL>();
//url中的参数category可以配置多个,相当于watch多个目录,这里for下
for (String path : toCategoriesPath(url)) {
                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() {
public void childChanged(String parentPath, List<String> currentChilds) {
                            ZookeeperRegistry.this.notify(url, listener, toUrlsWithEmpty(url, parentPath, currentChilds));
                        }
                    });
                    zkListener = listeners.get(listener);
                }
//watch的目录不存在就创建
                zkClient.create(path, false);
                List<String> children = zkClient.addChildListener(path, zkListener);
if (children != null) {
//toUrlsWithEmpty是将watch的路径转换为url,dubbo中服务信息,参数传递很多都是url来处理的
                    urls.addAll(toUrlsWithEmpty(url, path, children));
                }
            }
//这里最重要的,回调listen
            notify(url, listener, urls);
        }
    } catch (Throwable e) {
throw new RpcException("Failed to subscribe " + url + " to zookeeper " + getUrl() + ", cause: " + e.getMessage(), e);
    }
}

notify方法最后的处理逻辑在AbstractRegistry里面。

//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.size() == 0) 
&& ! 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) {
//isMatch主要做匹配,group,接口,版本,watch目录等是否一致
if (UrlUtils.isMatch(url, u)) {
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);
        }
    }
if (result.size() == 0) {
return;
    }
Map<String, List<URL>> categoryNotified = notified.get(url);
if (categoryNotified == null) {
        notified.putIfAbsent(url, new ConcurrentHashMap<String, List<URL>>());
        categoryNotified = notified.get(url);
    }
    for (Map.Entry<String, List<URL>> entry : result.entrySet()) {
String category = entry.getKey();
List<URL> categoryList = entry.getValue();
categoryNotified.put(category, categoryList);
//缓存到本地文件
saveProperties(url);
//回调listen
listener.notify(categoryList);
    }
}

做本地缓存和listen回调。

取消注册和取消订阅处理方式差不多,不做解释。

出处:https://blog.csdn.net/xiaoxufox/article/details/75256105
posted @ 2021-03-07 11:29  十点书屋  阅读(226)  评论(0编辑  收藏  举报