У меня есть требование проверить доступность 1000 различных URL-адресов из заданного текстового файла через одну виртуальную машину Windows Server 2016 с установленной PowerShell v5.1.
Требуемая проверка должна быть с интервалом каждые 5 минут.
Моим первым предположением было использование командлетов PowerShell: Get-Content с Invoke-WebRequest в цикле For-Each:
$urlList = Get-Content -Path "c:\URLsList.txt"
foreach ($url in $urlList) {
$result = Invoke-WebRequest $url
$result.StatusCode
}
Но, учитывая количество URL-адресов (1000), я не уверен, что PowerShell Invoke-WebRequest достаточно масштабируем для этой задачи.
Я не видел в официальной документации упоминаний о передовом опыте или каких-либо ограничений: https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.utility/invoke-webrequest?view= пауэршелл-5.1
Но во время поиска я узнал о рабочих местах PowerShell.
Но в связи с тем, Что 1000 URL должны проверяться каждые 5 минут.
Я не уверен, что это будет актуально.
@MathiasR.Jessen, я нахожусь на этапе разработки алгоритма, который будет использоваться в конечном решении.
@MathiasR.Jessen, добавил образец фрагмента для общей идеи. Но все же остается вопрос, можно ли масштабировать PowerShell для обработки 1000 веб-запросов каждые 5 минут. Или это ограничение виртуального сервера, на котором работает этот реальный скрипт.
используйте ForEach-Parallel learn.microsoft.com/en-us/powershell/module/psworkflow/about/…
@rinatgadeev, спасибо, но я написал, что использую PowerShell v5.1.
@edwio Хорошо, хорошее начало! Код завершает работу через 5 минут или нет?
@MathiasR.Jessen, совсем нехорошо. При использовании команды Measure-Command общее время выполнения сценария составляет 6 минут. Я не уверен, связана ли основная причина с текущим ресурсом виртуального сервера (8-ядерный ЦП, 16 ГБ оперативной памяти) или, возможно, с тем фактом, что некоторые URL-адреса недоступны, и из-за этого Invoke-WebRequest не имеет параметра тайм-аута. может быть установлено для каждого HTTP-запроса Get, который он выполняет.
Invoke-WebRequest в PowerShell 5.1 обязательно есть параметр тайм-аута: Invoke-WebRequest ... -TimeoutSec 2. При этом вы, вероятно, захотите группировать URL-адреса по 10 за раз (.NET FX имеет внутренний пул потоков для HTTP-запросов клиента, ограничивающий вас до 10 одновременных запросов) в качестве фоновых заданий.
@MathiasR.Jessen, тайм-аут находится на уровне запроса? И что касается пакетной обработки, следует ли использовать задания PowerShell? Если да, то как он будет вычислять список URL-адресов, чтобы в сумме все URL-адреса запускались каждые 5 минут?
Вы получите улучшения, если будете запускать задачу параллельно. Я бы ограничил количество параллельных задач количеством ядер вашего микропроцессора. Я бы взял список машин и разделил его на равные части, а затем использовал бы foreach с параметром parallel для запуска кода:
@jdweng, ограничение параллелизма количеством ядер ЦП для сетевых задач нецелесообразно. То, на что вы ссылаетесь, относится к устаревшей технологии рабочего процесса Windows PowerShell, которая отличается от обычного кода PowerShell и в настоящее время ее лучше избегать; он принципиально больше не доступен в PowerShell (Core) 7+, который теперь предлагает параметр -Parallel как часть обычного командлета ForEach-Object.





Неотъемлемым ограничением Invoke-WebRequest (и Invoke-RestMethod) является возможность одновременного действия только с одним URL.
Параллельное нацеливание на несколько URL-адресов требует командно-внешнего параллелизма, например, через (медленные и ресурсоемкие) задания PowerShell или (легкие и, следовательно, предпочтительнее) задания потоков — через модуль ThreadJob, который можно установить по запросу в Windows PowerShell поставляется с PowerShell (Core) 7+ и, что наиболее эффективно, с такой же поточной функцией ForEach-Object -Parallel в PowerShell 7+.
Однако командно-внешний параллелизм, даже в форме, основанной на потоках, неизменно влечет за собой нетривиальные накладные расходы.
Поэтому рассмотрите возможность использования curl.exe, который поставляется с последними версиями Windows и имеет встроенную поддержку таргетинга на несколько URL-адресов, а также параллельную работу.
Сравнение производительности на основе примера кода, который выполняет запросы GET с 10 URL-адресами и сообщает коды состояния HTTP ответов, используя различные последовательные и параллельные подходы.
Абсолютное время будет варьироваться даже между прогонами, но соотношение должно давать представление о том, что работает лучше всего.
Рейтинг может отличаться на Unix-подобных платформах. Любопытно, что на Mac M1 я вижу, что операции в целом медленнее, и curl даже медленнее, чем сопоставимые Invoke-WebRequest подходы.
Исходный код ниже; вы можете легко настроить его, чтобы предоставить больше URL-адресов и поэкспериментировать со степенью параллелизма.
Примеры результатов из Windows PowerShell (значения в секундах, сначала самые быстрые):
Method Duration
------ --------
curl, parallel 0.2868489
Invoke-WebRequest, Start-ThreadJob 0.5779788
curl, sequential 1.9407611
Invoke-WebRequest, sequential 2.3540807
Invoke-WebRequest, ForEach-Object -Parallel N/A
Примеры результатов PowerShell (Core) 7.3.4 (в Windows):
Method Duration
------ --------
curl, parallel 0.27
Invoke-WebRequest, ForEach-Object -Parallel 0.42
Invoke-WebRequest, Start-ThreadJob 0.52
curl, sequential 1.89
Invoke-WebRequest, sequential 2.05
Исходный код:
# Sample URLs
$urls = @(
'http://www.example.org'
'http://www.example.com'
'https://en.wikipedia.org'
'https://de.wikipedia.org'
'https://fr.wikipedia.org'
'https://it.wikipedia.org'
'https://es.wikipedia.org'
'https://ru.wikipedia.org'
'https://ru.wikipedia.org'
'https://als.wikipedia.org'
)
# Code that implements various approaches.
$scriptBlock = {
param(
[switch] $UseCurl,
[switch] $Parallel,
[switch] $UseThreadJobs
)
if ($useCurl) {
# use curl.exe
$curlExe = if ($IsCoreCLR) { 'curl' } else { 'curl.exe' }
$urlArgs = foreach ($url in $urls) { $url, '-o', '/dev/null' }
$parallelArgs = @()
if ($Parallel) { $parallelArgs = '--parallel', '--parallel-max', $numParallelTransfers }
& $curlExe -s -w '%{url} = %{http_code}\n' $parallelArgs -L $urlArgs
}
else {
# use Invoke-WebRequest
$ProgressPreference = 'SilentlyContinue'
if ($Parallel) {
if ($UseThreadJobs) {
$urls | ForEach-Object {
Start-ThreadJob -ThrottleLimit $numThreads { "$using:_ = " + (Invoke-WebRequest $using:_).StatusCode }
} | Receive-Job -Wait -AutoRemoveJob
}
else { # ForEach-Object -Parallel
$urls | ForEach-Object -ThrottleLimit $numThreads -Parallel { "$_ = " + (Invoke-WebRequest $_).StatusCode }
}
}
else { # sequential
$urls | ForEach-Object { "$_ = " + (Invoke-WebRequest $_).StatusCode }
}
}
}
# Set the desired number of parallel threads / transfers:
$numThreads = 10 # for ForEach-Object -Parallel, whose default is 5
$numParallelTransfers = 50 # For curl.exe: 50 is the default, and lowering it hurts performance
# Run benchmarks
@(
[pscustomobject] @{
Method = 'Invoke-WebRequest, sequential'
Duration = (Measure-Command { Write-Verbose -Verbose 'Invoke-WebRequest sequential solution:'; & $scriptBlock | Out-Host }).TotalSeconds
}
[pscustomobject] @{
Method = 'Invoke-WebRequest, ForEach-Object -Parallel'
Duration =
if ($PSVersionTable.PSVersion.Major -lt 7) { 'N/A' }
else { (Measure-Command { Write-Verbose -Verbose 'Invoke-WebRequest with ForEach-Object -Parallel:'; & $scriptBlock -Parallel | Out-Host }).TotalSeconds }
}
[pscustomobject] @{
Method = 'Invoke-WebRequest, Start-ThreadJob'
Duration =
if (-not (Get-Command -ErrorAction Ignore Start-ThreadJob)) { 'N/A' }
else { (Measure-Command { Write-Verbose -Verbose 'Invoke-WebRequest with Start-ThreadJob:'; & $scriptBlock -Parallel -UseThreadJobs | Out-Host }).TotalSeconds }
}
[pscustomobject] @{
Method = 'curl, sequential'
Duration = (Measure-Command { Write-Verbose -Verbose 'curl.exe sequential solution:'; & $scriptBlock -UseCurl | Out-Host }).TotalSeconds
}
[pscustomobject] @{
Method = 'curl, parallel'
Duration = (Measure-Command { Write-Verbose -Verbose 'curl.exe parallel solution:'; & $scriptBlock -UseCurl -Parallel | Out-Host }).TotalSeconds
}
) |
ForEach-Object -Begin {
Write-Verbose -Verbose "Timing in seconds for $($urls.Count) URLs, based on $numThreads simultaneous threads running Invoke-WebRequest / up to $numParallelTransfers parallel curl.exe transfers:"
} -Process {
$_
} |
Sort-Object Duration
Итак... вы написали какой-нибудь код, чтобы проверить, выполнимо это или нет?