PowerShell 多线程的使用

简介:

今天一个朋友问我在Powershell里面如何能够并发的ping上万台机器?默认的test-connection 尽管有-computer这个参数,他的方式是按顺序的挨个ping,全部跑下来可能有好几个小时。


比如我需要花18秒的时间才能ping完40台服务器,如果成千上万的话就很费时间了。


1
2
3
4
5
6
measure-commnd  -expression {
$computers = Get-ADComputer  - Filter  {operatingsystem  -like  "*2012*"
 
Test-Connection  -ComputerName  $computers .name -Count 1
 
}

wKiom1cErB7jvpi9AAAakf0JJIs452.png



这之前,豆子对多线程的使用仅仅限于了解invoke-command可以同时对30个对象操作,经过一番学习,终于发现还有其他 的高级方式。

PowerShell里面,对于多线程的使用大概是两大方式。

第一个是创建多个后台的job。这种方式通过start-job或者 -asjob创建后台job,然后通过get-job获取当前的任务,通过receive-job来获取完成任务的结果,最后还得remove-job来释放内存。缺点是性能不高,尤其在创建job和退出job的过程中会消耗大量时间和资源。


wKioL1cEr4qx_OieAAB0fPHYMxI751.png


第二个方式是创建多个runspace,这个工作原理和invoke-command一样,每一个远程的session绑定一个runspace。我们可以创建一个runspace pool,指定在这个资源池里面最多可以同时执行多少个runspace。

比起第一种方式,runspace的性能强悍了太多。下面有人做的对比实验,可以看见几乎是几十倍的性能差距。


http://learn-powershell.net/2012/05/13/using-background-runspaces-instead-of-psjobs-for-better-performance/


现在看看怎么来实现。豆子主要参考了这篇博客的方法和原理,写了一个简单的脚本来。 

http://thesurlyadmin.com/2013/02/11/multithreading-powershell-scripts/


思路很简单,创建runspace pool,指定runspace的数量,然后对要测试的对象集合,对每一个对象都创建一个后台的runspace job,绑定要执行的脚本,传入参数,把结果保存在ps对象或者hash表中,最后等待所有job结束,输出结果。


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
$Throttle  = 20  #threads
 
#脚本块,对指定的计算机发送一个ICMP包测试,结果保存在一个对象里面
  
$ScriptBlock  = {
    Param  (
       [string] $Computer
    )
    $a = test-connection  -ComputerName  $Computer  -Count 1 
    
    $RunResult  New-Object  PSObject -Property @{
       IPv4Adress= $a .ipv4address.IPAddressToString
       ComputerName= $Computer
       
    }
    Return  $RunResult
}
 
 
#创建一个资源池,指定多少个runspace可以同时执行
 
$RunspacePool  [RunspaceFactory] ::CreateRunspacePool(1,  $Throttle )
$RunspacePool .Open()
$Jobs  = @()
  
 
#获取Windows 2012服务器的信息,对每一个服务器单独创建一个job,该job执行ICMP的测试,并把结果保存在一个PS对象中
  
( get-adcomputer  - filter  {operatingsystem  -like  "*2012*" }).name | % {
    
    #Start-Sleep -Seconds 1
    $Job  [powershell] ::Create().AddScript( $ScriptBlock ).AddArgument( $_ )
    $Job .RunspacePool =  $RunspacePool
    $Jobs  +=  New-Object  PSObject -Property @{
       Server =  $_
       Pipe =  $Job
       Result =  $Job .BeginInvoke()
    }
}
  
 
  #循环输出等待的信息.... 直到所有的job都完成 
  
Write-Host  "Waiting.."  -NoNewline
Do  {
    Write-Host  "."  -NoNewline
    Start-Sleep  -Seconds 1
While  $Jobs .Result.IsCompleted  -contains  $false )
Write-Host  "All jobs completed!"
 
 
#输出结果 
$Results  = @()
ForEach  ( $Job  in  $Jobs )
{    $Results  +=  $Job .Pipe.EndInvoke( $Job .Result)
}
  
$Results

大概5秒之后 结果就出来了。 如果有兴趣的话可以使用measure-command命令来测试不同线程数的效果,根据我的测试,30个进程同时执行只需4秒出结果,而2个同时执行大概需要9秒才能出结果。


wKiom1cEqO_A3RQsAAA4ExOCLcs103.png


知道原理之后就可以进一步优化和抽象化脚本。这一点已经有人做好了。https://github.com/RamblingCookieMonster/Invoke-Parallel/blob/master/Invoke-Parallel/Invoke-Parallel.ps1


下载,Unlock和dot source之后就能直接调用了。这里提供了一些例子作为参考https://github.com/RamblingCookieMonster/Invoke-Parallel


依葫芦画瓢,我想通过他来调用test-connection也是成功的

1
get-adcomputer  - Filter  {operatingsystem  -like  "*2012*" } | select -ExpandProperty name |  Invoke-Parallel  -ScriptBlock { Test-Connection  -computername  $_  -count 1}


wKiom1cEqqyA47xvAAAzB3SlH5Y854.png


再比如我ping 一个IP范围的计算机

1
1..254|  Invoke-Parallel  -ScriptBlock { Test-Connection  -ComputerName  "10.2.100.$_"  -Count 1 -ErrorAction SilentlyContinue -ErrorVariable err | select Ipv4address, @{n= 'DNS' ;e={ [System.Net.Dns] ::gethostentry( $_ .ipv4address).hostname}}} -Throttle 20


wKioL1cFuu3RrNmKAAA0aXK7aUE813.png


最后,网上也有现成的脚本用来并发的测试ping,原理也是调用上面的invoke-parallel函数,不过他还增加了其他的函数用来测试rdp,winrm,rpc等远程访问的端口是否打开,进一步扩充了功能。可以直接在这里下载

http://ramblingcookiemonster.github.io/Invoke-Ping/

1
invoke-ping  -ComputerName ( Get-ADComputer  - Filter  {operatingsystem  -like  "*2012*" }).name -Detail RDP,rpc | ft -Wrap

wKioL1cErtWwJ0l0AABCM4b7Lm8565.png


参考资料:

1. http://learn-powershell.net/2012/05/13/using-background-runspaces-instead-of-psjobs-for-better-performance/

2. https://github.com/RamblingCookieMonster/Invoke-Parallel

3. http://thesurlyadmin.com/2013/02/11/multithreading-powershell-scripts/

4. http://ramblingcookiemonster.github.io/Invoke-Ping/

5. http://learn-powershell.net/2012/05/10/speedy-network-information-query-using-powershell/










本文转自 beanxyz 51CTO博客,原文链接:http://blog.51cto.com/beanxyz/1760880,如需转载请自行联系原作者

目录
相关文章
|
3月前
|
运维 开发工具 Windows
PowerShell系列(五):PowerShell通过脚本方式运行笔记
【1月更文挑战第7天】方便迁移,比如在之前工作经验积累下来的运维脚本,可以保存下来。如果业务场景用的到的话,直接文件拷贝过来就可以运行。
|
3月前
|
自然语言处理 运维 数据可视化
PowerShell系列(一):PowerShell介绍和cmd命令行的区别
【1月更文挑战第3天】什么是Windows系统的命令行环境,之前我们在使用XP、Win7系统的时候,用的最多的就是微软官方自带的cmd命令窗口了,我们通过敲命令行窗口可以实现和操作系统之间的交互。当然随着微软技术的快速发展,到了目前比较流行的Win10操作系
|
API C++ Windows
windows环境下线程编程(C++实现同步与互斥)
Windows系统为我们提供了相关API,我们可以使用他们来进行多线程编程。
|
Shell Linux
linux shell 多线程执行程序
Shell中并没有真正意义的多线程,要实现多线程可以启动多个后端进程,最大程度利用cpu性能。 直接看代码示例吧。 (1) 顺序执行的代码 1 #!/bin/bash 2 date 3 for i in `seq 1 5` 4 do 5 { 6 echo "sleep 5" ...
2120 0
|
Shell
Shell多进程执行任务
管道就像水管,有流入才会有流出,水管数水流的通道,管道是数据的通道。管道分为无名管道和有名管道。 无名管道:常用的|就是管道,只不过是无名的,可以直接作为两个进程的数据通道,比如:cat file.txt | grep test 有名管道:mkfilo 可以创建一个管道文件,比如:mkfiflo testfifo 管道有一个特点,如果管道中没有数据,那么取管道数据的操作就会阻塞,直到管道内进入数据,然后读出后才会终止这一操作,同理,写入管道的操作如果没有读取操作,这一个动作也会阻塞。
1075 0
|
Shell API C#
(C#)Windows Shell 外壳编程系列6 - 执行
(本系列文章由柠檬的(lc_mtt)原创,转载请注明出处,谢谢~)接上一节:(C#)Windows Shell 外壳编程系列5 - 获取图标 执行    许多人都知道 ShellExecute ,用于执行一个外部命令。
935 0