PowerShell 多線程的使用

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

}

wKiom1cErB7jvpi9AAAakf0JJIs452.png



這以前,豆子對多線程的使用僅僅限於瞭解invoke-command能夠同時對30個對象操做,通過一番學習,終於發現還有其餘 的高級方式。shell

PowerShell裏面,對於多線程的使用大概是兩大方式。express

第一個是建立多個後臺的job。這種方式經過start-job或者 -asjob建立後臺job,而後經過get-job獲取當前的任務,經過receive-job來獲取完成任務的結果,最後還得remove-job來釋放內存。缺點是性能不高,尤爲在建立job和退出job的過程當中會消耗大量時間和資源。服務器


wKioL1cEr4qx_OieAAB0fPHYMxI751.png


第二個方式是建立多個runspace,這個工做原理和invoke-command同樣,每個遠程的session綁定一個runspace。咱們能夠建立一個runspace pool,指定在這個資源池裏面最多能夠同時執行多少個runspace。cookie

比起第一種方式,runspace的性能強悍了太多。下面有人作的對比實驗,能夠看見幾乎是幾十倍的性能差距。session


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/ 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秒才能出結果。


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也是成功的

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


wKiom1cEqqyA47xvAAAzB3SlH5Y854.png


再好比我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


wKioL1cFuu3RrNmKAAA0aXK7aUE813.png


最後,網上也有現成的腳本用來併發的測試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

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/

相關文章
相關標籤/搜索