所以我有一个脚本将通过SQL Server中存储的列表中的所有服务器进行ping操作。该脚本工作正常,但它按顺序完成(跛脚)。
有人可以帮我解决一下如何使用多线程而不是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
}
答案 0 :(得分:4)
(根据Chad Miller的建议+限制要求+等待工作修正+ 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
答案 1 :(得分:2)
如果有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 :(得分:0)
这是一个page,其中包含一个可能对您有用的脚本。我自己还没有用过它,所以除此之外我不能评论它。
答案 3 :(得分:0)
Powershell根本不会真正做多线程。我已经设法通过伪造它与“start [powershell path] scriptname.ps1”开始的火灾和遗忘脚本将其撬到位。它会触发多个insance,但如果没有通过数据库或其他消息传递机制进行最终运行,则无法从中获取数据。跟踪子进程何时终止也很棘手。
cmd /c "start /min /low C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -command .\evtlogparse.ps1 "
在您的情况下,当您将SQL字符串设置为foreach循环的一部分时,您可以尝试将数据库更新代码放入您触发的第二个脚本中。您可能会有许多不同的进程尝试更新同一个数据库表,因此计时问题的可能性非常大。
此外,由于您正在开始使用新的PowerShell实例,因此您需要花费大量内存才能使其正常工作。 foreach循环可能比开始一堆进程更快。
所以,它可以做到,但它并不是什么相似的东西。
答案 4 :(得分:0)
首先,我建议只在'foreach ..'之外创建一次变量$ ping 也许是一个更简单的解决方案......现在您正在使用SQL 2008,为什么不使用SMO方法'enumAvailableSqlServers:“... SMOApplication] :: EnumAvailableSqlServers($ false)”。这将为您提供网络上所有可用服务器的列表。这是Microsoft MSDN链接,因此您可以阅读它: http://msdn.microsoft.com/en-us/library/ms210350.aspx
答案 5 :(得分:0)
如此接近....这就是我所拥有的
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)
答案 6 :(得分:0)
以下是来自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