Справка многопоточности w/Powershell

Таким образом, у меня есть сценарий, который пройдет и проверит с помощью ping-запросов все серверы из списка, который хранится в SQL Server. Сценарий хорошо работает, но он делает все это последовательно (хромое).

Кто-то может выручить меня относительно того, как я изменил бы это для использования многопоточности вместо цикла foreach?

    $Server = "ServerName"
$Database = "DatabaseName"

$con = "server=$Server;database=$Database;Integrated Security=sspi"
$cmd = "SELECT ServerName FROM dbo.vwServerListActive"

  $da = new-object System.Data.SqlClient.SqlDataAdapter ($cmd, $con)

  $dt = new-object System.Data.DataTable

  $da.fill($dt) | out-null


  foreach ($srv in $dt)
    {

    $ping = new-object System.Net.NetworkInformation.Ping
    $Reply = $ping.send($srv.ServerName)

    $ServerName = $srv.ServerName 

    $ServerName
    $Reply.status

    if ($Reply.status –eq “Success”)
    {
        $sql = "UPDATE dbo.ServerList SET GoodPing = 1 WHERE GoodPing <> 1 AND ServerName = '$ServerName'"

    }
    else
    {
        $sql = "UPDATE dbo.ServerList SET GoodPing = 0 WHERE GoodPing <> 0 AND ServerName = '$ServerName'"
    }

    $Reply = ""

    invoke-sqlcmd -serverinstance $Server -database $Database -query $sql


    }
8
задан ColinStasiuk 13 July 2010 в 17:10
поделиться

7 ответов

Если у вас есть PowerShell 2.0, вы можете использовать фоновые задания. Вам нужно разбить список серверов на "группы". Учитывая исходную таблицу с serverName и groupName:

CREATE TABLE [dbo].[vwServerListActive](
    [serverName] [varchar](50) NULL,
    [groupName] [char](1) NULL
) 

Небольшая модификация вашего скрипта (сохраните как forum.ps1):

param($groupName)

$Server = "$env:computername\sql2k8"
$Database = "dbautility" 

$con = "server=$Server;database=$Database;Integrated Security=sspi" 
$cmd = "SELECT ServerName FROM dbo.vwServerListActive WHERE groupName ='$groupName'" 

  $da = new-object System.Data.SqlClient.SqlDataAdapter ($cmd, $con) 

  $dt = new-object System.Data.DataTable 

  $da.fill($dt) | out-null 


  foreach ($srv in $dt) 
    { 

    $ping = new-object System.Net.NetworkInformation.Ping 
    $Reply = $ping.send($srv.ServerName) 

    new-object PSObject -Property @{ServerName=$($srv.ServerName); Reply=$($Reply.status)} 

    } 

Затем вы можете вызвать скрипт для разных групп:

#groupName A
start-job -FilePath .\forum.ps1 -Name "Test" -ArgumentList "A"
#groupName B
start-job -FilePath .\forum.ps1 -Name "Test" -ArgumentList "B"

Get-Job -name "test" | wait-job | out-null
Get-Job -name "test" | receive-job

#get-job -name "test" |remove-job

Если вы используете PowerShell V1 или sqlps, вы можете использовать System.Diagnostics.ProcessStartInfo для запуска отдельных процессов powershell.exe и передать имя группы.

param($groupName)

    $StartInfo = new-object System.Diagnostics.ProcessStartInfo
    $StartInfo.FileName = "$pshome\powershell.exe"
    $StartInfo.Arguments = " -NoProfile -Command C:\scripts\forum.ps1 $groupName"
    $StartInfo.WorkingDirectory = "C:\scripts"
    $StartInfo.LoadUserProfile = $true
    $StartInfo.UseShellExecute = $true
    [System.Diagnostics.Process]::Start($StartInfo) > $null
2
ответ дан 5 December 2019 в 22:16
поделиться

(Отредактировано в соответствии с предложением Чада Миллера + требование регулирования + исправление задания ожидания + исправление STA)

Support.ps1

powershell -File "Main.ps1" -Sta

Main.ps1

$Server = "ServerName"   
$Database = "DatabaseName"   

$con = "server=$Server;database=$Database;Integrated Security=sspi"   
$cmd = "SELECT ServerName FROM dbo.vwServerListActive"   

$da = New-Object System.Data.SqlClient.SqlDataAdapter -ArgumentList $cmd, $con  

$dt = New-Object System.Data.DataTable   

$da.Fill($dt) | Out-Null  

$ThrottleLimit = 10 
$activeJobs = New-Object 'System.Collections.Generic.List[Int32]' 

$JobStateChanged = { 
    param ( 
        [System.Object]$Sender, 
        [System.Management.Automation.JobStateEventArgs]$EventArgs 
    ) 

    switch ($EventArgs.JobStateInfo.State) 
    { 
        Blocked { return } 
        Completed { $activeJobs.Remove($Sender.Id); break } 
        Failed { $activeJobs.Remove($Sender.Id); break } 
        NotStarted { return } 
        Running { return } 
        Stopped { $activeJobs.Remove($Sender.Id); break } 
    }

    Unregister-Event -SourceIdentifier ("{0}.StateChanged" -f $Sender.Name)
} 

foreach ($srv in $dt)   
{ 
    while ($true) 
    { 
        if ($activeJobs.Count -lt $ThrottleLimit) 
        { 
            $job = Start-Job -InitializationScript {   
                Add-PSSnapin -Name SqlServerCmdletSnapin100   
            } -ScriptBlock {  
                param (  
                    [String]$Server,  
                    [String]$Database,  
                    [String]$ServerName  
                )  

                if (Test-Connection -ComputerName $ServerName -Quiet)   
                {   
                    $sql = "UPDATE dbo.ServerList SET GoodPing = 1 WHERE GoodPing <> 1 AND ServerName = '$ServerName'"  
                }   
                else   
                {   
                    $sql = "UPDATE dbo.ServerList SET GoodPing = 0 WHERE GoodPing <> 0 AND ServerName = '$ServerName'"  
                }  

                Invoke-SqlCmd -ServerInstance $Server -Database $Database -Query $sql  
            } -ArgumentList $Server, $Database, $srv.ServerName  

            $activeJobs.Add($job.Id) 

            Register-ObjectEvent -InputObject $job -EventName StateChanged -SourceIdentifier ("{0}.StateChanged" -f $job.Name) -Action $JobStateChanged 

            break 
        } 
    } 
} 

Get-Job | Where-Object { $_.State -eq "Running" } | Wait-Job
Get-Job | Remove-Job
4
ответ дан 5 December 2019 в 22:16
поделиться

Во-первых, я предлагаю создавать только один раз переменную $ ping вне «foreach ..».
Может быть, более простое решение ... теперь, когда вы используете SQL 2008, почему бы не использовать метод SMO enumAvailableSqlServers: "... SMOApplication] :: EnumAvailableSqlServers ($ false)". Это даст вам список всех доступных серверов в сети. Вот ссылка Microsoft MSDN, чтобы вы могли прочитать об этом: http://msdn.microsoft.com/en-us/library/ms210350.aspx

0
ответ дан 5 December 2019 в 22:16
поделиться

so close.... вот что у меня есть

add-pssnapin SqlServerCmdletSnapin100

$Server = "ServerName"
$Database = "DatabaseName"

$con = "server=$Server;database=$Database;Integrated Security=sspi"  
$cmd = "SELECT ServerName FROM dbo.vwServerListActive"  

$da = New-Object System.Data.SqlClient.SqlDataAdapter -ArgumentList $cmd, $con 

$dt = New-Object System.Data.DataTable  

$da.Fill($dt) | Out-Null 


foreach ($srv in $dt)  
{     
    Start-Job -ScriptBlock { 
        param ( 
            [String]$Server, 
            [String]$Database, 
            [String]$ServerName 
        ) 


    if (Test-Connection -ComputerName $ServerName -quiet)  
            {  
                $sql = "UPDATE dbo.ServerList SET GoodPing = 1 WHERE GoodPing <> 1 AND ServerName = '$ServerName'" 
            }  
            else  
            {  
                $sql = "UPDATE dbo.ServerList SET GoodPing = 0 WHERE GoodPing <> 0 AND ServerName = '$ServerName'" 
            } 

           Invoke-SqlCmd -ServerInstance $Server -Database $Database -Query $sql 
    } -ArgumentList $Server, $Database, $srv.ServerName 
} 

и похоже, что он запускает несколько заданий... но моя таблица никогда не обновляется. Если я удалю "Start-Job" и список аргументов и использую $srv.ServerName, то он работает последовательно, как и раньше. Есть идеи? (Большое спасибо BTW за все ответы)

0
ответ дан 5 December 2019 в 22:16
поделиться

Вот скрипт от Jim Truher для фоновых заданий в PowerShell v1.0:

http://jtruher.spaces.live.com/blog/cns!7143DA6E51A2628D!130.entry

В PowerShell v2.0 фоновые задания встроены:

http://technet.microsoft.com/en-us/library/dd347692.aspx

-Oisin

0
ответ дан 5 December 2019 в 22:16
поделиться

Вот страница со сценарием, который может быть вам полезен. Я сам еще не использовал его, поэтому не могу комментировать это, кроме этого.

0
ответ дан 5 December 2019 в 22:16
поделиться

Powershell на самом деле не делает многопоточность вообще. Мне удалось применить ее, сымитировав ее с помощью скрипта fire-and-forget, запускаемого командой "start [powershell path] scriptname.ps1". Он запустит несколько страховок, но вы не сможете получить от них данные, не выполнив конечный запуск через базу данных или другой механизм передачи сообщений. Отследить, когда дочерние процессы завершаются, тоже непросто.

cmd /c "start /min /low C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -command .\evtlogparse.ps1  "

В вашем случае, поскольку вы задаете строку SQL как часть цикла foreach, вы можете попытаться поместить код обновления БД во второй скрипт, который вы запускаете. Потенциально у вас будет много различных процессов, пытающихся обновить одну и ту же таблицу базы данных, так что вероятность возникновения проблем со временем довольно велика.

Кроме того, поскольку вы запускаете множество новых экземпляров powershell, вам потребуется много памяти, чтобы заставить это работать. Цикл foreach может быть просто быстрее, чем запуск кучи процессов.

Итак, это можно сделать, но это даже не похоже на красоту.

0
ответ дан 5 December 2019 в 22:16
поделиться
Другие вопросы по тегам:

Похожие вопросы: