dubbo連接zookeeper註冊中心因爲斷網導致線程無限等待問題【轉】

最近維護的系統切換了網絡環境,由聯通換成了電信網絡,因爲某些過濾規則導致系統連不上zookeeper服務器(應用系統機器在深圳,網絡爲電信線路,zookeeper服務器在北京,網絡爲聯通線路),因爲我不是運維人員也不懂運維相關的技術,所以排查了很久也不知道原因,最後無奈之下把深圳這邊的網絡切回了聯通,系統恢復正常。

但是因爲本次事故體現了一個很嚴重的問題,即當zookeeper註冊中心連不上時dubbo的線程會無限等待,因爲系統有一些定時任務會比較頻繁地開啓新線程連接dubbo,所以導致的結果是tomcat一會兒線程池就滿了,其它的不依賴dubbo的功能也被阻塞無法使用。

所以需要解決一個問題,即在斷網的情況下要保證應用程序可以運行(有很多功能不依賴外網),一開始我以爲dubbo應該有對ZKClient連接相關的超時時間配置,結果找了很久也沒發現,後來debug了dubbo的源代碼發現根本就沒有設置超時時間,ZKClient默認的超時時間是Integer.MAX_VALUE,幾乎等於無限等待,所以無奈之下只好重寫了dubbo的ZookeeperClient實現,好在dubbo的擴展性非常好,基於SPI的擴展非常方便,下面是我的擴展代碼:

1、增加一個文件com.alibaba.dubbo.remoting.zookeeper.ZookeeperTransporter放置於項目的META-INF/dubbo/internal文件夾下,dubbo便會自動掃描到這個文件。文件內容很簡單就一句話:zkclient=com.gwall.zookeeper.ZkclientZookeeperTransporter 
即把dubbo原來的默認實現替換爲我的實現。 
2、ZkclientZookeeperTransporter並不是我想替換的代碼,我要替換的是ZkclientZookeeperTransporter的成員變量ZookeeperClient,只是dubbo只在ZookeeperTransporter上加了SPI註解,所以只好這樣辦了,ZkclientZookeeperTransporter代碼照抄過來。 
3、在ZkclientZookeeperTransporter裏面使用自己的ZkclientZookeeperClient,代碼如下:

package com.gwall.zookeeper;

import java.util.List;

import org.I0Itec.zkclient.IZkChildListener;
import org.I0Itec.zkclient.IZkStateListener;
import org.I0Itec.zkclient.ZkClient;
import org.I0Itec.zkclient.exception.ZkNoNodeException;
import org.I0Itec.zkclient.exception.ZkNodeExistsException;
import org.apache.zookeeper.Watcher.Event.KeeperState;

import com.alibaba.dubbo.common.URL;
import com.alibaba.dubbo.remoting.zookeeper.ChildListener;
import com.alibaba.dubbo.remoting.zookeeper.StateListener;
import com.alibaba.dubbo.remoting.zookeeper.support.AbstractZookeeperClient;

/** * 修改dubbo提供的ZkclientZookeeperClient * 主要目的是增加一個連接zookeeper的超時時間,避免ZkClient默認的無限等待 * @author long.zr * */
public class ZkclientZookeeperClient extends AbstractZookeeperClient<IZkChildListener> {

    private final ZkClient client;

    private volatile KeeperState state = KeeperState.SyncConnected;

    public ZkclientZookeeperClient(URL url) {
        super(url);
        //設置超時時間爲5000毫秒
        client = new ZkClient(url.getBackupAddress(),5000);
        client.subscribeStateChanges(new IZkStateListener() {
            public void handleStateChanged(KeeperState state) throws Exception {
                ZkclientZookeeperClient.this.state = state;
                if (state == KeeperState.Disconnected) {
                    stateChanged(StateListener.DISCONNECTED);
                } else if (state == KeeperState.SyncConnected) {
                    stateChanged(StateListener.CONNECTED);
                }
            }
            public void handleNewSession() throws Exception {
                stateChanged(StateListener.RECONNECTED);
            }
        });
    }

    public void createPersistent(String path) {
        try {
            client.createPersistent(path, true);
        } catch (ZkNodeExistsException e) {
        }
    }

    public void createEphemeral(String path) {
        try {
            client.createEphemeral(path);
        } catch (ZkNodeExistsException e) {
        }
    }

    public void delete(String path) {
        try {
            client.delete(path);
        } catch (ZkNoNodeException e) {
        }
    }

    public List<String> getChildren(String path) {
        try {
            return client.getChildren(path);
        } catch (ZkNoNodeException e) {
            return null;
        }
    }

    public boolean isConnected() {
        return state == KeeperState.SyncConnected;
    }

    public void doClose() {
        client.close();
    }

    public IZkChildListener createTargetChildListener(String path, final ChildListener listener) {
        return new IZkChildListener() {
            public void handleChildChange(String parentPath, List<String> currentChilds)
                    throws Exception {
                listener.childChanged(parentPath, currentChilds);
            }
        };
    }

    public List<String> addTargetChildListener(String path, final IZkChildListener listener) {
        return client.subscribeChildChanges(path, listener);
    }

    public void removeTargetChildListener(String path, IZkChildListener listener) {
        client.unsubscribeChildChanges(path,  listener);
    }

}

 框架/平臺構成:

Maven+Springmvc + Mybatis + Shiro(權限)+ Tiles(模板) +ActiveMQ(消息隊列) + Rest(服務) + WebService(服務)+ EHcache(緩存) + Quartz(定時調度)+ Html5(支持PC、IOS、Android)

用戶權限系統:
組織結構:角色、用戶、用戶組、組織機構;權限點:頁面、方法、按鈕、數據權限、分級授權

項目管理新體驗
快速出原型系統、組件樹、版本控制、模塊移植、協同開發、實時監控、發佈管理

可持續集成:
所有組件可移植、可定製、可擴充,開發成果不斷積累,形成可持續發展的良性循環

支持平臺平臺: 
Windows XP、Windows 7 、Windows 10 、 Linux 、 Unix

服務器容器:
Tomcat 5/6/7 、Jetty、JBoss、WebSphere 8.5 

JEESZ通用版本分佈式模塊化開發平臺 - zookeeperflume - zookeeperflume的博客

 

JEESZ通用版本分佈式模塊化開發平臺 - zookeeperflume - zookeeperflume的博客

 

JEESZ通用版本分佈式模塊化開發平臺 - zookeeperflume - zookeeperflume的博客

 

JEESZ通用版本分佈式模塊化開發平臺 - zookeeperflume - zookeeperflume的博客

 


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