zoukankan      html  css  js  c++  java
  • day42---几种network IO模型

    I/O模型的简介

    前置知识

    """
    任务的提交方式
    同步:就是发生功能调用时,需要等待任务的返回结果,期间不做任何操作。
    异步:无需等待任务的返回结果,继续执行程序,任务的返回结果由异步回调机制返回。
    任务的提交结果
    阻塞:发生功能调用时(如I/O操作),会将当前线程挂起,直至得到返回结果,再将线程激活。
    非阻塞:与阻塞的概念是相反的
    """
    

    这里研究的I/O模型都是针对网络的I/O

    5种I/O模型

    """
    blocking I/O         阻塞I/O
    nonblocking I/O      非阻塞I/O
    I/O multiplexing     I/O多路复用
    signal driven I/O    信号驱动I/O(不需要掌握)
    asynchronous I/O     异步I/O
    """
    

    对于一个Network I/O (以read为例),它会涉及到两个系统对象。一是调用这个I/O的process(or thread),另一个是系统内核(kernel)。当一个read操作发生时,该操作会经历两个阶段。

    (1)等待数据准备(waitting for data to ready)
    (2)将数据从内核拷贝到进程中(copying the data from thekernel to the process)
    

    阻塞I/O模型

    在linux中,默认情况下所有的socket都是blocking。

    blocking IO的特点就是在IO执行的两个阶段(等待数据和拷贝数据两个阶段)都被block了

    import socket
    
    IP_ADDRESS = ('0.0.0.0', 9090)
    
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sock.bind(IP_ADDRESS)
    sock.listen(5)
    
    while True:
        conn, addr = sock.accept()
        while True:
            try:
                msg = conn.recv(1024)
                if not msg: break
                print(msg)
                conn.send(msg.upper())
            except ConnectionResetError as e:
                break
        conn.close()
    

    在服务端开设多进程或者多线程 进程池线程池 其实还是没有解决IO问题。该等的地方还是得等 ,没有规避
    只不过多个人等待的彼此互不干扰。

    非阻塞I/O模型

    在Linux下,可以设置socket使其变为nonblocking。

    在非阻塞式IO中,用户进程其实是需要不断的主动询问kernel数据准备好了没有。

    """
    服务端
    """
    import socket
    import time
    
    IP_ADDRESS = ('0.0.0.0', 9090)
    
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sock.bind(IP_ADDRESS)
    sock.listen(5)
    
    # 将network IO由阻塞变为非阻塞
    sock.setblocking(False)
    read_list = []
    del_list = []
    
    while True:
        try:
            conn, addr = sock.accept()
            read_list.append(conn)
        except BlockingIOError as e:
            """
            非阻塞模式下,没有阻塞状态,若没有连接进来会报错
            """
            # time.sleep(0.01)
            for conn in read_list:
                try:
                    recv_data = conn.recv(1024)
                    if not recv_data:
                        conn.close()
                        del_list.append(conn)
                        continue
                    print(recv_data.decode('utf-8'))
                    conn.send(recv_data.upper())
                except BlockingIOError as e:
                    continue
                except ConnectionResetError as e:
                    conn.close()
                    del_list.append(conn)
            """
            去掉无用的连接
            """
            for conn in del_list:
                read_list.remove(conn)
            read_list.clear()
    """
    客户端
    """
    import socket
    
    IP_ADDRESS = ('182.92.59.34', 9090)
    
    client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    client.connect_ex(IP_ADDRESS)
    
    while True:
        # msg = input('please enter the message>>>:').strip()
        # if not msg: continue
        client.send(b'hello world!')
        back_msg = client.recv(1024)
        print(back_msg.decode('utf-8'))
    

    但是非阻塞IO模型绝不被推荐。

    因为长时间占用着CPU并且不干活 让CPU不停的空转,我们实际应用中也不会考虑使用非阻塞IO模型。

    I/O多路复用

    """
    当用户进程调用了select,那么整个进程会被block,而同时,kernel会“监视”所有select负责的socket,当任何一个socket中的数据准备好了,select就会返回。这个时候用户进程再调用read操作,将数据从kernel拷贝到用户进程。 这个图和blocking IO的图其实并没有太大的不同,事实上还更差一些。因为这里需要使用两个系统调用(select和recvfrom),而blocking IO只调用了一个系统调用(recvfrom)。但是,用select的优势在于它可以同时处理多个connection。
    """
    强调:
    1. 如果处理的连接数不是很高的话,使用select/epoll的web server不一定比使用multi-threading + blocking IO的web server性能更好,可能延迟还更大。select/epoll的优势并不是对于单个连接能处理得更快,而是在于能处理更多的连接。
    2. 在多路复用模型中,对于每一个socket,一般都设置成为non-blocking,但是,如上图所示,整个用户的process其实是一直被block的。只不过process是被select这个函数block,而不是被socket IO给block。
    

    结论

    select的优势在于可以处理多个连接,不适用于单个连接

    """
    服务端
    """
    import socket
    import select
    
    IP_ADDRESS = ('0.0.0.0', 9090)
    
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sock.bind(IP_ADDRESS)
    sock.listen(5)
    
    sock.setblocking(False)
    read_list = [sock, ]
    write_list = []
    wdata = {}
    
    while True:
        r_list, w_list, x_list = select.select(read_list, write_list, [])
        for item in r_list:
            """
            针对不同的对象,做不同的处理,r_list里面可能是socket对象,也可能是conn对象
            """
            if item is sock:
                conn, addr = item.accept()
                """将conn加入到监管队列中去"""
                read_list.append(conn)
            else:
                try:
                    msg = item.recv(1024)
                    if not msg:
                        item.close()
                        read_list.remove(item)
                        continue
                    print(msg.decode('utf-8'))
                    write_list.append(item)
                    wdata[item] = msg.upper()
                except Exception:
                    item.close()
                    read_list.remove(item)
    
        for item in w_list:
            item.send(wdata[item])
            write_list.remove(item)
            wdata.pop(item)
            
    """
    客户端
    """
    import socket
    
    IP_ADDRESS = ('182.92.59.34', 9090)
    
    client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    client.connect_ex(IP_ADDRESS)
    
    while True:
        # msg = input('please enter the message>>>:').strip()
        # if not msg: continue
        client.send(b'hello world!')
        back_msg = client.recv(1024)
        print(back_msg.decode('utf-8'))
    

    总结:

    """
    监管机制其实有很多
    select机制  windows linux都有
    
    poll机制    只在linux有   poll和select都可以监管多个对象 但是poll监管的数量更多
    
    上述select和poll机制其实都不是很完美 当监管的对象特别多的时候
    可能会出现 极其大的延时响应
    
    epoll机制   只在linux有
    	它给每一个监管对象都绑定一个回调机制
    	一旦有响应 回调机制立刻发起提醒
    
    针对不同的操作系统还需要考虑不同检测机制 书写代码太多繁琐
    有一个人能够根据你跑的平台的不同自动帮你选择对应的监管机制
    selectors模块
    """
    

    异步I/O

    """
    异步IO模型是所有模型中效率最高的 也是使用最广泛的
    相关的模块和框架
    	模块:asyncio模块
    	异步框架:sanic tronado twisted
    		速度快!!!
    """
    
    import threading
    import time
    import asyncio
    
    
    async def hello():
        print(f'hello world {threading.current_thread().getName()}')
        """使用sleep来模拟IO操作"""
        time.sleep(1)
        print(f'hello world {threading.current_thread().getName()}')
    
    
    loop = asyncio.get_event_loop()
    task = [hello(), hello()]
    loop.run_until_complete(asyncio.wait(task))
    loop.close()
    

    注意:

    "@coroutine" decorator is deprecated since Python 3.8, use "async def" instead
    

    I/O模型的比较分析

    """
    经过上面的介绍,会发现non-blocking IO和asynchronous IO的区别还是很明显的。在non-blocking IO中,虽然进程大部分时间都不会被block,但是它仍然要求进程去主动的check,并且当数据准备完成以后,也需要进程主动的再次调用recvfrom来将数据拷贝到用户内存。而asynchronous IO则完全不同。它就像是用户进程将整个IO操作交给了他人(kernel)完成,然后他人做完后发信号通知。在此期间,用户进程不需要去检查IO操作的状态,也不需要主动的去拷贝数据。
    """
    

    selectors模块

    select,poll,epoll

  • 相关阅读:
    阿米巴
    linux系统和依赖包常用下载地址
    chm格式文件能打开,但看不到内容问题
    云计算的三层SPI模型
    云计算相关的一些概念Baas、Saas、Iaas、Paas
    IOS 开发环境,证书和授权文件等详解
    Android MDM参考
    理解RESTful架构
    联想小新Air2020锐龙版在Ubuntu下添加指纹识别
    避免踩坑,这才是为知笔记导入印象笔记数据的正确姿势
  • 原文地址:https://www.cnblogs.com/surpass123/p/12795055.html
Copyright © 2011-2022 走看看