zoukankan      html  css  js  c++  java
  • 记一次线上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.有些技术知识还是从官方网站学习,而且如果看书的话,需要从头看到尾,这样的话基本上能了解事务的全部内容,否则只看到部分内容。

      如果有写的不对的地方,欢迎同学们来拍砖~

  • 相关阅读:
    pycharm2018.1下载激活(mac平台)
    python 保存登录状态 cookie
    utf-8和utf-8-sig的区别
    AcWing 803. 区间合并
    AcWing 801. 二进制中1的个数
    AcWing 800. 数组元素的目标和
    AcWing 799. 最长连续不重复子序列
    AcWing 795. 前缀和
    AcWing 791. 高精度加法 解题记录
    九州缥缈录 合集序言
  • 原文地址:https://www.cnblogs.com/huangqingshi/p/8576948.html
Copyright © 2011-2022 走看看