• IO多路复用, 基于IO多路复用+socket实现并发请求(一个线程100个请求), 协程


    一. IO多路复用
        IO多路复用作用:检测多个socket是否已经发生变化(是否已经连接成功/是否已经获取数据)(可读/可写)

    二. 基于IO多路复用+socket实现并发请求(一个线程100个请求)
        IO多路复用
        socket非阻塞

        基于事件循环实现的异步非阻塞框架:aaaa.py
            非阻塞:不等待
            异步:执行完某个任务后自动调用我给他的函数。

        Python中开源 基于事件循环实现的异步非阻塞框架 Twisted

    # aaaa.py
    import socket
    import select
    
    class Req(object):
        def __init__(self,sk,func):
            self.sock = sk
            self.func = func
    
        def fileno(self):
            return self.sock.fileno()
    
    
    class Nb(object):
    
        def __init__(self):
            self.conn_list = []
            self.socket_list = []
    
        def add(self,url,func):
            client = socket.socket()
            client.setblocking(False)  # 非阻塞
            try:
                client.connect((url, 80))
            except BlockingIOError as e:
                pass
            obj = Req(client,func)
            self.conn_list.append(obj)
            self.socket_list.append(obj)
    
        def run(self):
    
            while True:
                rlist,wlist,elist = select.select(self.socket_list,self.conn_list,[],0.005)
                # wlist中表示已经连接成功的req对象
                for sk in wlist:
                    # 发生变换的req对象
                    sk.sock.sendall(b'GET /s?wd=alex HTTP/1.0
    host:www.baidu.com
    
    ')
                    self.conn_list.remove(sk)
                for sk in rlist:
                    chunk_list = []
                    while True:
                        try:
                            chunk = sk.sock.recv(8096)
                            if not chunk:
                                break
                            chunk_list.append(chunk)
                        except BlockingIOError as e:
                            break
                    body = b''.join(chunk_list)
                    # print(body.decode('utf-8'))
                    sk.func(body)
                    sk.sock.close()
                    self.socket_list.remove(sk)
                if not self.socket_list:
                    break
    

    IO多路复用作用
        检测多个socket是否发生变化。


        操作系统检测socket是否发生变化,有三种模式:
            select:最多1024个socket;循环去检测。
            poll:不限制监听socket个数;循环去检测(水平触发)。
            epoll:不限制监听socket个数;回调方式(边缘触发)。
        Python模块:
            select.select
            select.epoll

    提高并发方案:
        - 多进程
        - 多线程
        - 异步非阻塞模块(Twisted) scrapy框架(单线程完成并发)

    什么是异步非阻塞?
        - 非阻塞,不等待。
            比如创建socket对某个地址进行connect、获取接收数据recv时默认都会等待(连接成功或接收到数据),才执行后续操作。
            如果设置setblocking(False),以上两个过程就不再等待,但是会报BlockingIOError的错误,只要捕获即可。
        - 异步,通知,执行完成之后自动执行回调函数或自动执行某些操作(通知)。
            比如做爬虫中向某个地址baidu.com发送请求,当请求执行完成之后自执行回调函数。

    什么是同步阻塞?
        - 阻塞:等
        - 同步:按照顺序逐步执行

        key_list = ['alex','db','sb']
        for item in key_list:
            ret = requests.get('https://www.baidu.com/s?wd=%s' %item)
            print(ret.text)

    class Foo(object):
        def __init__(self,data,info):
            self.row = data
            self.info = info
    
        def append(self,item):
            self.row.append(item)
    
    v = [
        Foo([11,22],'线程'), # 每个都有一个append方法
        Foo([22,33],'地方'), # 每个都有一个append方法
        Foo([33,44],'水电费'), # 每个都有一个append方法
    ]
    
    for item in v:
        print(item.append)
        print(item.info)
        print(item.row)
    

     
    三. 协程
        概念:
            进程,操作系统中存在;
            线程,操作系统中存在;
            协程,是由程序员创造出来的一个不是真实存在的东西;

        协程:是微线程,对一个线程进程分片,使得线程在代码块之间进行来回切换执行,而不是在原来逐行执行。

        单纯的协程无用  协程 + 遇到IO就切换 => 这样就有用了  pip3 install gevent

        单线程提供并发:
            - 协程+IO切换:gevent
            - 基于事件循环的异步非阻塞框架:Twisted

    import greenlet
    
    
    def f1():
        print(11)
        gr2.switch()
        print(22)
        gr2.switch()
    
    
    def f2():
        print(33)
        gr1.switch()
        print(44)
    
    
    # 协程 gr1
    gr1 = greenlet.greenlet(f1)
    # 协程 gr2
    gr2 = greenlet.greenlet(f2)
    
    gr1.switch()
    
    # 代码:gevent
    from gevent import monkey
    monkey.patch_all() # 以后代码中遇到IO都会自动执行greenlet的switch进行切换
    import requests
    import gevent
    
    
    def get_page1(url):
        ret = requests.get(url)
        print(url,ret.content)
    
    def get_page2(url):
        ret = requests.get(url)
        print(url,ret.content)
    
    def get_page3(url):
        ret = requests.get(url)
        print(url,ret.content)
    
    gevent.joinall([
        gevent.spawn(get_page1, 'https://www.python.org/'), # 协程1
        gevent.spawn(get_page2, 'https://www.yahoo.com/'),  # 协程2
        gevent.spawn(get_page3, 'https://github.com/'),     # 协程3
    ])
    
    # 代码:twisted
    from twisted.web.client import getPage, defer
    from twisted.internet import reactor
    
    def all_done(arg):
        reactor.stop()
    
    def callback(contents):
        print(contents)
    
    deferred_list = []
    url_list = ['http://www.bing.com', 'http://www.baidu.com', ]
    for url in url_list:
        deferred = getPage(bytes(url, encoding='utf8'))
        deferred.addCallback(callback)
        deferred_list.append(deferred)
    
    dlist = defer.DeferredList(deferred_list)
    dlist.addBoth(all_done)
    
    reactor.run()
    

    其他协程方法, 用yield只做一个生成器

  • 相关阅读:
    再谈ORACLE CPROCD进程
    fopen()函数
    Java抓取网页数据(原网页+Javascript返回数据)
    Vmware ESX 5.0 安装与部署
    PostgreSQL服务端监听设置及client连接方法
    方向梯度直方图(HOG)和颜色直方图的一些比較
    Vim简明教程【CoolShell】
    FileSystemWatcher使用方法具体解释
    几种常见模式识别算法整理和总结
    ThreadPool.QueueUserWorkItem的性能问题
  • 原文地址:https://www.cnblogs.com/NachoLau/p/9643043.html
Copyright © 2020-2023  润新知