8

我有几十万个 URL 需要调用。这些是对应用程序服务器的调用,应用程序服务器将处理它们并将状态代码写入表。我不需要等待响应(成功/失败),只需要服务器收到请求。我还希望能够指定一次可以运行多少并发作业,因为我还没有计算出tomcat可以处理多少并发请求。

到目前为止,这是我所得到的,基本上是从其他人尝试做类似的事情中得到的,而不是通过 url 调用。文本文件在其自己的行中包含每个 url。网址如下所示:

http://webserver:8080/app/mwo/services/create?server=ServerName&e1user=admin&newMWONum=123456&sourceMWONum=0&tagNum=33-A-1B

和代码:

$maxConcurrentJobs = 10
$content = Get-Content -Path "C:\Temp\urls.txt"

foreach ($url in $content) {
    $running = @(Get-Job | Where-Object { $_.State -eq 'Running' })
    if ($running.Count -le $maxConcurrentJobs) {
        Start-Job {
             Invoke-WebRequest -UseBasicParsing -Uri $using:url
        }
    } else {
         $running | Wait-Job -Any
    }
    Get-Job | Receive-Job
}

我遇到的问题是每个“工作”都会出现 2 个错误,我不知道为什么。当我转储 url 数组 $content 时,它看起来很好,当我一一运行我的 Invoke-WebRequest 时,它们可以正常工作。

126    Job126          BackgroundJob   Running       True            localhost            ...                
Invalid URI: The hostname could not be parsed.
    + CategoryInfo          : NotSpecified: (:) [Invoke-RestMethod], UriFormatException
    + FullyQualifiedErrorId : System.UriFormatException,Microsoft.PowerShell.Commands.InvokeRestMethodComman 
   d
    + PSComputerName        : localhost

Invalid URI: The hostname could not be parsed.
    + CategoryInfo          : NotSpecified: (:) [Invoke-RestMethod], UriFormatException
    + FullyQualifiedErrorId : System.UriFormatException,Microsoft.PowerShell.Commands.InvokeRestMethodComman 
   d
    + PSComputerName        : localhost

任何帮助或替代实现将不胜感激。我愿意不使用 powershell,但我仅限于 Windows 7 桌面或 Windows 2008 R2 服务器,我可能会使用 url 中的 localhost 在服务器本身上运行最终脚本,以减少网络延迟。

4

3 回答 3

14

使用 Jobs 会产生大量开销,因为每个新 Job 都会产生一个新进程。

改用运行空间

$maxConcurrentJobs = 10
$content = Get-Content -Path "C:\Temp\urls.txt"

# Create a runspace pool where $maxConcurrentJobs is the 
# maximum number of runspaces allowed to run concurrently    
$Runspace = [runspacefactory]::CreateRunspacePool(1,$maxConcurrentJobs)

# Open the runspace pool (very important)
$Runspace.Open()

foreach ($url in $content) {
    # Create a new PowerShell instance and tell it to execute in our runspace pool
    $ps = [powershell]::Create()
    $ps.RunspacePool = $Runspace

    # Attach some code to it
    [void]$ps.AddCommand("Invoke-WebRequest").AddParameter("UseBasicParsing",$true).AddParameter("Uri",$url)

    # Begin execution asynchronously (returns immediately)
    [void]$ps.BeginInvoke()

    # Give feedback on how far we are
    Write-Host ("Initiated request for {0}" -f $url)
}

如链接的 ServerFault 帖子中所述,您还可以使用更通用的解决方案,例如Invoke-Parallel,它基本上可以完成上述操作

于 2015-07-20T19:43:57.223 回答
2

您还可以使用 .net 网络客户端的异步方法。假设您只需要向您的 URL 发送一个获取请求,Net.WebClient 就可以工作。下面是 example.com 的一个虚拟示例:

$urllist = 1..97
$batchSize = 20

$results = [System.Collections.ArrayList]::new()

$i = 1
foreach($url in $urllist) {

  $w = [System.Net.Webclient]::new().DownloadStringTaskAsync("http://www.example.com?q=$i")
  $results.Add($w) | Out-Null

  if($i % $batchSize -eq 0 -or $i -eq $urllist.Count) {
      While($false -in $results.IsCompleted) {sleep -Milliseconds 300} # waiting for batch to complete
       Write-Host " ........   Batch completed   ......... $i" -ForegroundColor Green
       foreach($r in $results) { 
         New-Object PSObject -Property @{url = $r.AsyncState.AbsoluteURI; jobstatus =$r.Status; success = !$r.IsFaulted} 
         # if you need response text use $r.Result
       }
     $results.Clear()
   }

$i+=1

}
于 2018-12-21T17:22:09.270 回答
1

我同意使用 Runspaces 的顶帖。但是,提供的代码没有显示如何从请求中取回数据。这是最近发布到我的 GitHub 页面的 PowerShell 模块:

https://github.com/phbits/AsyncHttps

它将通过 SSL/TLS(TCP 端口 443)向单个域提交异步 HTTP 请求。这是 README.md 中的一个示例

Import-Module AsyncHttps
Invoke-AsyncHttps -DnsName www.contoso.com -UriPaths $('dir1','dir2','dir3')

它返回一个包含每个请求结果的 System.Object[]。结果属性如下:

Uri       - Request Uri
Status    - Http Status Code or Exception Message
BeginTime - Job Start Time
EndTime   - Job End Time

查看您的示例后,您可能需要进行以下修改:

  1. 允许使用备用端口 ( webserver:8080)。最简单的方法是更新脚本块中的 URI。或者,仅为端口添加另一个参数到模块和脚本块。
  2. 测试查询参数在 HTTP 请求中使用时是否正确格式化并且没有被百分比编码损坏。UriBuilder只要您的 Uri 路径列表已知正常,请考虑跳过脚本块中的使用。
于 2019-05-07T19:53:53.953 回答