আমি কীভাবে আমার পাওয়ারশেল স্ক্রিপ্টগুলিকে কাজ না করে সমান্তরালে চালাব?


29

যদি আমার কাছে কোনও স্ক্রিপ্ট থাকে যা আমার একাধিক কম্পিউটারের বিপরীতে চালানো দরকার, বা একাধিক বিভিন্ন যুক্তি সহ, আমি কীভাবে এটিকে সমান্তরালভাবে চালাতে পারি, একটি নতুন পিএসজব এর সাথেStart-Job ওভারহেড ব্যয় না করে ?

উদাহরণস্বরূপ, আমি সমস্ত ডোমেন সদস্যের মতো সময়টি পুনরায় সিঙ্ক করতে চাই:

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

তবে আমি প্রতিটি পিএসএসসেশনটির সাথে সংযোগ স্থাপনের জন্য অপেক্ষা করতে চাই না। কীভাবে এই কাজগুলি সমান্তরালভাবে করা যায়?

উত্তর:


51

আপডেট - যদিও এই উত্তরটি পাওয়ার শেল রানস্পেসগুলির প্রক্রিয়া এবং যান্ত্রিকতার ব্যাখ্যা দেয় এবং তারা কীভাবে আপনাকে বহু-থ্রেড নন-সিক্যুয়ালি ওয়ার্কলোডগুলি সহায়তা করতে পারে, সহযোগী পাওয়ারশেল আফিসিয়ানাডো ওয়ারেন 'কুকি মনস্টার' এফ অতিরিক্ত মাইল চলে গেছে এবং এই একই ধারণাগুলিকে একটি একক সরঞ্জামে অন্তর্ভুক্ত করেছে বলা হয় - এটি আমি নীচে যা বর্ণনা করি তা করে এবং এটি তখন থেকে লগিংয়ের জন্য alচ্ছিক সুইচগুলি এবং আমদানি করা মডিউলগুলি সহ সত্যই শীতল স্টাফ সহ প্রস্তুত সেশন স্টেট সহ এটি প্রসারিত করে দেয় - আমি দৃ I়ভাবে আপনাকে চকচকে সমাধানের মালিকানা তৈরি করার আগে এটি পরীক্ষা করে দেখার পরামর্শ দিচ্ছি !Invoke-Parallel


সমান্তরাল রানস্পেস এক্সিকিউশন সহ:

অপরিহার্য অপেক্ষার সময় হ্রাস করা

মূল নির্দিষ্ট ক্ষেত্রে, এক্সিকিউটেবল ইনভয়েটেডের একটি /nowaitবিকল্প রয়েছে যা চাকরীটি (এই ক্ষেত্রে, সময় পুনরায় সমলয়করণ) এর নিজস্ব হয়ে গেলে শেষ হওয়া থ্রেডটি ব্লক করা বাধা দেয়।

এটি ইস্যুকারীদের দৃষ্টিকোণ থেকে সামগ্রিক কার্যকর সময়কে হ্রাস করে, তবে প্রতিটি মেশিনের সাথে সংযোগ এখনও ক্রমবর্ধমান ক্রমে সম্পন্ন হয়। ক্রম সহ কয়েক হাজার ক্লায়েন্টের সাথে সংযোগ স্থাপনের সময়সীমা অপেক্ষা অপেক্ষা জমা হওয়ার কারণে এক কারণে বা অপ্রয়োজনীয় অন্য কোনও মেশিনের সংখ্যার উপর নির্ভর করে দীর্ঘ সময় নিতে পারে।

একক বা একটানা কয়েক সময়সীমার ক্ষেত্রে পরবর্তী সমস্ত সংযোগগুলি সারি করার জন্য, আমরা সমান্তরালভাবে এক্সিকিউট করে পাওয়ারশেল রানস্পেসগুলি পৃথক করার জন্য কমান্ডগুলি সংযুক্ত করার ও অনুরোধ করার কাজটি প্রেরণ করতে পারি।

রানস্পেস কী?

একটি রানস্পেস হ'ল ভার্চুয়াল ধারক যেখানে আপনার পাওয়ারশেল কোডটি চালিত করে এবং একটি পাওয়ারশেল স্টেটমেন্ট / কমান্ডের দৃষ্টিকোণ থেকে পরিবেশকে প্রতিনিধিত্ব করে / ধরে রাখে।

বিস্তৃত ভাষায়, 1 রানস্পেস = এক্সিকিউশনের 1 থ্রেড, সুতরাং আমাদের পাওয়ারশেল স্ক্রিপ্টটি "স্পেসিফিকেশন" মাল্টি থ্রেড করা দরকার যা স্পেসসেসের সংগ্রহ যা পরবর্তীতে সমান্তরালে কার্যকর করতে পারে।

আসল সমস্যার মতো, একাধিক রানস্পেসে কমান্ড কমান্ডের কাজটি এখানে বিভক্ত হতে পারে:

  1. একটি রানস্পেসপুল তৈরি করা হচ্ছে
  2. একটি পাওয়ারশেল স্ক্রিপ্ট বা রানস্পেসপুলে এক্সিকিউটেবল কোডের সমতুল্য টুকরো বরাদ্দ করা হচ্ছে
  3. কোডটি তাত্পর্যপূর্ণভাবে চাওয়া (যেমন কোডটি ফেরার জন্য অপেক্ষা করতে হবে না)

রানস্পেসপুল টেম্পলেট

পাওয়ারশেলের একটি টাইপ এক্সিলারেটর রয়েছে যা [RunspaceFactory]রানস্পেস উপাদান তৈরিতে আমাদের সহায়তা করবে - আসুন এটি কাজ করতে দিন

1. একটি রানস্পেসপুল তৈরি করুন এবং Open()এটি:

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

দুটি আর্গুমেন্ট পাস CreateRunspacePool(), 1এবং 8হয় সর্বনিম্ন এবং যেকোনো দেওয়া সময়ে চালানো করার অনুমতি দেওয়া runspaces সর্বোচ্চ সংখ্যক, আমাদের একটি কার্যকর দান সর্বাধিক সাদৃশ্য ডিগ্রী 8।

২. পাওয়ারশেলের একটি উদাহরণ তৈরি করুন, এর সাথে কিছু এক্সিকিউটেবল কোড সংযুক্ত করুন এবং এটি আমাদের রানস্পেসপুলে বরাদ্দ করুন:

পাওয়ারশেলের একটি উদাহরণ powershell.exeপ্রক্রিয়াটির মতো নয় (যা সত্যই একটি হোস্ট অ্যাপ্লিকেশন), তবে চালিত করার জন্য পাওয়ারশেল কোডকে উপস্থাপন করে এমন একটি অভ্যন্তরীণ রানটাইম অবজেক্ট। [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

৩. এপিএম ব্যবহার করে পাওয়ারশেল উদাহরণটি অবিচ্ছিন্নভাবে আহ্বান করুন:

.NET বিকাশের পরিভাষায় অ্যাসিনক্রোনাস প্রোগ্রামিং মডেল হিসাবে পরিচিত যা ব্যবহার করে , আমরা Beginকোডটি কার্যকর করার জন্য "সবুজ আলো" দেওয়ার জন্য, Endফলাফল সংগ্রহের জন্য একটি পদ্ধতিতে একটি কমান্ডের অনুরোধকে একটি পদ্ধতিতে বিভক্ত করতে পারি । যেহেতু আমরা এই ক্ষেত্রে কোনও প্রতিক্রিয়ায় সত্যই আগ্রহী নই (আমরা w32tmযাইহোক আউটপুটটির জন্য অপেক্ষা করি না ), তাই আমরা প্রথম পদ্ধতিটিতে কেবল কল করেই উপযুক্ত করতে পারি

$PSinstance.BeginInvoke()

এটি রানস্পেসপুলে জড়িয়ে রাখছে

উপরের কৌশলটি ব্যবহার করে, আমরা নতুন সংযোগ তৈরি করার এবং ক্রমবর্ধমান ক্রম প্রবাহে রিমোট কমান্ডের অনুরোধের ক্রমিক ক্রিয়াকলাপগুলি মোড়তে পারি:

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

তবে দিনের শেষে, এটি সমস্তই নেমে আসে যে আমাদের স্থানীয় সিপিইউ কার্যকর করার কত ইউনিট পরিচালনা করতে পারে। অন্য কথায়, যতক্ষণ না আপনার কোডটি কার্যকর হচ্ছে, আপনার কাছে লজিকাল প্রসেসরগুলির চেয়ে কোড চালানো প্রেরণকারীদের চেয়ে বেশি রান স্পেসের অনুমতি দেওয়ার কোনও অর্থ হবে না।

ডাব্লুএমআইকে ধন্যবাদ, এই প্রান্তিকতা নির্ধারণ করা মোটামুটি সহজ:

$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

@ নিক ডাব্লু দুর্দান্ত প্রশ্ন। আমি আজ কাজগুলি ট্র্যাকিং এবং "ফসল কাটা" সম্ভাব্য আউটপুট নিয়ে একটি ফলোআপ করব, পরে থাকুন
ম্যাথিয়াস আর জেসেন

1
@ ম্যাথিয়াসআর.জেসেন খুব ভাল লিখিত উত্তর! আপডেটের অপেক্ষায় রয়েছি।
সিগন্যাল 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

@ মাথিয়াস-আর-জেসনের একটি দুর্দান্ত উত্তর রয়েছে যদিও আমি যুক্ত করতে চাই এমন বিশদ রয়েছে।

সর্বোচ্চ থ্রেডস

তত্ত্বের ক্ষেত্রে থ্রেডগুলি সিস্টেম প্রসেসরের সংখ্যা দ্বারা সীমাবদ্ধ করা উচিত। যাইহোক, AsyncTcpScan পরীক্ষা করার সময় আমি এর চেয়ে আরও বড় মান বেছে নিয়ে আরও ভাল পারফরম্যান্স অর্জন করেছি MaxThreads। সুতরাং কেন যে মডিউল একটি -MaxThreadsইনপুট পরামিতি আছে। মনে রাখবেন যে অনেক বেশি থ্রেড বরাদ্দ করা কার্যকারিতাকে বাধা দেয়।

রিটার্নিং ডেটা

কৌশলটি থেকে ডেটা ফিরে পাওয়া ScriptBlockকঠিন trick আমি ওপি কোডটি আপডেট করেছি এবং এ্যাসিঙ্কটিসিপিএস স্ক্যানের জন্য যা ব্যবহার করা হয়েছিল তার সাথে এটি সংহত করেছি ।

সতর্কতা: আমি নিম্নলিখিত কোডটি পরীক্ষা করতে পারিনি। অ্যাক্টিভ ডিরেক্টরি সেন্টিমিলেটসের সাথে আমার অভিজ্ঞতার ভিত্তিতে আমি ওপি স্ক্রিপ্টে কিছু পরিবর্তন করেছি।

# 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
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.