使用 Start-Job 同时运行多个脚本块(而不是循环) [英] Running multiple scriptblocks at the same time with Start-Job (instead of looping)

查看:26
本文介绍了使用 Start-Job 同时运行多个脚本块(而不是循环)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

大家好!

我一直在寻找一种方法来使我的脚本更高效,并且我得出的结论是(在 StackOverflow 上的好人的帮助下)Start-Job 是可行的方法.

I've been looking for a way to make my script more efficient and I've come to the conclusion (with help from the nice people here on StackOverflow) that Start-Job is the way to go.

我想在 $servers 中的所有服务器上同时运行以下 foreach 循环.我在理解如何实际收集从 Receive-Job 返回的信息并将其添加到 $serverlist 时遇到问题.

I have the following foreach-loop that I would like to run simultanously on all the servers in $servers. I have problems understanding how I actually collect the information returned from Receive-Job and add to $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
}

注意事项

  • 我将 $serverlist 输出到脚本末尾的 csv 文件
  • 我在完整脚本中列出了大约 500 个服务器

推荐答案

由于您的循环只需要处理一个字符串,因此很容易将其转换为并发脚本.

Since your loop only needs to work with a string it's easy to turn it into a concurrent script.

以下是使循环使用后台作业来加速处理的示例.

Below is an example of making making your loop use background jobs to speed up processing.

代码将遍历数组并启动后台作业以运行脚本块 $sb 中的代码.$maxJobs 变量控制一次运行的作业数量,$chunkSize 变量控制每个后台作业将处理的服务器数量.

The code will loop through the array and spin up background jobs to run the code in the script block $sb. The $maxJobs variable controls how many jobs run at once and the $chunkSize variable controls how many servers each background job will process.

在脚本块中添加您的其余处理,添加您想要返回到 PsObject 的任何其他属性.

Add the rest of your processing in the script block adding whatever other properties you want to return to the 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

这是每个作业处理单个服务器的版本:

Here is the version that processes a single server per job:

$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
}

这篇关于使用 Start-Job 同时运行多个脚本块(而不是循环)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆