重启服务之前往往需要检查一下是否还有未处理完的请求。此时可以使用 ss
命令查看端口是否还有 TCP 连接。例如:
1 2 3 pi@raspberrypi:~ $ ss -nt state established src 192.168.1.26:8888 Recv-Q Send-Q Local Address:Port Peer Address:Port 0 0 192.168.1.26:8888 192.168.1.25:50390
我们可以统计这条命令输出的行数,若小于 2,则说明服务已经处于闲置状态。脚本如下:
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 #!/bin/bash host=$(ifconfig -a | grep inet | grep -v 127.0.0.1 | grep -v inet6 | awk '{print $2}' | tr -d "addr:" ) timeout=3600 check (){ port="$1 " begin=$(date +'%s' ) while true do current=$(date +'%s' ) duration=$(($current -$begin )) if [[ $duration -gt $timeout ]]; then result="TIMEOUT" code=1 break count=$(ss -nt state established src $host :$port | wc -l) if [[ $count -lt 2 ]]; then result="IDLE" code=0 break else sleep 2 fi done echo "the result is $result " return $code } if [[ $# -ne 1 ]]; then echo "Usage: $0 [port]" exit 1 else check "$1 " exit $? fi
如果 ss
命令发生错误怎么办?为了进一步完善脚本,我们需要加入异常处理机制。bash
中没有 try...catch..
之类的语法,那么该如何捕获异常呢?下面介绍两个特殊的写法:
command1 || command2
:当且仅当 command1
发生错误时执行 command2
。
command1 && command2
:当且仅当 command1
没有错误时执行 command2
。
利用上面这两个特性,我们引入异常处理,把脚本改写成:
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/bash set -o pipefailhost=$(ifconfig -a | grep inet | grep -v 127.0.0.1 | grep -v inet6 | awk '{print $2}' | tr -d "addr:" ) timeout=3600 check (){ port="$1 " begin=$(date +'%s' ) while true do current=$(date +'%s' ) duration=$(($current -$begin )) if [[ $duration -gt $timeout ]]; then result="TIMEOUT" code=1 break { count=$(ss -nt state established src $host :$port | wc -l) && if [[ $count -lt 2 ]]; then result="IDLE" code=0 break else sleep 2 fi }||{ result="ERROR" code=1 break } done echo "the result is $result " return $code } if [[ $# -ne 1 ]]; then echo "Usage: $0 [port]" exit 1 else check "$1 " exit $? fi
ss -nt state established src $host:$port | wc -l
利用了管道,而默认情况下,前面的 ss
命令出错并不会影响后面的 wc
命令的执行。为了捕获管道中的错误,我们在脚本开头添加了 set -o pipefail
。
现在我们的脚本可以检测一个服务实例是否闲置。如果我们需要同时检查多个实例该怎么办?如果可以并行地执行我们的 check
函数就好了。我们知道,在一条命令后面加上 &
,就可以将它放到子 shell 中执行,达到多线程的效果。进一步地,我们可以用 wait
命令实现多线程同步。基于此,我们给出可以同时检查多个实例的 check_all
函数:
check_all (){
ports="$# "
anyfailed=0
for port in $ports
do
check $port &
done
for pid in $(jobs -p)
do
wait $pid
if [[ $? -ne 0 ]]; then
anyfailed=1
fi
done
return $anyfailed
}