php多线程抓取网页(【干货】Powershell上如何能够并发的ping上万台机器?)

优采云 发布时间: 2021-10-16 16:37

  php多线程抓取网页(【干货】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.

0 个评论

要回复文章请先登录注册


官方客服QQ群

微信人工客服

QQ人工客服


线