91超碰碰碰碰久久久久久综合_超碰av人澡人澡人澡人澡人掠_国产黄大片在线观看画质优化_txt小说免费全本

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

zookeeper 實現分布式鎖安全用法

發布時間:2020-07-21 15:02:04 來源:網絡 閱讀:1702 作者:王清培 欄目:軟件技術
  • 背景
  • ConnectionLoss 鏈接丟失
  • SessionExpired 會話過期
  • 繞開 zookeeper broker 進行狀態通知
  • leader 選舉與zkNode 斷開
  • 做好冪等
  • 靜態擴容、動態擴容

背景

分布式鎖現在用的越來越多,通常用來協調多個并發任務。在一般的應用場景中存在一定的不安全用法,不安全用法會帶來多個master在并行執行,業務或數據可能存在重復計算帶來的副作用,在沒有拿到lock的情況下扮演者master等諸如此類。

要想準確的拿到分布式鎖,并且準確的捕獲在分布式情況下鎖的動態轉移狀態,需要處理網絡變化帶來的連鎖反應。比如常見的 session expire、connectionLoss,在設置lock狀態的時候我們如何保證準確拿到lock。

在設計任務的時候我們需要具有 stop point 的策略,這個策略是用來在感知到lock丟失后能夠交付執行權的機制。但是是否需要這么嚴肅的處理這個問題還取決于業務場景,比如下游的任務已經做好冪等也無所謂重復計算。 但是在有些情況下確實需要嚴肅精準控制。

ConnectionLoss 鏈接丟失

先說第一個場景,connectionLoss事件,此事件表示提交的commit有可能執行成功也有可能執行失敗,成功是指在zookeeper broker 中執行成功但是返回的時候tcp斷開了,導致未能拿到返回的狀態。失敗是指根本就沒有提交到zookeper broker中鏈接就斷開了。

所以在我們獲取lock的時候需要做 connectionLoss 事件處理,我們看個例子。

protected void runForMaster() {

        logger.info("master:run for master.");

        AsyncCallback.StringCallback createCallback =
                (rc, path, ctx, name) -> {
                    switch (KeeperException.Code.get(rc)) {
                        case CONNECTIONLOSS:
                            checkMaster();//鏈接失效檢查znode設置是否成功
                            return;
                        case OK:
                            isLeader = true;
                            logger.info("master:I'm the leader serverId:" + serverId);
                            addMasterWatcher();//監控 master znode
                            this.takeLeadership();//執行leader權利
                            break;
                        case NODEEXISTS:
                            isLeader = false;
                            String serverId = this.getMasterServerId();
                            this.takeBackup(serverId);
                            break;

                    }
                };

        zk.create(rootPath + "/master", serverId.getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE,
                CreateMode.EPHEMERAL, createCallback, null);//創建master節點
    }

    /**
     * check master 循環檢查
     */
    private void checkMaster() {

        AsyncCallback.DataCallback masterCheckCallback =
                (rc, path, ctx, data[], stat) -> {
                    switch (KeeperException.Code.get(rc)) {
                        case CONNECTIONLOSS:
                            checkMaster();
                            return;
                        case NONODE:
                            runForMaster();
                            return;
                        default: {
                            String serverId = this.getMasterServerId();
                            isLeader = serverId.equals(this.serverId);
                            if (BooleanUtils.isNotTrue(isLeader)) {
                                this.takeBackup(serverId);
                            } else {
                                this.takeLeadership();
                            }
                        }

                        return;
                    }
                };

        zk.getData(masterZnode, false, masterCheckCallback, null);
    }

這里的master表示具有執行權,只有成功拿到master 角色才能履行master權利。

runForMaster 方法一旦發現有connectionLoss就發起checkMaster進行檢查,同時checkMaster方法中也進行connectinLoss檢查,直到拿到明確的狀態為止。在此時有可能有另外的節點獲取到了master角色,那么當前節點就做好backup等待機會。

我們需要捕獲zookeeper所有的狀態變化,要知道master什么時候失效做好申請準備,當自己是master時候會話失效需要釋放master權利。

/**
     * 監控 master znode 做 master/slave 切換
     */
    private void addMasterWatcher() {

        AsyncCallback.StatCallback addMasterWatcher = (rc, path, ctx, stat) -> {
            switch (KeeperException.Code.get(rc)) {
                case CONNECTIONLOSS:
                    addMasterWatcher();
                    break;
                case OK:
                    if (stat == null) {
                        runForMaster();//master 已經不存在
                    } else {
                        logger.info("master:watcher master znode ok.");
                    }
                    break;
                case NONODE:
                    logger.info("master:master znode delete.");
                    runForMaster();
                    break;
            }
        };

        zk.exists(masterZnode, MasterExistsWatcher, addMasterWatcher, null);
    }

通過zookeeper watcher 機制來進行狀態監聽,保持與網絡、zookeeper狀態變化聯動。

SessionExpired 會話過期

我們在來看第二個問題,第一個問題是獲取lock的時候如何保證一定可以準確拿到狀態,這里狀態是指master角色或者backup角色。

當我們成功與zookeeper broker建立鏈接,成功獲取到master角色并且正在履行master義務時突然zookeeper通知session過期,SessionExpired事件表示zookeeper將會刪除所有當前會話創建的臨時znode,也就意味這master znode將會被其他會話創建。

此時我們需要將自己的master 權利交出去,也就是我們必須放下目前手上執行的任務,這個停止的狀態必須能夠反應到全局。此時最容易出現到問題就是,我們已經不是master了但是還在偷偷到執行master權利,通過dashboard會看到很奇怪的問題,不是master的服務器還在執行。

case SESSIONEXPIRED:
    //執行 stop point 通知
    this.stopPoint();
    break;

所以這里需要我們在設計任務時有stop point 策略,類似jvm的safe point,隨時響應全局停止。

繞開 zookeeper broker 進行狀態通知

還有一種常見的使用方式是繞開zookeeper 來做狀態通知。

我們都知道zookeeper cluster 是由多臺實例組成,每個實例都在全國甚至全球的不同地方,leader到這些節點之間都有很大的同步延遲差異,zookeeper內部采用法定人數的兩階段提交的方式來完成一次commit。

比如有7個實例構成一套zookeeper cluster ,當一次client 寫入 commit只需要集群中有超過半數完成寫入就算這次commit提交成功了。但是cleint得到這個提交成功的響應之后立馬執行接下來的任務,這個任務可能是讀取某個znode下的所有狀態數據,此時有可能無法讀取到這個狀態。

如果是分布式鎖的話很有可能是鎖在zk集群中的轉移無法和client集群保持一直。所以只要是基于zookeeper做集群調度就要完全原來zookeeper來做狀態通知,不可以繞開zookeeper來自行調度。

leader 選舉與zkNode 斷開

zookeeper leader 是所有狀態變更的串行化器,add、update、delete都需要leader來處理,然后傳播給所有follower、observer節點。

所有的session是保存在leader中的,所有的watcher是保存在client鏈接的zookeper node中的,這里兩個場景都會導致狀態遷移的通知不準時。

如果zookeeper是由多數據中心構成的一套集群,存在異地同步延遲的問題,leader是肯定會放在寫入的數據中心中,同時zid應該是最大的,甚至是一組高zid的機器都在寫入的數據中心中,這樣保證leader宕機也不會輕易導致leader選舉到其他數據中心。

但是follower、observer都會有client在使用,也會有在這些節點進行協調的分布式集群。

先說leader選舉導致異地節點延遲感知問題,比如當前 zookeeper cluster 有7臺機器構成:

dataCenter shanghai:zid=100、zid=80、zid=50
dataCenter beijing: zid=10、zid=20
dataCenter shenzhen:zid=30、zid=40

由于網絡問題集群發生leader選舉,zid=100暫時脫離集群,zid=80成為leader,這里不考慮日志新舊問題,優先使用zid進行選舉。

由于集群中所有的session是保存在原來zid=100的機器中的,新leader沒有任何session信息,所以將導致所有session丟失。

session的保持時間是取決于我們設置的sessinoTimeout時間來的,client通過ping來將心跳傳播到所鏈接的zkNode,這個zkNode可能是任意角色的node,然后zkNode在與zkleaderNode進行心跳來保持會話,同時zkNode也會通過ping來保持會話超時時間。

此時當原有當client在重新鏈接上zkNode時會被告知sessionExpired。sessionExpired 是由zkNode通知出來的,當會話丟失或者過期,client在去嘗試鏈接zkNode時候會被zkNode告知會話過期。

如果client只捕獲了sessionExpired顯然會出現多個master運行情況,因為當你與zkNode斷開到時候,當時還沒有收到sessionExpired事件時,已經有另外client成功創建master拿到權利。

這種情況在zkNode出現脫離集群當時候也會出現,當zkNode斷開之后也會出現sessionExpired延遲通知問題。所有的watcher都是需要在新的zkNode上創建才會收到新的事件。

靜態擴容、動態擴容

在極端情況下靜態擴容可能會導致zookeeper集群出現嚴重的數據不一致問題,比如現有集群:A、B、C,現在需要進行靜態擴容,停止ABC實例,拉入DE實例,此時如果C實例是ABC中最滯后的實例,如果AB啟動的速度沒有C快就會導致CDE組成新的集群,新的紀元號會覆蓋原來的AB日志。當然現在基本上不會接受靜態擴容,基本上都是動態擴容。

動態擴容在極端情況下也會出現類似問題,比如現在有三個機房,1、2、3,1機房方leader zid=200、100,2機房zid=80、50,3機房zid=40,假設上次的commit是在zid=200、100、50之間提交的,此時機房1出現斷網,2機房zid=80、50與3機房zid=40開始組成新的集群,新的紀元在zid=50上產生。

做好冪等

在使用zookeeper來實現分布式鎖或者集群調度的時候會出現很多分布式下的問題,為了保證這些問題的出現不會帶來業務系統或者業務數據的不一致,我們還是在這些任務上做好冪等性考慮。

比如進行數據的計算,做個時間檢查,版本檢查之類的。如果本身是基于zookeeper實現的一套獨立的分布式系統需要的工作會更多點。

作者:王清培 (滬江集團資深架構師)
向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

龙州县| 启东市| 缙云县| 米泉市| 温州市| 旌德县| 宜川县| 宁安市| 永和县| 布尔津县| 白水县| 深州市| 吉木萨尔县| 修文县| 舞钢市| 文成县| 香格里拉县| 洛阳市| 改则县| 桦甸市| 乐平市| 汝南县| 都江堰市| 辽源市| 南陵县| 太康县| 新兴县| 云浮市| 丰都县| 祁连县| 景泰县| 深州市| 台南市| 宁南县| 开平市| 弋阳县| 浦东新区| 镇赉县| 衡水市| 剑河县| 安徽省|