更新時間:2021-09-08 12:01:17 來源:動力節點 瀏覽1471次
關于注冊中心,Dubbo提供了多個實現方式,有比較成熟的使用zookeeper 和 redis 的實現,也有實驗性質的Multicast實現。
Zookeeper是Apacahe Hadoop的子項目,是一個樹型的目錄服務,支持變更推送,適合作為Dubbo服務的注冊中心,工業強度較高,可用于生產環境,
所以Zookeeper注冊中心也是Dubbo推薦使用也是使用度比較高的注冊中心。
Zookeeper注冊中心支持以下功能:
當提供者出現斷電等異常停機時,注冊中心能自動刪除提供者信息。
當注冊中心重啟時,能自動恢復注冊數據,以及訂閱請求。
當會話過期時,能自動恢復注冊數據,以及訂閱請求。
當設置 check="false" 時,記錄失敗注冊和訂閱請求,后臺定時重試。
可通過 username="admin" password="1234" 設置zookeeper登錄信息。
可通過 group="dubbo" 設置zookeeper的根節點,不設置將使用無根樹。
支持號通配符 group="" version="*" ,可訂閱服務的所有分組和所有版本的提供者。
zookeeper注冊中心的源碼為com.alibaba.dubbo.registry.zookeeper.ZookeeperRegistry。
ZookeeperRegistry 類繼承自 FailbackRegistry,FailbackRegistry 又繼承自 AbstractRegistry,AbstractRegistry實現了 RegistryService 接口。
因此我們閱讀源碼順序為:RegistryService -> AbstractRegistry -> FailbackRegistry -> ZookeeperRegistry
關于RegistryService接口RegistryService,Dubbo提供了詳細的注釋。
public interface RegistryService {
/**
* 注冊數據,比如:提供者地址,消費者地址,路由規則,覆蓋規則,等數據。
*
* 注冊需處理契約:<br>
* 1. 當URL設置了check=false時,注冊失敗后不報錯,在后臺定時重試,否則拋出異常。<br>
* 2. 當URL設置了dynamic=false參數,則需持久存儲,否則,當注冊者出現斷電等情況異常退出時,需自動刪除。<br>
* 3. 當URL設置了category=routers時,表示分類存儲,缺省類別為providers,可按分類部分通知數據。<br>
* 4. 當注冊中心重啟,網絡抖動,不能丟失數據,包括斷線自動刪除數據。<br>
* 5. 允許URI相同但參數不同的URL并存,不能覆蓋。<br>
*
* @param url 注冊信息,不允許為空,如:dubbo://10.20.153.10/com.alibaba.foo.BarService?version=1.0.0&application=kylin
*/
void register(URL url);
/**
* 取消注冊.
*
* 取消注冊需處理契約:<br>
* 1. 如果是dynamic=false的持久存儲數據,找不到注冊數據,則拋IllegalStateException,否則忽略。<br>
* 2. 按全URL匹配取消注冊。<br>
*
* @param url 注冊信息,不允許為空,如:dubbo://10.20.153.10/com.alibaba.foo.BarService?version=1.0.0&application=kylin
*/
void unregister(URL url);
/**
* 訂閱符合條件的已注冊數據,當有注冊數據變更時自動推送.
*
* 訂閱需處理契約:<br>
* 1. 當URL設置了check=false時,訂閱失敗后不報錯,在后臺定時重試。<br>
* 2. 當URL設置了category=routers,只通知指定分類的數據,多個分類用逗號分隔,并允許星號通配,表示訂閱所有分類數據。<br>
* 3. 允許以interface,group,version,classifier作為條件查詢,如:interface=com.alibaba.foo.BarService&version=1.0.0<br>
* 4. 并且查詢條件允許星號通配,訂閱所有接口的所有分組的所有版本,或:interface=*&group=*&version=*&classifier=*<br>
* 5. 當注冊中心重啟,網絡抖動,需自動恢復訂閱請求。<br>
* 6. 允許URI相同但參數不同的URL并存,不能覆蓋。<br>
* 7. 必須阻塞訂閱過程,等第一次通知完后再返回。<br>
*
* @param url 訂閱條件,不允許為空,如:consumer://10.20.153.10/com.alibaba.foo.BarService?version=1.0.0&application=kylin
* @param listener 變更事件監聽器,不允許為空
*/
void subscribe(URL url, NotifyListener listener);
/**
* 取消訂閱.
*
* 取消訂閱需處理契約:<br>
* 1. 如果沒有訂閱,直接忽略。<br>
* 2. 按全URL匹配取消訂閱。<br>
*
* @param url 訂閱條件,不允許為空,如:consumer://10.20.153.10/com.alibaba.foo.BarService?version=1.0.0&application=kylin
* @param listener 變更事件監聽器,不允許為空
*/
void unsubscribe(URL url, NotifyListener listener);
/**
* 查詢符合條件的已注冊數據,與訂閱的推模式相對應,這里為拉模式,只返回一次結果。
*
* @see com.alibaba.dubbo.registry.NotifyListener#notify(List)
* @param url 查詢條件,不允許為空,如:consumer://10.20.153.10/com.alibaba.foo.BarService?version=1.0.0&application=kylin
* @return 已注冊信息列表,可能為空,含義同{@link com.alibaba.dubbo.registry.NotifyListener#notify(List<URL>)}的參數。
*/
List<URL> lookup(URL url);
}
從構造函數可以看出 AbstractRegistry 抽象類主要是提供了對注冊中心數據的文件緩存。
String filename = url.getParameter(Constants.FILE_KEY, System.getProperty("user.home") + "/.dubbo/dubbo-registry-" + url.getHost() + ".cache");
File file = null;
if (ConfigUtils.isNotEmpty(filename)) {
file = new File(filename);
if(! file.exists() && file.getParentFile() != null && ! file.getParentFile().exists()){
if(! file.getParentFile().mkdirs()){
throw new IllegalArgumentException("Invalid registry store file " + file + ", cause: Failed to create directory " + file.getParentFile() + "!");
}
}
}
Dubbo會在用戶目錄創建./dubbo文件夾及緩存文件,以windows為例,生成的緩存文件為:C:\Users\你的登錄用戶名/.dubbo/dubbo-registry-127.0.0.1.cache
FailbackRegistry 顧名思義是主要提供的是失敗自動恢復,同樣看一下構造函數,在構造函數中會通過 ScheduledExecutorService 一直執行Retry方法進行重試。
public FailbackRegistry(URL url) {
super(url);
int retryPeriod = url.getParameter(Constants.REGISTRY_RETRY_PERIOD_KEY, Constants.DEFAULT_REGISTRY_RETRY_PERIOD);
this.retryFuture = retryExecutor.scheduleWithFixedDelay(new Runnable() {
public void run() {
try {
retry();
} catch (Throwable t) {
logger.error("Unexpected error occur at failed retry, cause: " + t.getMessage(), t);
}
}
}, retryPeriod, retryPeriod, TimeUnit.MILLISECONDS);
}
retry()方法主要的從各個操作中的失敗列表取出失敗的操作進行重試。
protected void retry() {
if (! failedRegistered.isEmpty()) {
Set<URL> failed = new HashSet<URL>(failedRegistered);
if (failed.size() > 0) {
if (logger.isInfoEnabled()) {
logger.info("Retry register " + failed);
}
try {
for (URL url : failed) {
try {
doRegister(url);
failedRegistered.remove(url);
} catch (Throwable t) { // 忽略所有異常,等待下次重試
logger.warn("Failed to retry register " + failed + ", waiting for again, cause: " + t.getMessage(), t);
}
}
} catch (Throwable t) { // 忽略所有異常,等待下次重試
logger.warn("Failed to retry register " + failed + ", waiting for again, cause: " + t.getMessage(), t);
}
}
}
if(! failedUnregistered.isEmpty()) {
//......
doUnregister(url);
failedUnregistered.remove(url);
//.....
}
if (! failedSubscribed.isEmpty()) {
//.....
}
if (! failedUnsubscribed.isEmpty()) {
//.......
}
if (! failedNotified.isEmpty()) {
//.......
}
}
同時提供了幾個抽象方法
ZookeeperRegistry 類
ZookeeperRegistry流程
服務提供者啟動時
向/dubbo/com.foo.BarService/providers目錄下寫入自己的URL地址。
服務消費者啟動時
訂閱/dubbo/com.foo.BarService/providers目錄下的提供者URL地址。
并向/dubbo/com.foo.BarService/consumers目錄下寫入自己的URL地址。
監控中心啟動時
訂閱/dubbo/com.foo.BarService目錄下的所有提供者和消費者URL地址。
ZookeeperRegistry 主要是實現了FailbackRegistry的那幾個抽象方法。本次也主要分析 doRegister(),doSubscribe()這兩個方法。
protected void doRegister(URL url) {
try {
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);
}
}
public void create(String path, boolean ephemeral) {
int i = path.lastIndexOf('/');
if (i > 0) {
create(path.substring(0, i), false);
}
if (ephemeral) {
createEphemeral(path);
} else {
createPersistent(path);
}
}
}
doRegister() 主要是調用zkClient創建一個節點。 create()以遞歸的方式創建節點,通過判斷Url中dynamic=false 判斷創建的是持久化節點還是臨時節點。
創建的結果為:
doSubscribe() 訂閱Zookeeper節點是通過創建ChildListener來實現的具體調用的方法是 addChildListener()
addChildListener()又調用 AbstractZookeeperClient.addTargetChildListener()然后調用subscribeChildChanges()
最后調用ZkclientZookeeperClient ZkClientd.watchForChilds()
protected void doSubscribe(final URL url, final NotifyListener listener) {
//....
List<String> children = zkClient.addChildListener(path, zkListener);
//.....
}
public List<String> addChildListener(String path, final ChildListener listener) {
//......
return addTargetChildListener(path, targetListener);
}
```java
public List<String> addTargetChildListener(String path, final IZkChildListener listener) {
return client.subscribeChildChanges(path, listener);
}
public List<String> subscribeChildChanges(String path, IZkChildListener listener) {
//.....
return watchForChilds(path);
}
以上就是動力節點小編介紹的"Dubbo注冊中心分析",希望對大家有幫助,想了解更多可查看Dubbo教程。動力節點在線學習教程,針對沒有任何Java基礎的讀者學習,讓你從入門到精通,主要介紹了一些Java基礎的核心知識,讓同學們更好更方便的學習和了解Java編程,感興趣的同學可以關注一下。
0基礎 0學費 15天面授
有基礎 直達就業
業余時間 高薪轉行
工作1~3年,加薪神器
工作3~5年,晉升架構
提交申請后,顧問老師會電話與您溝通安排學習