问题排查总结

生产线上一些问题排查方法,常用手段小结。

今天给大家分享一些个人一些线上问题的排查手段,方法。欢迎评论区交流。

收藏本站,三伏磨会不定时分享实战经验。

常见问题分类

  • 日志排查
  • 代码逻辑排查
  • 配置排查
  • 接口问题
  • JVM问题
  • Redis问题
  • MySQL问题
  • 系统问题

日志排查

# 本地日志可能非常大,所以正常需要根据需要来过滤数据
# 如果直接打开超大文件,可能会导致一些问题,比如: 假死# 指定显示行数
cat test.log | tail -n 20 # 显示20行

# 查找包括Exception字符串的行cat test.log | grep “Exception”
# 指定行数,避免过大
cat test.log | grep “Exception” | tail -n 20
# and 查找包括Exception与java:81
cat test.log | grep “Exception” | grep “java:81”
# or 查找包括Exception或RequestMappingHandlerAdapter
cat test.log | grep -E “Exception|RequestMappingHandlerAdapter”

# 统计相关
# 统计Exception数量
cat test.log | grep “Exception” | wc -l

代码逻辑排查

  • 单元测试
  • 请求回放
  • 断点

# arthas 下载&安装祥见附录1
# jad 参考: https://arthas.gitee.io/jad.html
# 功能:反编译代码。 可以直观的确认当前运行版本代码
# 解决:提交了,不知道为什么没有作用?
jad com.nascent.ecrpsaas.openapi.interceptor.APIHandlerInterceptor
# 将编译代码输出
jad com.nascent.ecrpsaas.openapi.interceptor.APIHandlerInterceptor > /tmp/APIHandlerInterceptor.java

# arthas 下载&安装祥见附录1
# 解决:配置没有生效? 现在有配置是什么样的?

静态配置类&枚举类

# arthas 下载&安装祥见附录1
# ognl 参考: https://arthas.gitee.io/ognl.html
ognl “@xxx.GlobalsEnums@SYSTEM_ERROR.getMsg()”

比如项目中,配置了一个SpringBean对象

     

获取对应的pooledConnectionFactory当前配置

# 获取任何一个请求对象,因为都会存在对应的SpringContext容器 tt -n 1 -t org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter invokeHandlerMethod

特别强调: 这里在正式环境中,添加-n来指定次数,且应该设置成1。

否则当你执行一个调用量不高的方法时可能你还能有足够的时间用 CTRL+C 中断 tt 命令记录的过程,但如果遇到调用量非常大的方法,瞬间就能将你的 JVM 内存撑爆。

通过tt的回放能力,获取容器内的Bean

随意请求一个地址,只需要被Spring拦截

https://wd-jishu.oss-cn-hangzhou.aliyuncs.com/wd/2020/07/image.png@!large

此时,如果使用ognl可以得到target(当前被调用对象)。因为这个对象是由SpringContext管理的,所以可以获取得容器信息。

tt -i 1003 -w ’target.getApplicationContext().getBean(“pooledConnectionFactory”)’
# 获取最大连接数
tt -i 1003 -w ’target.getApplicationContext().getBean(“pooledConnectionFactory”).maxConnections'

接口问题

# arthas 下载&安装祥见附录1
# 解决:接口响应慢?哪个环节慢?
# trace 参考: https://arthas.gitee.io/trace.html
trace xxx.APIHandlerInterceptor preHandle -n 1
# 特别强调: 这里在正式环境中,添加-n来指定次数

https://wd-jishu.oss-cn-hangzhou.aliyuncs.com/wd/2020/07/image-1.png@!large

# 根据方法用时过滤
trace xxx.APIHandlerInterceptor preHandle -n 1 ‘#cost>10’
# 该过滤是过滤整个方法的用时,而不是指过滤方法内的调用方法用时

trace 的问题在于,

无法支持重载方法

无法直接定位方法下的方法

# arthas 下载&安装祥见附录1
# 解决:调用参数与响应结果跟踪。
# tt 参考: https://arthas.gitee.io/tt.html
# 表达式变量祥见附录2
tt -n 1 -t xxx.CustomerController getCustomerInfo4ThirdParty
# 请求参数
tt -i -w ‘params’
# 响应结果
tt -i -w ‘returnObj’

JVM问题

top

如果CPU一直过高,并且不见回落。则需要排查进程要关线程的状态。

# 查看进程内最耗费CPU的线程
top -Hp

https://wd-jishu.oss-cn-hangzhou.aliyuncs.com/wd/2020/07/image-2.png@!full

如何排查对应的线程问题? 需要结合jstack

# 跟踪所有线程
jstack 1 | more

https://wd-jishu.oss-cn-hangzhou.aliyuncs.com/wd/2020/07/image-3.png@!full

该命令可以查看出当前JVM中的线程情况。

主要关注以下三个状态:

WAITING:进入等待状态    
  方式:wait/join/park方法进入无限等待,通过notify/notifyAll/unpark唤醒;•
TIMED_WAITING:与WAITING类似。   •
方式:a. 给定等待时间的wait/join/park方法;b. sleep方法;•
BLOCKED:被动进入等待状态  
    方式:进入Synchronized块;

jstack 1 | grep “State: WAITING” | wc -l

查看线程Id=65的线程信息

# 转义进程Id为16进制
printf ‘%x\n’ 65 # 41
jstack 1 | grep 41

https://wd-jishu.oss-cn-hangzhou.aliyuncs.com/wd/2020/07/image-4.png@!full

cat /proc//status | grep Threads

jstack | grep “Druid-ConnectionPool” | wc -l

三伏磨整理。 收藏本站,不定时分享实战经验。

内存

  • 堆溢出

java.lang.OutOfMemoryError: Java heap space

代码中可能存在大对象分配

可能存在内存泄露,导致在多次GC之后,还是无法找到一块足够大的内存容纳当前对象。

  • 永久代/元空间溢出

存放了被虚拟机加载的类信息、常量、静态变量、JIT编译后的代码等

java.lang.OutOfMemoryError: PermGen space

java.lang.OutOfMemoryError: Metaspace

运行期间生成了大量的代理类,导致方法区被撑爆,无法卸载

应用长时间运行,没有重启

  • 方法栈溢出

java.lang.OutOfMemoryError : unable to create new native Thread

创建的了大量的线程导致的

JVM相关配置:

-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/home/dump.hprof

正常情况下,JVM需要配置相关的OOM时dump,防止OOM时没有较好的分析依据。

需要注意的是, 如果环境分配的内存较大,dump出来的文件大小会与内存大小一致。

如32G内存,dump出来的文件大小也要为32G。

在K8S容器中,需要注意文件的保存位置,防止保存在POD内,并且POD重启(或者策略驱逐)导致文件丢失。

知识点: OOM并不会导致容器直接无法服务。 发生OOM,说明该线程正在申请内存,受影响的线程局限于抛出异常的线程(daemon子线程除外)。而其他线程已经有足够内存,不需要再额外申请,所以不会受影响。且OOM后,受影响的线程因异常而退出,只被该线程所持有的资源不可达后,GC自动回收资源。

top

https://wd-jishu.oss-cn-hangzhou.aliyuncs.com/wd/2020/07/image-5.png@!large

如果在观察的时候,%MEN一直往上增,但是基本不见回落(GC时无法回收),则表示此时,在大量的创建对象,并且对象一直被持有,无法被GC回收,有可能发生了内存泄露问题。

此时,需要结合jmap 来排查问题。

jps -l # 查看JAVA相关进程

jmap -heap

注意: 该操作会导致触发一次FullGC(STW),并暂停服务(STW)。非必要时勿操作!

jmap -histo:live
#或者,导出全部对象。该操作不会引发FullGC。但是也会暂停服务
jmap -histo

注意: 该操作会导致触发一次FullGC,并暂停服务。非必要时勿操作!

jmap -dump:live,format=b,file=dump.hprof
#live 是指只导出存活对象 # 或者
jmap -dump:format=b,file=dump.hprof

JVM相关配置:

-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/home/dump.hprof

http://p1-tt.byteimg.com/large/pgc-image/4dbd84ad390b423a8cd0a2099db990c3?from=pc

行为数据

  1. Histogram 直方图 - 类数量列表
  2. Dominator Tree 支配树 - 类引用关系
  3. Top Consumers 跟直方图相似 - 按包分组
  4. Duplicate Classes 重复类 - 被不同ClassLoader加载的类

报表数据

  1. Leak Suspects 可疑泄露
  2. Top Consumers 占用总堆1%以上的报表

展示某个特定类的对象个数和每个对象使用的内存

https://wd-jishu.oss-cn-hangzhou.aliyuncs.com/wd/2020/07/image-6.png@!full

Shallow Heap

Shallow Heap是指对象本身占用的内存大小,不包括它的引用对象。 针对非数组类型的对象,它的大小就是对象与它所有的成员变量大小的总和。 针对数组类型 的对象,它的大小是数组元素对象的大小总和。

Retained Heap

Retained Heap = 当前对象大小 + 当前对象直接或间接引用的对象的大小总和。

相当于对象被GC之后,可以从Heap上释放的内存大小。

(注:实际释放的内存大小需要根据是否有被GCRoot引用,是否可回收影响)

Retained Heap大小有两种不同的计算方式。

  • Calculate Minimum Retained Size(quick approx..) 快速估算
  • Calculate Precise Retained Size 精确计算

如果所有指向对象Y的路径都经过对象X,则认为对象X支配对象Y。

https://wd-jishu.oss-cn-hangzhou.aliyuncs.com/wd/2020/07/image-7.png@!full

https://wd-jishu.oss-cn-hangzhou.aliyuncs.com/wd/2020/07/image-8.png@!full

显示MAT发现的可能导致内存泄漏的地方,和用于分析这些发现的工具和图表的链接。

https://wd-jishu.oss-cn-hangzhou.aliyuncs.com/wd/2020/07/image-9.png@!full

在直方图Histogram中,可以查看特定类的支配树。

https://wd-jishu.oss-cn-hangzhou.aliyuncs.com/wd/2020/07/image-10.png@!full

https://wd-jishu.oss-cn-hangzhou.aliyuncs.com/wd/2020/07/image-11.png@!full

with all references 查看所有的引用链

exclude weak references 过滤弱引用

exclude soft references 过滤软引用

exclude phantom references 过滤虚引用

exclude weak/soft references 过滤弱/软引用

exclude phantom/soft references 过滤虚/软引用

exclude phantom/weak references 过滤虚/弱引用

exclude phantom/weak/soft etc. references 过滤虚/弱/软引用

  • exclude custom fields… 自定义过滤

https://wd-jishu.oss-cn-hangzhou.aliyuncs.com/wd/2020/07/image-12.png@!full

https://wd-jishu.oss-cn-hangzhou.aliyuncs.com/wd/2020/07/image-13.png@!full

GC

https://wd-jishu.oss-cn-hangzhou.aliyuncs.com/wd/2020/07/image-14.png@!full

-Xms #-Xms128M 堆初始大小,默认为物理内存的1/64(<1GB)
-Xmx #-Xmx128M 堆最大大小,默认如果空余堆大小大于70%(MaxHeapFreeRatio可以修改大小)时,JVM会自动减少堆直到
-Xms的最小限制-XX:NewSize #新生代空间初始大小-XX:MaxNewSize #新生代空间最大大小-Xmn #新生代空间大小(eden + 2 survivor space)-XX:MetaspaceSize #元空间初始大小-XX:MaxMetaspaceSize #元空间最大大小

注意,老年代的大小会根据新生代自动设定:

老年代初始大小=堆最大大小(-Xmx) - 新生代初始大小(-XX:NewSize)

老年代最大大小 = 堆最大大小(-Xmx) - 新生代最大大小(-XX:MaxNewSize)

从参数配置来看,在设置的时候,应该尽量的将堆-Xms与-Xmx设置大小一致,避免JVM一直扩容、缩容。

# GC日志指令
-XX:+PrintGC -XX:+PrintGCTimeStamps -XX:+PrintGCDetails -Xloggc:
# GC日志对性能的影响极小,在生产环境也可以开启# 触发条件: 1、自动 2、监控工具强制调用 3、jmap -histo:live pid

https://wd-jishu.oss-cn-hangzhou.aliyuncs.com/wd/2020/07/image-16.png@!large

三伏磨整理。 收藏本站,不定时分享实战经验。

https://wd-jishu.oss-cn-hangzhou.aliyuncs.com/wd/2020/07/image-15.png@!full

https://wd-jishu.oss-cn-hangzhou.aliyuncs.com/wd/2020/07/image-17.png@!full

https://wd-jishu.oss-cn-hangzhou.aliyuncs.com/wd/2020/07/image-18.png@!full

https://wd-jishu.oss-cn-hangzhou.aliyuncs.com/wd/2020/07/image-19.png@!full

https://wd-jishu.oss-cn-hangzhou.aliyuncs.com/wd/2020/07/image-20.png@!full

主要关注JVM的相关信息。

# jinfo jinfo 1

会输超多信息

虚拟机信息

JVM version is 25.252-b09

系统配置属性

Java System Properties: java.vendor = Oracle Corporation sun.java.launcher = SUN_STANDARD catalina.base = /usr/fffmo.com/tomcat … java.vm.name = OpenJDK 64-Bit Server VM ignore.endorsed.dirs = file.encoding = UTF-8 java.specification.version = 1.8

JVM配置

Non-default VM flags: -XX:CICompilerCount=2 -XX:InitialHeapSize=3145728000 -XX:MaxHeapSize=3145728000 -XX:MaxNewSize=1048576000 -XX:MinHeapDeltaBytes=524288 -XX:NewSize=1048576000 -XX:OldSize=2097152000 -XX:-OmitStackTraceInFastThrow -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:+UseParallelGC Command line: -javaagent:/home/admin/.opt/ArmsAgent/arms-bootstrap-1.7.0-SNAPSHOT.jar -Darms.licenseKey=cmmsmf4y87@9634a50d2fa7317-Darms.appName=ts-ecrp-open -Darms.agent.env=ACSK8S -Darms.agent.args= -Djava.util.logging.config.file=/usr/fffmo.com/tomcat/conf/logging.properties -Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager -XX:-OmitStackTraceInFastThrow -Xms3000m -Xmx3000m -Djdk.tls.ephemeralDHKeySize=2048 -Djava.protocol.handler.pkgs=org.apache.catalina.webresources -Dorg.apache.catalina.security.SecurityListener.UMASK=0027 -Dignore.endorsed.dirs= -Dcatalina.base=/usr/fffmo.com/tomcat -Dcatalina.home=/usr/fffmo.com/tomcat -Djava.io.tmpdir=/usr/fffmo.com/tomcat/temp

或者查看特定JVM参数

jinfo -flags # 查看所有的JVM flag
jinfo -flag NewSize # 查看JVM -XX:NewSize 的数据
# 如上,会输出如: -XX:NewSize=1048576000

对Java应用程序的资源和性能进行实时的命令行的监控,包括了对Heap size和垃圾回收状况的监控。

# 查看GC情况
jstat -gc 1 3000
# jstat -options 查看所有支持的option
-class # 显示ClassLoad的相关信息
-compiler # 显示JIT编译的相关信息
-gc # 显示和gc相关的堆信息
-gccapacity # 显示各个代的容量以及使用情况
-gccause # 显示垃圾回收的相关信息(同-gcutil),同时显示最后一次或当前正在发生的垃圾回收的诱因
-gcmetacapacity # 显示metaspace的大小
-gcnew # 显示新生代信息
-gcnewcapacity # 显示新生代大小和使用情况
-gcold # 显示老年代和永久代的信息
-gcoldcapacity # 显示老年代的大小
-gcutil # 显示垃圾收集信息
-printcompilation # 输出JIT编译的方法信息

Redis常见问题排查

参考: https://redis.io

中文参考: http://doc.redisfans.com/index.html

# 连接到Redis中
# 参考: https://redis.io/commands/info
info

info命令输出的数据可分为10个类别,分别是:

  • server 一般 Redis 服务器信息
  • clients 已连接客户端信息
  • memory 内存信息
  • persistence RDB 和 AOF 的相关信息
  • stats 一般统计信息
  • replication 主/从复制信息
  • cpu CPU 计算量统计信息
  • commandstats Redis 命令统计信息
  • cluster Redis 集群信息
  • keyspace 数据库相关的统计信息

# 查看内存使用 info memory

used_memory_rss:从操作系统上显示已经分配的内存总量。

mem_fragmentation_ratio: 内存碎片率。

used_memory_lua: Lua脚本引擎所使用的内存大小。

mem_allocator: 在编译时指定的Redis使用的内存分配器,可以是libc、jemalloc、tcmalloc。

Linux服务器导致的性能问题(不常见)

因内存交换引起的性能问题

内存使用率是Redis服务最关键的一部分。如果一个Redis实例的内存使用率超过可用最大内存 (used_memory > 可用最大内存),那么操作系统开始进行内存与swap空间交换,把内存中旧的或不再使用的内容写入硬盘上(硬盘上的这块空间叫Swap分区),以便腾出新的物理内存给新页或活动页(page)使用。

在硬盘上进行读写操作要比在内存上进行读写操作,时间上慢了近5个数量级,内存是0.1μs单位、而硬盘是10ms。如果Redis进程上发生内存交换,那么Redis和依赖Redis上数据的应用会受到严重的性能影响。 通过查看used_memory指标可知道Redis正在使用的内存情况,如果used_memory>可用最大内存,那就说明Redis实例正在进行内存交换或者已经内存交换完毕。管理员根据这个情况,执行相对应的应急措施。

# 延迟时间 redis-cli –latency -h 127.0.0.1 -p 6379

以毫秒为单位测量Redis的响应延迟时间,正常的延迟是0.3左右

client list

redis-cli –bigkeys

# 对执行时间大于多少微秒(microsecond,1秒 = 1,000,000 微秒)的查询进行记录

执行以下命令将让 slow log 记录所有查询时间大于等于 100 微秒的查询

CONFIG SET slowlog-log-slower-than 100

slow log 最多能保存多少条日志

让 slow log 最多保存 1000 条日志:

CONFIG SET slowlog-max-len 1000

查看 slow log

slowlog get

指定数量

slowlog get 100

清空日志

slowlog reset

# 一次Redis调用需要用时30ms+

慢日志

slowlog get

….

1985 1594086857 17038 KEYS

….

# 初步怀疑是Keys导致了Redis响应缓慢

排查发现Keys是商城后台在特定情况下使用的,使用较少,应该基本不影响

redis-cli –latency -h -p

发现延迟达到10ms+

通过监控平台,发现CPU接近100%,每秒并发操作12W+

info clients

连接数只有1200+左右

info commandstats

命令统计, 发现Ping次数达到

cmdstat_ping:calls=878105367911,usec=818766996940,usec_per_call=0.93

再次统计

info commandstats

将二个数据相加,可以大概得到每秒的相差数量。

当初的问题ping每分钟达到400W+

三伏磨整理。 收藏本站,不定时分享实战经验。

排查项目Redis相关使用

项目中并没有直接PING命令

发现问题

如果设置了testOnBorrow则从连接池拿出连接都都会执行一次PingPong

同理,设置了testOnReturn时,归还连接时也会执行一次PingPong

解决:

MySQL常见问题排查

字段意义 • id SELECT识别符。这是SELECT查询序列号。这个不重要,查询序号即为sql语句执行的顺序 • select_type select类型 • SIMPLE 进行不需要Union操作或不含子查询的简单select查询时, 响应查询语句的select_type 即为simple,无论查询语句是多么复杂, 执行计划中select_type为simple的单位查询一定只有一个 • PRIMARY 一个需要Union操作或含子查询的select查询执行计划中, 位于最外层的select_type即为primary。 与simple一样,select_type为primary的单位select查询也只存在1个 • union 由union操作联合而成的单位select查询中,除第一个外, 第二个以后的所有单位select查询的select_type都为union。 union的第一个单位select的select_type不是union,而是DERIVED。 它是一个临时表,用于存储联合(Union)后的查询结果 • DEPENDENT UNION dependent 与UNION select_type一样, dependent union出现在union或union all 形成的集合查询中。 此处的dependent表示union或union all联合而成的单位查询受外部影响 • union result union result为包含union结果的数据表 • table 表名 • type 连接类型,有多个参数,先从最佳类型到最差类型介绍 也是本篇的重点 • const,表最多有一个匹配行,const用于比较primary key 或者unique索引。 因为只匹配一行数据,所以很快,也可以理解为最优化的索引,常数查找 • eq_ref 对于eq_ref的解释,mysql手册是这样说的:”对于每个来自于前面的表的行组合,从该表中读取一行。 除了const类型,这可能是最好的联接类型” • ref 对于每个来自于前面的表的行组合,所有有匹配索引值的行将从这张表中读取。 如果联接只使用键的最左边的前缀,或如果键不是UNIQUE或PRIMARY KEY (换句话说,如果联接不能基于关键字选择单个行的话),则使用ref。 如果使用的键仅仅匹配少量行,该联接类型是不错的 • ref_or_null 该联接类型如同ref,但是添加了MySQL可以专门搜索包含NULL值的行。 在解决子查询中经常使用该联接类型的优化 • index_merge 该联接类型表示使用了索引合并优化方法。 在这种情况下,key列包含了使用的索引的清单,key_len包含了使用的索引的最长的关键元素 • unique_subquery • index_subquery • range 给定范围内的检索,使用一个索引来检查行 • index 该联接类型与ALL相同,除了只有索引树被扫描。这通常比ALL快,因为索引文件通常比数据文件小。 (也就是说虽然all和Index都是读全表,但index是从索引中读取的,而all是从硬盘中读的) • ALL 对于每个来自于先前的表的行组合,进行完整的表扫描。 如果表是第一个没标记const的表,这通常不好,并且通常在它情况下很差。 通常可以增加更多的索引而不要使用ALL,使得行能基于前面的表中的常数值或列值被检索出 • possible_keys 提示使用哪个索引会在该表中找到行,不太重要 • keys 指明MYSQL查询使用的索引 • key_len MYSQL使用的索引长度 • ref 显示使用哪个列或常数与key一起从表中选择行 • rows 显示MYSQL执行查询的行数,数值越大越不好,说明没有用好索引 • Extra 该列包含MySQL解决查询的详细信息

美团技术团队分享 https://www.jb51.net/article/75438.htm

‘Using Index’的意思是“覆盖索引”。

一个包含查询所需字段的索引称为“覆盖索引”

MySQL只需要通过索引就可以返回查询所需要的数据,而不必在查到索引之后进行回表操作,减少IO,提高了效率。

# 索引

AK_out_nick_platform out_nick, platform UNIQUE

SELECT SQL_NO_CACHE es_party_time, out_nick, platform from kd_all_customer order by id desc limit 1000

SELECT SQL_NO_CACHE out_nick, platform from kd_all_customer order by id desc limit 1000

# 索引选择性=索引基数/数据总数

索引基数

show index from 表名 # cardinality

索引选择性平均数值组越接近1就越有可能利用索引

明明有索引,但是不走索引?

两个同样结构的语句一个没有用到索引的问题?

原因: 二叉树索引本来最适合的就是点查询,和小范围的range查询, 当预估返回的数据量超过一定比例( 大概在预估的查询量达到总量的30%,没找到实际文档 )的时候, 再根据索引一条一条去查就慢了,反而不如全表扫描快了。

例如联合索引有三个索引字段(A,B,C)

查询条件:

(A,,)— 使用索引

(A,B,)— 使用索引

(A,B,C)— 使用索引

(,B,C)— 不会使用索引

(,,C)— 不会使用索引

三伏磨整理。 收藏本站,不定时分享实战经验。

系统问题

当应用程序要请求新的内存页的时候,如果已经没有足够的物理内存,就会把目前物理内存中的一部分空间释放出来,以供当前运行的程序使用。

这部分被释放的空间可能属于某一个程序,并且所谓的释放,是把这部分内存页存放到SWAP空间。

如果这个程序是活跃的,那么当它的内存页被存放到SWAP之后,下一刻它又要用到这一部分,那么就又要把这一部分换入内存中,这个时候,系统就要把其它程序的内存页换出到SWAP中,腾出空间给它。

反复如此,就会造成性能的问题。

所以如果频繁使用到SWAP来换出换入内存,那么就意味着负载过高,或者物理内存不够。

并发调优时,往往需要预先调优linux参数,其中修改linux最大文件句柄数是最常修改的参数之一。

linux最大文件句柄数为1024个。当你的服务器在大并发达到极限时,就会报出“too many open files”。


最终排查手册

日志、经验、怀疑!!

附录1: arthas 安装&启动

官方文档: https://arthas.gitee.io/ 下载安装 wget -qO /tmp/arthas.zip “https://maven.aliyun.com/repository/public/com/taobao/arthas/arthas-packaging/3.2.0/arthas-packaging-3.2.0-bin.zip" && \ mkdir -p /opt/fffmo.com/arthas && \ unzip /tmp/arthas.zip -d /opt/fffmo.com/arthas && \ rm /tmp/arthas.zip 启动 java -jar /opt/fffmo.com/arthas/arthas-boot.jar

附录2:arthas ognl表达式核心变量

# 官方文档: https://arthas.gitee.io/

参考: https://arthas.gitee.io/advice-class.html


附录3:arthas 常用功能

# 官方文档: https://arthas.gitee.io/

# 参考: https://arthas.gitee.io/trace.html trace -n 1 com.nascent.ecrpsaas.openapi.interceptor.APIHandlerInterceptor preHandle # 强调: 在生产环境时, 必须先把-n带上

比较有问题的是,如果方法复杂度过高,会导致无法Agent进去。同时,目前无法支持重载方法。

tt - 时空隧道

记录下指定方法每次调用的入参和返回信息,并能对这些不同的时间下调用进行观测

# 参考: https://arthas.gitee.io/tt.html tt -n 3 -t com.nascent.ecrpsaas.openapi.interceptor.APIHandlerInterceptor preHandle

强调: 在生产环境时, 必须先把-n带上

https://wd-jishu.oss-cn-hangzhou.aliyuncs.com/wd/2020/07/image-21.png@!large

上文中的 arthas获取SpringContext的Bean信息就是利用tt的调用信息,然后使用ognl表达式获取的。

查看请求信息

#tt -i tt -i 1000

https://wd-jishu.oss-cn-hangzhou.aliyuncs.com/wd/2020/07/image-22.png@!full

获取对象中的配置信息

tt -i 1000 -w ’target’

https://wd-jishu.oss-cn-hangzhou.aliyuncs.com/wd/2020/07/image-23.png@!full

调用

tt -i 1000 -w ’target.requestNonce’

jad –source-only com.nascent.ecrpsaas.openapi.interceptor.APIHandlerInterceptor > /tmp/APIHandlerInterceptor.java

直接看

jad –source-only com.nascent.ecrpsaas.openapi.interceptor.APIHandlerInterceptor

反编译之后的源码:

https://wd-jishu.oss-cn-hangzhou.aliyuncs.com/wd/2020/07/image-24.png@!large

mc & redefine - 动态更新代码

不支持vi处理: apt-get install vim -y

编辑类,并保存成功之后,启动arthas

# 查找类对应的加载器 sc -d *APIHandlerInterceptor | grep classLoaderHash

输出: classLoaderHash 726a17c4

内存编译代码

mc -c 726a17c4 /tmp/APIHandlerInterceptor.java -d /tmp

需要注意的是,JAVA的泛型在编译之后会被擦除。所以,最合适的方法就是将本地代码上传。

而不是基于反编译之后的代码来修改。

输出: Memory compiler output:

/tmp/com/nascent/ecrpsaas/openapi/interceptor/APIHandlerInterceptor.class

更行热替换

redefine /tmp/com/nascent/ecrpsaas/openapi/interceptor/APIHandlerInterceptor.class

输出: redefine success, size: 1

https://wd-jishu.oss-cn-hangzhou.aliyuncs.com/wd/2020/07/image-25.png@!large

随机文章