当前位置:首页 >系统运维 >Java服务化系统线上应急和技术攻关,你必须拥有的那些应用层脚本和Java虚拟机命令 正文

Java服务化系统线上应急和技术攻关,你必须拥有的那些应用层脚本和Java虚拟机命令

来源:益强资讯优选   作者:应用开发   时间:2025-11-05 12:10:25

无论是服务技术开发人员还是架构设计人员都是在实践中成长起来的,他们通过实践进行总结,化系总结后把经验升华并再次应用到实践中去,统线进一步提供生产效率。急和技术机命

本文介绍笔者在互联网公司里线上应急和技术攻关过程中积累的攻关应用层脚本和Java虚拟机命令,这些脚本和命令在发现问题和定位问题的必须本和过程中起到关键作用,在特定的拥有应用问题环境下,堪称快速定位问题的层脚小倚天剑以及快速解决问题的微屠龙刀。

本文在介绍脚本和命令之前,虚拟先给大家介绍笔者的服务Linux环境以及在Linux环境下搭建的一个原创Java发号器服务,用来向大家演示脚本和命令的化系使用方法,力争做到让大家拿来即用的统线效果。另外,急和技术机命在介绍完所有的攻关脚本和命令之后,我会把所有的必须本和命令和脚本收集在一个表格中,便于大家随时参考和使用,并推荐大家把这个表格打印出来放在自己的办公桌上,需要的源码库时候看一眼,便可快速发现和解决问题的工具。

脚本和命令系列主题中计划提供两篇文章,这篇文章是脚本和命令系列主题中的其中一篇,本文聚焦在那些“神奇的”应用层脚本和Java虚拟机命令,曾经在不同程度上帮助笔者在线上应急和技术攻关的过程中解决过不小的问题,通过这篇文章把这些脚本和命令推广给读者,让读者也能够应用在实践中,切实有效的帮助读者解决实际问题。

另外一篇文章聚焦在线上应急和技术攻关过程中,你必须学会使用的那些Linux底层基础命令,包括Linux内存、CPU利用率、IO、网络IO等的监控工具、性能测试工具、安全工具、以及其他实用工具等,这篇文章会在后续尽快发布给读者。

1. 环境搭建和示例服务启动

首先,使用的Linux版本为:

OS:Ubuntu 14.04.2 LTS 内核:3.16.0-30-generic 硬件架构:x86_64

使用的JDK版本为:

jdk1.8.0_20

为了让大家了解使用命令的真实环境,服务器托管我们在Linux系统中搭建了原创发号器服务Vesta,在介绍下面的每个命令的同时,都会以监控和维护原创发号器服务为例来说明,如果大家对发号器服务的设计、实现和使用方式感兴趣,或者大家想了解如何开发一款专业的开源软件,请参考文章如何设计一款多场景分布式发号器,本文聚焦在如何使用这些高效的脚本和Linux命令来维护一款线上服务。

为了搭建Vesta发号器服务,我们需要从Github地址

https://github.com/robertleepeak/vesta-id-generator.git下载源代码:

robert@robert-ubuntu1410:~/working/workspace$ git clone https://github.com/robertleepeak/vesta-id-generator.git 正克隆到 vesta-id-generator... remote: Counting objects: 944, done. remote: Compressing objects: 100% (53/53), done. 接收对象中: 100% (944/944), 474.66 KiB | 337.00 KiB/s, done. remote: Total 944 (delta 19), reused 0 (delta 0), pack-reused 881 处理 delta 中: 100% (221/221), done. 检查连接... 完成。 

下载后工作目录为:

robert@robert-ubuntu1410:~/working/workspace$ ll 总用量 12 drwxrwxr-x  3 robert robert 4096  4月  8 18:31 ./ drwxr-xr-x  6 robert robert 4096  4月  8 17:33 ../ drwxrwxr-x 12 robert robert 4096  4月  8 18:31 vesta-id-generator/ 

进入Vesta发号器项目根目录并列出目录结构:

robert@robert-ubuntu1410:~/working/workspace/vesta-id-generator$ ll 总用量 108 drwxrwxr-x 12 robert robert  4096  4月  8 18:31 ./ drwxrwxr-x  3 robert robert  4096  4月  8 18:31 ../ -rwxrwxr-x  1 robert robert  3843  4月  8 18:31 assembly.xml* -rwxrwxr-x  1 robert robert   494  4月  8 18:31 brief.txt* -rwxrwxr-x  1 robert robert    65  4月  8 18:31 deploy-maven.sh* -rw-rw-r--  1 robert robert 12292  4月  8 18:31 .DS_Store drwxrwxr-x  8 robert robert  4096  4月  8 18:31 .git/ -rwxrwxr-x  1 robert robert   231  4月  8 18:31 .gitignore* -rwxrwxr-x  1 robert robert 11358  4月  8 18:31 LICENSE* -rwxrwxr-x  1 robert robert   902  4月  8 18:31 make-release.sh* -rwxrwxr-x  1 robert robert  1985  4月  8 18:31 pom.xml* -rwxrwxr-x  1 robert robert  2515  4月  8 18:31 README.md* -rwxrwxr-x  1 robert robert  1429  4月  8 18:31 todo.txt* drwxrwxr-x  3 robert robert  4096  4月  8 18:31 vesta-client/ drwxrwxr-x  2 robert robert  4096  4月  8 18:31 vesta-doc/ drwxrwxr-x  3 robert robert  4096  4月  8 18:31 vesta-intf/ drwxrwxr-x  3 robert robert  4096  4月  8 18:31 vesta-rest/ drwxrwxr-x  3 robert robert  4096  4月  8 18:31 vesta-rest-netty/ drwxrwxr-x  4 robert robert  4096  4月  8 18:31 vesta-sample/ drwxrwxr-x  3 robert robert  4096  4月  8 18:31 vesta-server/ drwxrwxr-x  4 robert robert  4096  4月  8 18:31 vesta-service/ drwxrwxr-x  3 robert robert  4096  4月  8 18:31 vesta-theme/ 

然后,直接运行发布脚本make-release.sh进行打包发布:

robert@robert-ubuntu1410:~/working/workspace/vesta-id-generator$ ./make-release.sh  [INFO] Scanning for projects... [INFO] ------------------------------------------------------------------------ [INFO] Reactor Build Order: [INFO]  [INFO] vesta-id-generator [INFO] vesta-intf [INFO] vesta-service [INFO] vesta-rest [INFO] vesta-rest-netty [INFO] vesta-client [INFO] vesta-server [INFO] vesta-sample [INFO] vesta-sample-embed [INFO] vesta-sample-client ...... [INFO] ------------------------------------------------------------------------ [INFO] Reactor Summary: [INFO]  [INFO] vesta-id-generator ................................. SUCCESS [ 11.630 s] [INFO] vesta-intf ......................................... SUCCESS [  3.489 s] [INFO] vesta-service ...................................... SUCCESS [  1.140 s] [INFO] vesta-rest ......................................... SUCCESS [  4.749 s] [INFO] vesta-rest-netty ................................... SUCCESS [  2.482 s] [INFO] vesta-client ....................................... SUCCESS [  0.111 s] [INFO] vesta-server ....................................... SUCCESS [  1.879 s] [INFO] vesta-sample ....................................... SUCCESS [  0.005 s] [INFO] vesta-sample-embed ................................. SUCCESS [  0.475 s] [INFO] vesta-sample-client ................................ SUCCESS [  0.200 s] [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESS [INFO] ------------------------------------------------------------------------ [INFO] Total time: 27.026 s [INFO] Finished at: 2017-04-08T21:31:34+08:00 [INFO] Final Memory: 42M/616M [INFO] ------------------------------------------------------------------------ 

最后,启动发号器REST服务:

robert@robert-ubuntu1410:~/working/workspace/vesta-id-generator/releases/vesta-id-generator-0.0.1-release/bin$ ll 总用量 110968 drwxrwxr-x 2 robert robert     4096  4月  8 21:34 ./ drwxrwxr-x 6 robert robert     4096  4月  8 21:34 ../ -rw-rw-r-- 1 robert robert 47117221  4月  8 21:34 vesta-all-src-0.0.1.tar.gz -rw-rw-r-- 1 robert robert 34524462  4月  8 21:34 vesta-lib-0.0.1.tar.gz -rw-rw-r-- 1 robert robert 14745549  4月  8 21:33 vesta-rest-0.0.1-bin.tar.gz -rw-rw-r-- 1 robert robert  8656473  4月  8 21:33 vesta-rest-netty-0.0.1-bin.tar.gz -rw-rw-r-- 1 robert robert  8539966  4月  8 21:33 vesta-server-0.0.1-bin.tar.gz -rw-rw-r-- 1 robert robert    27079  4月  8 21:34 vesta-src-0.0.1.tar.gz robert@robert-ubuntu1410:~/working/workspace/vesta-id-generator/releases/vesta-id-generator-0.0.1-release/bin$ tar xzvf vesta-rest-0.0.1-bin.tar.gz vesta-rest-0.0.1/bin/ vesta-rest-0.0.1/bin/server.sh vesta-rest-0.0.1/bin/start.sh vesta-rest-0.0.1/bin/check.sh vesta-rest-0.0.1/bin/stop.sh vesta-rest-0.0.1/lib/vesta-rest-0.0.1.jar vesta-rest-0.0.1/lib/vesta-rest-0.0.1-sources.jar robert@robert-ubuntu1410:~/working/workspace/vesta-id-generator/releases/vesta-id-generator-0.0.1-release/bin/vesta-rest-0.0.1/bin$ ./start.sh apppath: /home/robert/working/workspace/vesta-id-generator/releases/vesta-id-generator-0.0.1-release/bin/vesta-rest-0.0.1 Vesta Rest Server is started. 

然后,使用curl语句测试发号器服务是否正常运行:

robert@robert-ubuntu1410:~/working/workspace/vesta-id-generator/releases/vesta-id-generator-0.0.1-release/bin/vesta-rest-0.0.1/bin$ curl "http://localhost:8080/genid" 2382742310220727293 robert@robert-ubuntu1410:~/working/workspace/vesta-id-generator/releases/vesta-id-generator-0.0.1-release/bin/vesta-rest-0.0.1/bin$ curl "http://localhost:8080/expid?id=2382742310220727293" {"machine":1021,"seq":0,"time":71618055,"genMethod":2,"type":0,"version":0} 

看见发号器正确产生ID=2382742310220727293,并能反解成JSON字符串:

{"machine":1021,"seq":0,"time":71618055,"genMethod":2,"type":0,"version":0} 

这说明发号器服务启动正常,然后使用Linux命令查看服务进程,确保服务进程运行正常:

robert@robert-ubuntu1410:~$ ps -elf | grep java 0 S robert    8244  1847  2  80   0 - 231991 futex_ 21:54 pts/7   00:00:23 java -server -Xms512m -Xmx512m -Xmn128m -XX:PermSize=128m -Xss256k -XX:+DisableExplicitGC -XX:+UseConcMarkSweepGC -XX:+CMSParallelRemarkEnabled -XX:+UseCMSCompactAtFullCollection -XX:+UseCMSInitiatingOccupancyOnly -XX:CMSInitiatingOccupancyFraction=60 -verbose:gc -XX:+PrintGCDateStamps -XX:+PrintTenuringDistribution -XX:+PrintGCDetails -Xloggc:./logs/gc.log -cp /home/robert/working/workspace/vesta-id-generator/releases/vesta-id-generator-0.0.1-release/bin/vesta-rest-0.0.1/extlib -jar ./lib/vesta-rest-0.0.1.jar 

接下来的章节我们围绕这个服务来介绍后续的每个脚本和命令。

2. 神奇的脚本解决不可思议的问题

本章介绍那些“神奇的”脚本,这些“神奇的”脚本伴我度过了多少个日日夜夜已经不记得了,凡是线上有紧急问题的服务器租用时候,或者做一个疑难问题的技术攻关的时候,我都会与他们并肩作战,同时也为多个同事解决了不少“不可思议的”问题,因此,这里我希望能够把这些脚本分享给每一个奋斗在一线的技术人员,帮助他们解决手头上的棘手问题。

如果大家对脚本开发感兴趣,可以加入我的开源项目wizard-scripts,我们把实践过程中使用的脚本逐渐的汇总,最后形成一个专业的开源项目,来帮助更多的同行们,欢迎大家加入开源队伍。

2.1 show-busiest-java-threads

此命令通过结合Linux操作系统的ps命令和jvm自带的jstack命令,查找Java进程内CPU利用率最高的线程,一般适用于服务器负载较高的场景,并需要快速定位导致负载高的原因。

本脚本来自一个叫候鸟树(笔者在互联网行业入行前的引路人)的网友,原作者不详,这里保留原作者名为了表示对技术人的尊重。

命令格式:

./show-busiest-java-threads -p 进程号 -c 显示条数 ./show-busiest-java-threads -h 

使用示例:

./show-busiest-java-threads -p 8244 -c 3 

示例输出:

robert@robert-ubuntu1410:~/working/scripts$ ./show-busiest-java-threads -p 8244 -c 3 The stack of busy(0.3%) thread(8257/0x2041) of java process(8244) of user(robert): "C2 CompilerThread1" #7 daemon prio=9 os_prio=0 tid=0xc545d400 nid=0x2041 waiting on condition [0x00000000]    java.lang.Thread.State: RUNNABLE The stack of busy(0.3%) thread(8256/0x2040) of java process(8244) of user(robert): "C2 CompilerThread0" #6 daemon prio=9 os_prio=0 tid=0xc545bc00 nid=0x2040 waiting on condition [0x00000000]    java.lang.Thread.State: RUNNABLE The stack of busy(0.1%) thread(8260/0x2044) of java process(8244) of user(robert): "VM Periodic Task Thread" os_prio=0 tid=0xc5463c00 nid=0x2044 waiting on condition 

脚本源码:

#!/bin/bash # @Function # Find out the highest cpu consumed threads of java, and print the stack of these threads. # # @Usage #   $ ./show-busy-java-threads # # @author Jerry Lee PROG=`basename $0` usage() {     cat < /dev/null; then     [ -n "$JAVA_HOME" ] && [ -f "$JAVA_HOME/bin/jstack" ] && [ -x "$JAVA_HOME/bin/jstack" ] && {         export PATH="$JAVA_HOME/bin:$PATH"     } || {         redEcho "Error: jstack not found on PATH and JAVA_HOME!"         exit 1     } fi uuid=`date +%s`_${RANDOM}_$$ cleanupWhenExit() {     rm /tmp/${uuid}_* &> /dev/null } trap "cleanupWhenExit" EXIT printStackOfThread() {     while read threadLine ; do         pid=`echo ${threadLine} | awk {print $1}`         threadId=`echo ${threadLine} | awk {print $2}`         threadId0x=`printf %x ${threadId}`         user=`echo ${threadLine} | awk {print $3}`         pcpu=`echo ${threadLine} | awk {print $5}`         jstackFile=/tmp/${uuid}_${pid}         [ ! -f "${jstackFile}" ] && {             jstack ${pid} > ${jstackFile} || {                 redEcho "Fail to jstack java process ${pid}!"                 rm ${jstackFile}                 continue             }         }         redEcho "The stack of busy(${pcpu}%) thread(${threadId}/0x${threadId0x}) of java process(${pid}) of user(${user}):"         sed "/nid=0x${threadId0x}/,/^$/p" -n ${jstackFile}     done } [ -z "${pid}" ] && {     ps -Leo pid,lwp,user,comm,pcpu --no-headers | awk $4=="java"{print $0} |     sort -k5 -r -n | head --lines "${count}" | printStackOfThread } || {     ps -Leo pid,lwp,user,comm,pcpu --no-headers | awk -v "pid=${pid}" $1==pid,$4=="java"{print $0} |     sort -k5 -r -n | head --lines "${count}" | printStackOfThread } 

2.2 find-in-jar

此脚本在Jar包中的包名和类名中查找某一关键字,并高亮显示匹配的Jar包名称和路径,多用于定位java.lang.NoClassDefFoundError和java.lang.ClassNotFoundException的问题,以及类版本重复或者冲突的问题等。

此脚本是笔者的原创,在不同的公司里帮助很多同事解决了大项目中类版本重复和冲突的问题。

命令格式:

find-in-jar 关键字或者类名 路径 

使用示例:

find-in-jar ByteBufferHolder . 

示例输出:

robert@robert-ubuntu1410:~/working/workspace/vesta-id-generator$ find-in-jar ByteBufferHolder . ./releases/vesta-id-generator-0.0.1-release/lib/tomcat-embed-core-8.0.20.jar      1165  2015-02-15 18:11   org/apache/coyote/ByteBufferHolder.class ./target/vesta-id-generator-0.0.1-release/lib/tomcat-embed-core-8.0.20.jar      1165  2015-02-15 18:11   org/apache/coyote/ByteBufferHolder.class 

脚本源码:

#!/bin/bash find . -name "*.jar" > /tmp/find_in_jar_temp while read line do   if unzip -l $line | grep $1 &> /tmp/find_in_jar_temp_second   then     echo $line | sed s#\(.*\)#\x1b[1;31m\1\x1b[00m#     cat /tmp/find_in_jar_temp_second   fi done < /tmp/find_in_jar_temp 

2.3 grep-in-jar

次脚本在Jar包中进行二进制内容查找,通常会解决一些线上出现的“不可思议”的问题,例如:某些功能上线没有生效、某些日志没有打印等,通常是上线工具或者上线过程出现了问题,把线上的二进制包拉下来并查找特定的关键字来定位问题。

此脚本最初来自于微博同事Axb,后来进行了些许修改,在很多棘手的问题上表现了强大的战斗力。

命令格式:

grep-in-jar 关键字 路径 

使用示例:

grep-in-jar "vesta" . 

示例输出:

robert@robert-ubuntu1410:~/working/workspace/vesta-id-generator$ grep-in-jar "vesta" . find vesta in .  ==> Found "vesta" in ./vesta-sample/vesta-sample-embed/target/vesta-sample-embed-0.0.1.jar ==> Found "vesta" in ./vesta-sample/vesta-sample-embed/target/vesta-sample-embed-0.0.1-sources.jar ==> Found "vesta" in ./vesta-sample/vesta-sample-client/target/vesta-sample-client-0.0.1.jar ==> Found "vesta" in ./vesta-sample/vesta-sample-client/target/vesta-sample-client-0.0.1-sources.jar ==> Found "vesta" in ./vesta-rest-netty/target/vesta-rest-netty-0.0.1-sources.jar ==> Found "vesta" in ./vesta-rest-netty/target/vesta-rest-netty-0.0.1.jar 

脚本源码:

#!/bin/bash ### grep text in jars if [ $# -lt 2 ];then     echo Usage : jargrep text path     exit 1; fi LOOK_FOR=$1 LOOK_FOR=`echo ${LOOK_FOR//.//}` folder=$2 echo "find $LOOK_FOR in $folder " for i in `find $2 -name "*jar"` do     unzip -p $i | grep "$LOOK_FOR" > /dev/null     if [ $? = 0   ]     then         echo "==> Found \"$LOOK_FOR\" in $i"     fi done 

2.4 jar-conflict-detect

此脚本用于识别冲突的Jar包,可以在一个根目录下找到所有包含相同类的Jar包,并且根据相同类的多少来判断Jar包的相似度,常常用于某些功能上线不可用或者没有按照预期起到作用,使用此脚本分析是否存在两个版本的类,而老版本的类被Java虚拟机加载,其实,JVM规范并没有规定类路径下相同类的加载顺序,实现JVM规范的虚拟机的实现机制也各不相同,因此无法判断相同的类中哪个版本的类会被先加载,因此Jar包冲突是个非常讨厌的问题。

此脚本最初来自于作者hongjiang,为了能够在我的Linux版本上正常运行,后来进行了些许修改,它在很多棘手的问题上表现了强大的战斗力。

命令格式:

jar-conflict-detect 路径 

使用示例:

jar-conflict-detect . 

示例输出:

robert@robert-ubuntu1410:~/working/workspace/vesta-id-generator/target/vesta-id-generator-0.0.1-release/lib$ jar-conflict-detect . Similarity  DuplicateClasses  File1                        File2 %21         6                 commons-logging-1.1.3.jar    jcl-over-slf4j-1.7.11.jar %02         10                commons-beanutils-1.8.0.jar  commons-collections-3.2.1.jar See /tmp/cp-verbose.log for more details. 

脚本源码:

#!/bin/bash if [ $# -eq 0 ];then     echo "please enter classpath dir"     exit -1 fi if [ ! -d "$1" ]; then     echo "not a directory"     exit -2 fi tmpfile="/tmp/.cp$(date +%s)" tmphash="/tmp/.hash$(date +%s)" verbose="/tmp/cp-verbose.log" declare -a files=(`find "$1" -name "*.jar"`) for ((i=0; i < ${#files[@]}; i++)); do     jarName=`basename ${files[$i]}`     list=`unzip -l ${files[$i]} | awk -v fn=$jarName /\.class$/{print $NF,fn}`     size=`echo "$list" | wc -l`     echo $jarName $size >> $tmphash     echo "$list" done | sort | awk NF{     a[$1]++;m[$1]=m[$1]","$2}END{for(i in a) if(a[i] > 1) print i,substr(m[i],2) } > $tmpfile awk {print $2} $tmpfile | awk -F, {i=1;for(;i<=NF;i++) for(j=i+1;j<=NF;j++) print $i,$j} | sort | uniq -c | sort -nrk1 | while read line; do     dup=${line%% *}     jars=${line#* }     jar1=${jars% *}     jar2=${jars#* }     len_jar1=`grep -F "$jar1" $tmphash | grep ^"$jar1" | awk {print $2}`     len_jar2=`grep -F "$jar2" $tmphash | grep ^"$jar2" | awk {print $2}`     # Modified by Robert 2017.4.9     #len=$(($len_jar1 > $len_jar2 ? $len_jar1 : $len_jar2))     len_jar1=`echo $len_jar1 | awk -F  {print $1}`     len_jar2=`echo $len_jar2 | awk -F  {print $1}`     if [ $len_jar1 -gt $len_jar2 ]     then       len=$len_jar1     else       len=$len_jar2     fi     per=$(echo "scale=2; $dup/$len" | bc -l)     echo ${per/./} $dup $jar1 $jar2 done | sort -nr -k1 -k2 | awk NR==1{print "Similarity DuplicateClasses File1 File2"}{print "%"$0}| column -t sort $tmpfile | awk {print $1,"ntt",$2} > $verbose echo "See $verbose for more details." rm -f $tmpfile rm -f $tmphash 

2.5 http-spy

此脚本利用Linux命令nc检查HTTP请求参数、请求头和请求体等信息,常常用于调试基于HTTP协议的服务调用,如果一次HTTP调用没有的达到预期的效果,首先检查传递的参数是否正确,这包括请求参数、请求头、请求体等信息,这种场景正式此脚本的用武之地。

命令格式:

http-spy 

使用示例:

http-spy 

示例输出:

robert@robert-ubuntu1410:~/working/workspace/vesta-id-generator/target/vesta-id-generator-0.0.1-release/lib$ curl "http://localhost:8888?abc=def" robert@robert-ubuntu1410:~/working/scripts$ ./http-spy  GET /?abc=def HTTP/1.1 User-Agent: curl/7.35.0 Host: localhost:8888 Accept: */* 

脚本源码:

#!/bin/bash while true; do nc -l 8888; done 

2.6 show-mysql-qps

此脚本可以用于快速查看Mysql实例的负载情况,包括QPS、TPS、提交数、回滚数、并发线程、执行线程等。

命令格式:

show-mysql-qps 用户名 密码 

使用示例:

show-mysql-qps root ** 

示例输出:

robert@robert-ubuntu1410:~/working/scripts$ show-mysql-qps.sh root 

标签:

责任编辑:应用开发