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

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

问题描述

大家好!



我一直在寻找让我的脚本更高效的方法,并且得出结论(在StackOverflow的好友们的帮助下)Start-Job是要走的路。

我有以下foreach-loop,我想同时运行在$服务器中的所有服务器上。我有问题了解如何实际收集从接收作业返回的信息,并添加到$ serverlist。

我知道,我离得越来越远但是我真的很感激一些帮助,因为我非常难以理解Start-Job和Receive-Job的工作方式。



<$ p $
$ servers = Get-QADComputer -sizelimit 4 -WarningAction SilentlyContinue -OSName * server *,* hyper *

#创建列表
$ serverlistlist = @()

#循环服务器
foreach($ servers在$ servers){

#提取IP
$ ipaddress = [System.Net.Dns] :: GetHostAddresses($ Server.name)| select-object IPAddressToString -expandproperty IPAddressToString

#通过WMI收集OSName $ b $ OSName =(Get-WmiObject Win32_OperatingSystem -ComputerName $ server.name).caption

# Ping服务器
if(Test-Connection -ComputerName $ server.name -count 1 -Quiet){
$ reachable =Yes
}

#保存有关服务器的信息
$ serverInfo = New-Object -TypeName PSObject -Property @ {
SystemName =($ server.name).ToLower()
IPAddress = $ IPAddress
OSName = $ OSName
}
$ serverlist + = $ serverinfo |选择对象SystemName,IPAddress,OSName
}

注释


  • 我将$ serverlist输出到脚本末尾的csv文件中

  • 在我的完整脚本中列出aprox 500服务器

    解决方案

    由于您的循环只需要使用字符串很容易把它变成一个并发脚本。



    下面是一个让你的循环使用后台作业来加速处理的例子。 b

    代码将循环访问数组,并启动后台作业以运行脚本块 $ sb 中的代码。 $ maxJobs 变量控制一次运行多少个作业, $ chunkSize 变量控制每个后台作业的服务器数量
    $ b

    在脚本块中添加剩余的处理,添加任何其他属性,以便返回到PsObject。

      $ sb = {
    $ serverInfos = @()
    $ args | %{
    $ IPAddress = [Net.Dns] :: GetHostAddresses($ _)|选择-expand IPAddressToString
    #在此处进行更多处理...
    $ serverInfos + = New-Object -TypeName PsObject -Property @ {IPAddress = $ IPAddress}
    }
    return $ serverInfos

    $ b $ string []] $ servers = Get-QADComputer -sizelimit 500 -WarningAction SilentlyContinue -OSName * server *,* hyper * |选择展开名称

    $ maxJobs = 10#最大并发运行作业。
    $ chunkSize = 5#作业中处理的服务器数量。
    $ jobs = @()

    #进程服务器列表。
    for($ i = 0; $ i -le $ servers.Count; $ i + =($ chunkSize)){
    if($ servers.Count - $ i -le $ chunkSize)
    {$ c = $ servers.Count - $ i} else {$ c = $ chunkSize}
    $ c--#数组索引为0。

    #分手工作。
    $ jobs + = Start-Job -ScriptBlock $ sb -ArgumentList($ servers [($ i)..($ i + $ c)])
    $ running = @($ jobs |?{$ _.State -eq'正在运行'})

    #加速作业。
    while($ running.Count -ge $ maxJobs){
    $ finished = Wait-Job -Job $ jobs --Any
    $ running = @($ jobs |?{$ _。State -eq'Running'})
    }
    }

    #等待剩余。
    Wait-Job -Job $ jobs> $ null

    $ jobs |接收作业|选择IPAddress

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

      $ servers = Get-QADComputer -WarningAction SilentlyContinue -OSName * server *,* hyper * 

    #创建列表
    $ serverlist = @()

    $ sb = {
    param([string] $ ServerName)
    尝试{
    #取得IP
    $ ipaddress = [System.Net.Dns] :: GetHostAddresses($服务器名称)| select-object IPAddressToString -expandproperty IPAddressToString

    #通过WMI收集OSName $ b $ OSName =(Get-WmiObject Win32_OperatingSystem -ComputerName $ ServerName).caption

    #Ping the server
    if(Test-Connection -ComputerName $ ServerName -count 1 -Quiet){
    $ reachable =Yes
    }

    #保存有关服务器的信息
    $ serverInfo = New-Object -TypeName PSObject -Property @ {
    SystemName =($ ServerName).ToLower()
    IPAddress = $ IPAddress
    OSName = $ OSName
    }
    return $ serverInfo
    } catch {
    throw'无法处理名为{0}的服务器。错误是{1}。-f $ ServerName,$ _
    }
    }

    #循环服务器
    $ max = 5
    $ jobs = @()
    foreach($ servers在$ servers中)
    $ jobs + = Start-Job -ScriptBlock $ sb -ArgumentList $ server.Name
    $ running = @($作业|?{$ _。State -eq'正在运行'})

    #油门作业。
    while($ running.Count -ge $ max){
    $ finished = Wait-Job -Job $ jobs --Any
    $ running = @($ jobs |?{$ _。State -eq'Running'})
    }
    }

    #等待剩余。
    Wait-Job -Job $ jobs> $ null

    #检查失败的作业。
    $ failed = @($ jobs |?{$ _。State -eq'Failed'})
    if($ failed.Count -gt 0){
    $ failed | %{
    $ _。ChildJobs [0] .JobStateInfo.Reason.Message
    }
    }

    #收集作业数据。
    $ jobs | %{
    $ serverlist + = $ _ |接收作业|选择对象SystemName,IPAddress,OSName
    }


    Hi all!

    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.

    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: I know that I am far away from getting this nailed down, but I would really appreciate some help starting out as I am quite stumped on how Start-Job and Receive-Job works..

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

    Notes

    • I am outputting $serverlist to a csv-file at the end of the script
    • I list aprox 500 servers in my full script

    解决方案

    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.

    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.

    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天全站免登陆