原文需求并发检测1000台web服务器状态或者并发为1000台web服务器分发文件等如何用shell实现方案一这应该是大多数人都第一时间想到的方法吧思路一个for循环1000次顺序执行1000次任务。实现#!/bin/bashstart_timedate%s#定义脚本运行的开始时间for((i1;i1000;i))dosleep1#sleep 1用来模仿执行一条命令需要花费的时间可以用真实命令来代替echosuccess$i;donestop_timedate%s#定义脚本运行的结束时间echoTIME:expr$stop_time - $start_time运行结果[rootiZ94yyzmpgvZ ~]# . test.sh success1 success2 success3 success4 success5 success6 success7 ........此处省略 success999 success1000 TIME:1000代码解析以及问题一个for循环1000次相当于需要处理1000个任务循环体用sleep 1代表运行一条命令需要的时间用success$i来标示每条任务.这样写的问题是1000条命令都是顺序执行的完全是阻塞时的运行假如每条命令的运行时间是1秒的话那么1000条命令的运行时间是1000秒效率相当低而我的要求是并发检测1000台web的存活如果采用这种顺序的方式那么假如我有1000台web这时候第900台机器挂掉了检测到这台机器状态所需要的时间就是900s吃屎都吃不上热乎的。所以问题的关键集中在一点如何并发方案二思路一个for循环1000次循环体里面的每个任务都放入后台运行在命令后面加符号代表后台运行。实现#!/bin/bashstartdate%s#定义脚本运行的开始时间for((i1;i1000;i))do{sleep1#sleep 1用来模仿执行一条命令需要花费的时间可以用真实命令来代替echosuccess$i;}#用{}把循环体括起来后加一个符号代表每次循环都把命令放入后台运行#一旦放入后台就意味着{}里面的命令交给操作系统的一个线程处理了#循环了1000次就有1000个把任务放入后台操作系统会并发1000个线程来处理#这些任务donewait#wait命令的意思是等待wait命令上面的命令放入后台的都执行完毕了再#往下执行。#在这里写wait是因为一条命令一旦被放入后台后这条任务就交给了操作系统#shell脚本会继续往下运行也就是说shell脚本里面一旦碰到符号就只管把它#前面的命令放入后台就算完成任务了具体执行交给操作系统去做脚本会继续#往下执行所以要在这个位置加上wait命令等待操作系统执行完所有后台命令enddate%s#定义脚本运行的结束时间echoTIME:expr$end - $start运行结果[rootiZ94yyzmpgvZ /]# .......[989]Done{sleep1;echosuccess$i;}[990]Done{sleep1;echosuccess$i;}success992[991]Done{sleep1;echosuccess$i;}[992]Done{sleep1;echosuccess$i;}success993[993]Done{sleep1;echosuccess$i;}success994 success995[994]Done{sleep1;echosuccess$i;}success996[995]Done{sleep1;echosuccess$i;}[996]Done{sleep1;echosuccess$i;}success997 success998[997]Done{sleep1;echosuccess$i;}success999[998]Done{sleep1;echosuccess$i;}[999]- Done{sleep1;echosuccess$i;}success1000[1000] Done{sleep1;echosuccess$i;}TIME:2代码解析以及问题shell中实现并发就是把循环体的命令用符号放入后台运行1000个任务就会并发1000个线程,运行时间2s比起方案一的1000s已经非常快了。可以看到输出结果success4 …success3完全都是无序的因为大家都是后台运行的这时候就是cpu随机运行了所以并没有什么顺序这样写确实可以实现并发然后大家可以想象一下1000个任务就要并发1000个线程这样对操作系统造成的压力非常大它会随着并发任务数的增多操作系统处理速度会变慢甚至出现其他不稳定因素就好比你在对nginx调优后你认为你的nginx理论上最大可以支持1w并发了实际上呢你的系统会随着高并发压力会不断攀升处理速度会越来越慢你以为你扛着500斤的东西你还能跑的跟原来一样快吗方案三思路基于方案二使用linux管道文件特性制作队列控制线程数目知识储备一.管道文件1:无名管道ps aux | grep nginx2:有名管道mkfifo /tmp/fd1有名管道特性1.cat /tmp/fd1(如果管道内容为空则阻塞)实验新开个终端写入数据2.echo “test” /tmp/fd1(如果没有读管道的操作则阻塞)总结:利用有名管道的上述特性就可以实现一个队列控制了你可以这样想一个女士公共厕所总共就10个蹲位这个蹲位就是队列长度女厕所门口放着10把药匙要想上厕所必须拿一把药匙上完厕所后归还药匙下一个人就可以拿药匙进去上厕所了这样同时来了1千位美女上厕所那前十个人抢到药匙进去上厕所了后面的990人需要等一个人出来归还药匙才可以拿到药匙进去上厕所这样10把药匙就实现了控制1000人上厕所的任务os中称之为信号量二.文件描述符1.管道具有存一个读一个读完一个就少一个没有则阻塞放回的可以重复取这正是队列特性但是问题是当往管道文件里面放入一段内容没人取则会阻塞这样你永远也没办法往管道里面同时放入10段内容想当与10把药匙解决这个问题的关键就是文件描述符了。mkfifo /tmp/fd1创建有名管道文件exec 3/tmp/fd1创建文件描述符3关联管道文件这时候3这个文件描述符就拥有了管道的所有特性还具有一个管道不具有的特性无限存不阻塞无限取不阻塞而不用关心管道内是否为空也不用关心是否有内容写入引用文件描述符 3可以执行n次echo 3 往管道里放入n把钥匙exec命令用法 http://blog.sina.com.cn/s/blog_7099ca0b0100nby8.html实现#!/bin/bashstart_timedate%s#定义脚本运行的开始时间[-e/tmp/fd1]||mkfifo/tmp/fd1#创建有名管道exec3/tmp/fd1#创建文件描述符以可读可写的方式关联管道文件这时候文件描述符3就有了有名管道文件的所有特性rm-rf/tmp/fd1#关联后的文件描述符拥有管道文件的所有特性,所以这时候管道文件可以删除我们留下文件描述符来用就可以了for((i1;i10;i))doecho3#3代表引用文件描述符3这条命令代表往管道里面放入了一个令牌donefor((i1;i1000;i))doread-u3#代表从管道中读取一个令牌{sleep1#sleep 1用来模仿执行一条命令需要花费的时间可以用真实命令来代替echosuccess$iecho3#代表我这一次命令执行到最后把令牌放回管道}donewaitstop_timedate%s#定义脚本运行的结束时间echoTIME:expr$stop_time - $start_timeexec3-#关闭文件描述符的读exec3-#关闭文件描述符的写运行结果[rootiZ94yyzmpgvZ /]# .success4 success6 success7 success8 success9 success5......success935 success941 success942......success992[992]Done{sleep1;echosuccess$i;echo13;}success993[993]Done{sleep1;echosuccess$i;echo13;}success994[994]Done{sleep1;echosuccess$i;echo13;}success998 success999 success1000 success997 success995 success996[995]Done{sleep1;echosuccess$i;echo13;}TIME:101代码解析以及问题两个for循环第一个for循环10次相当于在女士公共厕所门口放了10把钥匙第二个for循环1000次相当于1000个人来上厕所read -u3相当于取走一把药匙{}里面最后一行代码echo 3相当于上完厕所送还药匙。这样就实现了10把药匙控制1000个任务的运行运行时间为101s肯定不如方案二快但是比方案一已经快很多了这就是队列控制同一时间只有最多10个线程的并发既提高了效率又实现了并发控制。注意创建一个文件描述符exec 3/tmp/fd1 不能有空格代表文件描述符3有可读可写权限注意打开的时候可以写在一起关闭的时候必须分开关exec 3-关闭读exec 3-关闭写想一下1.假如一台优化后的nginx单机可以接受最大的并发是1w那么同时来了2w人那nginx是怎么做的2.或者一家餐厅有10个服务员餐厅的资金的支持它最多可以再招收90个服务员apache可设置默认开始10个进程最大可开启100个进程也就是说这家餐厅最多可以有100个服务员。那现在同时有1000个人来吃饭这时候会出现的现象是前10个人会立马有人招待吃饭然后餐厅每招收一个新服务员就有一个人可以坐下来吃饭好假设现在总共有100个服务员在提供服务了那么还剩下900人如何处理很简单吃完一个滚一个滚一个就进来一个吃饭。这样就实现了100个服务员去为1000个人提供服务。实际上就是用一个100人的队列去控制1000个任务的运行同一时间最多并发100个线程这样既节省了系统资源又提高了效率
shell队列实现线程并发控制
原文需求并发检测1000台web服务器状态或者并发为1000台web服务器分发文件等如何用shell实现方案一这应该是大多数人都第一时间想到的方法吧思路一个for循环1000次顺序执行1000次任务。实现#!/bin/bashstart_timedate%s#定义脚本运行的开始时间for((i1;i1000;i))dosleep1#sleep 1用来模仿执行一条命令需要花费的时间可以用真实命令来代替echosuccess$i;donestop_timedate%s#定义脚本运行的结束时间echoTIME:expr$stop_time - $start_time运行结果[rootiZ94yyzmpgvZ ~]# . test.sh success1 success2 success3 success4 success5 success6 success7 ........此处省略 success999 success1000 TIME:1000代码解析以及问题一个for循环1000次相当于需要处理1000个任务循环体用sleep 1代表运行一条命令需要的时间用success$i来标示每条任务.这样写的问题是1000条命令都是顺序执行的完全是阻塞时的运行假如每条命令的运行时间是1秒的话那么1000条命令的运行时间是1000秒效率相当低而我的要求是并发检测1000台web的存活如果采用这种顺序的方式那么假如我有1000台web这时候第900台机器挂掉了检测到这台机器状态所需要的时间就是900s吃屎都吃不上热乎的。所以问题的关键集中在一点如何并发方案二思路一个for循环1000次循环体里面的每个任务都放入后台运行在命令后面加符号代表后台运行。实现#!/bin/bashstartdate%s#定义脚本运行的开始时间for((i1;i1000;i))do{sleep1#sleep 1用来模仿执行一条命令需要花费的时间可以用真实命令来代替echosuccess$i;}#用{}把循环体括起来后加一个符号代表每次循环都把命令放入后台运行#一旦放入后台就意味着{}里面的命令交给操作系统的一个线程处理了#循环了1000次就有1000个把任务放入后台操作系统会并发1000个线程来处理#这些任务donewait#wait命令的意思是等待wait命令上面的命令放入后台的都执行完毕了再#往下执行。#在这里写wait是因为一条命令一旦被放入后台后这条任务就交给了操作系统#shell脚本会继续往下运行也就是说shell脚本里面一旦碰到符号就只管把它#前面的命令放入后台就算完成任务了具体执行交给操作系统去做脚本会继续#往下执行所以要在这个位置加上wait命令等待操作系统执行完所有后台命令enddate%s#定义脚本运行的结束时间echoTIME:expr$end - $start运行结果[rootiZ94yyzmpgvZ /]# .......[989]Done{sleep1;echosuccess$i;}[990]Done{sleep1;echosuccess$i;}success992[991]Done{sleep1;echosuccess$i;}[992]Done{sleep1;echosuccess$i;}success993[993]Done{sleep1;echosuccess$i;}success994 success995[994]Done{sleep1;echosuccess$i;}success996[995]Done{sleep1;echosuccess$i;}[996]Done{sleep1;echosuccess$i;}success997 success998[997]Done{sleep1;echosuccess$i;}success999[998]Done{sleep1;echosuccess$i;}[999]- Done{sleep1;echosuccess$i;}success1000[1000] Done{sleep1;echosuccess$i;}TIME:2代码解析以及问题shell中实现并发就是把循环体的命令用符号放入后台运行1000个任务就会并发1000个线程,运行时间2s比起方案一的1000s已经非常快了。可以看到输出结果success4 …success3完全都是无序的因为大家都是后台运行的这时候就是cpu随机运行了所以并没有什么顺序这样写确实可以实现并发然后大家可以想象一下1000个任务就要并发1000个线程这样对操作系统造成的压力非常大它会随着并发任务数的增多操作系统处理速度会变慢甚至出现其他不稳定因素就好比你在对nginx调优后你认为你的nginx理论上最大可以支持1w并发了实际上呢你的系统会随着高并发压力会不断攀升处理速度会越来越慢你以为你扛着500斤的东西你还能跑的跟原来一样快吗方案三思路基于方案二使用linux管道文件特性制作队列控制线程数目知识储备一.管道文件1:无名管道ps aux | grep nginx2:有名管道mkfifo /tmp/fd1有名管道特性1.cat /tmp/fd1(如果管道内容为空则阻塞)实验新开个终端写入数据2.echo “test” /tmp/fd1(如果没有读管道的操作则阻塞)总结:利用有名管道的上述特性就可以实现一个队列控制了你可以这样想一个女士公共厕所总共就10个蹲位这个蹲位就是队列长度女厕所门口放着10把药匙要想上厕所必须拿一把药匙上完厕所后归还药匙下一个人就可以拿药匙进去上厕所了这样同时来了1千位美女上厕所那前十个人抢到药匙进去上厕所了后面的990人需要等一个人出来归还药匙才可以拿到药匙进去上厕所这样10把药匙就实现了控制1000人上厕所的任务os中称之为信号量二.文件描述符1.管道具有存一个读一个读完一个就少一个没有则阻塞放回的可以重复取这正是队列特性但是问题是当往管道文件里面放入一段内容没人取则会阻塞这样你永远也没办法往管道里面同时放入10段内容想当与10把药匙解决这个问题的关键就是文件描述符了。mkfifo /tmp/fd1创建有名管道文件exec 3/tmp/fd1创建文件描述符3关联管道文件这时候3这个文件描述符就拥有了管道的所有特性还具有一个管道不具有的特性无限存不阻塞无限取不阻塞而不用关心管道内是否为空也不用关心是否有内容写入引用文件描述符 3可以执行n次echo 3 往管道里放入n把钥匙exec命令用法 http://blog.sina.com.cn/s/blog_7099ca0b0100nby8.html实现#!/bin/bashstart_timedate%s#定义脚本运行的开始时间[-e/tmp/fd1]||mkfifo/tmp/fd1#创建有名管道exec3/tmp/fd1#创建文件描述符以可读可写的方式关联管道文件这时候文件描述符3就有了有名管道文件的所有特性rm-rf/tmp/fd1#关联后的文件描述符拥有管道文件的所有特性,所以这时候管道文件可以删除我们留下文件描述符来用就可以了for((i1;i10;i))doecho3#3代表引用文件描述符3这条命令代表往管道里面放入了一个令牌donefor((i1;i1000;i))doread-u3#代表从管道中读取一个令牌{sleep1#sleep 1用来模仿执行一条命令需要花费的时间可以用真实命令来代替echosuccess$iecho3#代表我这一次命令执行到最后把令牌放回管道}donewaitstop_timedate%s#定义脚本运行的结束时间echoTIME:expr$stop_time - $start_timeexec3-#关闭文件描述符的读exec3-#关闭文件描述符的写运行结果[rootiZ94yyzmpgvZ /]# .success4 success6 success7 success8 success9 success5......success935 success941 success942......success992[992]Done{sleep1;echosuccess$i;echo13;}success993[993]Done{sleep1;echosuccess$i;echo13;}success994[994]Done{sleep1;echosuccess$i;echo13;}success998 success999 success1000 success997 success995 success996[995]Done{sleep1;echosuccess$i;echo13;}TIME:101代码解析以及问题两个for循环第一个for循环10次相当于在女士公共厕所门口放了10把钥匙第二个for循环1000次相当于1000个人来上厕所read -u3相当于取走一把药匙{}里面最后一行代码echo 3相当于上完厕所送还药匙。这样就实现了10把药匙控制1000个任务的运行运行时间为101s肯定不如方案二快但是比方案一已经快很多了这就是队列控制同一时间只有最多10个线程的并发既提高了效率又实现了并发控制。注意创建一个文件描述符exec 3/tmp/fd1 不能有空格代表文件描述符3有可读可写权限注意打开的时候可以写在一起关闭的时候必须分开关exec 3-关闭读exec 3-关闭写想一下1.假如一台优化后的nginx单机可以接受最大的并发是1w那么同时来了2w人那nginx是怎么做的2.或者一家餐厅有10个服务员餐厅的资金的支持它最多可以再招收90个服务员apache可设置默认开始10个进程最大可开启100个进程也就是说这家餐厅最多可以有100个服务员。那现在同时有1000个人来吃饭这时候会出现的现象是前10个人会立马有人招待吃饭然后餐厅每招收一个新服务员就有一个人可以坐下来吃饭好假设现在总共有100个服务员在提供服务了那么还剩下900人如何处理很简单吃完一个滚一个滚一个就进来一个吃饭。这样就实现了100个服务员去为1000个人提供服务。实际上就是用一个100人的队列去控制1000个任务的运行同一时间最多并发100个线程这样既节省了系统资源又提高了效率