本文链接:个人站 | 简书 | CSDN
版权声明:除特别声明外,本博客文章均采用 BY-NC-SA 许可协议。转载请注明出处。
重启服务之前往往需要检查一下是否还有未处理完的请求。此时可以使用 ss
命令查看端口是否还有 TCP 连接。例如:
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,则说明服务已经处于闲置状态。脚本如下:
#!/bin/bash
# 获取本机 IP
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
。
利用上面这两个特性,我们引入异常处理,把脚本改写成:
#!/bin/bash
set -o pipefail
# 获取本机 IP
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
}||{
# 异常退出
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
}