記一次線上Curator使用過程JVM棧溢出解決


    為了同學們看起來一目了,特按如下思路進行講解。

      1.出現的場景

    2.分析及解決的過程

    3.總結

  最近公司要使用zookeeper做配置管理(后面簡稱ZK),然后自己就提前用虛擬機進行了ZK三台集群的搭建。之后開始選擇使用zookeeper的java client工具,google了半天,發現了一個很名強大的Apache的Curator工具,很多底層的東西都已經給你封裝好了,所以用起來很方便,因為我使用的場景是做配置管理,所以使用Curator的Framework就夠了。Curator相對於zookeeper,就相當於Guava之於Java.

  因為每天的訪問量上億級的,所以考慮的因素還是很多,因此從網上找了一些demo,然后自己就開始寫一些測試的類,下邊的這個方法是用於獲取客戶端,並且加入了一些監聽和輸出:

private static CuratorFramework getClient(String namespace) throws Exception{

        ACLProvider aclProvider = new ACLProvider() {
            private List<ACL> acl ;
            @Override
            public List<ACL> getDefaultAcl() {
                if(acl ==null){
                    ArrayList<ACL> acl = ZooDefs.Ids.CREATOR_ALL_ACL;
                    acl.clear();
                    acl.add(new ACL(Perms.ALL, new Id("auth", "admin:admin") ));
                    this.acl = acl;
                }
                return acl;
            }
            @Override
            public List<ACL> getAclForPath(String path) {
                return acl;
            }
        };
        String scheme = "digest";
        byte[] auth = "admin:admin".getBytes();
        int connectionTimeoutMs = 1000;
        String connectString = "127.0.0.1:2181";        
        CuratorFramework client = CuratorFrameworkFactory.builder().
                aclProvider(aclProvider).
                authorization(scheme, auth).
                connectionTimeoutMs(1).
                connectString(connectString).sessionTimeoutMs(50).
                namespace(namespace).
                retryPolicy(new RetryOneTime(1)).build();
        client.getConnectionStateListenable().addListener(new ConnectionStateListener() {
            @Override
            public void stateChanged(CuratorFramework client, ConnectionState newState) {
                System.out.println("** STATE CHANGED TO : " + newState);
            }
        });
        client.start();
        client.getZookeeperClient().internalBlockUntilConnectedOrTimedOut();

//        client.getZookeeperClient().blockUntilConnectedOrTimedOut();
        System.out.println(client.getZookeeperClient().isConnected());
        System.out.println(client.getState());
        return client;
    }

  獲取客戶端之后就可以啟動(client.start())客戶端並且創建相當的Node以及它的payload. 感覺寫的已經可以了,而且經過簡單的測試,覺得可以了,然后就上到測試環境上了,測試環境的訪問量並不是很大,所以也沒有什么特別異常,之后就放到線上了。

  當把程序放到線上去之后,系統的JVM監控系統就開始報警,線程數由幾百迅速增加到了3、4千個,直接超過了我們設置的報警閾值,所以感覺使用jstack命令 jstack -l pid > threadDump,找一個 stack analyzer online的一個網站 fastthread.io, upload做好的threadDump文件,上邊有很多匯總,然后基本上一目了然:

  1700多個TIMED_WATING,還有1700多個TIMED_WATING,這里邊肯定有問題,然后繼續往下拉,會按線程分組進行展示:

  會發現有大概有77%的線程和Curator有關系,這個應該就是它的問題了,那么點開里邊的內容,就能看到線程的明細了,繼續:

  里邊有Curator Framework的代碼了,找到相當的行907,發現只要Client一啟動的話就會使得BlockingQueue會有一個take()的動作,這個take的含義是將head取到,如果沒有的話就等待,這就是線程WAITING的狀態,然后繼續看是在什么地方調用的它。

    找到了,原來是客戶端啟動(client.start())的時候進行的調用,因為我在網上看到很多地方說build模式拿到的Client是線程安全的,所以我就每次拿一次client,然后調用其start()。這樣每個不同的線程就會都等待在那個位置上。我沒有在Finally調用 CloseableUtils.closeQuietly(client); 因為請求量太大,如果頻繁的調用關閉客戶端會造成性能下降,必須保持一個長連接。

  打開Curator的官網上,里邊也進行了說明,創建采用build的方式是線程安全的,但是要保持單例。

  這樣問題找到了,下邊開始想着如果修復和優化,首先讓它實現單例,同時還不能用完之后就直接關閉。同時要保持長連接,在特定情況下進行連接關閉,那就如果出現異常為

KeeperException.ConnectionLossException時需要捕獲並且進行計數和關閉。同時也為了效率考慮,再獲取Node的payload時將payload進行緩存,這樣再次減少了對zk的大量訪問。同時可以根據自己的實際情況去考慮緩存的時間。
if(client == null || client.getState().equals(CuratorFrameworkState.STOPPED) || !client.getNamespace().equals(namespace)) {
                synchronized (ZookeeperUtil.class) {
                    if(client == null || client.getState().equals(CuratorFrameworkState.STOPPED) || !client.getNamespace().equals(namespace)) {
               CloseableUtils.closeQuietly(client);   client
= getClient(namespace); } } }

  同時在網上找到zookeeper集群上從3.4開始,從客戶端連接數maxClientCnxns(配置在zoo.cfg)默認連接數為60,改為0時不限制。

  總結:

  1. 當遇到線程數增加或CPU過高時需要使用jstack將JVM的線程數據導出到文件,然后通過在線工具或自己下載的工具進行分析,我還是比較喜歡這個在線的分析工具,它能分析出總的線程數中按狀態進行分析,還可以按線程類型進行分組,很強大。

  2. 遇到問題要冷靜思考,然后多寫幾個小的demo進行測試。我其實在寫這個問題的過程中我是寫了測試類進行模擬的,然后通過本機的jvisualvm查看棧的情況,根我推斷的一致的,所以就會找到解決的方法。

  3.有些技術知識還是從官方網站學習,而且如果看書的話,需要從頭看到尾,這樣的話基本上能了解事務的全部內容,否則只看到部分內容。

  如果有寫的不對的地方,歡迎同學們來拍磚~

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM