ETCD數據監聽

ETCD

更新/刪除/重新設置鍵的ttl 都會觸發watcher , 但是如果在body中增加refresh=true , 更新ttl(必須存在) , 將不會出發watcher事件。

?wait=true        監聽當前節點
?recursive=true    遞歸監聽當前節點和子目錄
?waitIndex=xxx   監聽過去已經發生的。過去值的查詢或監聽, 必選與wait一起使用。

watch 一個ttl自刪除的key時,收到如下 “expire” action。

{
     "action": "expire",
     "node": {
         "createdIndex": 2223,
         "key": "/message",
         "modifiedIndex": 2224
     },
     "prevNode": {
         "createdIndex": 2223,
         "expiration": "2018-11-12T09:25:00.028597482Z",
         "key": "/message",
         "modifiedIndex": 2223,
         "value": ""
     }
 }

GET 對過去的鍵值操作進行查詢:類似上面提到的監控,在其基礎上指定過去某次修改的索引編號,就可以查詢歷史操作。默認可查詢的歷史記錄爲1000條。
?waitIndex=xxx   監聽過去已經發生的。 這個在確保在watch命令中,沒有丟失事件非常有用。例如:我們反覆watch 我們得到節點的 modifiedIndex+1。

因爲 node的modifiedIndex的值是不連續,如果waitIndex的值沒有相應modifiedIndex,返回比它大的最近的modifedIndex的節點信息。 如果大於節點中所有的modifiedIndex,等待,直到節點的modifiedIndex值大於等於waitIndex的值。

即使刪除key後,也可以查詢歷史數據。

store中有一個全局的currentIndex,每次變更,index會加1.然後每個event都會關聯到currentIndex.

當客戶端調用watch接口(參數中增加 wait參數)時,如果監聽的waitIndex 不存在與key對應的EventHistroy 中(currentIndex >= waitIndex),並且key對應的modifyIndex > waitIndex , 則會查找到第一個大於waitIndex 的數據進行展示。
如果歷史表中沒有或者請求沒有帶 waitIndex,則放入WatchHub中,每個key會關聯一個watcher列表。 當有變更操作時,變更生成的event會放入EventHistroy表中,同時通知和該key相關的watcher。

注意:
1. 必須與 wait 一起使用;
2. curl 中url需要使用引號。
3. etcd 僅僅保留系統中所有key最近的1000條event,建議將獲取到的response發送到另一個線程處理,而不是處理response而阻塞watch。
4. 如果watch超出了etcd保存的最近1000條,建議get後使用response header中的 X-Etcd-Index + 1進行重新watch,而不是使用node中的modifiedIndex+1. 因爲  X-Etcd-Index  永遠大於等於modifiedIndex, 使用modifiedIndex可能會返回401錯誤碼,同樣超出。
5. long polling可能會被服務器關閉,如超時或服務器關閉。導致僅僅收到header 200OK,body爲空,此時應重新watch。

etcd4j 測試案例:

import mousio.etcd4j.EtcdClient;
import mousio.etcd4j.promises.EtcdResponsePromise;
import mousio.etcd4j.responses.EtcdKeysResponse;
import org.junit.Before;
import org.junit.Test;

import java.io.IOException;
import java.net.URI;
import java.util.List;

/**
 * @Description
 * @auther bozhu
 * @create 11\ 12\ 2018
 */
public class EtcdClientAddListenerTest {
    EtcdClient client = null;

    @Before
    public void executeBefore() {
        client = new EtcdClient(URI.create("http://172.16.85.20:2379"));
    }

    private String getConfig(String configFile , EtcdKeysResponse dataTree) {
        List<EtcdKeysResponse.EtcdNode> nodes = null;
        if(null != dataTree ) {
            return dataTree.getNode().getValue();
        }
        System.out.println("Etcd configFile"+ configFile+"is not exist,Please Check");
        return null;
    }
    @Test
    public void testStask() throws Exception {
        for (int i = 1; i < 5000; i++) {
            EtcdKeysResponse etcdKeysResponse = client.put("/xdriver/test/value", "" + i).send().get();
            System.out.println(etcdKeysResponse);
        }
    }
    @Test
    public void testListener() throws Exception{
        this.startListenerThread(client , "/xdriver/test/value");
        Thread.sleep(1000000L);
    }
    public void startListenerThread(EtcdClient client , String dir) throws Exception{
        EtcdKeysResponse etcdKeysResponse = client.get(dir).send().get();
        System.out.println(etcdKeysResponse.getNode().getValue());
        new Thread(()->{
            startListener(client,dir,etcdKeysResponse.getNode().getModifiedIndex() + 1);
        }).start();
    }
    public void startListener(final EtcdClient client , final String dir , long waitIndex)  {
        EtcdResponsePromise<EtcdKeysResponse> promise = null;
        try {
            // 如果監聽的waitIndex 不存在與key對應的EventHistroy 中(currentIndex >= waitIndex) ,
            // 並且key對應的modifyIndex > waitIndex , 則會查找到第一個大於waitIndex 的數據進行展示
            promise = client.get(dir).waitForChange(waitIndex).consistent().send();
        } catch (IOException e) {
            e.printStackTrace();
        }
        promise.addListener(promisea -> {
            try {
                EtcdKeysResponse etcdKeysResponse = promisea.get();
                new Thread(() -> {startListener(client , dir , etcdKeysResponse.getNode().getModifiedIndex() + 1);}).start();
                String config = getConfig(dir, etcdKeysResponse);//加載配置項
                System.out.println(config);
            } catch (Exception e) {
                e.printStackTrace();
                System.out.println("listen etcd 's config change cause exception:{}"+e.getMessage());
            }
        });
    }
}

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章