• 中原银行的 Arthas 技术实践分享


    转载自:https://blog.csdn.net/zybank_IT/article/details/108585520

    Arthas 是一款 Java 开源诊断工具,由于其强大的问题排查及诊断能力,自其开源以来广受开发者的关注和使用,多次登顶 GitHub Trending,并得到国内多家技术媒体的分享。

    Arthas 可以通过简单的命令交互模式,接入运行的JVM,快速定位和诊断线上程序运行问题。在不重启服务的情况下,实时、动态的修改相关code,并实时生效。具体工作原理如下:

    1. 连接JVM:通过 attach 机制,使用 pid 连接正在运行的JVM;

    2. 查看及修改JVM字节码: 通过 instrument 技术对运行中的 JVM 附加或修改字节码来实现增强的逻辑。

    中原银行技术团队于 2019 年初开始推广和使用 Arthas 定位和诊断线上问题。由于对系统平稳运行的严格要求及对客户敏感信息的保护,我们对arthas的部分功能进行了定制化改造,对watch、mc、redefine等命令进行了隐藏。

    以下为我行在 arthas 技术实践中的一些案例分享(由于行内代码保密性要求,下文所示案例均为场景复现所写示例代码)

    案例一:健康码CPU高案例分析

    问题描述:业务人员反馈后台管理系统其中一个页面响应时间很长,登录服务器上发现CPU使用率较 高,达到80%左右。

    1. thread命令查看CPU使用率高的线程

    启动arthas,附加到对应的java进程,执行thread -n 5 查看CPU使用率最高的5个线程的堆栈。

    在这里插入图片描述

    2. 通过monitor命令查看方法的调用次数与耗时

    通过thread命令已经定位到CPU主要消耗在TreeUtil的findMenuChildren方法上,通过monitor命令查看方法的具体调用次数与耗时
    在这里插入图片描述
    通过monitor命令可以明确该方法单次调用平均耗时为17 ~ 20ms,但是调用次数多,所以整体上页面响应慢。

    3. 通过jad命令反编译TreeUtil类,查看源码

    在这里插入图片描述
    通过jad命令查看源码可以发现,此处的业务逻辑大致是通过ResourceTreeVo 对象的resourceParentId 字段把一个列表构建一个树。在findMenuChildren 方法中存在递归调用,而且每一次调用都需要遍历整个ResourceTreeVo 列表来查找子节点,时间复杂度为O(n2)。所以在ResourceTreeVo 列表元素比较多的时候,会很耗时。

    4. 解决问题

    定位到问题就方便解决了,可以通过提前基于list构建一个parentId->List的map,每个节点查找子节 点列表的时候可以从map中获取。这样整个构建树的时间算法为O(n)。

    案例二:应用线程连接数异常

    问题描述:服务器句柄数耗尽,查看发现某个应用占用句柄数较多。

    在这里插入图片描述
    1. thread命令查看线程信息

    启动arthas,附加到对应的java进程,执行thread查看线程情况。
    在这里插入图片描述

    在这里插入图片描述
    在这里插入图片描述
    看到有大量的MasterListener-mymaster-* 线程处于连接状态,一直没有释放
    在这里插入图片描述
    在这里插入图片描述
    发现这些线程是由redis.clients.JedisSentinelPool$ MasterListener 产生的,那么接下来就来查看一下JedisSentinelPool$MasterListener 的调用情况。

    2. stack命令查看堆栈信息

    在这里插入图片描述
    触发一次应用请求,打印出如下堆栈信息

    在这里插入图片描述
    通过调用链定位到RedisUtil 类,发现每次请求否会触发RedisUtil.getJedis 方法调用
    JedisSentinelPool$MasterListener ,那么下一步我们反编译一下REedisUtil 类。

    3. jad命令反编译查看代码

    在这里插入图片描述
    在这里插入图片描述
    查看getJedis 方法

    在这里插入图片描述
    发现getJedis 每调用一次都会新建一个JedisSentinelPool

    通过分析发现,每次使用redis时,都会调用getJedis 方法创建一个新的JedisSentinelPool ,从而启动一个MasterListener-mymaster-* 线程,由于该线程会一直保持监听,不会自动释放,故随着应用请求的增加线程数一直增加从而导致连接数占满。

    4. 解决问题

    针对该问题,只需创建一个全局的JedisSentinelPool ,每次获取redis连接时都从该连接池获取即可,这里不再对代码进行展示。

    总结

    使用Arthas以前,线上问题排查往往需要查网络、jps、jstack、jmap、jhat、jstat、hprof 等一通操作,费时费力。而现在,大多数的常见问题都可以使用 Arthas 轻松定位,迅速解决。

    当然,Arthas只是提供了各个维度的监控数据,具体问题排查依然靠人来进行分析,因此我们还是需要多多实践,多积累经验才能提高定位、解决问题的能力。

    作者:于爽
    指导老师:陈郓,王东,杨晓兵

  • 相关阅读:
    移动开发基础(二)touch事件
    js的性能优化
    理解Java的Class.forName()方法
    Linux 串口读写(一)
    PreparedStatement是如何大幅度提高性能的
    简单图像匹配(转)
    共享内存
    Oracle ORA12505, TNS:listener does not currently know of SID given in connect descriptor 解决
    Top Half & Bottom Half
    vue 插件 使用 Echarts 创建图表 (转)
  • 原文地址:https://www.cnblogs.com/kebibuluan/p/14171675.html
Copyright © 2020-2023  润新知