php多线程抓取网页(【干货】Powershell上如何能够并发的ping上万台机器?)
优采云 发布时间: 2021-10-16 16:37php多线程抓取网页(【干货】Powershell上如何能够并发的ping上万台机器?)
今天有朋友问我,如何在Powershell中同时ping几万台机器?虽然默认的test-connection有-computer参数,但它的方法是依次ping通,可能需要几个小时才能全部跑掉。
例如,ping 40 个服务器需要 18 秒。如果有数千个,那将是耗时的。
measure-commnd -expression {
$computers=Get-ADComputer -Filter {operatingsystem -like "*2012*"}
Test-Connection -ComputerName $computers.name -Count 1
}
在此之前,Bean 对多线程的使用仅限于理解 invoke-command 可以同时操作 30 个对象。经过一番学习,我终于发现还有其他的高级方法。
在 PowerShell 中,可能有两种方法可以使用多线程。
首先是创建多个后台作业。这样,通过start-job或者-asjob创建一个后台job,然后通过get-job获取当前任务,通过receive-job获取完成任务的结果,最后需要remove-job释放记忆。缺点是性能不高,尤其是在创建作业和退出作业的过程中会消耗大量的时间和资源。
第二种方法是创建多个运行时。工作原理与调用命令相同。每个远程会话都绑定到一个运行时。我们可以创建一个运行时池,并指定该资源池中可以同时执行的最大运行时数。
与第一种方法相比,Runspace 的性能要强大得多。在下面的对比实验中,可以看到性能差距几乎是几十倍。
现在让我们看看如何实现它。豆子主要参考了这个博客的方法和原理,写了一个简单的脚本。
思路很简单,创建一个runtime pool,指定runtimes的数量,然后为每个待测试的对象集创建一个后台runtime job,绑定要执行的脚本,传入参数,将结果保存在ps中object 或者在hash表中,等待所有作业结束并输出结果。
$Throttle = 20 #threads
#脚本块,对指定的计算机发送一个ICMP包测试,结果保存在一个对象里面
$ScriptBlock = {
Param (
[string]$Computer
)
$a=test-connection -ComputerName $Computer -Count 1
$RunResult = New-Object PSObject -Property @{
IPv4Adress=$a.ipv4address.IPAddressToString
ComputerName=$Computer
}
Return $RunResult
}
#创建一个资源池,指定多少个runspace可以同时执行
$RunspacePool = [RunspaceFactory]::CreateRunspacePool(1, $Throttle)
$RunspacePool.Open()
$Jobs = @()
#获取Windows 2012服务器的信息,对每一个服务器单独创建一个job,该job执行ICMP的测试,并把结果保存在一个PS对象中
(get-adcomputer -filter {operatingsystem -like "*2012*"}).name | % {
#Start-Sleep -Seconds 1
$Job = [powershell]::Create().AddScript($ScriptBlock).AddArgument($_)
$Job.RunspacePool = $RunspacePool
$Jobs += New-Object PSObject -Property @{
Server = $_
Pipe = $Job
Result = $Job.BeginInvoke()
}
}
#循环输出等待的信息.... 直到所有的job都完成
Write-Host "Waiting.." -NoNewline
Do {
Write-Host "." -NoNewline
Start-Sleep -Seconds 1
} While ( $Jobs.Result.IsCompleted -contains $false)
Write-Host "All jobs completed!"
#输出结果
$Results = @()
ForEach ($Job in $Jobs)
{ $Results += $Job.Pipe.EndInvoke($Job.Result)
}
$Results
大约 5 秒后,结果出来了。有兴趣的可以使用measure-command命令来测试不同线程的效果。根据我的测试,30道工序同时出结果只需要4秒,2道工序同时出结果大约需要9秒。
知道原理后,就可以进一步优化和抽象脚本。已经有人这样做了。
下载后可以直接调用解锁和点源。这里有一些例子供参考
根据葫芦图,我觉得通过他调用test-connection也是成功的
get-adcomputer -Filter {operatingsystem -like "*2012*"} | select -ExpandProperty name | Invoke-Parallel -ScriptBlock {Test-Connection -computername $_ -count 1}
再举一个例子,我用一个 IP 范围 ping 一台计算机
1..254| Invoke-Parallel -ScriptBlock {Test-Connection -ComputerName "10.2.100.$_" -Count 1 -ErrorAction SilentlyContinue -ErrorVariable err | select Ipv4address, @{n='DNS';e={[System.Net.Dns]::gethostentry($_.ipv4address).hostname}}} -Throttle 20
最后,网上也有现成的脚本可以并发测试ping。原理就是调用上面的invoke-parallel函数,不过还增加了其他函数来测试rdp、winrm、rpc等远程访问端口是否打开,进一步扩展了功能。你可以在这里直接下载
invoke-ping -ComputerName (Get-ADComputer -Filter {operatingsystem -like "*2012*"}).name -Detail RDP,rpc | ft -Wrap
参考资料:
1.
2.
3.
4.
5.