मैं नौकरियों का उपयोग किए बिना समानांतर में अपनी PowerShell स्क्रिप्ट कैसे चलाऊं?


29

अगर मेरे पास एक स्क्रिप्ट है जिसे मुझे कई कंप्यूटरों के खिलाफ, या कई अलग-अलग तर्कों के साथ चलाने की आवश्यकता है, तो मैं एक नए PSJob के साथStart-Job spawning के ओवरहेड को जन्म देने के बिना, समानांतर में इसे कैसे निष्पादित कर सकता हूं ?

एक उदाहरण के रूप में, मैं सभी डोमेन सदस्यों पर समय को फिर से सिंक करना चाहता हूं , जैसे:

$computers = Get-ADComputer -filter * |Select-Object -ExpandProperty dnsHostName
$creds = Get-Credential domain\user
foreach($computer in $computers)
{
    $session = New-PSSession -ComputerName $computer -Credential $creds
    Invoke-Command -Session $session -ScriptBlock { w32tm /resync /nowait /rediscover }
}

लेकिन मैं कमांड जोड़ने और आह्वान करने के लिए प्रत्येक PSSession की प्रतीक्षा नहीं करना चाहता। जॉब्स के बिना, समानांतर में यह कैसे किया जा सकता है?

जवाबों:


51

अद्यतन - जबकि यह उत्तर पावरशेल रनवे की प्रक्रिया और यांत्रिकी की व्याख्या करता है और कैसे वे आपको बहु-सूत्र गैर-अनुक्रमिक कार्यभार में मदद कर सकते हैं, साथी PowerShell aficionado Warren 'Cook मॉन्स्टर' F ने अतिरिक्त मील चला गया है और इन समान अवधारणाओं को एक एकल उपकरण में शामिल किया है। कहा जाता है - यह वही करता है जिसका मैं नीचे वर्णन करता हूं, और उसने तब से इसका विस्तार किया है, जिसमें आयातित मॉड्यूल सहित लॉगिंग और तैयार सत्र राज्य के लिए वैकल्पिक स्विच, वास्तव में अच्छा सामान - मैं दृढ़ता से अनुशंसा करता हूं कि आप इसे स्वयं चमकदार समाधान बनाने से पहले इसकी जांच करें !Invoke-Parallel


समानांतर रनस्पेस निष्पादन के साथ:

अपरिहार्य प्रतीक्षा समय को कम करना

मूल विशिष्ट मामले में, निष्पादन योग्य आह्वान में एक /nowaitविकल्प होता है, जो नौकरी करते समय चालान थ्रेड को ब्लॉक करने से रोकता है (इस मामले में, समय पुन: सिंक्रनाइज़ेशन) अपने आप ही समाप्त हो जाता है।

यह जारीकर्ता के दृष्टिकोण से समग्र निष्पादन समय को बहुत कम कर देता है, लेकिन प्रत्येक मशीन से कनेक्ट करना अभी भी अनुक्रमिक क्रम में किया जाता है। अनुक्रम में हजारों ग्राहकों से जुड़ने के लिए मशीनों की संख्या के आधार पर एक लंबे समय के लिए लग सकता है जो एक कारण या किसी अन्य दुर्गम के कारण होता है, टाइमआउट इंतजार की एक संचय के कारण।

एक एकल या कुछ लगातार टाइमआउट के मामले में बाद के सभी कनेक्शनों को कतारबद्ध करने के लिए, हम समानांतर में निष्पादित करते हुए, PowerShell Runspaces को अलग करने के लिए कमांड कनेक्ट करने और आह्वान करने का काम भेज सकते हैं।

एक रनस्पेस क्या है?

एक रनस्पेस एक वर्चुअल कंटेनर है जिसमें आपका पावरशेल कोड निष्पादित करता है, और पावरस्ले स्टेटमेंट / कमांड के परिप्रेक्ष्य से पर्यावरण का प्रतिनिधित्व करता है / रखता है।

व्यापक रूप से, 1 रनस्पेस = निष्पादन का 1 धागा, इसलिए हम सभी को "मल्टी-थ्रेड" की आवश्यकता होती है हमारी पॉवरशेल स्क्रिप्ट रनस्पेस का एक संग्रह है जो तब समानांतर में निष्पादित कर सकती है।

मूल समस्या की तरह, कई रनवे को कमांड देने का काम टूट सकता है:

  1. एक RunspacePool बनाना
  2. एक PowerShell स्क्रिप्ट या रनस्पेसपूल के लिए निष्पादन योग्य कोड का एक समान टुकड़ा असाइन करना
  3. कोड को असिंक्रोनस रूप से इनवॉइस करें (यानी कोड के वापस आने का इंतजार न करें)

RunspacePool टेम्पलेट

[RunspaceFactory]पॉवरशेल में एक प्रकार का त्वरक होता है, जो हमें रनस्पेस घटकों के निर्माण में सहायता करेगा - चलो इसे काम करने के लिए रखें

1. एक RunspacePool बनाएँ और Open()इसे:

$RunspacePool = [runspacefactory]::CreateRunspacePool(1,8)
$RunspacePool.Open()

दो तर्कों को पारित किया गया CreateRunspacePool(), 1और 8किसी भी समय निष्पादित करने के लिए अनुमत न्यूनतम और अधिकतम संख्या में रनस्पेस है, जिससे हमें 8 के समानांतर अधिकतम प्रभावी डिग्री प्राप्त हुई

2. PowerShell का एक उदाहरण बनाएं, इसके लिए कुछ निष्पादन योग्य कोड संलग्न करें और इसे हमारे RunspacePool में असाइन करें:

PowerShell का एक उदाहरण powershell.exeप्रक्रिया के समान नहीं है (जो वास्तव में एक होस्ट अनुप्रयोग है), लेकिन निष्पादित करने के लिए PowerShell कोड का प्रतिनिधित्व करने वाला एक आंतरिक रनटाइम ऑब्जेक्ट। हम [powershell]PowerShell के भीतर एक नया PowerShell उदाहरण बनाने के लिए प्रकार त्वरक का उपयोग कर सकते हैं :

$Code = {
    param($Credentials,$ComputerName)
    $session = New-PSSession -ComputerName $ComputerName -Credential $Credentials
    Invoke-Command -Session $session -ScriptBlock {w32tm /resync /nowait /rediscover}
}
$PSinstance = [powershell]::Create().AddScript($Code).AddArgument($creds).AddArgument("computer1.domain.tld")
$PSinstance.RunspacePool = $RunspacePool

3. APM का उपयोग करते हुए PowerShell उदाहरण को अतुल्यकालिक रूप से शामिल करें:

.NET डेवलपमेंट शब्दावली में एसिंक्रोनस प्रोग्रामिंग मॉडल के रूप में जो ज्ञात है उसका उपयोग करके , हम Beginकोड को निष्पादित करने के लिए "हरी बत्ती" और Endपरिणामों को इकट्ठा करने के लिए एक विधि के लिए एक कमांड के आह्वान को एक विधि में विभाजित कर सकते हैं । चूंकि हम इस मामले में वास्तव में किसी भी प्रतिक्रिया में रुचि नहीं रखते हैं (हम w32tmवैसे भी आउटपुट से इंतजार नहीं करते हैं ), हम केवल पहली विधि को कॉल करके बना सकते हैं

$PSinstance.BeginInvoke()

इसे एक RunspacePool में लपेटकर

उपरोक्त तकनीक का उपयोग करते हुए, हम नए कनेक्शन बनाने और एक समानांतर निष्पादन प्रवाह में दूरस्थ कमांड को लागू करने के अनुक्रमिक पुनरावृत्तियों को लपेट सकते हैं:

$ComputerNames = Get-ADComputer -filter * -Properties dnsHostName |select -Expand dnsHostName

$Code = {
    param($Credentials,$ComputerName)
    $session = New-PSSession -ComputerName $ComputerName -Credential $Credentials
    Invoke-Command -Session $session -ScriptBlock {w32tm /resync /nowait /rediscover}
}

$creds = Get-Credential domain\user

$rsPool = [runspacefactory]::CreateRunspacePool(1,8)
$rsPool.Open()

foreach($ComputerName in $ComputerNames)
{
    $PSinstance = [powershell]::Create().AddScript($Code).AddArgument($creds).AddArgument($ComputerName)
    $PSinstance.RunspacePool = $rsPool
    $PSinstance.BeginInvoke()
}

यह मानते हुए कि सीपीयू में एक ही समय में सभी 8 रनस्पेस को निष्पादित करने की क्षमता है, हमें यह देखने में सक्षम होना चाहिए कि निष्पादन का समय बहुत कम है, लेकिन उपयोग किए गए "उन्नत" तरीकों के कारण स्क्रिप्ट की पठनीयता की कीमत पर।


समानता की अधिकतम डिग्री का निर्धारण:

हम एक रनस्पेसपूल आसानी से बना सकते हैं जो एक ही समय में 100 रनस्पेस के निष्पादन की अनुमति देता है:

[runspacefactory]::CreateRunspacePool(1,100)

लेकिन दिन के अंत में, यह सब नीचे आता है कि हमारे स्थानीय सीपीयू निष्पादन की कितनी इकाइयों को संभाल सकते हैं। दूसरे शब्दों में, जब तक आपका कोड निष्पादित हो रहा है, तब तक अधिक रनवे की अनुमति देने का कोई मतलब नहीं है, क्योंकि आपके पास कोड के निष्पादन को भेजने के लिए तार्किक प्रोसेसर हैं।

WMI के लिए धन्यवाद, यह सीमा निर्धारित करने के लिए काफी आसान है:

$NumberOfLogicalProcessor = (Get-WmiObject Win32_Processor).NumberOfLogicalProcessors
[runspacefactory]::CreateRunspacePool(1,$NumberOfLogicalProcessors)

यदि, दूसरी ओर, आप जिस कोड को निष्पादित कर रहे हैं, वह नेटवर्क विलंबता जैसे बाहरी कारकों के कारण बहुत प्रतीक्षा समय को पूरा करता है, तब भी आप तार्किक प्रोसेसर की तुलना में अधिक साथ-साथ रन-वे चलाने से लाभ उठा सकते हैं, इसलिए आप शायद परीक्षण करना चाहते हैं ब्रेक- इवन खोजने के लिए अधिकतम संभव रनस्पेस की सीमा :

foreach($n in ($NumberOfLogicalProcessors..($NumberOfLogicalProcessors*3)))
{
    Write-Host "$n: " -NoNewLine
    (Measure-Command {
        $Computers = Get-ADComputer -filter * -Properties dnsHostName |select -Expand dnsHostName -First 100
        ...
        [runspacefactory]::CreateRunspacePool(1,$n)
        ...
    }).TotalSeconds
}

4
यदि नौकरियां नेटवर्क पर प्रतीक्षा कर रही हैं, जैसे कि आप दूरस्थ कंप्यूटर पर पावरशेल कमांड चला रहे हैं, तो आप किसी भी सीपीयू टोंटी को हिट करने से पहले आसानी से तार्किक प्रोसेसर की संख्या पर जा सकते हैं।
माइकल हैम्पटन

खैर, यह सच है। इसे थोड़ा बदल दिया और परीक्षण के लिए एक उदाहरण प्रदान किया
मैथियास आर जेसेन

यह सुनिश्चित करने के लिए कि सभी काम आखिर में कैसे किए जाते हैं? (सभी स्क्रिप्ट ब्लॉक समाप्त होने के बाद कुछ करने की आवश्यकता हो सकती है)
sjzls

@NickW महान सवाल। मैं नौकरियों पर नज़र रखने और "कटाई" के संभावित उत्पादन पर बाद में एक अनुवर्ती कार्रवाई करूंगा, आज बने रहिए
मथियास आर। जेसेन

1
@ MathiasR.Jessen बहुत अच्छा लिखा जवाब! अपडेट का इंतजार कर रहे हैं।
सिग्नल 15

5

इस चर्चा में जोड़कर, जो कुछ याद आ रहा है, वह एक डेटा है जो रनस्पेस से बनाया गया है, और एक रनवे की स्थिति की जाँच करने के लिए वैरिएबल स्टोर करता है, यानी यह पूरा हो गया है या नहीं।

#Add an collector object that will store the data
$Object = New-Object 'System.Management.Automation.PSDataCollection[psobject]'

#Create a variable to check the status
$Handle = $PSinstance.BeginInvoke($Object,$Object)

#So if you want to check the status simply type:
$Handle

#If you want to see the data collected, type:
$Object

3

की जाँच करें PoshRSJob । यह मूल * -जोब कार्यों के समान / समान कार्य प्रदान करता है, लेकिन रनस्पेस का उपयोग करता है जो मानक पॉवर्सशेल नौकरियों की तुलना में बहुत तेज और अधिक संवेदनशील होते हैं।


1

@ mathias-r-jessen का शानदार जवाब है, हालांकि विवरण हैं जिन्हें मैं जोड़ना चाहूंगा।

मैक्स थ्रेड्स

सिद्धांत रूप में थ्रेड्स को सिस्टम प्रोसेसर की संख्या से सीमित किया जाना चाहिए। हालाँकि, AsyncTcpScan का परीक्षण करते समय मैंने इसके लिए अधिक बड़ा मूल्य चुनकर बेहतर प्रदर्शन प्राप्त किया MaxThreads। इस प्रकार उस मॉड्यूल में एक -MaxThreadsइनपुट पैरामीटर है। ध्यान रखें कि बहुत सारे धागे आवंटित करने से प्रदर्शन में बाधा होगी।

रिटर्निंग डाटा

डेटा वापस प्राप्त करना ScriptBlockमुश्किल है। मैंने ओपी कोड को अपडेट किया है और इसे AsyncTcpScan के लिए उपयोग किया गया है ।

चेतावनी: मैं निम्नलिखित कोड का परीक्षण करने में सक्षम नहीं था। मैंने सक्रिय निर्देशिका cmdlets के साथ काम करने के अपने अनुभव के आधार पर ओपी स्क्रिप्ट में कुछ बदलाव किए।

# Script to run in each thread.
[System.Management.Automation.ScriptBlock]$ScriptBlock = {

    $result = New-Object PSObject -Property @{ 'Computer' = $args[0];
                                               'Success'  = $false; }

    try {
            $session = New-PSSession -ComputerName $args[0] -Credential $args[1]
            Invoke-Command -Session $session -ScriptBlock { w32tm /resync /nowait /rediscover }
            Disconnect-PSSession -Session $session
            $result.Success = $true
    } catch {

    }

    return $result

} # End Scriptblock

function Invoke-AsyncJob
{
    [CmdletBinding()]
    param(
        [parameter(Mandatory=$true)]
        [System.Management.Automation.PSCredential]
        # Credential object to login to remote systems
        $Credentials
    )

    Import-Module ActiveDirectory

    $Results = @()

    $AllJobs = New-Object System.Collections.ArrayList

    $AllDomainComputers = Get-ADComputer -Filter * -Properties dnsHostName

    $HostRunspacePool = [System.Management.Automation.Runspaces.RunspaceFactory]::CreateRunspacePool(2,10,$Host)

    $HostRunspacePool.Open()

    foreach($DomainComputer in $AllDomainComputers)
    {
        $asyncJob = [System.Management.Automation.PowerShell]::Create().AddScript($ScriptBlock).AddParameters($($($DomainComputer.dnsName),$Credentials))

        $asyncJob.RunspacePool = $HostRunspacePool

        $asyncJobObj = @{ JobHandle   = $asyncJob;
                          AsyncHandle = $asyncJob.BeginInvoke()    }

        $AllJobs.Add($asyncJobObj) | Out-Null
    }

    $ProcessingJobs = $true

    Do {

        $CompletedJobs = $AllJobs | Where-Object { $_.AsyncHandle.IsCompleted }

        if($null -ne $CompletedJobs)
        {
            foreach($job in $CompletedJobs)
            {
                $result = $job.JobHandle.EndInvoke($job.AsyncHandle)

                if($null -ne $result)
                {
                    $Results += $result
                }

                $job.JobHandle.Dispose()

                $AllJobs.Remove($job)
            } 

        } else {

            if($AllJobs.Count -eq 0)
            {
                $ProcessingJobs = $false

            } else {

                Start-Sleep -Milliseconds 500
            }
        }

    } While ($ProcessingJobs)

    $HostRunspacePool.Close()
    $HostRunspacePool.Dispose()

    return $Results

} # End function Invoke-AsyncJob
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.