我正在构建一个视图(使用web2py)和其他一些网站的数据(大约10个),因此,对于我创建了一个函数的每个网站,它准备了一些参数,用params发送请求,得到响应并进行一些处理,在此之后我将响应传递给控制器,它将在完成后以请求的所有10个响应构建视图。
我的问题与速度有关,因为HTTP协议的性质所有请求都是同步的,当服务器花了很长时间来响应时,我的应用程序一直在等待。我很想知道是否有任何方法可以"并行化"我的函数调用所以我可以在等待来自其他服务器的响应时执行其他操作。
我已经检查过grequest和request-futures但我认为这并不适合这种情况因为我不提出请求,我会像之前说的那样对请求进行预处理和后期处理。
答案 0 :(得分:1)
寻找grequest
等解决方案。
原因是大多数时候,你等待工作I / O操作完成(下载页面)。与I / O时间相比,前处理和后处理很可能是微不足道的。
我已经看到了grequest
用于网页抓取的非常大的加速。
如果您的预处理和后处理也很耗时,您必须转到multiprocessing
模块并在多个进程中运行任务(如果您受CPU限制,多线程将无济于事)。
但首先 - 真的尝试grequest
。
由于预期会有一些预处理和后期处理,我们必须以某种方式组织它。
grequests
有一个"未发送请求"的概念。以后要完成这项工作。 grequests
允许开始这些工作例如grequests.map
或grequests.imap
。
每个未发送的请求都可以附加挂钩处理返回的响应。
一个未发送的请求可能会附加更多钩子。
我们将把它用于后期处理。
每个实例我们希望以某种方式组织以下与工作相关的事情:
稍后我们将在以下步骤中使用它:
post_process
调用后处理最终响应。grequests.map
或grequests.imap
。诀窍在于,每个作业实例都可以利用自己的参数来保持 整个工作生命周期中的上下文信息清理。
这是真实的代码:
import grequests
class HttpJobMyIp(object):
url = "https://httpbin.org/ip"
def __init__(self, nickname="myip over httpbin.org"):
self.nickname = nickname
self.pre_process()
def pre_process(self):
"""Whatever pre-processing you need."""
print("Preprocessing {self.nickname} with {self.url}".format(self=self))
@property
def unsent_request(self):
"""Create requests for grequests.
Override by whatever construct you need.
"""
return grequests.get(self.url, hooks={"response": [self.post_process]})
def post_process(self, response, **kwargs):
msg = "Post-processing {self.nickname} with {self.url}"
print(msg.format(self=self))
assert "origin" in response.json()
myip = response.json()["origin"]
print("My IP is {}".format(myip))
为了使示例完整,我们可以添加另一个HTTP作业类。这次允许拨打网址, 这会有一些延迟。
class HttpJobDelay(object):
url = "https://httpbin.org/delay/{delay}"
def __init__(self, delay=1, nickname="Delayed response"):
self.nickname = nickname
self.delay = delay
self.pre_process()
def pre_process(self):
"""Whatever pre-processing you need."""
msg = ("Preprocessing {self.nickname} "
"with {self.url} and delay {self.delay}")
print(msg.format(self=self))
self.delay = self.delay + 0.7
@property
def unsent_request(self):
"""Create requests for grequests.
Override by whatever construct you need.
"""
url = self.url.format(delay=self.delay)
return grequests.get(url, hooks={"response": [self.post_process]})
def post_process(self, response, **kwargs):
msg = ("Post-processing {self.nickname} "
"with {self.url} and expected delay {self.delay}")
print(msg.format(self=self))
print("Finally we got (a bit delayed) response")
这里我们故意通过添加0.7秒来修改延迟,预处理有一个 改变一些事情的机会。
def exception_handler(request, exception):
return exception
def main():
http_jobs = []
job = HttpJobMyIp("Get my IP")
http_jobs.append(job.unsent_request)
for delay in [3, 1, 6, 2]:
job = HttpJobDelay(delay, "Delay "+str(delay))
http_jobs.append(job.unsent_request)
# grequests.map(http_jobs, exception_handler=exception_handler))
list(grequests.imap(http_jobs, exception_handler=exception_handler), size=6)
print("DONE")
if __name__ == "__main__":
main()
计划是调用一个HttpJobMyIp
作业实例和4个HttpJobDelay
作业实例
不断要求的延迟。故意不对延迟进行排序。
将上述所有代码放在mreq.py
文件中,我们可以运行它:
$ python mreq.py 1 ↵
Preprocessing Get my IP with https://httpbin.org/ip
Preprocessing Delay 3 with https://httpbin.org/delay/{delay} and delay 3
Preprocessing Delay 1 with https://httpbin.org/delay/{delay} and delay 1
Preprocessing Delay 6 with https://httpbin.org/delay/{delay} and delay 6
Preprocessing Delay 2 with https://httpbin.org/delay/{delay} and delay 2
Post-processing Get my IP with https://httpbin.org/ip
My IP is 87.257.712.26
Post-processing Delay 1 with https://httpbin.org/delay/{delay} and expected delay 1.7
Finally we got (a bit delayed) response
Post-processing Delay 2 with https://httpbin.org/delay/{delay} and expected delay 2.7
Finally we got (a bit delayed) response
Post-processing Delay 3 with https://httpbin.org/delay/{delay} and expected delay 3.7
Finally we got (a bit delayed) response
Post-processing Delay 6 with https://httpbin.org/delay/{delay} and expected delay 6.7
Finally we got (a bit delayed) response
DONE
grequests
文档没有ReadTheDocs文档。
改为使用:
请注意,HttpJobXxx
类不是必需的,只是因为我觉得它很方便而创建。
**kwargs
回调函数应有两个参数:
response
(由HTTP电话提供)**kwargs
如果没有**kwargs
代码,则默认无效。
如果没有异常处理程序,如果出现问题,你通常会不知道,是什么
继续使用exception_handler
您可以例如得到异常作为结果并学习,发生了什么
错。
grequests.imap
是生成器立即致电grequests.imap(http_jobs, exception_handler=exception_handler), size=6)
返回生成器,如果没有消耗它的值,它会继续并退出。
因此,调用封装在list()
。
我们可以看到,无论HttpJobDelay
实例的延迟是否未排序,都会返回结果
按顺序 - 最短的延迟,最后的延迟。使用grequests.imap
未发送请求和结果的顺序可能不同。
另一方面,grequests.map
结果按照它们的顺序返回
工作清单要求。
grequest.imap
参数size
默认为2 如果您未指定size
,则默认值为2.这可能会影响结果的顺序。
grequests
使用"绿色线程"在一个进程中运行。这意味着,在不同之间切换
"绿线"更有效率,因为它是在代码更喜欢因此保存的时候完成的
CPU和OS完成上下文切换的开销。
由于任务是I / O有限(大部分时间我们都在等待一些数据),我们可能会生活得很好 单一过程。
如果请求数量增加,将会看到最高的加速。