博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Python:通过一个小案例深入理解IO多路复用
阅读量:4639 次
发布时间:2019-06-09

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

通过一个小案例深入理解IO多路复用

假如我们现在有这样一个普通的需求,写一个简单的爬虫来爬取校花网的主页

import requestsimport timestart = time.time()url = 'http://www.xiaohuar.com/'result = requests.get(url).textprint(result)print(time.time()-start)

  

  这样子是显然没啥问题的,总共耗时约为6秒

 

但是有没有办法更进一步优化呢,这里如果需要优化我们首先需要知道一个知识点

就是requests这个模块它底层其实是封装了urllib2和urllib3的,而这两个模块底层其实就是socket

如果需要优化,从requests是实现不了的,那么能不能从socket来呢

如果从socket,又该如何优化呢?

 

首先我们得知道socket到底做了什么,

import socketclient = socket.socket(socket.AF_INET, socket.SOCK_STREAM)url = 'www.xiaohuar.com/'client.connect((url, 80))client.send("GET {} HTTP/1.1\r\nHost:{}\r\nConnection:close\r\n\r\n".format('/',80).encode('utf8'))data = b''while 1:    d = client.recv(1024)    if d:        data +=d    else:        breakprint(data)

  这里的代码就是上面那个requests版本的代码的底层

  在这一坨代码中,有几个点需要注意

  connect和recv,这两个方法都是阻塞io,也就是说,如果连接不到或者接受不到消息的话,程序就会一直等,等到预期的效果为止。

  这就是阻塞

 

  阻塞有个很大的弊端,那就是cpu无法得到充分利用,因为等待的时间里,cpu是空闲的,而我们又没有执行其他的操作,那么这段时间我们能不能充分利用起来呢

  答案是肯定的,socket提供了一个非阻塞的办法

  

client.setblocking(False)

  直接运行试试效果

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

  结果是抛出了这个异常,这是因为当变为非阻塞时候,连接校花网的url的时候,三次握手还没建立完成,我们就去执行下一步了

  

try:    client.connect((url, 80))except BlockingIOError as e:   #处理其他事情    pass

  那么我们可以这样改,抓到这个异常但是不处理,这样子,我们就能在except后面加入其他的代码了,也就是说cpu发个请求就不管了,然后去执行后面的代码,这样效率就提高了。

  再运行一次。

  OSError: [WinError 10057] 由于套接字没有连接并且(当使用一个 sendto 调用发送数据报套接字时)没有提供地址,发送或接收数据的请求没有被接受。

  又抛出了一个异常,和上面的原理差不多,因为是非阻塞模式

最终代码如下

import socketclient = socket.socket(socket.AF_INET, socket.SOCK_STREAM)client.setblocking(False)url = 'www.xiaohuar.com'try:    client.connect((url, 80))except BlockingIOError as e:    passwhile 1:    try:        client.send("GET {} HTTP/1.1\r\nHost:{}\r\nConnection:close\r\n\r\n".format('/',80).encode('utf8'))        break    except Exception as e:        passdata = b''while 1:    try:        d = client.recv(1024)    except Exception as e:        continue    if d:        data += d    else:        breakprint(data)

  这样子虽然有一段时间更充分利用了cpu 但是代码很乱,很麻烦,其次虽然是非阻塞,但是有两个地方只是把之前的阻塞的时间花费了在循环上,那么有没有更好的办法呢?

 

这里就要引入IO多路复用的概念了

IO复用就是通过一种机制,一个进程可以监视多个描述符,一旦某个描述符就绪(读或者写),都能够通知程序来进行相应的读写操作,但是select,poll和epoll都是同步io,也就是说这个读写过程是阻塞的,而异步io则无需自己进行读写,异步io的实现会负责把数据从内核拷贝到用户内存。

 

select在windows,OS X, 或者linux都能用,但是select最大监视数量只能为1024

而poll的话其他几乎与select一样,只是突破了最大限制

而epoll就与前面这两个都不一样了,它底层使用了红黑树的数据结构,epoll使用一个文件描述符来管理多个文件描述符,将用户关系的文件描述符的事件存放到内核的一个事件表之中,这样在用户空间和内核空间的copy只需一次。

而poll和select都是才用轮询的方式,所以效率差就在这里体现出来了

 

最终代码 异步IO

from selectors import DefaultSelector, EVENT_READ, EVENT_WRITEimport socketselector = DefaultSelector()class Fetcher():    def send_msg(self, key):        selector.unregister(key.fd)        self.client.send("GET {} HTTP/1.1\r\nHost:{}\r\nConnection:close\r\n\r\n".format('/', 80).encode('utf8'))        selector.register(self.client.fileno(), EVENT_READ, self.recv)    def recv(self, key):        d = self.client.recv(1024)        if d:            self.data += d        else:            selector.unregister(key.fd)            print(self.data.decode('utf8'))    def get_url(self, url):        self.data = b''        try:            self.client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)            self.client.connect((url, 80))        except Exception as e:            # 加入另外的逻辑            pass        selector.register(self.client.fileno(), EVENT_WRITE, self.send_msg)def loop_forever():    while 1:        ready = selector.select()        for key, mask in ready:            call_back = key.data            call_back(key)                        if __name__ == '__main__':    fet = Fetcher()    fet.get_url('www.xiaohuar.com')    loop_forever()

  

 

转载于:https://www.cnblogs.com/Miracle-boy/p/10004684.html

你可能感兴趣的文章
解决 CS0006 未能找到元数据文件
查看>>
HDU 5131.Song Jiang's rank list (2014ACM/ICPC亚洲区广州站-重现赛)
查看>>
mysql搭建主从数据库
查看>>
新的一年,新的开始
查看>>
python模块struct
查看>>
图像的灰度级和动态范围(转)
查看>>
C# MODBUS协议 上位机(转)
查看>>
CSS box-shadow 属性
查看>>
vue:图片切换动态显示
查看>>
04 JDK并发包相关类
查看>>
备忘录
查看>>
科协第三期
查看>>
软件工程个人作业02
查看>>
寒假刷题之5——竹简文
查看>>
Android JSON、GSON、FastJson的封装与解析
查看>>
Luogu P1340 兽径管理
查看>>
pip install 问题
查看>>
Vagrant 入门 - 配置
查看>>
Luogu P1315 观光公交
查看>>
vue-router导航守卫,限制页面访问权限
查看>>