Linux 实战技巧

常用命令

adduser

1
2
3
4
5
6
7
8
9
10
11
12
# 以创建 Apache Eagle 用户为例
$ adduser eagle
$ passwd eagle # ur password for eagle user
# 赋予用户可以 sudo 的权限
$ chmod u+w /etc/sudoers
$ vim /etc/sudoers
# 找到 `root ALL=(ALL) ALL` 这行,并在下面添加 eagle 用户
eagle ALL=(ALL) ALL
$ chmod u-w /etc/sudoers
# 切换到 eagle 用户
$ su - eagle

chown

1
2
3
4
# 软链接
$ chown -h superset:superset superset
# 所有子目录及文件
$ chown -R superset:superset superset-0.15.4

crontab

1
2
3
4
5
6
7
# 查看脚本是否执行
$ tail -f /var/log/cron
# 防止 nohup 的定时任务发邮件给 root,导致 var 目录堆积(/var/spool/cron)
$ crontab -e
# 加在文件开头
MAILTO=""

date

1
2
# 该时间戳格式为 13 位毫秒级别
$ echo "`date -d '2017-04-21 10:00:00' +%s`000"

df

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 磁盘信息
$ df -h
Filesystem Size Used Avail Use% Mounted on
/dev/mapper/systemvg-rootlv 7.9G 388M 7.1G 6% /
tmpfs 3.9G 12K 3.9G 1% /dev/shm
/dev/vda1 485M 39M 421M 9% /boot
/dev/mapper/systemvg-homelv 30G 11G 18G 38% /home
/dev/mapper/systemvg-optlv 30G 187M 28G 1% /opt
/dev/mapper/systemvg-tmplv 2.0G 68M 1.9G 4% /tmp
/dev/mapper/systemvg-usrlv 9.9G 2.7G 6.7G 29% /usr
/dev/mapper/systemvg-varlv 6.0G 352M 5.3G 7% /var
/dev/mapper/datavg-datalv 98G 4.1G 89G 5% /data
# 找到 device 对应的 disk
$ ls -trl /dev/mapper/
crw-rw---- 1 root root 10, 58 May 27 2017 control
lrwxrwxrwx 1 root root 7 May 27 2017 systemvg-lv_swap -> ../dm-0
lrwxrwxrwx 1 root root 7 May 27 2017 systemvg-optlv -> ../dm-4
lrwxrwxrwx 1 root root 7 May 27 2017 systemvg-tmplv -> ../dm-5
lrwxrwxrwx 1 root root 7 May 27 2017 systemvg-usrlv -> ../dm-6
lrwxrwxrwx 1 root root 7 May 27 2017 systemvg-varlv -> ../dm-3
lrwxrwxrwx 1 root root 7 May 27 2017 systemvg-rootlv -> ../dm-1
lrwxrwxrwx 1 root root 7 Jun 1 2017 datavg-datalv -> ../dm-7
lrwxrwxrwx 1 root root 7 Jun 1 2017 systemvg-homelv -> ../dm-2

du

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 文件大小
$ du -h /home/ --max-depth=1
3.1G /home/eagle
40.8G /home/
$ du -sh *
64K logs
276M software
28K zkdata
# 同理,使用 `ll -h` 也可以得到文件的大小
$ ll -h
总用量 546M
-rw-rw-r-- 1 zookeeper zookeeper 41M 8月 31 10:08 zookeeper.log
-rw-rw-r-- 1 zookeeper zookeeper 101M 8月 31 00:06 zookeeper.log.1
-rw-rw-r-- 1 zookeeper zookeeper 101M 8月 29 11:39 zookeeper.log.2
-rw-rw-r-- 1 zookeeper zookeeper 101M 8月 27 16:01 zookeeper.log.3
-rw-rw-r-- 1 zookeeper zookeeper 101M 8月 25 20:38 zookeeper.log.4
-rw-rw-r-- 1 zookeeper zookeeper 101M 8月 24 00:34 zookeeper.log.5
-rw-rw-r-- 1 zookeeper zookeeper 4.2M 8月 31 09:35 zookeeper.out

grep

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
# 或操作
$ grep -E 'bin|etc'
$ egrep 'bin|etc'
$ awk '/bin|etc/'
# 与操作
$ grep bin | grep etc
# 不区分大小写
$ grep -i BIN # (bin/sbin)
# 全词匹配
$ grep -w bin # (bin)
# 匹配,并指定显示多少行上下文
$ grep -C 1 bin # (bin/boot root/sbin/script)
# 过滤脚本输出(|& 相当于 stdout + stderr)
$ zkServer.sh status |& grep Mode
Mode: follower
# 特殊字符,需要增加反斜杠(\),进行转义
$ grep \$
# 根据字段 str 匹配到某一行,并获取这一行随后的 n 行(grep -A <n> <str>)
$ vim yuzhouwan.txt
1
2
3
4
5
6
$ cat yuzhouwan.txt | grep -A 2 3
3
4
5

hostname

1
2
3
# 查看机器的 IP 地址
$ hostname -i
10.10.10.1

iostat

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# 查看磁盘 I/O
$ iostat -x 1 10
Linux 2.6.32-504.3.3.el6.centos.plus.x86_64 (yuzhouwan01.com) 01/19/2018 _x86_64_ (24 CPU)
avg-cpu: %user %nice %system %iowait %steal %idle
17.28 0.00 2.02 0.08 0.00 80.62
Device: rrqm/s wrqm/s r/s w/s rsec/s wsec/s avgrq-sz avgqu-sz await svctm %util
sdb 0.01 159.15 2.51 9.62 493.46 1350.14 151.96 0.02 1.77 0.28 0.34
sdd 0.01 162.24 2.49 9.69 493.13 1375.44 153.42 0.02 1.85 0.28 0.34
sdc 0.01 162.57 2.49 9.83 487.72 1379.15 151.58 0.02 2.02 0.27 0.34
sde 0.01 161.54 2.54 9.80 500.08 1370.68 151.61 0.02 1.87 0.28 0.34
sdg 0.01 162.06 2.53 9.83 498.98 1375.10 151.64 0.02 1.93 0.28 0.34
sdf 0.01 163.34 2.53 9.74 496.43 1384.64 153.22 0.02 1.92 0.28 0.35
sdh 0.01 161.44 2.56 9.74 502.61 1369.43 152.20 0.02 1.90 0.28 0.34
sdi 0.01 162.58 2.54 9.78 498.87 1378.82 152.48 0.02 1.99 0.28 0.35
sda 0.29 83.48 0.29 146.49 14.22 1146.37 7.91 0.04 0.28 0.05 0.69
sdj 0.01 164.20 2.60 9.77 512.11 1391.74 153.90 0.02 1.97 0.28 0.35
sdk 0.01 161.76 2.60 9.70 505.17 1371.69 152.56 0.03 2.04 0.28 0.35
sdl 0.01 168.31 2.50 9.84 490.59 1425.20 155.30 0.03 2.57 0.28 0.35
sdm 0.01 162.34 2.52 9.69 486.76 1376.20 152.68 0.02 1.97 0.28 0.34
dm-0 0.00 0.00 0.02 0.31 0.70 2.45 9.64 0.00 6.04 0.43 0.01
dm-1 0.00 0.00 0.39 0.42 3.10 3.32 8.00 0.00 3.42 0.27 0.02
dm-2 0.00 0.00 0.10 173.34 7.42 1035.66 6.01 0.02 0.04 0.03 0.55
dm-3 0.00 0.00 0.01 7.47 0.22 59.05 7.93 0.00 0.56 0.07 0.05
dm-4 0.00 0.00 0.00 4.10 0.00 32.78 8.00 0.00 0.56 0.04 0.02
dm-5 0.00 0.00 0.04 0.81 1.26 6.51 9.07 0.00 0.59 0.17 0.01
dm-6 0.00 0.00 0.01 0.82 1.51 6.59 9.68 0.00 2.02 0.16 0.01

jq

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# https://stedolan.github.io/jq/download/ 下载 jq-linux64
$ mv jq-linux64 ~/software/
$ cd ~/software/
$ chmod +x jq-linux64
$ ln -s jq-linux64 jq
$ vim ~/.bashrc
alias jq='/home/connect/software/jq'
$ source ~/.bashrc
$ curl localhost:8083/ | jq
{
"version": "0.11.0.0-cp1",
"commit": "5cadaa94d0a69e0d"
}

jstack

1
2
# 线程 dump,可以判断进程是否是假死状态
$ jstack -m <pid>

jstat

1
2
3
4
5
6
7
8
9
10
11
12
13
# 查看 Java 进程的 GC 情况
# Usage: jstat -gcutil <pid> <interval_ms>
$ jstat -gcutil 24909 1000
S0 S1 E O P YGC YGCT FGC FGCT GCT
40.77 0.00 98.56 6.38 46.86 2 0.147 0 0.000 0.147
40.77 0.00 98.56 6.38 46.86 2 0.147 0 0.000 0.147
40.77 0.00 98.56 6.38 46.86 2 0.147 0 0.000 0.147
40.77 0.00 98.56 6.38 46.86 2 0.147 0 0.000 0.147
40.77 0.00 98.56 6.38 46.86 2 0.147 0 0.000 0.147
40.77 0.00 98.56 6.38 46.86 2 0.147 0 0.000 0.147
40.77 0.00 98.56 6.38 46.86 2 0.147 0 0.000 0.147
40.77 0.00 98.56 6.38 46.86 2 0.147 0 0.000 0.147
40.77 0.00 99.16 6.38 46.87 2 0.147 0 0.000 0.147

lsof

1
2
3
# 系统级 监控 & 诊断工具
# 指定进程号,可以查看该进程打开的文件
$ lsof -p <pid>

nmon

生成 nmon 文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 获得帮助文档
$ nmon_x86_64_centos6 -h
# 不同的操作系统,可能 nmon 命令不一样
# -f 使得 xxx.nmon 文件名包含文件创建的时间
# -N 指定需要对 NFS 活动情况进行监控
# -m 指定生成的 xxx.nmon 存放的目录
# -s 指定相隔多少秒,做一次监控
# -c 指定采集多少次监控数据,生成一个 xxx.nmon 文件
$ /nmon/nmon_x86_64_rhel6 -f -N -m /nmon -s 60 -c 1440
$ /nmon/nmon_x86_64_centos6 -f -N -m /nmon -s 60 -c 1440
# 转换 .nmon 文件为 .csv 文件
$ sort yuzhouwan-prd3_170831_0001.nmon > yuzhouwan-prd3_170831_0001.csv
# 查看监控数据
# 在 https://www.ibm.com/developerworks/community/wikis/home?lang=en#!/wiki/Power%20Systems/page/nmon_analyser 页面,下载 nmon analyser v52_1.xlsm 文件
# 打开后,会提示“启用宏定义”,点击确定
# 一共会有 Analyser / Settings / Release Notes 三个 sheet,跳转到第一个 Analyser 里
# 点击 “Analyze nmon data” 按钮,选择 yuzhouwan-prd3_170831_0001.csv 文件
# 会生成一个 yuzhouwan-prd3_170831_0001.nmon.xlsx,并直接打开

系统资源实时监控

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
$ /nmon/nmon_x86_64_centos6
# 常用组合:nml
lnmonq14g-------------------Hostname=yuzhouwan-prd3-Refresh= 2secs ---11:04.35-----------|
| CPU +-------------------------------------------------------------------------+ |
|100%-| | |
| 95%-| | | # 为方便展示,此处省去 25% ~ 90%
| 20%-| | |
| 15%-| | |
| 10%-| ss | |
| 5%-|UUUUsUUUUsUUsUUUUwUUssUssUUssUUUsUUsUUUUUsU| |
| +--------------------User---------System----+----Wait---------------------+ |
| Memory Stats --------------------------------------------------------------------------|
| RAM High Low Swap Page Size=4 KB |
| Total MB 129013.3 -0.0 -0.0 10240.0 |
| Free MB 584.1 -0.0 -0.0 10240.0 |
| Free Percent 0.5% 100.0% 100.0% 100.0% |
| MB MB MB |
| Cached= 76656.7 Active= 54649.3 |
| Buffers= 4908.1 Swapcached= 0.0 Inactive = 68809.5 |
| Dirty = 131.6 Writeback = 0.0 Mapped = 75.1 |
| Slab = 4059.3 Commit_AS = 47663.8 PageTables= 96.6 |
| Network I/O ---------------------------------------------------------------------------|
|I/F Name Recv=KB/s Trans=KB/s packin packout insize outsize Peak->Recv Trans |
| lo 1044.0 1044.0 335.5 335.5 3186.8 3186.8 8639.4 8639.4 |
| eth0 5262.7 1168.7 5999.0 2762.1 898.3 433.3 10531.6 43312.1 |
| eth1 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 |
|---------Warning: Some Statistics may not shown-----------------------------------------|

nohup

1
2
3
4
5
6
7
8
9
10
11
# 启动一个后台进程
$ nohup sleep 1000 &
[1] 30408
# 列出所有的后台进程
$ jobs
[1]+ Running nohup sleep 1000 &
# 将后台进程 [1] 提到前台,并 Ctrl+C 停止进程
$ fg %1
nohup sleep 1000
# 检查后台进程已经被停止
$ jobs

make

1
2
3
4
5
$ make -j<thread_num>
# 查看 CPU 核数
$ cat /proc/cpuinfo | grep processor | wc -l
# 如果是两个处理器的话,一般 `-j2` 可以达到最高效率(某些进程主要耗时是在 I/O 上,并不能充分利用单个 cpu 的时间,则可以考虑 -j4)
# 一般的,使用 `thread_num = number_of_cores + 1` 公式来计算即可

tar

1
2
3
4
# 压缩
$ tar zcvf gc.tar.gz gc
# 解压
$ tar zxvf gc.tar.gz

top

1
2
3
4
5
# 查看进程内线程资源消耗
$ top -Hp <pid>
# 在 batch 模式(-b),并控制命令执行迭代次数为 1 次(-n),达到生成一次 CPU 使用情况的快照
$ top -b -n 1

tree

1
2
3
4
5
6
$ yum install tree -y
$ tree -L 1 /
/
├── bin
├── usr
└── var

rpm

1
$ rpm -i --badreloc --relocate /usr/java=/home/eagle/software/java jdk-7u80-linux-x64.rpm

rsync

1
2
# 软链接、隐藏文件等特殊文件的复制,需要用 `rsync` 命令而不能用 `scp`
$ rsync -avuz -e ssh eagle/ root@eagle:/home/eagle

ssh

1
2
3
4
5
# 登录远程机器,执行命令
$ ssh bj@192.168.1.101 "df -h"
# 使用逗号分隔多个执行命令
$ ssh bj@192.168.1.101 "pwd; ls"

sz

1
2
3
4
# 安装
$ yum install lrzsz -y
# 将服务器上文件,拉取到本机
$ sz <file name>

ping

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 如果遇到 Zookeeper 报警网络延迟,不能只看延迟,还需要关注丢包率(packet loss)
# 如果出现网络丢包率过高,可能是交换机的负载过大,接口松动,或者是网线质量问题
$ ping yuzhouwan01
PING yuzhouwan01.com (192.168.1.101) 56(84) bytes of data.
64 bytes from yuzhouwan01.com (192.168.1.101): icmp_seq=1 ttl=64 time=0.055 ms
64 bytes from yuzhouwan01.com (192.168.1.101): icmp_seq=2 ttl=64 time=0.066 ms
64 bytes from yuzhouwan01.com (192.168.1.101): icmp_seq=3 ttl=64 time=0.064 ms
64 bytes from yuzhouwan01.com (192.168.1.101): icmp_seq=4 ttl=64 time=0.083 ms
^C
--- yuzhouwan01.com ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 3443ms
rtt min/avg/max/mdev = 0.055/0.067/0.083/0.010 ms
# 控制 ping 的次数 和 timeout 时间(单位:秒)
$ ping -c 4 yuzhouwan01 -W 5

ps

1
2
3
4
5
6
# 查看进程的 “启动时间” 和 “已运行时长”
$ ps -eo pid,lstart,etime | grep <pid>
# 查看进程分配内存大小
# RSS(Resident Set Size)常驻内存集,表示该进程分配的内存大小
$ ps -e -o pid,rss

uname

1
2
3
4
5
6
7
8
$ uname -a
Linux yuzhouwan 2.6.32-504.3.3.el6.centos.plus.x86_64 #1 SMP Wed Dec 17 01:21:03 UTC 2014 x86_64 x86_64 x86_64 GNU/Linux
# kernel version
$ uname -r
2.6.32-504.3.3.el6.centos.plus.x86_64
# 具体含义:<内核主版本>.<偶数:稳定版本/奇数:开发中版本>.<错误修补的次数>-<?>

umask

1
2
3
4
5
6
7
8
9
10
11
# umask 全称 user file-creatiopn mode mask,用来控制用户权限的
# 查看 umask
$ umask
0022
$ umask -S
u=rwx,g=rx,o=rx
# 修改 umask
$ umask 0022
# 存在 umask 之后,会减少用户的 umask 权限(例如,0777 - 0022 = 0755)
# 所以,部署安装包的时候,如果 *.sh / *.py 脚本的执行权限分配不足的话,同时 umask 设置较大时,解压安装包后,会出现文件执行权限不足的情况
# 这时候,需要考虑分配适当的文件和文件夹权限

vim

撤销

1
2
3
# u 撤销上一步的操作
# U 恢复当前行(即一次撤销对当前行的全部操作)
# Ctrl+r 恢复上一步被撤销的操作

退出并保存

1
# esc + ZZ (esc 是确保 vim 已经退出了编辑模式)

wget

1
2
# 从 ftp 服务器下载文件
$ wget -nd -m --ftp-user=<user> --ftp-password=<password> ftp://<ftp_server>/zookeeper/zookeeper-3.4.6.4.tar.gz

标准 I/O

1
2
3
4
# 只输出 “脚本执行异常信息”
$ ./commands.sh 1>/dev/null 2>./error.log
# 完全不作输出
$ ./commands.sh 1>/dev/null 2>&1

Shell 编程

dirname

1
2
# 跳转到,当前脚本的文件目录
cd `dirname $0`

.bashrc

1
2
# 在执行脚本开始的时候,载入环境变量
source ~/.bash_profile

cut string

1
2
3
# 将 string 的前后各删除一个字符
arr="[leader, election, zookeeper]"
arr=`echo ${arr:1:${#arr}-2}` # leader, election, zookeeper

command

1
2
3
4
# 通过 `command` 命令,可以在脚本开始执行前,对需要的命令进行 check
command -v nc >/dev/null 2>&1 || {
echo >&2 "I require nc but it's not installed!"; exit 1;
}

for loop

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
arr="leader, election, zookeeper"
OLD_IFS="$IFS"
IFS=$", "
arr=(${arr})
for a in ${arr[@]}; do
echo ${a}
done
IFS="$OLD_IFS"
# 设置 IFS,前文 "实战技巧 - IFS 设置" 部分已经提到了
# 下面介绍一种 `fori` 的遍历方式
# 0 /election
# 0 /leader
# 2 /zookeeper
sorted_num=`echo ${sorted} | wc -l`
if [ ${sorted_num} -gt ${top_n} ]; then
sorted_num=${top_n}
fi
json="{"
for (( i=1; i<=${sorted_num}; i++ )); do
line=`echo "${sorted}" | sed -n "${i} p"`
json="${json}`echo ${line} | awk '{print $1}'`: `echo ${line} | awk '{print $2}'`"
if [ ${i} -lt ${sorted_num} ]; then
json="${json}, "
fi
done
json="${json}}"
# 当然,也有其他的实现方法,比如下面 `seq` 这种
# 但是,`seq` 这种方式需要注意两点
# 第一点,$(seq 1 1 10) 里面的 初始值、步长、最大值 都必须要是 Integer 类型的,如果是通过 shell 外部传参进来的,需要做类型转换
# 第二点,`seq` 方式在 console 里面执行没问题,但是,写在 shell 脚本里面,可能会失效(原因尚不明确)
for i in $(seq 1 2 10); do echo "skip by 2 step, value is $i"; done
1
2
3
# 获取到文件
$ readlink -f logs/zookeeper.log
/home/zookeeper/logs/zookeeper.log

sed

1
2
3
# 先后从 zk_con 里面,将 ":" 冒号后面 和 "/" 斜线之前的都删掉
zk_con=" /192.168.1.1:35632[1](queued=0,recved=146,sent=146,sid=0x25dd0c02a0a02a7,lop=PING,est=1503994626336,to=40000,lcxid=0x0,lzxid=0xffffffffffffffff,lresp=1503996561355,llat=0,minlat=0,avglat=0,maxlat=3)"
zk_con=`echo $zk_con | sed 's/:.*//g' | sed 's/.*\///g'` # 192.168.1.1

sort

1
2
3
4
5
6
7
8
9
# 通过 `-k1` 指定 针对第一列进行排序
# 通过 `-n` 指定 针对数字进行排序,避免 9 排在了 10 前面
# 当然也可以通过增加 `-r`,使得排序变成降序
# 如果,列与列之间的分隔符不是默认的 \t,则需要通过 `-t` 参数进行指定
# 0 /election
# 0 /leader
# 2 /zookeeper
sorted=`echo ${result} | sort -k1 -n`

uniq

1
2
3
4
# 计数
echo -e "1\n1\n0" | uniq -c
# 2 1
# 1 0

exit 0

1
2
# 养成一个,脚本结尾,添加 `exit 0` 的好习惯
exit 0

实用技巧

IFS 设置

Shell 脚本中 IFS 变量全称 Internal Field Seprator ,内部域分隔符

1
2
3
4
5
6
7
8
9
10
11
12
13
14
$ echo $IFS
$ echo "$IFS" | od -b
0000000 040 011 012 012
0000004
# 遍历多行文本之前,设置 IFS 为 \n
pids=`ps -ef | grep "${PROCESS_NAME}" | grep -v grep | grep -v "${SELF_NAME}" | awk '{print $2}'`
IFS=$'\n' read -rd '' -a pids <<<"$pids"
echo "pids: ${pids}"
for pid in "${pids}"; do
echo "kill -9 ${pid}"
kill -9 "${pid}"
done

每秒执行一次

1
$ while true; do sleep 1; <command>; done

获得当前机器的资源使用情况

CPU

1
2
idle_cpu_percent=`top -b -n 1 | grep Cpu | awk '{print $5}' | cut -f 1 -d "."`
used_cpu_percent=$(echo "100-${idle_cpu_percent}" | bc)

Memory

1
2
3
4
5
free_m=`free -m`
free_m_detail=`echo ${free_m} | sed 's/.*Mem://g' | sed 's/-\/+.*//g'`
total_mem=$(echo "${free_m_detail}" | awk '{print $1}')
used_mem=$(echo "${free_m_detail}" | awk '{print $2}')
used_mem_percent=$[${used_mem} * 100 / ${total_mem}]

Disk

空间使用率
1
used_disk_percent=`df -P | grep "home" | awk '{print $5}'`
I/O 使用率
1
2
3
virtual_device=`df -h | grep "home" | awk '{print $1}' | cut -f 4 -d '/'`
device=`ls -trl /dev/mapper/ | grep ${virtual_device} | sed 's/.*\///g'`
device_io=`iostat -x 1 1 | grep ${device} | awk '{print $12}'`

Network

获得当前机器的 IP
1
2
3
4
now_ip=`ifconfig | grep "inet addr" | grep -v "127.0.0.1" | awk '{print $2}' | sed 's/.*://g'`
if [ "${now_ip}" == "" ]; then
now_ip=`hostname -i | awk '{print $1}'`
fi
从机器列表中获取目标 IP
1
2
3
4
5
# 这里以 Zookeeper 为例
aim_ip=`cat ${zk_home}/conf/zoo.cfg | grep server | sed 's/.*=//g' | cut -d ':' -f 1 | grep -v ${now_ip} | tail -1`
if [ "${aim_ip}" == "" ]; then
aim_ip="127.0.0.1"
fi
网络延迟和丢包率
1
2
3
ping_result=`ping -c 4 ${aim_ip} -W 5`
ping_packet_loss=`echo ${ping_result} | sed 's/.*received, //g' | sed 's/ packet loss.*//g'`
ping_avg_ms=`echo ${ping_result} | sed 's/.*rtt //g' | awk '{print $3}' | cut -d '/' -f 2`

从 JVM 进程中,找到占用 CPU 最高的 Java 线程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# 使用命令 top -p <pid>,显示 Java 进程的内存情况
$ top -p 15108
# 按大写的 H,获取每个线程的内存情况
# 找到内存和 CPU 占用最高的线程 pid
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
15133 druid 20 0 24.9g 1.1g 7776 S 244.9 0.9 0:07.36 java
15108 druid 20 0 24.9g 1.1g 7776 S 0.0 0.9 0:00.57 java
15114 druid 20 0 24.9g 1.1g 7776 S 0.0 0.9 0:08.96 java
15115 druid 20 0 24.9g 1.1g 7776 S 0.0 0.9 0:07.26 java
15116 druid 20 0 24.9g 1.1g 7776 S 0.0 0.9 0:07.25 java
15117 druid 20 0 24.9g 1.1g 7776 S 0.0 0.9 0:07.19 java
# 获得线程 pid 的十六进制
$ printf 0x%x 15133
0x3b1d
# 从线程堆栈信息中找到 3b1d 这个线程,并展示所在行的后面 10 行
$ jstack 15108 | grep -A 10 3b1d
"qtp2028042905-211-acceptor-3@41374390-ServerConnector@2faa55bb{HTTP/1.1}{0.0.0.0:8090}" #211 daemon prio=4 os_prio=0 tid=0x00007f789bc47800 nid=0x3b1d runnable [0x00007f74e29ec000]
java.lang.Thread.State: RUNNABLE
at sun.nio.ch.ServerSocketChannelImpl.accept0(Native Method)
at sun.nio.ch.ServerSocketChannelImpl.accept(ServerSocketChannelImpl.java:422)
at sun.nio.ch.ServerSocketChannelImpl.accept(ServerSocketChannelImpl.java:250)
- locked <0x00000007122b1fa0> (a java.lang.Object)
at org.eclipse.jetty.server.ServerConnector.accept(ServerConnector.java:377)
at org.eclipse.jetty.server.AbstractConnector$Acceptor.run(AbstractConnector.java:500)
at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:620)
at org.eclipse.jetty.util.thread.QueuedThreadPool$3.run(QueuedThreadPool.java:540)
at java.lang.Thread.run(Thread.java:745)
# 查看对应的堆栈信息,找出可能存在问题的代码

Windows 相关

Windows 下执行 shell 脚本

1
2
3
4
5
6
7
# 安装 git
$ doskey bash="%GIT_HOME%\bin\bash.exe" $*
$ doskey sh="%GIT_HOME%\bin\sh.exe" $*
$ bash shell.sh
# 类似的可以设置 `np` 为 `notepad++` 的快捷启动命令
$ doskey np=D:\apps\Notepad++\notepad++.exe $*

Cmd 设置 Proxy

1
2
3
4
$ set http_proxy=http://your_proxy:your_port
$ set http_proxy=http://username:password@your_proxy:your_port
$ set https_proxy=https://your_proxy:your_port
$ set https_proxy=https://username:password@your_proxy:your_port

Cygwin

安装

官网找到 setup-x86_64.exe 安装包,下载并执行(安装的过程中,如果提示有冲突,选择 contiune 而不能是 retry,否则,将会全部重新开始)

使用
1
2
3
# 切换到想要的盘符下
$ cd /cygdrive/e
$ cd e:/
参考

防火墙

CentOS

CentOS 6
1
2
3
4
5
6
7
# 查看状态
$ service iptable status
# 临时关闭
$ servcie iptables stop
# 永久关闭
$ chkconfig iptables off
CentOS 7
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 查看默认防火墙状态(CentOS 7.0)
$ firewall-cmd --state
# 查看默认防火墙状态(CentOS 7.x)
$ systemctl list-unit-files | grep firewalld.service
# 启动 firewall
$ systemctl start firewalld.service
# 重启 firewall
$ systemctl restart firewalld.service
# 停止 firewall
$ systemctl stop firewalld.service
# 查看 firewall 状态
$ systemctl status firewalld.service
# 是否 / 开启 / 禁止 firewall 的开机自启
$ systemctl is-enabled/enable/disable firewalld.service

Ubuntu

1
2
3
4
5
6
7
# 开启防火墙
$ ufw enable
# 关闭防火墙
$ ufw disable
# 卸载 iptables 服务
$ apt-get remove iptables

优化实战

关闭 Swappiness

1
2
3
4
5
6
7
8
9
10
$ cat /proc/sys/vm/swappiness
# default: 60
# memory first: 0
# swap first: 100
# 使得 swappiness 设置永久生效
$ vim /etc/sysctl.conf
vm.swappiness=10
# 可以配合 JVM 中的 `-XX:+AlwaysPreTouch` 参数,在进程启动的时候,让 jvm 通过 demand-zeroed 方式将内存一次分配到位,提高 daemon 常驻进程性能

控制 Overcommit

1
2
3
4
5
$ cat /proc/sys/vm/overcommit_memory
# 0: 表示内核将检查是否有足够的可用内存供应用进程使用
# 如果有足够的可用内存,内存申请允许;否则,内存申请失败,并把错误返回给应用进程
# 1: 表示内核允许分配所有的物理内存,而不管当前的内存状态如何
# 2: 表示内核允许分配超过所有物理内存和交换空间总和的内存

禁用透明巨页

Page Size

1
2
$ getconf PAGE_SIZE
4096

Huge Page Size

1
2
$ cat /proc/meminfo | grep Hugepagesize
Hugepagesize: 2048 kB

Transparent Huge Pages

1
2
3
4
5
6
7
$ cat /sys/kernel/mm/transparent_hugepage/enabled
[always] madvise never
$ echo never > /sys/kernel/mm/transparent_hugepage/enabled
$ echo never > /sys/kernel/mm/transparent_hugepage/defrag
# Redhat 的相关目录: /sys/kernel/mm/redhat_transparent_hugepage/

参考

清理 Cache

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
$ free -g && sync && echo 3 > /proc/sys/vm/drop_caches && free -g
# before
total used free shared buffers cached
Mem: 3833 3726 106 0 107 272
-/+ buffers/cache: 3346 486
Swap: 10239 0 10239
# after
total used free shared buffers cached
Mem: 3833 3345 487 0 1 31
-/+ buffers/cache: 3312 520
Swap: 10239 0 10239
# To free pagecache:
$ echo 1 > /proc/sys/vm/drop_caches
# To free dentries and inodes:
$ echo 2 > /proc/sys/vm/drop_caches
# To free pagecache, dentries and inodes:
$ echo 3 > /proc/sys/vm/drop_caches
# 执行完清空 Cache 的命令之后,需要重新设置为 0,否则 Cache 机制就相当于被 disable 了
$ echo 0 > /proc/sys/vm/drop_caches

增大预留空闲内存

1
2
3
4
5
6
7
# 系统默认预留内存空间,只有 88 MB
$ cat /proc/sys/vm/min_free_kbytes
90112
# 为了系统稳定性,可以增加到 1 GB
$ vim /proc/sys/vm/min_free_kbytes
1048576

系统架构

Cache & Buffer

定义

在 Linux 的内存管理中,Cache(Page Cache)属于 页面缓存,Buffer(Buffer Cache)则属于 缓冲区缓存。前者,针对 页(Page)内存进行管理,后者,则针对 块(Block)内存进行管理

分析 Cache

fincore
安装
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 安装两个 perl 依赖包(大小仅 336 KB)
$ yum install perl-Inline perl-Parse-RecDescent -y
# 下载编译 fincore
$ wget https://github.com/david415/linux-ftools/archive/master.zip
$ unzip linux-ftools-master.zip
$ cd linux-ftools-master
$ chmod 777 configure
$ ./configure --prefix=/usr/local/ && make -j2 && make -j2 install
# 如果报错 WARNING: `automake-1.10` is missing on your system.
$ ln -sf /usr/bin/aclocal /usr/bin/aclocal-1.10
$ ln -sf /usr/bin/automake /usr/bin/automake-1.10
# 如果需要在线上环境,进行离线安装
$ mkdir -p /tmp/fincore
$ yum reinstall --downloadonly --downloaddir=/tmp/fincore perl-Inline perl-Parse-RecDescent -y
$ ll /tmp/fincore
-rw-r--r-- 1 root root 151484 Jul 22 2010 perl-Inline-0.46-1.el6.noarch.rpm
-rw-r--r-- 1 root root 193348 Sep 25 2011 perl-Parse-RecDescent-1.965-1.el6.noarch.rpm
编码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
#!/bin/sh
command -v fincore >/dev/null 2>&1 || {
echo >&2 "I require fincore but it's not installed. Try install..." ; exit 1;
}
pids_tmp=/tmp/cache.pids
cache_tmp=/tmp/cache.files
fincore_tmp=/tmp/cache.fincore
result_tmp=/tmp/result.fincore
echo "The top 3 cache pid:"
ps -e -o pid,rss | sort -nk2 -r | grep -vw 1 | head -3 | awk '{print $1}' > ${pids_tmp}
echo -e "`cat ${pids_tmp}`\n"
for pid in `cat ${pids_tmp}`; do
ps -ef | grep ${pid}
done
echo -e "\n"
if [ -f ${cache_tmp} ]; then
rm -f ${cache_tmp}
fi
while read line; do
echo "Pid: ${line}, Files:"
file=`lsof -p ${line} 2>/dev/null | awk '{print $9}'`
echo "${file}" >> ${cache_tmp}
echo -e "${file}\n" | grep -vw "NAME"
done<${pids_tmp}
if [ -f ${fincore_tmp} ]; then
rm -f ${fincore_tmp}
fi
for i in `cat ${cache_tmp}`; do
if [ -f ${i} ]; then
echo ${i} >> ${fincore_tmp}
fi
done
fincore --pages=false `cat ${fincore_tmp} | grep -v /usr/sbin/sshd | grep -v /lib64/ld-2.12.so | grep -v /bin/bash | grep -v /bin/login` > ${result_tmp}
echo "filename size total pages cached pages cached size cached percentage"
len=`cat ${result_tmp} | wc -l`
cat ${result_tmp} | tail -$(echo "${len}-1" | bc) | sort -nk4 -r
exit 0

Tips: Full code is here.

System Tap

依赖包太多,线上环境不适合

pcstat

卡在了安装的第一步 go get golang.org/x/sys/unix

回收 Cache

操作

具体操作,请看上文 “优化实战 - 清理 Cache” 部分

成本

清理 Cache 之前,需要保证 Cache 中的数据,和对应文件中的数据一致,才能对 Cache 进行释放。因此,清除 Cache 之前,内核会检查 Cache 数据与对应磁盘文件数据一致性,并将不一致的 Cache 写回磁盘。这一过程,将消耗大量的磁盘 I/O 资源

无法被回收的情况
tmpfs

Linux 提供一种 "临时"文件系统(重启之后数据不会被保存),拿出一部分的内存空间当做文件系统使用,可以动态调整文件系统的大小,并且相比磁盘文件系统的读写速度,会快上很多倍。在 tmpfs 系统里面的内存,是不会被回收的,除非 tmpfs 里面的文件被删除

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
# 操作系统默认就创建了一个 `/dev/shm` 的 tmpfs 目录,可以用来做缓存,进行提速(Automatic Memory Management、Web 缓存等)
$ ll -h /dev/shm
total 76K
-rw-r--r-- 1 root root 46 Aug 13 11:05 10694
-r-------- 1 root root 65M May 25 14:43 pulse-shm-17791060
-r-------- 1 root root 65M May 25 14:43 pulse-shm-31652098
# 当然,也可以创建自己的 tmpfs
$ mkdir /tmp/tmpfs
$ mount -t tmpfs -o size=3G none /tmp/tmpfs/
$ df -h
Filesystem Size Used Avail Use% Mounted on
tmpfs 63G 0 63G 0% /dev/shm
none 3.0G 0 3.0G 0% /tmp/tmpfs
$ free -g
total used free shared buffers cached
Mem: 125 79 46 0 0 0
-/+ buffers/cache: 79 46
Swap: 0 0 0
$ dd if=/dev/zero of=/tmp/tmpfs/yuzhouwan bs=1G
dd: writing '/tmp/tmpfs/yuzhouwan': No space left on device
4+0 records in
3+0 records out
3221225472 bytes (3.2 GB) copied, 2.57476 s, 1.3 GB/s
$ free -g
total used free shared buffers cached
Mem: 125 83 42 0 0 3
-/+ buffers/cache: 80 45
Swap: 0 0 0
$ echo 3 > /proc/sys/vm/drop_caches
$ free -g
total used free shared buffers cached
Mem: 125 84 41 0 0 3
-/+ buffers/cache: 80 44
Swap: 0 0 0
$ rm /tmp/tmpfs/yuzhouwan
$ free -g
total used free shared buffers cached
Mem: 125 86 39 0 0 0
-/+ buffers/cache: 85 40
Swap: 0 0 0
ipcs

进程间通信(IPC)方式,只需要在 “输入文件” 到 “共享内存区” 之间进行两次数据的拷贝。但是,如果通过 shmget 创建完共享内存之后,没有在结束的时候,使用 shmctl 删除共享内存,那么,这块内存将一直无法被清理出去

1
2
3
# 可以通过以下两个命令,查看共享内存空间,并删除
$ ipcs -m
$ ipcrm -m <shmid>
mmap

进程之间还可以通过 mmap 方法,映射同一个普通文件,来实现共享内存。普通文件被映射到进程地址空间后,进程可以像访问普通内存一样对文件进行访问,不必再调用 read / write 方法。同样地,如果没有在进程退出时,使用 munmap 方法解除映射关系,也会存在共享内存无法被清除的情况

参考

资源

Book

Blog

Tool

Memory Test

欢迎加入我们的技术群,一起交流学习 🎉 🎉

Technical Discussion Group:(人工智能 1020982(高级)& 1217710(进阶)| BigData 1670647)

  • 本文作者: Benedict Jin
  • 本文链接: https://yuzhouwan.com/posts/15691/
  • 版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明出处!
显示 Gitment 评论