记一次线上Curator使用过程JVM栈溢出解决

    为了同窗们看起来一目了,特按以下思路进行讲解。java

      1.出现的场景缓存

    2.分析及解决的过程安全

    3.总结session

  最近公司要使用zookeeper作配置管理(后面简称ZK),而后本身就提早用虚拟机进行了ZK三台集群的搭建。以后开始选择使用zookeeper的java client工具,google了半天,发现了一个很名强大的Apache的Curator工具,不少底层的东西都已经给你封装好了,因此用起来很方便,由于我使用的场景是作配置管理,因此使用Curator的Framework就够了。Curator相对于zookeeper,就至关于Guava之于Java.ide

  由于天天的访问量上亿级的,因此考虑的因素仍是不少,所以从网上找了一些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监控系统就开始报警,线程数由几百迅速增长到了三、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.有些技术知识仍是从官方网站学习,并且若是看书的话,须要从头看到尾,这样的话基本上能了解事务的所有内容,不然只看到部份内容。

  若是有写的不对的地方,欢迎同窗们来拍砖~

相关文章
相关标签/搜索