1.ZookeeperRegister模塊簡介
本文章針對Zookeeper註冊中心,註冊,訂閱源碼設計導讀;至於Zookeeper的優勢,特點我們不做太多講解;
下圖爲zookeeper存放數據的原理,爲樹狀;因此我們這個模塊本質要做的就是:
- 將Zookeeper的數據存放,刪除:Dubbo集成CuratorFramework,客戶端可以對Redis進行連接,刪除,修改,新增節點,添加監聽器等操作,來滿足我們的業務需求;
數據結構分析:
- dubbo的Root層是根目錄,通過<dubbo:registry group="dubbo" />的“group”來設置zookeeper的根節點,默認是“dubbo”。
- Service層是服務接口的全名。
- Type層是分類,一共有四種分類,分別是providers(服務提供者列表)、consumers(服務消費者列表)、routes(路由規則列表)、configurations(配置規則列表)。
- URL層:根據不同的Type目錄:可以有服務提供者 URL 、服務消費者 URL 、路由規則 URL 、配置規則 URL 。不同的Type關注的URL不同。
ZookeeperRegistry
這個類繼承FailbackRegistry,在原本的重試機制等下進行了Zoookeeper註冊中心的實現;
基本屬性:
// 日誌輸出
private final static Logger logger = LoggerFactory.getLogger(ZookeeperRegistry.class);
// 默認根節點
private final static String DEFAULT_ROOT = "dubbo";
// 就是group
private final String root;
// 服務集合
private final Set<String> anyServices = new ConcurrentHashSet<>();
// 監聽器集合
private final ConcurrentMap<URL, ConcurrentMap<NotifyListener, ChildListener>> zkListeners = new ConcurrentHashMap<>();
// zk客戶端,默認爲CuratorFramework
private final ZookeeperClient zkClient;
初始化方法:
public ZookeeperRegistry(URL url, ZookeeperTransporter zookeeperTransporter) {
super(url);
if (url.isAnyHost()) {
throw new IllegalStateException("registry address == null");
}
// 獲取group信息,默認爲"dubbo"
String group = url.getParameter(GROUP_KEY, DEFAULT_ROOT);
// group->/group
if (!group.startsWith(PATH_SEPARATOR)) {
// /dubbo開頭
group = PATH_SEPARATOR + group;
}
this.root = group;
// 客戶端連接
zkClient = zookeeperTransporter.connect(url);
// 監聽狀態
zkClient.addStateListener((state) -> {
if (state == StateListener.RECONNECTED) {
logger.warn("Trying to fetch the latest urls, in case there're provider changes during connection loss.\n" +
" Since ephemeral ZNode will not get deleted for a connection lose, " +
"there's no need to re-register url of this instance.");
ZookeeperRegistry.this.fetchLatestAddresses();
} else if (state == StateListener.NEW_SESSION_CREATED) {
logger.warn("Trying to re-register urls and re-subscribe listeners of this instance to registry...");
try {
ZookeeperRegistry.this.recover();
} catch (Exception e) {
logger.error(e.getMessage(), e);
}
} else if (state == StateListener.SESSION_LOST) {
logger.warn("Url of this instance will be deleted from registry soon. " +
"Dubbo client will try to re-register once a new session is created.");
} else if (state == StateListener.SUSPENDED) {
} else if (state == StateListener.CONNECTED) {
}
});
}
public boolean isAvailable()存活,public void destroy()銷燬方法
@Override
public boolean isAvailable() {
return zkClient.isConnected();
}
@Override
public void destroy() {
// 調用父類方法
super.destroy();
try {
// 關閉連接
zkClient.close();
} catch (Exception e) {
logger.warn("Failed to close zookeeper client " + getUrl() + ", cause: " + e.getMessage(), e);
}
}
註冊,取消註冊方法:調用zookeeper客戶端進行刪除,增加節點
@Override
public void doRegister(URL url) {
try {
// 創建節點
zkClient.create(toUrlPath(url), url.getParameter(DYNAMIC_KEY, true));
} catch (Throwable e) {
throw new RpcException("Failed to register " + url + " to zookeeper " + getUrl() + ", cause: " + e.getMessage(), e);
}
}
@Override
public void doUnregister(URL url) {
try {
// 刪除節點
zkClient.delete(toUrlPath(url));
} catch (Throwable e) {
throw new RpcException("Failed to unregister " + url + " to zookeeper " + getUrl() + ", cause: " + e.getMessage(), e);
}
}
訂閱方法:public void doSubscribe(final URL url, final NotifyListener listener)
@Override
public void doSubscribe(final URL url, final NotifyListener listener) {
try {
// 如果是訂閱所有Service,監控中心就會
if (ANY_VALUE.equals(url.getServiceInterface())) {
String root = toRootPath();
// 獲取url監聽器
ConcurrentMap<NotifyListener, ChildListener> listeners = zkListeners.get(url);
// 初始化監聽器集合
if (listeners == null) {
zkListeners.putIfAbsent(url, new ConcurrentHashMap<>());
listeners = zkListeners.get(url);
}
// 節點監聽器
ChildListener zkListener = listeners.get(listener);
if (zkListener == null) {
// 創建監聽器:
listeners.putIfAbsent(listener, (parentPath, currentChilds) -> {
for (String child : currentChilds) {
child = URL.decode(child);
// 如果沒有這個節點,添加,然後訂閱
if (!anyServices.contains(child)) {
anyServices.add(child);
subscribe(url.setPath(child).addParameters(INTERFACE_KEY, child,
Constants.CHECK_KEY, String.valueOf(false)), listener);
}
}
});
zkListener = listeners.get(listener);
}
// 創建serive節點
zkClient.create(root, false);
List<String> services = zkClient.addChildListener(root, zkListener);
if (CollectionUtils.isNotEmpty(services)) {
for (String service : services) {
service = URL.decode(service);
anyServices.add(service);
subscribe(url.setPath(service).addParameters(INTERFACE_KEY, service,
Constants.CHECK_KEY, String.valueOf(false)), listener);
}
}
} else {
List<URL> urls = new ArrayList<>();
for (String path : toCategoriesPath(url)) {
ConcurrentMap<NotifyListener, ChildListener> listeners = zkListeners.get(url);
if (listeners == null) {
zkListeners.putIfAbsent(url, new ConcurrentHashMap<>());
listeners = zkListeners.get(url);
}
// 添加監聽器
ChildListener zkListener = listeners.get(listener);
if (zkListener == null) {
listeners.putIfAbsent(listener, (parentPath, currentChilds) -> ZookeeperRegistry.this.notify(url, listener, toUrlsWithEmpty(url, parentPath, currentChilds)));
zkListener = listeners.get(listener);
}
// 創建節點
zkClient.create(path, false);
List<String> children = zkClient.addChildListener(path, zkListener);
if (children != null) {
urls.addAll(toUrlsWithEmpty(url, path, children));
}
}
// 監聽數據變化
notify(url, listener, urls);
}
} catch (Throwable e) {
throw new RpcException("Failed to subscribe " + url + " to zookeeper " + getUrl() + ", cause: " + e.getMessage(), e);
}
}
取消訂閱:
@Override
public void doUnsubscribe(URL url, NotifyListener listener) {
ConcurrentMap<NotifyListener, ChildListener> listeners = zkListeners.get(url);
if (listeners != null) {
// 調用可客戶端,清除節點監聽器
ChildListener zkListener = listeners.get(listener);
if (zkListener != null) {
if (ANY_VALUE.equals(url.getServiceInterface())) {
String root = toRootPath();
zkClient.removeChildListener(root, zkListener);
} else {
for (String path : toCategoriesPath(url)) {
zkClient.removeChildListener(path, zkListener);
}
}
}
}
}
LookUp
@Override
public List<URL> lookup(URL url) {
if (url == null) {
throw new IllegalArgumentException("lookup url == null");
}
try {
List<String> providers = new ArrayList<String>();
// 遍歷分組類別
for (String path : toCategoriesPath(url)) {
// 獲得子節點
List<String> children = zkClient.getChildren(path);
if (children != null) {
providers.addAll(children);
}
}
// 獲得 providers 中,和 consumer 匹配的 URL 數組
return toUrlsWithoutEmpty(url, providers);
} catch (Throwable e) {
throw new RpcException("Failed to lookup " + url + " from zookeeper " + getUrl() + ", cause: " + e.getMessage(), e);
}
}
ZookeeperRegisterFactory
工廠模式
public class ZookeeperRegistryFactory extends AbstractRegistryFactory {
private ZookeeperTransporter zookeeperTransporter;
/**
* Invisible injection of zookeeper client via IOC/SPI
* @param zookeeperTransporter
*/
public void setZookeeperTransporter(ZookeeperTransporter zookeeperTransporter) {
this.zookeeperTransporter = zookeeperTransporter;
}
@Override
public Registry createRegistry(URL url) {
return new ZookeeperRegistry(url, zookeeperTransporter);
}
}
總結:
- zookeeperRegister實現了Zookeeper註冊中心的註冊,訂閱等等行爲實現;
- 針對於Zkclient後續通信模塊會講述;