大家好!
我一直在寻找一种方法来提高我的脚本效率,我得出结论(在StackOverflow上的好人帮助下),Start-Job是可行的方法。
我有以下foreach-loop,我想在$ servers中的所有服务器上同时运行。我在理解如何实际收集从Receive-Job返回的信息并添加到$ serverlist时遇到问题。
PS:我知道我远没有把它钉死,但我真的很感激一些帮助,因为我对Start-Job和Receive-Job的运作方式感到非常困惑。
# List 4 servers (for testing)
$servers = Get-QADComputer -sizelimit 4 -WarningAction SilentlyContinue -OSName *server*,*hyper*
# Create list
$serverlistlist = @()
# Loop servers
foreach($server in $servers) {
# Fetch IP
$ipaddress = [System.Net.Dns]::GetHostAddresses($Server.name)| select-object IPAddressToString -expandproperty IPAddressToString
# Gather OSName through WMI
$OSName = (Get-WmiObject Win32_OperatingSystem -ComputerName $server.name ).caption
# Ping the server
if (Test-Connection -ComputerName $server.name -count 1 -Quiet ) {
$reachable = "Yes"
}
# Save info about server
$serverInfo = New-Object -TypeName PSObject -Property @{
SystemName = ($server.name).ToLower()
IPAddress = $IPAddress
OSName = $OSName
}
$serverlist += $serverinfo | Select-Object SystemName,IPAddress,OSName
}
备注
答案 0 :(得分:10)
由于您的循环只需要使用字符串,因此很容易将其转换为并发脚本。
以下是使循环使用后台作业加速处理的示例。
代码将遍历数组并启动后台作业以运行脚本块$sb
中的代码。 $maxJobs
变量控制一次运行的作业数,$chunkSize
变量控制每个后台作业将处理的服务器数量。
在脚本块中添加剩余的处理,添加要返回到PsObject的其他属性。
$sb = {
$serverInfos = @()
$args | % {
$IPAddress = [Net.Dns]::GetHostAddresses($_) | select -expand IPAddressToString
# More processing here...
$serverInfos += New-Object -TypeName PsObject -Property @{ IPAddress = $IPAddress }
}
return $serverInfos
}
[string[]] $servers = Get-QADComputer -sizelimit 500 -WarningAction SilentlyContinue -OSName *server*,*hyper* | Select -Expand Name
$maxJobs = 10 # Max concurrent running jobs.
$chunkSize = 5 # Number of servers to process in a job.
$jobs = @()
# Process server list.
for ($i = 0 ; $i -le $servers.Count ; $i+=($chunkSize)) {
if ($servers.Count - $i -le $chunkSize)
{ $c = $servers.Count - $i } else { $c = $chunkSize }
$c-- # Array is 0 indexed.
# Spin up job.
$jobs += Start-Job -ScriptBlock $sb -ArgumentList ( $servers[($i)..($i+$c)] )
$running = @($jobs | ? {$_.State -eq 'Running'})
# Throttle jobs.
while ($running.Count -ge $maxJobs) {
$finished = Wait-Job -Job $jobs -Any
$running = @($jobs | ? {$_.State -eq 'Running'})
}
}
# Wait for remaining.
Wait-Job -Job $jobs > $null
$jobs | Receive-Job | Select IPAddress
以下是每个作业处理单个服务器的版本:
$servers = Get-QADComputer -WarningAction SilentlyContinue -OSName *server*,*hyper*
# Create list
$serverlist = @()
$sb = {
param ([string] $ServerName)
try {
# Fetch IP
$ipaddress = [System.Net.Dns]::GetHostAddresses($ServerName)| select-object IPAddressToString -expandproperty IPAddressToString
# Gather OSName through WMI
$OSName = (Get-WmiObject Win32_OperatingSystem -ComputerName $ServerName ).caption
# Ping the server
if (Test-Connection -ComputerName $ServerName -count 1 -Quiet ) {
$reachable = "Yes"
}
# Save info about server
$serverInfo = New-Object -TypeName PSObject -Property @{
SystemName = ($ServerName).ToLower()
IPAddress = $IPAddress
OSName = $OSName
}
return $serverInfo
} catch {
throw 'Failed to process server named {0}. The error was "{1}".' -f $ServerName, $_
}
}
# Loop servers
$max = 5
$jobs = @()
foreach($server in $servers) {
$jobs += Start-Job -ScriptBlock $sb -ArgumentList $server.Name
$running = @($jobs | ? {$_.State -eq 'Running'})
# Throttle jobs.
while ($running.Count -ge $max) {
$finished = Wait-Job -Job $jobs -Any
$running = @($jobs | ? {$_.State -eq 'Running'})
}
}
# Wait for remaining.
Wait-Job -Job $jobs > $null
# Check for failed jobs.
$failed = @($jobs | ? {$_.State -eq 'Failed'})
if ($failed.Count -gt 0) {
$failed | % {
$_.ChildJobs[0].JobStateInfo.Reason.Message
}
}
# Collect job data.
$jobs | % {
$serverlist += $_ | Receive-Job | Select-Object SystemName,IPAddress,OSName
}
答案 1 :(得分:3)
你需要了解的关于Start-Job的一点是,它启动了一个新的Powershell实例,作为一个单独的进程运行。 Receive-job为您提供了一种机制,可以将该会话的输出拉回到本地会话中,以便在主脚本中使用它。听起来很有吸引力,同时运行所有这些将意味着在您的计算机上启动500个Powershell实例,所有这些都会立即运行。这可能会产生一些意想不到的后果。
如果有帮助的话,这是分割工作的一种方法:
将一组计算机名称拆分为$ n个数组,并使用每个数组作为脚本块的参数列表启动一个新作业:
$computers = gc c:\somedir\complist.txt
$n = 6
$complists = @{}
$count = 0
$computers |% {$complists[$count % $n] += @($_);$count++}
0..($n-1) |% {
start-job -scriptblock {gwmi win32_operatingsystem -computername $args} - argumentlist $complists[$_]
}