您好,登錄后才能下訂單哦!
今天一個朋友問我在Powershell里面如何能夠并發的ping上萬臺機器?默認的test-connection 盡管有-computer這個參數,他的方式是按順序的挨個ping,全部跑下來可能有好幾個小時。
比如我需要花18秒的時間才能ping完40臺服務器,如果成千上萬的話就很費時間了。
measure-commnd -expression { $computers=Get-ADComputer -Filter {operatingsystem -like "*2012*"} Test-Connection -ComputerName $computers.name -Count 1 }
這之前,豆子對多線程的使用僅僅限于了解invoke-command可以同時對30個對象操作,經過一番學習,終于發現還有其他 的高級方式。
PowerShell里面,對于多線程的使用大概是兩大方式。
第一個是創建多個后臺的job。這種方式通過start-job或者 -asjob創建后臺job,然后通過get-job獲取當前的任務,通過receive-job來獲取完成任務的結果,最后還得remove-job來釋放內存。缺點是性能不高,尤其在創建job和退出job的過程中會消耗大量時間和資源。
第二個方式是創建多個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結束,輸出結果。
$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
參考資料:
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/
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。