美文网首页
使用select + 回调 + 事件轮询模拟http请求

使用select + 回调 + 事件轮询模拟http请求

作者: ___大鱼___ | 来源:发表于2019-05-31 15:58 被阅读0次
    # 1. epoll 不代表一定比select好
    # 在并发高的情况下, 连接活跃度不高, epoll比select好
    # 并发性不高, 同时连接很活跃, select比epoll好
    
    # 通过非阻塞io发起http请求
    
    # DefaultSelector会自动选择平台是使用poll还是epoll  win就是poll unix就是epoll
    
    # select + 回调 + 事件轮询
    
    import socket
    from urllib.parse import urlparse
    # EVENT_READ 读事件  EVENT_WRITE写事件
    from selectors import DefaultSelector, EVENT_READ, EVENT_WRITE
    
    selector = DefaultSelector()
    # 使用select完成http请求
    
    # 声明一个全局变量用来结束事件轮询
    stop = False
    
    
    class Fetcher:
    
        def __init__(self, urls):
            self.urls = urls
            self.data = b''
    
        def connected(self, key):
            # 将监听取消注册
            selector.unregister(key.fd)
            # 证明已经连接好了
            self.client.send('GET {} HTTP/1.1\r\nHOST:{}\r\nConnection:close\r\n\r\n'.format(self.path, self.host).encode('utf8'))
            # 监听读的事件
            selector.register(self.client.fileno(), EVENT_READ, self.read_func)
    
        def read_func(self, key):
    
    
    
            # 如果没有连接正常 recv同样会报错BlockingIOError
            d = self.client.recv(1024)
            if d:
                self.data += d
            else:
                selector.unregister(key.fd)
                self.data = self.data.decode('utf8')
                print(self.data)
                self.client.close()
                print(self.urls)
                self.urls.remove(self.spider_url)
                print(self.urls)
                if not self.urls:
                    global stop
                    stop = True
    
        def get_url(self, url):
            self.spider_url = url
            url = urlparse(url)
            self.host = url.netloc
            self.path = url.path
            if self.path == '':
                self.path = '/'
    
            # 建立socket连接
            self.client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            self.client.setblocking(False)  # 非阻塞 不用等待返回继续执行
            try:
                # connect 需要判断是否连接完成负责会发生BlockingIOError异常
                self.client.connect((self.host, 80))
            except BlockingIOError:
                pass
            # 使用select注册  self.client.fileno()获取client的文件描述符
            # register(self, fileobj, events, data=None):
            # fileobj 文件对象 events 事件  data 回调函数
    
            selector.register(self.client.fileno(), EVENT_WRITE, self.connected)
    
    
    def loop():
        # 事件循环,不停的请求socket的状态并调用回调函数
        # 回调 + 事件循环 + select(poll、epoll) 模式
        # 1. select本身是不支持register模式
        # 2. select状态变化以后的回调是由程序员来完成的
        # 3. 不停的监听是否有
        while 1:
            if not stop:
                ready = selector.select()
                for key, mask in ready:
                    call_back = key.data  # 找到当时注册的函数是什么
                    call_back(key)  # 拿到方法只会调用传递参数
            else:
                break
    
    
    if __name__ == '__main__':
        import time
        time1 = time.time()
        url_list = ['https://www.baidu.com'] * 20
        for url in url_list:
            fectch = Fetcher(url_list)
            fectch.get_url(url)
        loop()
        time2 = time.time()
        print(time2 - time1)
    
    QQ截图20190531163407.png
    QQ截图20190531163659.png

    相关文章

      网友评论

          本文标题:使用select + 回调 + 事件轮询模拟http请求

          本文链接:https://www.haomeiwen.com/subject/bkawtctx.html