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

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

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

记一次线上Curator使用过程JVM栈溢出解决的更多相关文章

  1. 记一次线上bug排查-quartz线程调度相关

    记一次线上bug排查,与各位共同探讨. 概述:使用quartz做的定时任务,正式生产环境有个任务延迟了1小时之久才触发.在这一小时里各种排查找不出问题,直到延迟时间结束了,该任务才珊珊触发.原因主要就 ...

  2. 解Bug之路-记一次线上请求偶尔变慢的排查

    解Bug之路-记一次线上请求偶尔变慢的排查 前言 最近解决了个比较棘手的问题,由于排查过程挺有意思,于是就以此为素材写出了本篇文章. Bug现场 这是一个偶发的性能问题.在每天几百万比交易请求中,平均 ...

  3. 记一次线上gc调优的过程

           近期公司运营同学经常表示线上我们一个后台管理系统运行特别慢,而且经常出现504超时的情况.对于这种情况我们本能的认为可能是代码有性能问题,可能有死循环或者是数据库调用次数过多导致接口运行 ...

  4. 记一次线上服务CPU 100%的处理过程

    告警 正在开会,突然钉钉告警声响个不停,同时市场人员反馈客户在投诉系统登不进了,报504错误.查看钉钉上的告警信息,几台业务服务器节点全部报CPU超过告警阈值,达100%. 赶紧从会上下来,SSH登录 ...

  5. 记一次排查线上MySQL死锁过程,不能只会curd,还要知道加锁原理

    昨晚我正在床上睡得着着的,突然来了一条短信. 啥,线上MySQL死锁了,我赶紧登录线上系统,查看业务日志. 能清楚看到是这条insert语句发生了死锁. MySQL如果检测到两个事务发生了死锁,会回滚 ...

  6. 记一次线上Kafka消息堆积踩坑总结

    2018年05月31日 13:26:59 xiaoguozi0218 阅读数:2018更多 个人分类: 大数据   年后上线的系统,与其他业务系统的通信方式采用了第三代消息系统中间件Kafka.由于是 ...

  7. 记一次线上由nginx upstream keepalive与http协议"协作"引起的接口报错率飙高事件

    年前接到个任务,说要解决线上一些手机客户端接口报错率很高的问题.拿到了监控邮件,粗略一看,各种50%+的错误率,简直触目惊心.这种疑难杂症解决起来还是挺好玩的,于是撸起袖子action. 最终的结果虽 ...

  8. 【MySQL】记一次线上重大事故:二狗子竟然把线上数据库删了!!

    写在前面 估计二狗子这几天是大姨夫来了,心情很郁闷,情绪也很低落,工作的时候也有点心不在焉.让他发个版本,结果,一行命令下去把线上的数据库删了!你没听错:是删掉了线上的数据库!运营那边顿时炸了锅:怎么 ...

  9. 线上问题debug过程(cat,grep,tr,awk,sort,uniq,comm等工具的综合使用)

    问题:发现线上到货单的数量,小于实际到货的数量. 怀疑一些隐藏的条件,将部分唯一码进行了过滤,导致数量变少. 开展了如下的跟踪流程: 1.找到其中一个明细的唯一码 grep 6180e-4b09f p ...

随机推荐

  1. 以kaggle-titanic数据为基础的完整的机器学习

    1. 引入所有需要的包 # -*- coding:utf-8 -*- # 忽略警告 import warnings warnings.filterwarnings('ignore') # 引入数据处理 ...

  2. Java反射获取字节码以及判断类型

    一.获取类的字节码的三种方法: 1.使用Class.class   Class<?> c1=String.class; 2.使用实例.getClass()   String s= Clas ...

  3. 为什么要使用Docker?

    作为一种新兴的虚拟化方式,Docker跟传统的虚拟化方式相比具有众多的优势. 更高效的利用系统资源 由于容器不需要进行硬件虚拟及运行完整操作系统等额外开销,Docker对系统资源的利用率更高.无论是应 ...

  4. Flask從入門到入土(一)——程序的基本結構

    一.初始化 所有Flask程序都必須創建一個程序實例.Web服務器使用一種名爲Web服務器網關接口的協議,把接收自客戶端的所有請求都轉交給這個對象處理.程序實例書Flask類的對象,創建代碼: fro ...

  5. nyoj 1129 Salvation 模拟

    思路:每个坐标有四种状态,每个点对应的每种状态只能走一个方向,如果走到一个重复的状态说明根本不能走到终点,否则继续走即可. 坑点:有可能初始坐标四周都是墙壁,如果不判断下可能会陷入是死循环. 贴上测试 ...

  6. nyoj281 整数中的1(二) 数位DP

    和整数中的1一毛一样.就是输入时改了一下罢了. AC代码: #include<cstdio> const int maxn = 35; int w[maxn], h[maxn]; void ...

  7. Spring / Hibernate 应用性能调优

    来源:ImportNew - 陈晓舜 对大部分典型的Spring/Hibernate企业应用来说,应用的性能大部分由持久层的性能决定. 这篇文章会重温一下怎么去确认我们的应用是否是”数据库依赖(dat ...

  8. AES对称加密

    import javax.crypto.Cipher; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.Secre ...

  9. web工程导入新环境的注意事项

    问题一:The type java.lang.Object cannot be resolved. 解决办法: 当你在Eclipse引用不同版本JDK工程时会发生该问题.由于你开发环境中应用了多个版本 ...

  10. 用GA算法设计22个地点之间最短旅程-R语言实现

    数据挖掘入门与实战  公众号: datadw 相关帖子 转载︱案例 基于贪心算法的特征选择 用GA算法设计22个地点之间最短旅程-R语言实现 ----------------------------- ...