原创

利用shell脚本模拟消耗linux带宽

最近在性能测试的时,遇到了一个测试场景,需要去模拟测试业务在带宽压力较大的情况下的TPS和响应时间情况。
首先想到的方法是带上其他的业务一起压测,但是后来发现并没有特别消耗带宽的一些业务。

然后想到在weblogic上放上静态资源例如图片或者大文件,准备好之后,从压力机开始多并发下载这个文件,但是效果却也没有达到预期,最多也只有消耗约50MB左右,仔细研究了下也没有发现具体原因(后来觉得可能是应用的限制吧),也只好作罢。

最后想到利用脚本实现多进程在主机之间通过SFTP或者SCP来传输文件来消耗带宽。

有了这个想法之后,开始着手写脚本,好了,问题又来了,发现不管是通过SFTP还是SCP来实现文件传输都需要途中输入主机密码,这个有交互的东西在shell脚本里确实不好写,之前也解决过这种问题,但是是通过expect命令的,也比较麻烦。

百度了下,发现免密登录实现其实比较简单,于是照着脚本来了一遍,其中因为有好几个教程写的并不详细,出了很多岔子,最后还好都算解决了:

分别在两台主机上执行:ssh-keygen -t rsa
生成的密匙和公匙文件默认是在~/.ssh文件夹中,如果没有这个文件夹则手动创建
执行命令过程中一直按确认就可以了。
生成的文件为:id_rsa id_rsa.pub ,前者为秘钥文件,后者为公钥文件。
在~/.ssh目录分别执行命令 cp id_rsa.pub authorized_keys
然后分别把对方的id_rsa.pub文件中的内容复制到对方机器中的authorized_keys文件中,注意源文件中内容是一行,手动复制之后可能变成三行,这个注意下就行了。
最后重启下ssh服务 service sshd restart就行了。两台主机就能免密登录了。
最后是脚本内容:

#!/bin/bash
#利用多线程和scp来消耗主机带宽
#两个主机之间必须可以免密登录
######################################################
trap "exit_out" 2 3 15

if [ $# != 1 ]
then
    echo "The parameters you enter is not correct !";
  exit -1;
fi
thread=$1 # 此处定义线程数
max_thread=500 #最大的线程数

#scp数据
r_host="188.18.28.19"  #远程主机ip
r_username="root"  #远程主机登录名
r_dir="/home/test0208/tmp" #远程主机文件路径
r_file="$r_dir/ddfile2m" #远程主机文件名
l_dir="/home/yangjuying/tmp" #本地存放文件路径
l_file="$l_dir/ddfile2m" #本地存放文件名

#带宽日志
network_log="./net.log"

#各进程详细日志
thread_detail="./kill_network_detail_log/thread.log"

stop_flag=1

######################################################
exit_out(){
stop_flag=0
echo "请等待所有传输任务完成...、"
}

a_sub()
{
    thread_detail_thread=$thread_detail_$1
    network_log_thread=$network_log_$1
    # 此处定义一个函数,作为一个线程(子进程)
    echo "$string is running...."
    while [ $stop_flag -eq 1 ]
        do
        echo "----------$(date +%Y%m%d-%H%M%S)----------" >>$thread_detail_thread
        start_t="$(date +%s)"
        scp -P22 -o connecttimeout=10 -o GSSAPIAuthentication=no $r_username@$r_host:$r_file $l_file >>$thread_detail_thread 2>&1
        rc="$?"
        if [ "$rc" -eq 0 ];then
            stop_t="$(date +%s)"
            file_stat="$(stat $l_file)"
            file_ctime="$(echo "$file_stat" | awk -F "[ .]" 'FNR==5{print $2,$3}' | sed 's/[- :]/ /g')"
            file_ct="$(awk -v t="$file_ctime" 'BEGIN{print mktime(t)}')"
            file_size="$(du -sm $l_file | cut -f1)"
            rm "$l_file"
        else
            stop_t=""
        fi
        if [ -n "$stop_t" ];then
            connect_t=$(($file_ct-$start_t))
            transfer_t=$(($stop_t-$file_ct))
            all_t=$(($stop_t-$start_t))
            speed="$(echo "scale=3;$file_size/$transfer_t" | bc)"
            echo "size:${file_size}MB, all_time:$all_t, connect_time:$connect_t, transfer_time:$transfer_t, speed:${speed}MB/s" >>$thread_detail_thread
            echo "${speed}" > $network_log_thread
        else
            echo "scp error!" >>$thread_detail_thread
            echo "0" > $network_log_thread
        fi
        done
}

show_all_speed(){
while [ $stop_flag -eq 1 ]
do
all_speed=0
for ((i=0;i<$thread;i++))
do

if [ -f $network_log_$i ]
then
curr_speed=`cat $network_log_$i`
all_speed=`echo "${all_speed}+${curr_speed}"|bc`
fi

done
echo $all_speed
sleep 2
done
}


######################################################
rm -rf ${network_log}*
rm -rf ${thread_detail}*
rm -rf thread.lock
echo "THREAD=$thread start..."


if [ $max_thread -le $thread ]
then
thread=$max_thread
fi

if [ ! -d ./kill_network_detail_log ]
then
mkdir ./kill_network_detail_log
fi

#############################################################
tmp_fifofile=$$.fifo
mkfifo $tmp_fifofile      # 新建一个fifo类型的文件
exec 6<>$tmp_fifofile      # 将fd6指向fifo类型

for ((i=0;i<$thread;i++))
do
    echo
done >&6 # 事实上就是在fd6中放置了$thread个回车符

for ((i=0;i<$max_thread;i++))
do #max_thread次循环,可以理解为max_thread个主机,或其他 
    read -u 6 #一个read -u6命令执行一次,就从fd6中减去一个回车符,然后向下执行, 
    #fd6中没有回车符的时候,就停在这了,从而实现了线程数量控制 
    { # 此处子进程开始执行,被放到后台 
        a_sub $i
        #echo>&6
    } & #当进程结束以后,再向fd6中加上一个回车符,即补上了read -u6减去的那个
done
show_all_speed
wait # 等待所有的后台子进程结束 

exec 6>&- #关闭df6 
rm -f $tmp_fifofile
rm -rf ${network_log}*

echo "进程运行完毕,脚本已退出!"

最后想说的是,其实这个方法在scp的过程中也是可能占用一点CPU和内存资源,另外对IO也是有影响的,在实际的测试中,需要自己把握。

正文到此结束