博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
聊聊异步非阻塞IO
阅读量:5266 次
发布时间:2019-06-14

本文共 3295 字,大约阅读时间需要 10 分钟。

io异步模块

知识必备:

socket实现web请求

如果说socket是所有web操作的本质,爬虫在web操作中扮演的角色,应该就是一个“客户端”。

obj=socket()obj.connect((192.168.1.1,80))# socket 连接是一个耗时阻塞的操作obj.send('get /url http1.1 \r\n host:...\r\n content-type \r\n\r\n')# 通过\r\n将请求参数分割开按照一定格式发送给服务端

下面是一段用socket向百度发送请求的实例:

import socketclient=socket.socket()client.connect(('61.135.169.125',80)) # 阻塞data=b'GET / HTTP/1.0\r\nhost: www.baidu.com\r\n\r\n'# 请求,访问的url(这里无,所以用/)然后是协议:http1.1# 最后一定要记得用\r\n分割开,因为服务器靠这个来split信息client.sendall(data)response=client.recv(8096) # 阻塞print(response)client.close()

可以看到我在上面两个地方标注了“阻塞”。

如果想要让这个socket不发生“阻塞”,其实只需要

client.setblocking(False)

但是这种操作带来的问题是:

BlockingIOError: [WinError 10035] 无法立即完成一个非阻止性套接字操作。

暂时不考虑这个错误,我们其实已经将这个socket做成了一个“非阻塞”的socket程序了,想要让他正常的运行,可以通过time模块来模拟一些其他计算耗时或者叫做“异步任务”。

代码如下:

client=socket.socket()client.setblocking(False)try:    client.connect(('61.135.169.125',80)) # 阻塞except BlockingIOError as e:    print(e)import timetime.sleep(3)   # 假设这里有一个耗时3秒的操作,操作完后client.connect已经完成data=b'GET / HTTP/1.0\r\nhost: www.baidu.com\r\n\r\n'client.sendall(data)time.sleep(3)  # 假设这里又有一个操作,结束后client已经获取了服务器返回值response=client.recv(8096) # 阻塞print(response)client.close()

观察上面代码的结果,可以发现连接一开始被判断不成功的时候的确返回了错误信息,但是最终我们还是获取到了网页的信息,说明最后连接完成后的整个流程是被正确地走完了的。其中的sleep可以替换成任何其他操作,可以去读取获取文件,进行计算等等……

通过合理安排各个任务,既节约了任务的时间,并且完成了多任务的效果。这就是异步IO

但是我们怎么样判断“第一个任务(连接)阻塞的期间,我们可以完成多少别的任务”,“假如我们的支线任务做完后,主线任务仍然没有结束,我们该干什么”。

利用while循环判断主线任务是否返回,如果返回了值,就break进行下一步,如果没有,就继续进行支线任务

总结:

非阻塞---报错---利用try让程序继续运行
定义一些操作(把所有的请求,在第一个请求发送的等待期间,全部发送过去。)

io多路复用

用于检测【多个】IO对象(socket对象)是否有变化。

r,w,e=selcet.select([socket,socket.....],[],[],0.5)# 第一个传入参数为socket对象列表#
  • w:返回一个列表,表示“连接成功”
  • r:返回一个列表,如果socket中返回内容,就表示“需要接受数据”,这个就会被进入进r返回的列表中

完成代码:

import socketimport selectclass ReqIO(object):    def __init__(self,sock,info):        self.sock=sock        self.info=info    def fileno(self):        return self.sock.fileno()class IOtest(object):    def __init__(self):        self.socklist=[]        self.conns=[]    def add_request(self,req_info):        sock=socket.socket()        sock.setblocking(False)        try:            sock.connect((req_info['host'],req_info['port']))        except Exception as e:            pass  obj=ReqIO(sock,req_info)        self.socklist.append(obj)        self.conns.append(obj)    def run(self):        while True:            r,w,e=select.select(self.socklist,self.conns,[],0.05)            """  补充:select中添加的可以是任何对象,但是这个对象一定要有fileno方法 w 是否连接成功 检查循环到的i是哪个字典 """  for i in w:                data = 'GET %s HTTP/1.0\r\nhost: %s\r\n\r\n'%(i.info['path'],i.info['host'])                i.sock.send(data.encode('utf-8'))                self.conns.remove(i)            """  这时候w中的元素是ReqIO对象,但是他仍然能够连接成功, 并且他会利用封装过的info来获取当前i的info """  for j in r:                """  数据返回接收数据 """  response = j.sock.recv(8096)                print(j.info['host'],':\r\n',response)                self.socklist.remove(j)            if not self.socklist:                # 当所有请求都已经返回  breakurl_list=[    {'host':'www.baidu.com','IP':'61.135.169.125','port':80,'path':'/'},  {'host':'dig.chouti.com','IP':'111.206.193.95','port':80,'path':'/'},  {'host':'www.bing.com','IP':'118.178.213.186','port':80,'path':'/'}]test=IOtest()for item in url_list:    test.add_request(item)test.run()

转载于:https://www.cnblogs.com/scott-lv/p/7753882.html

你可能感兴趣的文章
mysql启动过程
查看>>
2017前端面试题总结
查看>>
Http GetPost网络请求
查看>>
SWIFT国际资金清算系统
查看>>
Sping注解:注解和含义
查看>>
站立会议第四天
查看>>
如何快速掌握一门技术
查看>>
利用AMPScript获取Uber用户数据的访问权限
查看>>
vagrant 同时设置多个同步目录
查看>>
python接口自动化28-requests-html爬虫框架
查看>>
生成随机数的模板
查看>>
Mysql 数据库操作
查看>>
转:linux终端常用快捷键
查看>>
A-Softmax的总结及与L-Softmax的对比——SphereFace
查看>>
UVa 11059 最大乘积
查看>>
数组分割问题求两个子数组的和差值的小
查看>>
composer 报 zlib_decode(): data error
查看>>
linux下WPS的使用
查看>>
Web Api 利用 cors 实现跨域
查看>>
hdu 3938 并查集
查看>>