
今天一個朋友問我在Powershell里面如何能夠并發(fā)的ping上萬臺機器?默認的test-connection 盡管有-computer這個參數(shù),他的方式是按順序的挨個ping,全部跑下來可能有好幾個小時。

比如我需要花18秒的時間才能ping完40臺服務器,如果成千上萬的話就很費時間了。
measure-commnd -expression {
$computers=Get-ADComputer -Filter {operatingsystem -like "*2012*"}
Test-Connection -ComputerName $computers.name -Count 1
}
這之前,豆子對多線程的使用僅僅限于了解invoke-command可以同時對30個對象操作,經過一番學習,終于發(fā)現(xiàn)還有其他 的高級方式。
PowerShell里面,對于多線程的使用大概是兩大方式。
第一個是創(chuàng)建多個后臺的job。這種方式通過start-job或者 -asjob創(chuàng)建后臺job,然后通過get-job獲取當前的任務,通過receive-job來獲取完成任務的結果,最后還得remove-job來釋放內存。缺點是性能不高,尤其在創(chuàng)建job和退出job的過程中會消耗大量時間和資源。

第二個方式是創(chuàng)建多個runspace,這個工作原理和invoke-command一樣,每一個遠程的session綁定一個runspace。我們可以創(chuàng)建一個runspace pool,指定在這個資源池里面最多可以同時執(zhí)行多少個runspace。
比起第一種方式,runspace的性能強悍了太多。下面有人做的對比實驗,可以看見幾乎是幾十倍的性能差距。
http://learn-powershell.net/2012/05/13/using-background-runspaces-instead-of-psjobs-for-better-performance/
現(xiàn)在看看怎么來實現(xiàn)。豆子主要參考了這篇博客的方法和原理,寫了一個簡單的腳本來。
http://thesurlyadmin.com/2013/02/11/multithreading-powershell-scripts/
思路很簡單,創(chuàng)建runspace pool,指定runspace的數(shù)量,然后對要測試的對象集合,對每一個對象都創(chuàng)建一個后臺的runspace job,綁定要執(zhí)行的腳本,傳入?yún)?shù),把結果保存在ps對象或者hash表中,最后等待所有job結束,輸出結果。
$Throttle = 20 #threads
#腳本塊,對指定的計算機發(fā)送一個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
}
#創(chuàng)建一個資源池,指定多少個runspace可以同時執(zhí)行
$RunspacePool = [RunspaceFactory]::CreateRunspacePool(1, $Throttle)
$RunspacePool.Open()
$Jobs = @()
#獲取Windows 2012服務器的信息,對每一個服務器單獨創(chuàng)建一個job,該job執(zhí)行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()
}
}
#循環(huán)輸出等待的信息.... 直到所有的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命令來測試不同線程數(shù)的效果,根據(jù)我的測試,30個進程同時執(zhí)行只需4秒出結果,而2個同時執(zhí)行大概需要9秒才能出結果。

知道原理之后就可以進一步優(yōu)化和抽象化腳本。這一點已經有人做好了。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
最后,網上也有現(xiàn)成的腳本用來并發(fā)的測試ping,原理也是調用上面的invoke-parallel函數(shù),不過他還增加了其他的函數(shù)用來測試rdp,winrm,rpc等遠程訪問的端口是否打開,進一步擴充了功能。可以直接在這里下載
http://ramblingcookiemonster.github.io/Invoke-Ping/
invoke-ping -ComputerName (Get-ADComputer -Filter {operatingsystem -like "*2012*"}).name -Detail RDP,rpc | ft -Wrap
參考資料:
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/
另外有需要云服務器可以了解下創(chuàng)新互聯(lián)scvps.cn,海內外云服務器15元起步,三天無理由+7*72小時售后在線,公司持有idc許可證,提供“云服務器、裸金屬服務器、高防服務器、香港服務器、美國服務器、虛擬主機、免備案服務器”等云主機租用服務以及企業(yè)上云的綜合解決方案,具有“安全穩(wěn)定、簡單易用、服務可用性高、性價比高”等特點與優(yōu)勢,專為企業(yè)上云打造定制,能夠滿足用戶豐富、多元化的應用場景需求。