【问题标题】:Running multiple scriptblocks at the same time with Start-Job (instead of looping)使用 Start-Job 同时运行多个脚本块(而不是循环)
【发布时间】:2012-03-07 12:00:06
【问题描述】:

大家好!

我一直在寻找一种方法来提高我的脚本效率,并且我已经得出结论(在 StackOverflow 上好心人的帮助下)Start-Job 是要走的路。

我希望在 $servers 中的所有服务器上同时运行以下 foreach 循环。我无法理解如何实际收集从 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
}

备注

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

【问题讨论】:

    标签: powershell foreach jobs


    【解决方案1】:

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

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

    代码将遍历数组并启动后台作业以运行脚本块$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
    }
    

    【讨论】:

    • 似乎这会让你在创造工作的开销上吃掉你。每台服务器一项工作意味着您将不得不加载(和卸载)powershell.exe 500 次,然后才能全部通过它们,并且每个工作只做微不足道的工作(查找 IP 地址和操作系统名称 1服务器)。
    • @mjolinor 取决于循环的每次迭代需要多长时间。我对其进行了一些修改,以将一大块服务器发送到每个后台作业。感谢您的评论:)
    • 非常感谢!这是太棒了!我在每台服务器上执行了很多命令,所以我真的不需要块大小。每个作业还生成 5 个 csv 文件。我昨晚登录并在您添加 chunksize 部分之前看到了代码。没有它,代码将如何?非常感谢!这太棒了:)
    • @Sune 我重新添加了它。您可以使用第一个并将块大小设置为 1 或仅使用原始版本。
    • @Sune 您必须将多个对象放入一个数组并返回该数组。
    【解决方案2】:

    关于 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[$_]
      }
    

    【讨论】:

    • 谢谢!开始20个工作怎么样?那可行吗?还是我完全走错了路?
    • 20 可能是可行的。它取决于计算机以及在您尝试运行它时还发生了什么。基本上它相当于“我的计算机现在能处理启动 20 个以上的 Powersehll 实例吗?”
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-10-20
    • 2011-04-25
    • 2013-01-17
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多