今天一個朋友問我在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/