我有1000个源网址位于MySQL数据库表中。我需要每隔2分钟向所有这些网址发送一个http请求。我写了一个PHP脚本来做,但脚本需要5分30秒才能运行。
我希望能够在一分钟内完成所有1000个请求。有没有办法运行多个异步进程来更快地完成工作?任何帮助表示赞赏。提前谢谢。
答案 0 :(得分:6)
由于您的问题是关于发送http请求,而不是真正ping,您可以使用Grequests
(Requests + gevent)轻松快速地完成(根据我的经验)几百个网址请求的秒数:
import grequests
urls = [
'http://www.python.org',
'http://python-requests.org',
'http://www.google.com',
]
rs = (grequests.get(u) for u in urls)
grequests.map(rs) # [<Response [200]>, <Response [200]>, <Response [200]>]
您的Php脚本需要5分钟才能运行,因为它是同步代码,这意味着对于您发送的每个请求,您必须等待在发送下一个请求之前到达的响应。
这里的诀窍不是等待(或阻止尽可能多的人调用)以获得响应,而是直接发出下一个请求,并且您可以使用gevent
(协同程序)轻松实现(基于)或nodejs
。您可以在其上阅读更多内容here。
答案 1 :(得分:4)
查看AnyEvent::Ping
上的AnyEvent::FastPing
或CPAN模块。
以下是使用AnyEvent::Ping
ping 10000个网址的简单示例:
use strict;
use warnings;
use AnyEvent;
use AnyEvent::Ping;
my $cv = AnyEvent->condvar;
my $ping = AnyEvent::Ping->new;
my @results;
for my $url (get_ten_thousand_urls()) {
$cv->begin;
# ping each URL just once
$ping->ping( $url, 1, sub {
# [ url address, ping status ]
push @results, [ $url, $_[0]->[0][0] ];
$cv->end;
});
}
$cv->recv;
# now do something with @results
上面使用10,000个随机网址进行的一些快速测试在我的Macbook Air上运行时间仅为7秒。通过调整和/或使用更快的事件循环,此时间将进一步下降(高于使用的默认纯Perl事件循环)。
NB。 AnyEvent
是一个抽象库,允许您使用系统提供的(或安装在系统上)的异步事件系统。如果您想使用特定的事件循环,请记住从CPAN安装相关的Perl模块,例如EV
如果使用libev
。如果没有找到(安装)其他内容,AnyEvent
将默认为纯Perl事件循环。
BTW,如果您只需要检查HTTP请求(即不ping),则只需将AnyEvent::Ping
部分替换为AnyEvent::HTTP
。
答案 2 :(得分:3)
你用“python”标记了这个,所以我假设使用Python是一个选项。看看multiprocessing module。例如:
#!/usr/bin/env python
import multiprocessing
import os
import requests
import subprocess
addresses = ['1.2.3.4', '1.2.3.5', '4.2.2.1', '8.8.8.8']
null = open(os.devnull, 'w')
def fbstatus(count):
"""Returns the address, and True if the ping returned in under 5 seconds or
else False"""
return (count,
requests.get('http://www.facebook.com/status.php').status_code)
def ping(address):
"""Returns the address, and True if the ping returned in under 5 seconds or
else False"""
return address, not subprocess.call(['ping', '-c1', '-W5', address],
stdout=null)
pool = multiprocessing.Pool(15)
if False:
print pool.map(ping, addresses)
else:
pool.map(fbstatus, range(1000))
fbstatus()
函数从Facebook获取页面。这与通过30个并发进程的池的大小几乎呈线性关系。它在我的笔记本电脑上平均总运行时间约为80秒。在30名工人中,总共需要大约3.75个挂钟才能完成。
这使用subprocess
模块调用ping
命令,超时时间为5秒,计数为1.它使用ping
的返回值(0表示成功,1表示失败)并否定它导致False
失败,True
取得成功。 ping()
函数返回调用它的地址加上布尔结果。
最后一位创建一个包含5个子进程的多处理池,然后对ping()
中的每个值调用addresses
。由于ping()
返回其地址,因此很容易看到ping每个地址的结果。
运行它,我得到这个输出:
[('1.2.3.4', False), ('1.2.3.5', False), ('4.2.2.1', True), ('8.8.8.8', True)]
该运行需要5.039秒的挂钟时间和0%的CPU。换句话说,它几乎100%的时间都在等待ping
返回。在您的脚本中,您希望使用Requests之类的内容来获取您的Feed网址(而不是我正在使用的文字ping
命令),但基本结构可能几乎相同
答案 3 :(得分:2)
你可以在python上尝试多线程ping。 这是一个很好的例子。
#!/usr/bin/env python2.5
from threading import Thread
import subprocess
from Queue import Queue
num_threads = 4
queue = Queue()
ips = ["10.0.1.1", "10.0.1.3", "10.0.1.11", "10.0.1.51"]
#wraps system ping command
def pinger(i, q):
"""Pings subnet"""
while True:
ip = q.get()
print "Thread %s: Pinging %s" % (i, ip)
ret = subprocess.call("ping -c 1 %s" % ip,
shell=True,
stdout=open('/dev/null', 'w'),
stderr=subprocess.STDOUT)
if ret == 0:
print "%s: is alive" % ip
else:
print "%s: did not respond" % ip
q.task_done()
#Spawn thread pool
for i in range(num_threads):
worker = Thread(target=pinger, args=(i, queue))
worker.setDaemon(True)
worker.start()
#Place work in queue
for ip in ips:
queue.put(ip)
#Wait until worker threads are done to exit
queue.join()
答案 4 :(得分:1)
[更新:使用maxSockets = 100重新测试此项,同时连接到非常良好的网络连接。该脚本以&lt; 1秒,意味着最大因素可能是网络吞吐量/延迟,如前所述。您的结果几乎肯定会有所不同。 ;)]
您可以使用node.js,因为它用于执行HTTP的API功能强大,干净且简单。例如。以下脚本在我的MacBook Pro上以 10秒不到一秒的时间内提取~1000个请求:
<强> test.js 强>
var http = require('http');
// # of simultaneouse requests allowed
http.globalAgent.maxSockets = 100;
var n = 0;
var start = Date.now();
function getOne(url) {
var id = n++;
var req = http.get(url, function(res) {
res.on('data', function(chunk){
// do whatever with response data here
});
res.on('end', function(){
console.log('Response #' + id + ' complete');
n--;
if (n == 0) {
console.log('DONE in ' + (Date.now() - start)/1000 + ' secs');
}
});
});
}
// Set # of simultaneous connections allowed
for (var i = 0; i < 1000; i++) {
getOne('http://www.facebook.com/status.php');
}
输出......
$ node test.js
Response #3 complete
Response #0 complete
Response #2 complete
...
Response #999 complete
DONE in 0.658 secs
答案 5 :(得分:1)
我使用Perl的POE Ping Component模块来完成这项任务。
答案 6 :(得分:0)
感谢Alex Lunix提出的建议。我查了curl_multi_ *并找到了一个解决方案来完成curl,所以我不必更改我的代码。但是,谢谢所有其他人的答案。这是我做的:
<?php
require("class.php");
$obj=new module();
$det=$obj->get_url();
$batch_size = 40;
function curlTest2($urls) {
clearstatcache();
$batch_size = count($urls);
$return = '';
echo "<br/><br/>Batch:";
foreach ($urls as &$url)
{
echo "<br/>".$url;
if(substr($url,0,4)!="http") $url = "http://".$url;
$url = "https://ajax.googleapis.com/ajax/services/feed/load?v=1.0&num=-1&q=".$url;
}
$userAgent = 'Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; .NET CLR 1.1.4322)';
$chs = array();
for ($i = 0; $i < $batch_size; $i++)
{
$ch = curl_init();
array_push($chs, $ch);
}
for ($i = 0; $i < $batch_size; $i++)
{
curl_setopt($chs[$i], CURLOPT_HEADER, 1);
curl_setopt($chs[$i], CURLOPT_NOBODY, 1);
curl_setopt($chs[$i], CURLOPT_USERAGENT, $userAgent);
curl_setopt($chs[$i], CURLOPT_RETURNTRANSFER, 1);
curl_setopt($chs[$i], CURLOPT_CONNECTTIMEOUT, 15);
curl_setopt($chs[$i], CURLOPT_FAILONERROR, 1);
curl_setopt($chs[$i], CURLOPT_FRESH_CONNECT, 1);
curl_setopt($chs[$i], CURLOPT_URL, $urls[$i]);
}
$mh = curl_multi_init();
for ($i = 0; $i < $batch_size; $i++)
{
curl_multi_add_handle($mh, $chs[$i]);
}
$active = null;
//execute the handles
do {
$mrc = curl_multi_exec($mh, $active);
} while ($mrc == CURLM_CALL_MULTI_PERFORM);
while ($active && $mrc == CURLM_OK) {
if (curl_multi_select($mh) != -1) {
do {
$mrc = curl_multi_exec($mh, $active);
} while ($mrc == CURLM_CALL_MULTI_PERFORM);
}
}
//close the handles
for ($i = 0; $i < $batch_size; $i++)
{
curl_multi_remove_handle($mh, $chs[$i]);
}
curl_multi_close($mh);
}
$startTime = time();
$urls = array();
foreach($det as $key=>$value){
array_push($urls, $value['url']);
if (count($urls) == $batch_size)
{
curlTest2($urls);
$urls = array();
}
}
echo "<br/><br/>Time: ".(time() - $startTime)."sec";
?>
这使我的处理时间从332秒减少到18秒。代码可能会稍微优化一下,但你会得到它的要点。