本文最后更新于 2024-08-10,文章内容已经超过7天没更新了。

项目在开发过程中难免会遇到服务器cpu使用率过低的情况,有些甲方会以服务器cpu使用率过低为理由回收我们的服务器资源,为了保持服务器资源充足,以及保证在特殊情况下服务器请求压力过大而导致资源不足的情况,下边给大家分享一个拉升cpu使用率的shell脚本

1、脚本

先展示脚本

#!/bin/bash
declare -a pid_array

kill_all(){
  for i in "${pid_array[@]}"; do
    `kill -9 $i`
    echo 'kill ' $i ';';
  done
  unset pid_array;
  echo "kill_all; unset pid_array";
}

CPU_hope=5000;
TIME_limit=1;
if [ -n "$1" ]
then
                  expr $1top "+" 10 &> /dev/null
  if [ $? -eq 0 ];then
    CPU_hope=$(printf "%.0f" `echo "scale=2;$1*100" |bc`);
  else
    echo "$1 not number";
    echo "USAGE: $0 <CPU_use%>";
    exit 1;
  fi
fi
if [ -n "$2" ]
then
                  expr $2 "+" 10 &> /dev/null
  if [ $? -eq 0 ];then
    TIME_limit=$2;
  else
    echo "$2 not number";
    echo "USAGE: $0 <CPU_use%> <TIME_limit>";
    exit 1;
  fi
fi
CURRENT_HOUR=$(date +%H)
START_HOUR=0
END_HOUR=24
if [ -n "$3" ]
then
                  expr $3 "+" 10 &> /dev/null
  if [ $? -eq 0 ];then
    START_HOUR=$3
  else
    echo "$3 not number";
    echo "USAGE: $0 <CPU_use%> <TIME_limit> <START_HOUR>";
    exit 1;
  fi
fi
if [ -n "$4" ]
then
                  expr $4 "+" 10 &> /dev/null
  if [ $? -eq 0 ];then
    END_HOUR=$4
  else
    echo "$4 not number";
    echo "USAGE: $0 <CPU_use%> <TIME_limit> <START_HOUR> <END_HOUR>";
    exit 1;
  fi
fi

if [ $START_HOUR -lt 0 ] || [ $START_HOUR -gt 24 ] || [ $END_HOUR -lt 0 ] || [ $END_HOUR -gt 24 ]; then
    echo "$3 and $4 range is 0-24"
    exit 1
fi

if [ $START_HOUR -ge $END_HOUR ]; then
    echo "$3 must be less than $4"
    exit 1
fi


DATESTRINT=$(date +%Y%m%d%H%M --date="+$TIME_limit hour")

script_abs=$(readlink -f "$0")
script_dir=$(dirname $script_abs)
if [ -e $script_dir/run.signal ]
then
  echo "Service is already running!";
  exit 1;
fi
touch $script_dir/run.signal
rm -f $script_dir/quit.signal

echo "************begin MM***author:Cary Wen************"
echo "CPU_hope:$CPU_hope";
echo "DATESTRINT:$DATESTRINT";

CPU_count=`cat /proc/cpuinfo | grep 'processor' | wc -l`;
echo "CPU_count:$CPU_count";

CURRENT_TIME=$(printf "%.0f" $(date +%Y%m%d%H%M))
echo "========================CURRENT_TIME:$CURRENT_TIME========================"
while [ $CURRENT_TIME -lt $DATESTRINT ]
do
  CPU_idle=`mpstat -P ALL 1 1 |grep "all" |sed -n '2p' |awk -F" " '{print $NF}'`;
  CPU_use=$(printf "%.0f" `echo "scale=2;(100-$CPU_idle)*100" |bc`);
  echo "CPU_use:$CPU_use;CPU_idle:$CPU_idle";
  echo "CURRENT_HOUR:$CURRENT_HOUR;START_HOUR:$START_HOUR;END_HOUR:$END_HOUR";
 if [ $CURRENT_HOUR -ge $START_HOUR ] && [ $CURRENT_HOUR -lt $END_HOUR ]; then
  if [ $CPU_hope -eq $CPU_use ]
  then
                  echo 'CPU_hope == CPU_use';
  elif [ $CPU_hope -gt $CPU_use ]
  then
    PROC_count=`echo "scale=2;(($CPU_hope-$CPU_use)*$CPU_count+4999)/10000" |bc`;
    echo "PROC_count:$PROC_count";
    PROC_count=$(printf "%.0f" $PROC_count); #四舍五入的
    echo "append PID count:$PROC_count";
    PID_ARRAY_CUT=${#pid_array[@]};
    PID_ARRAY_BEGIN=$PID_ARRAY_CUT;
    PID_ARRAY_END=$(( $PID_ARRAY_CUT + $PROC_count - 1 ))
    echo "PID_ARRAY_CUT:$PID_ARRAY_CUT;PID_ARRAY_BEGIN:$PID_ARRAY_BEGIN;PID_ARRAY_END:$PID_ARRAY_END";
       
    for i in `seq $PID_ARRAY_BEGIN $PID_ARRAY_END`
    do
      echo -ne "
      i=0;
      while true
      do
        i=100+100;
      done" | /bin/sh &
      pid_array[$i]=$!;
      echo 'start ' $! ';';
    done
  else
    PROC_count=`echo "scale=0;(($CPU_use-$CPU_hope)*$CPU_count)/10000" |bc`
    echo "delete PID count:$PROC_count";
   
    if [ $PROC_count -gt 0 ] ;then
                  PID_ARRAY_CUT=${#pid_array[@]};
    
                  if [ $PID_ARRAY_CUT -eq 0 ];
      then
                    echo "exists PID count:$PID_ARRAY_CUT";
      elif [ $PID_ARRAY_CUT -gt $PROC_count ]
      then
                    PID_ARRAY_BEGIN=$(( $PID_ARRAY_CUT - 1 ));
                    PID_ARRAY_END=$(( $PID_ARRAY_CUT - $PROC_count ))
                    echo "PID_ARRAY_CUT:$PID_ARRAY_CUT;PID_ARRAY_BEGIN:$PID_ARRAY_BEGIN;PID_ARRAY_END:$PID_ARRAY_END";
                    for i in `seq $PID_ARRAY_BEGIN -1 $PID_ARRAY_END`
        do
          PID_ID=${pid_array[$i]};
          `kill -9 $PID_ID`
          unset pid_array[$i];
          echo "kill ${PID_ID}; unset pid_array[${i}]";
        done
      else
                    kill_all;
      fi
    fi
  fi
else
          kill_all;
 fi
  sleep 30s
  if [ -e $script_dir/quit.signal ]
  then
                  echo "break while!";
                  break;
  fi
  CURRENT_TIME=$(printf "%.0f" $(date +%Y%m%d%H%M))
  CURRENT_HOUR=$(date +%H)
  echo "========================CURRENT_TIME:$CURRENT_TIME========================";
done
echo "Shutting down ..........";
kill_all;
echo "***********************end***********************"
rm -f $script_dir/run.signal
exit 0;

2、脚本使用

接下来在服务器上新建一个目录,把上述脚本main.sh放到目录下,创建一个运行脚本 start.sh,运行脚本用来初始化运行参数,并后台运行内容如下

nohup sh main.sh [a] [b] [c] [d] > main-log.log 2>&1 & echo $! > main-log.pid

参数介绍

  • a: cpu使用率拉升至,默认50%。例如35,表示将cpu使用率拉升至35%

  • b: 拉升持续时间,默认1h,单位:h。例如10,表示脚本持续拉升时间是10个小时

  • c: 拉升开始时间,默认0,单位:h。例如10,表示每天的10点开始

  • d: 拉升结束时间,默认24,单位:h。例如21,表示每天的21点结束

注意:参数b可能会和c、d冲突,例如需要拉升的时间是1个小时,指定的执行时间是20-24,但是当前系统时间是10,那么脚本是不会执行的

上述运行脚本会在目录下创建两个文件,main-log.log是脚本运行日志文件,可以查看脚本运行打印的日志,main-log.pid是脚本运行的pid,如果想停止拉升cpu可以直接kill掉这个pid

当然,脚本也提供了另外一种更安全的停止方式,在脚本目录 touch quit.signal创建一个quit.signal文件,脚本会kill掉所有正在拉升的进程

3、脚本介绍

脚本的核心是下边这部分代码

  do
    echo -ne "
    i=0;
    while true
    do
      i=100+100;
    done" | /bin/sh &
    pid_array[$i]=$!;
    echo 'start ' $! ';';
  done

这段代码会循环创建一个计算的进程,使cpu使用率达到预设的百分比

pid_array记录了所有循环创建的进程,kill_all()方法会kill掉所有的进程

if [ -e $script_dir/quit.signal ]
  then
      echo "break while!";
      break;
fi

这段代码会判断工作目录下有没有quit.signal文件,如果有则跳出循环,然后kill掉所有进程

当然,脚本里也做了安全控制,如果cpu使用率超过了预定的百分比,脚本会自动根据当前使用率选择性地kill掉某些进程,直到所有进程都kill掉,完全不会影响其他程序需要cpu的时候