今天一个朋友问我在Powershell里面如何可以并发的ping上万台机器?默认的test-connection 尽管有-computer这个参数,他的方式是按顺序的挨个ping,所有跑下来可能有好几个小时。git
好比我须要花18秒的时间才能ping完40台服务器,若是成千上万的话就很费时间了。github
measure-commnd -expression { $computers=Get-ADComputer -Filter {operatingsystem -like "*2012*"} Test-Connection -ComputerName $computers.name -Count 1 }
这以前,豆子对多线程的使用仅仅限于了解invoke-command能够同时对30个对象操做,通过一番学习,终于发现还有其余 的高级方式。shell
PowerShell里面,对于多线程的使用大概是两大方式。express
第一个是建立多个后台的job。这种方式经过start-job或者 -asjob建立后台job,而后经过get-job获取当前的任务,经过receive-job来获取完成任务的结果,最后还得remove-job来释放内存。缺点是性能不高,尤为在建立job和退出job的过程当中会消耗大量时间和资源。服务器
第二个方式是建立多个runspace,这个工做原理和invoke-command同样,每个远程的session绑定一个runspace。咱们能够建立一个runspace pool,指定在这个资源池里面最多能够同时执行多少个runspace。cookie
比起第一种方式,runspace的性能强悍了太多。下面有人作的对比实验,能够看见几乎是几十倍的性能差距。session
如今看看怎么来实现。豆子主要参考了这篇博客的方法和原理,写了一个简单的脚原本。 并发
http://thesurlyadmin.com/2013/02/11/multithreading-powershell-scripts/ ide
思路很简单,建立runspace pool,指定runspace的数量,而后对要测试的对象集合,对每个对象都建立一个后台的runspace job,绑定要执行的脚本,传入参数,把结果保存在ps对象或者hash表中,最后等待全部job结束,输出结果。
$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秒才能出结果。
知道原理以后就能够进一步优化和抽象化脚本。这一点已经有人作好了。https://github.com/RamblingCookieMonster/Invoke-Parallel/blob/master/Invoke-Parallel/Invoke-Parallel.ps1
下载,Unlock和dot source以后就能直接调用了。这里提供了一些例子做为参考https://github.com/RamblingCookieMonster/Invoke-Parallel
依葫芦画瓢,我想经过他来调用test-connection也是成功的
get-adcomputer -Filter {operatingsystem -like "*2012*"} | select -ExpandProperty name | Invoke-Parallel -ScriptBlock {Test-Connection -computername $_ -count 1}
再好比我ping 一个IP范围的计算机
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
最后,网上也有现成的脚本用来并发的测试ping,原理也是调用上面的invoke-parallel函数,不过他还增长了其余的函数用来测试rdp,winrm,rpc等远程访问的端口是否打开,进一步扩充了功能。能够直接在这里下载
http://ramblingcookiemonster.github.io/Invoke-Ping/
invoke-ping -ComputerName (Get-ADComputer -Filter {operatingsystem -like "*2012*"}).name -Detail RDP,rpc | ft -Wrap
参考资料:
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/