shell脚本如何实现goto_Shell脚本实现并发和超时控制
点击蓝字 关注我们
01 背景
从事Linux主机建设和运维的同事们在⼯作中应该经常会遇到批量修改配置信息或部署应⽤环境的需求,需要根据需求依次登录⽬标主机执⾏⼀些命令或脚本,使⽤shell脚本的循环语句是实现这⼀需求最直观⽅式。但是普通的for或do while循环都是串⾏执⾏的,脚本耗时每个循环耗时*循环次数,在较⼤规模实施或者⽬标语句耗时较长的情况下,串⾏⽅式的循环脚本执⾏时间也不容忽视。
要减少执⾏串⾏循环的耗时,⾃然要考虑如何⽤并⾏⽅式解决。在shell之外有⼀些现成的管理部署⼯具如parallel、ansible、puppet、saltstack都能解决并发执⾏多任务的问题,但⽣产系统⼀般不允许随意安装新软件,因⽽我们这⾥只讨论不借助⼯具,只使⽤shell脚本如何实现并发执⾏多任务。
并发和超时是我们在⽇常运维中经常碰到的问题。由于线上机器较多,数据量较⼤,⼀般我们在开发运维脚本的时候,都会加⼊并发机制以减少脚本的运⾏时间。通常⽤shell做并发⽆⾮是把⼀个进程扔到后台去执⾏。但这样做就会⾯临⼀个问题:如何有效的控制并发度,以防并发太⾼影响机器的负载。
02 并发管理
要实现可控的并发,就需要进程间通信来保证进程能够知晓正在运⾏的进程数量。众所周知,Linux的进程间通信的⽅法⼤概分为六种:
1、信号( Singal )
2、管道 ( Pipe ) 及命名管道(FIFO)
3、信号量 ( Semaphore )
4、共享内存 ( SharedMessage)
5、消息队列 ( MessageQueue )
6、套接字 ( Socket )
本⽅案采⽤了管道进⾏进程间的通信,因此只对管道⽅法进⾏介绍。管道其实可以分为⽆名管道和命名管道。
⽆名管道(Pipe)使⽤相对简单,但是只能够连接相关管道,由前⼀个进程负责管道的创建,后⼀个进程负责管道的回收,它在处理多个进程通信时就⼒不从⼼了;
命名管道则可以连接不相关的进程,它在⽂件系统中是⼀个特殊的设备⽂件,可以脱离其他进程⽽独⽴存在。顾名思义,命名管道(FIFO)是⼀个先⼊先出的队列,进程在写⼊时将数据加⼊队列尾部,读出时读取队列头部。命名管道是阻塞的,当没有数据可读时系统会将读进程挂起直到有数据写⼊管道,同理,当管道满时写进程也将⼀直被阻塞直到管道中的数据被取⾛。
命名管道的这⼀特性使我们很容易的就可以⽤它写出⼀个基于⽣产者消费者模型的程序。我们应⽤的基本思路就是⾸先初始化⼀个命名管道,假定我们的并发度为 N,则在进程开始时先向命名管道中写⼊ N 个数据,后续我们的后台进程在运⾏前都要先从命名管道中读取⼀个数据,然后进⼊后台执⾏,在执⾏结束后再向命名管道中写⼊⼀个数据。若命名管道中数据为空,则表⽰已经有 N 个进程在后台执⾏。此时再有进程来读取命名管道时便会被阻塞,⽆法继续执⾏。这就达到了并发度控制的⽬的。shell最简单脚本
整体思路如下图所⽰:
程序代码及注释如下所⽰:
#!/bin/bash#设置并发度Thread=100#模拟机器的数量MachineNum=200 ## 要给200台机器下发相同的命令fun(){    sleep 3}Concurrency(){    #创建⼀个管道⽂件,⽤上⾯介绍了并发度的控制机制,引⼊并发可以显著减少程序的运⾏时间。但是会引⼊⼏个新的问题:
1)如何跟踪后台进程的运⾏情况;
2)如何防⽌后台程序 Hang 死。
对于程序的运⾏情况我们可以通过打印⽇志的⽅法来进⾏跟踪,但是当程序 Hang 死该如何解决呢。尽管我们的程序是并发的,由于我们加
⼊了 wait 命令,⼀个后台进程 Hang 死⾜以导致我们的任务并发度控制代码调度超时,影响下游其他服务。基于此问题这⾥给出了⼀个超
时控制的⽅法。通过该脚本我们可以在命令执⾏前设置超时时间,超过该时间仍未结束的进程我们直接 kill 掉,当然,具体的超时策略也可
以根据应⽤的不同进⾏更改。
03 超时控制
程序的思路是把⼀个⼯作进程放在后台执⾏,然后另起⼀个监控进程,监控进程的主要 ⼯作就是 sleep ⼀定时间后杀掉⼯作进程。⾸先最
简单的⽅法就是在脚本前加⼀条命令 sleep $timeout && kill $$ &,然后后⾯写脚本的具体命令就好了。这样后⾯的命令执⾏超时的话就会
被杀掉,但是这样会有⼀定风险,那就是会产⽣误杀和漏杀。如果程序提前执⾏完并未超时,那$$取到的进程号可能已经不存在,如果碰巧
另⼀个进程使⽤了$$这个进程号的话那就真⼼悲剧了;另外⼀个风险就是脚本中放到后台运⾏的⼦进程是⽆法被杀掉的,⽽是会变成孤⼉进
程。
基于上⾯的分析,我们给出了⼀个解决⽅案。为了防⽌误杀程序会在 kill 之前进⾏检查, 只有当被杀的进程号存在并且被杀的进程是⾃⾝的
⼦进程的话才会执⾏ kill 操作。若进程正常执⾏结束,要对监控进程进⾏回收,以保证主程序能够正常退出。⾄于漏杀,只要每次执 ⾏后台
命令时都加⼊超时控制就可以进⾏解决。
程序代码如下所⽰:
#!/bin/bash#控制命令执⾏的超时时间WAITING_TIME=2TimeOut() {#取到要执⾏的命令  Command=$*  #命令放到后台执⾏  ${Command} &  #取到后台执⾏命令的进
参考
扫码关注我们
号|DBA成长之路分享⽣活和⼯作的点滴

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。