精华内容
下载资源
问答
  • 多路复用流的原理
    千次阅读
    2019-09-17 22:09:27

    10、Redis IO多路复用技术以及epoll实现原理

           Redis是一个单线程的但性能是非常好的内存数据库,主要用来作为缓存系统。Redis采用网络IO多路复用技术来保证在多连接的时候,系统吞吐量高。

    10.1 为什么Redis要使用IO多路复用

           首先,Redis是跑在单线程中的,所有的操作都是顺序线性执行的,但是由于读写操作等待用户输入或者输出都是阻塞的,所以I/O操作往往不能直接返回,这会导致某一文件的I/O阻塞导致整个进程无法为客户服务,而I/O多路复用模型就是为了解决这个问题而出现的。

     

           select、poll、epoll都是IO多路复用的模型。I/O多路复用就是通过一种机制,可以监视多个文件描述符,一旦某个描述符就绪,能够通知应用程序进行相应操作。

           Redis的I/O模型使用的就是epoll,不过它也提供了select和kqueue的实现,默认采用epoll。

           那么epoll到底什么东西?我们一起来看看。

     

    10.2 epoll实现机制

    10.2.1 场景举例

           设想一个如下场景:

           有100W个客户端同时与一个服务器保持着TCP连接。而每一时刻只有几百上千个TCP连接是活跃着的(事实上大部分场景都是这样的情况)。如何实现这样的高并发?

           在select/poll时代,服务器进程每次都要把100W个连接告诉操作系统(从用户态复制句柄数据结构到内核态),让操作系统内核去查询这些套接字上是否有事件发生,轮询完后,再将句柄数据复制到用户态,让服务器应用程序轮询处理已经发生的网络事件,这一过程资源消耗较大,因此select/poll一般只能处理几千的并发连接。

           如果没有I/O事件发生,我们的程序就会阻塞在select处。但是依然存在一个问题,我们从select那里仅仅知道了有I/O事件发生,但却并不知道是哪几个流(可能一个,多个,甚至全部),我们只能无差别轮询所有流,找出能读出或者写入数据的流,对它们进行操作。

           但是,使用select,我们有O(n)无差别轮询复杂度,同时处理的流越多,每一次无差别的轮询时间就越长。

     

    10.2.2 select/poll缺点

           总结:select/poll缺点如下:

    1. 每次调用select/poll都需要把fd集合从用户态拷贝到内核态,这个开销在fd很多时会很大;
    2. 同时每次调用select/poll都需要在内核遍历传进来的所有fd,这个开销在fd很多时会很大;
    3. select支持的文件描述符数量太小,默认1024;
    4. select返回的是含有整个句柄的数组,应用程序需要遍历整个数组才能发现哪些句柄发生了事件;
    5. select的触发方式是水平触发,应用程序如果没有完成对一个已经就绪的文件描述符进行IO操作,那么之后每次select调用还是会将这些文件描述符通知给进程;

     

            相比于select模型,poll模型使用链表保存文件描述符,因此没有了监视文件数量的限制,但是其他缺点依然存在。

     

    10.2.3 epoll实现机制

           epoll的设计和实现与select完全不同。epoll是poll的一种优化,返回后不需要对所有的fd进行遍历,它在内核中维护了fd列表。select和poll是将这个内核列表维持在用户态,然后复制到内核态。与select/poll不同,epoll不在是一个单独的调度系统,而是由epoll_create / epolll_ctl / epoll_wait三个系统组成,后面将会看到这样做的好处。epoll在2.6以后的内核才支持。

           epoll通过在Linux内核中申请一个简易的文件系统(文件系统一般用什么数据结构?B+树)。把原先的select/poll调用分成三个部分。

    1. 调用epoll_create()建立一个epoll对象(在epoll文件系统中为这个句柄对象分配资源);
    2. 调用epoll_ctl向epoll对象中添加这100W个连接的套接字;
    3. 调用epoll_wait收集在这上面发生的事件连接。

            如此一来,要实现上面所说的的场景,只要在进程启动的时候创建一个epoll对象,然后在需要的时候向这个epoll对象中添加或者删除socket连接。同时,epoll_wait的效率也是非常高的,因为调用epoll_wait时,并没有一股脑的向操作系统复制这100W个连接的句柄数据,内核也不需要去遍历全部的连接。

     

    10.2.4 epoll优点

    1)epoll没有最大并发连接限制,上限是最大可以打开的文件的数目,这个数字远大于“2048”,**一般来说,这个数目跟系统内存关系很大**,具体数目可以cat /proc/sys/fs/file-max查看。

     

    2)效率提升,epoll最大的优点就在于它只管你活跃的连接,而跟连接总数无关,因此在实际的网络环境中,epoll的效率会远远高于select/poll。

     

    3)无内存拷贝,epoll在这点上使用了“共享内存”,这个内存拷贝也就省略了。

     

    10.3 Redis epoll底层实现

            当某一进程调用epoll_create方法时,Linux内核会创建一个eventpoll结构体,这个结构体中有两个成员与epoll的使用方式密切相关。

    eventpoll结构体如下所示:
    	struct eventpoll{
    
    	……
    	/* 红黑树根节点,这棵树中存储着所有添加到epoll中需要监控的事件 */
    	struct rb_root rbr;
    
    	/* 双链表中则存放着将要通过epoll_wait返回给用户的满足条件的事件 */
    	struct list_head rdlist;
    	……
    
    };
    

            每一个epoll对象都有一个独立的eventpoll结构体,用于存放通过epoll_ctl方法向epoll对象中添加进来的事件。这些事件都会挂在红黑树中,如此,重复添加的事件就可以通过红黑树高效标识出来(红黑树插入事件效率是lgn,其中n为树的高度)。

     

            而所有添加到epoll中的事件都会与设备(网卡)驱动程序建立回调关系,也就是说,当相应的事件发生时,会调用这个回调方法。这个回调方法在内核中叫ep_poll_callback,它会将发生的事件添加到rdlist双向链表中。

    在epoll中,每个事件都会建立一个epitem结构体,如下所示:
    struct epitem{
    	// 红黑树节点
    	struct rb_node rbn;
    
    	// 双向链表节点
    	struct list_head rdlist;
    
    	// 事件句柄信息
    	struct epoll_filefd ffd;
    
    	// 指向其所属的eventpoll对象
    	struct eventpoll *ep;
    
    	// 期待发生的事件类型
    	struct epoll_event event;
    
    }
    

            当调用epoll_wait方法检查是否有事件发生时,只需要检查eventpoll对象中的rdlist双向链表中是否有epitem元素即可。如果rdlist不为空,则把发生的事件复制到用户态,同时将事件的数量返回给用户。

    优势:

    1. 不用重复传递。我们调用epoll_wait时候就相当于以前调用select/poll,但这时却不用传递socket文件句柄给内核,因为内核已经在epoll_ctl中拿到了要监控的文件句柄列表。
    2. 在内核里,一切皆文件。所以,epoll向内核注册了一个文件系统,用于存储上述被监控socket。当你调用epoll_create时,就会在这个虚拟的epoll文件系统中创建一个file结点。当然这个file不是普通的文件,它只服务于epoll。
    3. 极其高效的原因。这是由于我们在调用epoll_create时候,内核除了帮我们在epoll文件系统中创建了file结点,在内核cache里创建了个红黑树用于储存以后epoll_ctl传来的socket外,还会再建立一个list链表,用于存储准备就绪的事件,当epoll_wait调用时候,仅仅观察这个list链表有没有数据即可。如果有数据就立即返回,没有数据就sleep,等到timeout时候,即使list没有数据也返回。所以epoll_wait非常高效。 

            epoll在被内核初始化时(操作系统启动),同时会开辟出epoll自己的内核高速cache区,用于安置我们每一个想要监控的socket,这些socket会以红黑树的形式保存在内核cache里,以支持快速的查找、插入、删除。这个内核高速cache区,就是建立连续的物理内存页,然后在此之上建立slab层,简单的说,就是物理上分配好你想要的size内存对象,每次使用都是使用空闲的已经分配好的对象。

     

    这个准备就绪的list链表是怎么维护的呢?

            当我们执行epoll_ctl时,除了把socket放到epoll文件系统里file对象对应的红黑树上之外,还会给内核中断处理程序注册一个回调函数,告诉内核,如果这个句柄的中断到了,就把它放到准备就绪的list链表里。所以,当一个socket上有数据到了,内核再把网卡中的数据copy到内核中后,就把socket插入到准备就绪的链表里了。(备注:好好理解这句话)

            从上面可以看出,epoll基础就是回调。

            如此,一颗红黑树,一张准备就绪的句柄链表,少量的内核cache,就帮我们解决了高并发下的socket处理问题。

            执行epoll_create时,创建了红黑树和就绪链表,执行epoll_ctl时,如果增加socket句柄,则检查红黑树中是否存在,存在就立即返回,不存在则添加进红黑树,然后向内核注册回调函数,用于当中断事件来临时向准备就绪链表中插入数据。执行epoll_wait时,立刻返回准备就绪链表里的数据即可。

           最后看看epoll独有的两种模式LT和ET。无论是LE还是ET都适用于以上所说的流程。区别是,LT模式下,只要一个句柄上的事件一次没有处理完,会在以后调用epoll_wait时此次返回这个句柄,而ET模式仅在第一次返回。

           关于LT和ET,有一段描述,LT和ET都是电子里面的术语,ET是边缘触发,LT是水平触发,一个表示只有在变化的边际触发,一个表示在某个阶段都会触发。

           LT,ET这件事怎么做到的呢?当一个socket句柄上有事件时,内核会把该句柄插入上面所说的准备就绪的list链表,最后,epoll_wait干了这件事情,就是检查这些socket,如果不是ET模式(就是LT模式的句柄了),并且这些socket上确实有未处理的事件时,又把该句柄放回刚刚清空的准备就绪链表了。所以,非ET的句柄,只要它上面还有事件,epoll_wait每次都会返回这个句柄。(从上面这段,可以看出,LT还有一个回放过程,低效了。)

     

    觉着老铁我写的还不错滴请继续关注下一章:终章篇咯!

     

    更多相关内容
  • 你知道的越,不知道的就越,业余的像一棵小草!成功路上并不拥挤,因为坚持的人不。编辑:业余草blog.csdn.net/Seky_fei推荐:https://www.xttblog.c...

    你知道的越多,不知道的就越多,业余的像一棵小草!

    成功路上并不拥挤,因为坚持的人不多。

    编辑:业余草

    blog.csdn.net/Seky_fei

    推荐:https://www.xttblog.com/?p=5238

    本文内容基于 Redis 6.0 以前的版本编写,因为 6.0 之后 Redis 在网络处理这一块采用了多线程模式,但是 I/O 多路复用的模型还在,变化不大。本文有不当之处,大家轻喷!

    Redis之I/O多路复用模型实现原理

    Redis 的 I/O 多路复用模型有效的解决单线程的服务端,使用不阻塞方式处理多个 client 端请求问题。在看 I/O 多路复用知识之前,我们先来看看 Redis 的客服端怎么跟客服端建立连接的、单线程 socket 服务端为什么会存在 I/O 阻塞。

    Redis客户端连接

    Redis 通过监听一个 TCP 端口或者 Unix socket 的方式来接收来自 client 端的连接,当一个连接建立后,Redis 内部会进行以下一些操作:

    • 首先,客户端 socket 会被设置为非阻塞模式,因为 Redis 在网络事件处理上采用的是非阻塞多路复用模型。

    • 然后为这个 socket 设置 TCP_NODELAY 属性,禁用 Nagle 算法。

    • 然后创建一个可读的文件事件用于监听这个客户端 socket 的数据发送。

    阻塞I/O

    在 socket 连接中,一个服务器进程和一个客户端进行通信时,当一个 client 端向服务端写数据时,如果 client 端没有发送数据,那么服务端的 read 将一直阻塞,直到客户端 write 发来数据。在一个客户和服务器通信时没什么问题,当多个客户 与 一个服务器通信时,就存在问题了。如下图,两个客服端同时连接一个服务端进行写数据的时序图。

    从上图可以看出一个服务器进程和多个客户端进程通信的问题:

    • (1) client1 和服务端建立连接后,服务端会一直阻塞于 client1,直到 client1 客户端 write 发来数据才开始后面的操作。服务端阻塞期间,即使其他客服端 client2 的数据提前到来,也不能处理 client2 客服端的请求。

    • (2) 有一个严重的问题就是,如果客户端 client1 一直没有 write 数据到来,那么服务端 service 会一直阻塞,不能处理其他客户的服务。

    上面就是 Redis 通过 Unix socket 的方式来接收来自 client 端的连接存在的 I/O 阻塞问题,而 「I/O 多路复用」就是为了解决服务端一直阻塞等待某一个 client 的数据到来,即使其他client的数据提前到来,也不会被处理的问题。

    I/O多路复用

    为什么 Redis 中要使用 I/O 多路复用这种技术呢?因为 Redis 是跑在「单线程」中的,所有的操作都是按照顺序线性执行的,但是「由于读写操作等待用户输入 或 输出都是阻塞的」,所以 I/O 操作在一般情况下往往不能直接返回,这会导致某一文件的 I/O 阻塞导,致整个进程无法对其它客户提供服务。而 I/O 多路复用就是为了解决这个问题而出现的。「为了让单线程(进程)的服务端应用同时处理多个客户端的事件,Redis 采用了 IO 多路复用机制。」

    这里**“多路”「指的是多个网络连接客户端,」“复用”**指的是复用同一个线程(单进程)。I/O 多路复用其实是使用一个线程来检查多个 Socket 的就绪状态,在单个线程中通过记录跟踪每一个 socket(I/O流)的状态来管理处理多个 I/O 流。如下图是 Redis 的 I/O 多路复用模型:

    #1.文件描述符(file descriptor):
        Linux 系统中,把一切都看做是文件,当进程打开现有文件或创建新文件时,内核向进程返回一个文件描述符。可以理解文件描述符是一个索引,这样,要操作文件的时候,我们直接找到索引就可以对其进行操作了。我们将这个索引叫做文件描述符(file descriptor),简称fd。
    

    如上图对 Redis 的 I/O 多路复用模型进行一下描述说明:

    • (1)一个 socket 客户端与服务端连接时,会生成对应一个套接字描述符(套接字描述符是文件描述符的一种),每一个 socket 网络连接其实都对应一个文件描述符。

    • (2)多个客户端与服务端连接时,Redis 使用 「I/O 多路复用程序」 将客户端 socket 对应的 FD 注册到监听列表(一个队列)中。当客服端执行 read、write 等操作命令时,I/O 多路复用程序会将命令封装成一个事件,并绑定到对应的 FD 上。

    • (3)「文件事件处理器」使用 I/O 多路复用模块同时监控多个文件描述符(fd)的读写情况,当 acceptreadwriteclose 文件事件产生时,文件事件处理器就会回调 FD 绑定的事件处理器进行处理相关命令操作。

    #例如:以 Redis 的 I/O 多路复用程序 epoll 函数为例
        多个客户端连接服务端时,Redis 会将客户端 socket 对应的 fd 注册进 epoll,然后 epoll 同时监听多个文件描述符(FD)是否有数据到来,如果有数据来了就通知事件处理器赶紧处理,这样就不会存在服务端一直等待某个客户端给数据的情形。
    
    #(I/O多路复用程序函数有 select、poll、epoll、kqueue)
    
    • (5)整个文件事件处理器是在单线程上运行的,但是通过 I/O 多路复用模块的引入,实现了同时对多个 FD 读写的监控,当其中一个 client 端达到写或读的状态,文件事件处理器就马上执行,从而就不会出现 I/O 堵塞的问题,提高了网络通信的性能。

    • (6)如上图,Redis 的 I/O 多路复用模式使用的是 「Reactor 设置模式」的方式来实现。

    「总结:」

    • (1) Redis 的 I/O 多路复用程序函数有 select、poll、epoll、kqueue。select 作为备选方案,由于其在使用时会扫描全部监听的文件描述符,并且只能同时服务 1024 个文件描述符,所以是备选方案。

    • (2) I/O 多路复用模型是利用 select、poll、epoll 函数可以同时监察多个流的 I/O 事件的能力,在空闲的时候,会把当前线程阻塞掉。当有一个或多个流有 I/O 事件时,就从阻塞态中唤醒,于是程序就会轮询一遍所有的流(epoll 是只轮询那些真正发出了事件的流),依次顺序的处理就绪的流,这种做法就避免了大量无用的等待操作。

    展开全文
  • 简单的单向流多路复用器,功能非常的少
  • Redis之I/O多路复用模型实现原理

    千次阅读 多人点赞 2020-06-14 19:29:42
    在I/O多路复用之前我们先来看看Redis是怎么和客服端建立连接的、单线程的socket服务端为什么I/O会阻塞。 (1)Redis客户端连接 Redis通过监听一个 TCP 端口或者 Unix socket 的方式来接收来自client端的连接,当一个...

    Redis的I/O多路复用模型有效的解决单线程的服务端,使用不阻塞方式处理多个client端请求问题。在看I/O多路复用知识之前,我们先来看看Redis是客服端怎么跟客服端建立连接的、单线程socket服务端为什么会存在I/O阻塞。

    Redis客户端连接

    Redis通过监听一个 TCP 端口或者 Unix socket 的方式来接收来自client端的连接,当一个连接建立后,Redis 内部会进行以下一些操作:

    • 首先,客户端 socket 会被设置为非阻塞模式,因为 Redis 在网络事件处理上采用的是非阻塞多路复用模型。

    • 然后为这个 socket 设置 TCP_NODELAY 属性,禁用 Nagle 算法。

    • 然后创建一个可读的文件事件用于监听这个客户端 socket 的数据发送。

    阻塞I/O

    在socket连接中,一个服务器进程和一个客户端进行通信时,当一个client端向服务端写数据时,如果client端没有发送数据,那么服务端的read将一直阻塞,直到客户端write发来数据。在一个客户和服务器通信时没什么问题,当多个客户 与 一个服务器通信时,就存在问题了。如下图,两个客服端同时连接一个服务端进行写数据的时序图。

    从上图可以看出一个服务器进程和多个客户端进程通信的问题:

    • (1) client1和服务端建立连接后,服务端会一直阻塞于client1,直到client1客户端write发来数据才开始后面的操作。服务端阻塞期间,即使其他客服端client2的数据提前到来,也不能处理client2客服端的请求。

    • (2) 有一个严重的问题就是,如果客户端client1一直没有write数据到来,那么服务端service会一直阻塞,不能处理其他客户的服务。

    上面就是Redis通过Unix socket 的方式来接收来自client端的连接存在的I/O阻塞问题,而 I/O 多路复用就是为了解决服务端一直阻塞等待某一个client的数据到来,即使其他client的数据提前到来,也不会被处理的问题。

    I/O多路复用

    为什么Redis中要使用 I/O 多路复用这种技术呢?因为Redis 是跑在单线程中的,所有的操作都是按照顺序线性执行的,但是由于读写操作等待用户输入 或 输出都是阻塞的,所以 I/O 操作在一般情况下往往不能直接返回,这会导致某一文件的 I/O 阻塞导,致整个进程无法对其它客户提供服务。而 I/O 多路复用就是为了解决这个问题而出现的。为了让单线程(进程)的服务端应用同时处理多个客户端的事件,Redis采用了IO多路复用机制。

    这里“多路”指的是多个网络连接客户端,“复用”指的是复用同一个线程(单进程)。I/O 多路复用其实是使用一个线程来检查多个Socket的就绪状态,在单个线程中通过记录跟踪每一个socket(I/O流)的状态来管理处理多个I/O流。如下图是Redis的I/O多路复用模型:

    #1.文件描述符(file descriptor):
        Linux 系统中,把一切都看做是文件,当进程打开现有文件或创建新文件时,内核向进程返回一个文件描述符。可以理解文件描述符是一个索引,这样,要操作文件的时候,我们直接找到索引就可以对其进行操作了。我们将这个索引叫做文件描述符(file descriptor),简称fd。

    如上图对Redis的I/O多路复用模型进行一下描述说明:

    • (1)一个socket客户端与服务端连接时,会生成对应一个套接字描述符(套接字描述符是文件描述符的一种),每一个socket网络连接其实都对应一个文件描述符。

    • (2)多个客户端与服务端连接时,Redis使用 I/O多路复用程序 将客户端socket对应的FD注册到监听列表(一个队列)中,并同时监控多个文件描述符(fd)的读写情况。当客服端执行accept、read、write、close等操作命令时,I/O多路复用程序会将命令封装成一个事件,并绑定到对应的FD上。

    • (3)当socket有文件事件产生时,I/O 多路复用模块就会将那些产生了事件的套接字fd传送给文件事件分派器。

    • (4)文件事件分派器接收到I/O多路复用程序传来的套接字fd后,并根据套接字产生的事件类型,将套接字派发给相应的事件处理器来进行处理相关命令操作。

    #例如:以Redis的I/O多路复用程序 epoll函数为例
        多个客户端连接服务端时,Redis会将客户端socket对应的fd注册进epoll,然后epoll同时监听多个文件描述符(FD)是否有数据到来,如果有数据来了就通知事件处理器赶紧处理,这样就不会存在服务端一直等待某个客户端给数据的情形。
    
    #(I/O多路复用程序函数有select、poll、epoll、kqueue)
    • (5)整个文件事件处理器是在单线程上运行的,但是通过 I/O 多路复用模块的引入,实现了同时对多个 FD 读写的监控,当其中一个client端达到写或读的状态,文件事件处理器就马上执行,从而就不会出现I/O堵塞的问题,提高了网络通信的性能。
    • (6)如上图,Redis的I/O多路复用模式使用的是 Reactor设计模式的方式来实现。

    总结:

    • (1) Redis的I/O多路复用程序函数有select、poll、epoll、kqueue。select 作为备选方案,由于其在使用时会扫描全部监听的文件描述符,并且只能同时服务 1024 个文件描述符,所以是备选方案。

    • (2) I/O 多路复用模型是利用select、poll、epoll函数可以同时监察多个流的 I/O 事件的能力,在空闲的时候,会把当前线程阻塞掉。当有一个或多个流有I/O事件时,就从阻塞态中唤醒,于是程序就会轮询一遍所有的流(epoll是只轮询那些真正发出了事件的流),依次顺序的处理就绪的流,这种做法就避免了大量无用的等待操作。

                                                                                                        2020年06月14日 晚 于北京记

    展开全文
  • http/2.0“多路复用”实现原理 //转载

    千次阅读 多人点赞 2019-12-04 10:11:55
    1.HTTP/2较HTTP/1.1优化亮点HTTP/2是一个二进制协议,其基于“帧”的结构设计,改进了很多HTTP/1.1痛点问题。下面列举一些最常被津津乐道的改进之处:多路复用头部...

    1.HTTP/2较HTTP/1.1优化亮点

    HTTP/2是一个二进制协议,其基于“帧”的结构设计,改进了很多HTTP/1.1痛点问题。下面列举一些最常被津津乐道的改进之处:

    • 多路复用的流

    • 头部压缩

    • 资源优先级和依赖设置

    • 服务器推送

    • 流量控制

    • 重置消息

    以上列举的每一项都值得做深入细致的研究,这里就只针对“多路复用”功能的实现进行深入的学习。

    2.“多路复用”的原理解析

    2.1 什么是多路复用?

    网络上有一张图能清晰的解释这个问题:

    HTTP/1.1协议的请求-响应模型大家都是熟悉的,我们用“HTTP消息”来表示一个请求-响应的过程,那么HTTP/1.1中的消息是“管道串形化”的:只有等一个消息完成之后,才能进行下一条消息;而HTTP/2中多个消息交织在了一起,这无疑提高了“通信”的效率。这就是多路复用:在一个HTTP的连接上,多路“HTTP消息”同时工作。

    2.2 为什么HTTP/1.1不能实现“多路复用”?

    简单回答就是:HTTP/2是基于二进制“帧”的协议,HTTP/1.1是基于“文本分割”解析的协议。
    看一个HTTP/1.1简单的GET请求例子:

    GET / HTTP/1.1
    Accept:text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
    Accept-Encoding:gzip, deflate, br
    Accept-Language:zh-CN,zh;q=0.9,en;q=0.8
    Cache-Control:max-age=0
    Connection:keep-alive
    Cookie:imooc_uuid=b2076a1d-6a14-4cd5-91b0-17a9a2461cf4; imooc_isnew_ct=1517447702; imooc_isnew=2; zg_did=%7B%22did%22%3A%20%221662d799f3f17d-0afe8166871b85-454c092b-100200-1662d799f4015b%22%7D; loginstate=1; apsid=Y4ZmEwNGY3OTUwMTdjZTk0ZTc4YzBmYThmMDBmZDYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAANDEwNzI4OQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA5NTMzNjIzNjVAcXEuY29tAAAAAAAAAAAAAAAAAAAAADBmNmM5MzczZTVjMTk3Y2VhMDE2ZjUxNmQ0NDUwY2IxIDPdWyAz3Vs%3DYj; Hm_lvt_fb538fdd5bd62072b6a984ddbc658a16=1541222935,1541224845; Hm_lvt_f0cfcccd7b1393990c78efdeebff3968=1540010199,1541222930,1541234759; zg_f375fe2f71e542a4b890d9a620f9fb32=%7B%22sid%22%3A%201541297212384%2C%22updated%22%3A%201541297753524%2C%22info%22%3A%201541222929083%2C%22superProperty%22%3A%20%22%7B%5C%22%E5%BA%94%E7%94%A8%E5%90%8D%E7%A7%B0%5C%22%3A%20%5C%22%E6%85%95%E8%AF%BE%E7%BD%91%E6%95%B0%E6%8D%AE%E7%BB%9F%E8%AE%A1%5C%22%2C%5C%22%E5%B9%B3%E5%8F%B0%5C%22%3A%20%5C%22web%5C%22%7D%22%2C%22platform%22%3A%20%22%7B%7D%22%2C%22utm%22%3A%20%22%7B%7D%22%2C%22referrerDomain%22%3A%20%22%22%2C%22cuid%22%3A%20%22Jph3DQ809OQ%2C%22%7D; PHPSESSID=h5jn68k1fcaadn61bpoqa9hch2; cvde=5be7a057c314b-1; IMCDNS=1
    Host:www.imooc.com
    Referer:https://www.imooc.com/
    Upgrade-Insecure-Requests:1
    User-Agent:Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36

    以上就是HTTP/1.1发送请求消息的文本格式:以换行符分割每一条key:value的内容,解析这种数据用不着什么高科技,相反的,解析这种数据往往速度慢且容易出错。“服务端”需要不断的读入字节,直到遇到分隔符(这里指换行符,代码中可能使用\n或者\r\n表示),这种解析方式是可行的,并且HTTP/1.1已经被广泛使用了二十多年,这事已经做过无数次了,问题一直都是存在的:

    • 一次只能处理一个请求或响应,因为这种以分隔符分割消息的数据,在完成之前不能停止解析。

    • 解析这种数据无法预知需要多少内存,这会带给“服务端”很大的压力,因为它不知道要把一行要解析的内容读到多大的“缓冲区”中,在保证解析效率和速度的前提下:内存该如何分配?

    2.3 HTTP/2帧结构设计和多路复用实现

    前边提到:HTTP/2设计是基于“二进制帧”进行设计的,这种设计无疑是一种“高超的艺术”,因为它实现了一个目的:一切可预知,一切可控。
    帧是一个数据单元,实现了对消息的封装。下面是HTTP/2的帧结构:

    帧的字节中保存了不同的信息,前9个字节对于每个帧都是一致的,“服务器”解析HTTP/2的数据帧时只需要解析这些字节,就能准确的知道整个帧期望多少字节数来进行处理信息。我们先来了解一下帧中每个字段保存的信息:

    名称长度描述
    Length3 字节表示帧负载的长度,默认最大帧大小2^14
    Type1 字节当前帧的类型,下面会做介绍
    Flags1 字节具体帧的标识
    R1 位保留位,不需要设置,否则可能带来严重后果
    Stream Identifier31 位每个流的唯一ID
    Frame Payload不固定真实帧的长度,真实长度在Length中设置

    如果使用HTTP/1.1的话,你需要发送完上一个请求,才能发送下一个;由于HTTP/2是分帧的,请求和响应可以交错甚至可以复用。
    为了能够发送不同的“数据信息”,通过帧数据传递不同的内容,HTTP/2中定义了10种不同类型的帧,在上面表格的Type字段中可对“帧”类型进行设置。下表是HTTP/2的帧类型:

    名称ID描述
    DATA0x0传输流的核心内容
    HEADERS0x1包含HTTP首部,和可选的优先级参数
    PRIORITY0x2指示或者更改流的优先级和依赖
    RST_STREAM0x3允许一端停止流(通常是由于错误导致的)
    SETTINGS0x4协商连接级参数
    PUSH_PROMISE0x5提示客户端,服务端要推送些东西
    PING0x6测试连接可用性和往返时延(RTT)
    GOAWAY0x7告诉另外一端,当前端已结束
    WINDOW_UPDATE0x8协商一端要接收多少字节(用于流量控制)
    CONTINUATION0x9用以拓展HEADER数据块

    有了以上对HTTP/2帧的了解,我们就可以解释多路复用是怎样实现的了,不过在这之前我们先来了解“流”的概念:HTTP/2连接上独立的、双向的帧序列交换。流ID(帧首部的6-9字节)用来标识帧所属的流
    下面两张图分别表示了HTTP/2协议上POST请求数据流“复用”的过程,很容易看的明白:

    3. 更多特性与简介

    由于HTTP/2消息中“帧”的设计,客户端和服务端在通信的过程中能够彼此了解更多的信息。下面再简单说一下其他几点比较重要的特性,算是一个学习引导方向吧。

    3.1 流量控制(滑窗)

    HTTP/2的新特性之一是基于流的流量控制。不同于HTTP/1.1,只要客户端可以处理,服务端就会尽可能快的发送数据,HTTP/2提供了客户端调整传输速度的能力(服务端也可以)。WINDOW_UPDATE帧用来完成这件事情,每个帧告诉对方,发送方想要接收多少字节,它将发送一个WINDOW_UPDATE帧以指示其更新后的处理字节能力。

    3.2 设置资源优先级和依赖关系

    流的一个重要特性是可以设置优先级和资源数据的依赖关系。HTTP/2通过流的依赖可以实现这些功能。通过HEADERS帧和PRIORITY帧,客户端可以明确的告诉服务端它最需要什么,这是通过声明依赖关系和权重实现的。

    • 依赖关系为客户端提供了一种能力,通过指明某些对象对另外一些对象的依赖,告知服务器哪些资源应该被优先传输。

    • 权重让客户端告诉服务器如何具体确定具有共同依赖关系对象的优先级。

    3.3 服务端推送

    《孙子兵法》中有一句名言:“兵马未到,粮草先行”。服务端推送功能就可以实现这样一个功能。当页面还没有开始请求具体的资源时,服务端就已经把一些资源(像css和js)已经推送到客户端了。当浏览器要渲染页面时,资源已经在缓存中了,听起来是一件很酷的事情,实际上也正是这样。服务端推送是通过PUSH_PROMISE帧实现的,当然其实现的细节是非常复杂的,感兴趣的同学可以研究一下。
    HTTP/2的“多路复用”问题已经说明白了,还补充了一些新特性的介绍。当然想要深入了解HTTP/2的一些原理,有太多太多的内容需要阅读,实践。比如“首部压缩”算法HPACK是HTTP/2的关键元素之一,是HTTP/2制定开发组长时间的研究成果,其思想内容也是特别值得学习借鉴的。本节所设计到的东西只是HTTP/2协议中的冰山一角,RFC7540可以帮助你充分了解该协议的方方面面。

    展开全文
  • 一、多路复用 1.概念引入 2.BIO处理并发情况 3.多路复用IO并发情况 4.两者的对比 二、多路复用模型 1.Selector和Poll模型 2.epoll模型(仅限Linux系统) 3.Reactor多线程模型 三、Tomcat之IO多路复用应用 ...
  • redis的多路复用原理

    千次阅读 2018-12-03 14:31:46
    这里“多路”指的是多个网络连接,“复用”指的是复用同一个线程 采用多路 I/O 复用技术可以让单个线程高效的处理多个连接请求(尽量减少网络 IO 的时间消耗), 且 Redis 在内存中操作数据的速度非常快,也就是说...
  • HTTP/2协议“多路复用”实现原理

    千次阅读 多人点赞 2018-11-11 21:01:53
    下面列举一些最常被津津乐道的改进之处: ...多路复用 头部压缩 资源优先级和依赖设置 服务器推送 流量控制 重置消息 以上列举的每一项都值得做深入细致的研究,这里就只针对“多路复用”功能的实现进行深入的...
  • Redis IO 多路复用底层的实现原理

    千次阅读 2021-04-01 21:57:53
    了解Redis底层关于IO多路复用的epoll实现原理前,先介绍关于IO模型,内存与磁盘交互方式、同步IO、异步IO,有助于对多路复用更好的理解。 用户空间与内核空间 User space 是用户程序的运行空间,Kernel space 是...
  • 本文分析了多路数字电视节目系统级复用原理,并对数字电视节目专用信息PSI的提取和重构及系统层节目时间参考PCR值的修正等关键技术进行研究,提出了进行软件复用的具体方法,可以节约硬件成本,降低系统的开发费用;...
  • IO多路复用底层原理分析

    千次阅读 2019-04-10 21:43:44
    其实网上关于Netty的文章很多,但是能够从底层原理去解释的却不多,我们都知道Netty底层是通过IO多路复用来实现的,那么你们有没有考虑过在底层上IO多路复用又是是如何实现的? 这篇就从底层的select函数以及文件...
  • HTTP2多路复用原理以及gRPC抓包分析

    千次阅读 2022-04-17 21:23:12
    二、HTTP2多路复用基本原理 1、帧 HTTP2在HTTP1基础上引入了二进制分帧层,数据封包给下层协议之前将数据打散成更小的帧并以二进制的方式进行编码传输。 帧结构 2、 是一个虚拟的概念,我们把一次...
  • NIO多路复用底层实现原理

    千次阅读 2020-03-16 23:50:30
    可以线程优化代码 这种方式也存在缺点:如果每个请求过来都使用一个线程,这时候非常浪费CPU的资源。 所以在网络编程服务器中,是否使用单线程提高响应的效率问题,所以nio出现; public class ...
  • 多路复用器介绍】

    千次阅读 2021-05-06 06:13:02
    多路复用器将接收的复合数据,依照信道分离数据,并将它们送到对应的输出线上,故称为解多路复用器。 实际生活中,使用多路复用器可使多路数据信息共享一路信道,能充分地利用通信信道的容量,大大降低系统的成本...
  • 计算机网络基础之多路复用技术

    千次阅读 多人点赞 2021-02-28 19:00:15
    今天给大家带来的是“多路复用技术”,什么是多路复用技术呢?简单的说就是许多单个的信号通过高速线路上的信道同时进行传输,这里有两点是要说的。第一,前面所说的高速线路的信道实际上是由一条信道分割出来的多条...
  • 操作系统- IO多路复用

    2022-01-06 14:41:56
    1) IO多路复用是操作系统的原理,但是很多中间件的实现都是基于它去做的,IO多复用需要 知道整个链路是样子的,输入是什么,输出是什么 2) 了解IO多路复用作用的位置是哪里 2 数据 ...
  • Redis的多路复用机制

    千次阅读 2021-03-23 19:55:28
    Redis是单线程还是线程? 通常我们所说的Redis 是单线程,主要是指 Redis 的网络 IO 和键值对读写是由一个线程来完成的,这也是 Redis 对外提供键值存储服务的主要流程。但 Redis 的其他功能,比如持久化、异步...
  • NIO多路复用机制

    千次阅读 2020-06-23 11:04:51
    首先介绍下socket 客户端与服务器每次网络通讯, 会建立一个连接, 创建socket, 以soket为媒介, 发送和接收输入输出... 线程了会占用过多的资源,浪费堆的内存空间(可以采用线程池去管理线程, 但是如果线程池满的情况下
  • 本文分析了多路数字电视节目系统级复用原理,并对数字电视节目专用信息PSI的提取和重构及系统层节目时间参考PCR值的修正等关键技术进行研究,提出了进行软件复用的具体方法,最后在Windows平台上,应用VC++开发环境...
  • 简单讲解一下 http2 的多路复用 在 HTTP/1 中,每次请求都会建立一次HTTP连接,也就是我们常说的3次握手4次挥手,这个过程在一次请求过程中占用了相当长的时间,即使开启了 Keep-Alive ,解决了多次连接的问题,但是...
  • 对IO多路复用的理解

    千次阅读 2021-07-09 16:35:18
    一、概念 IO多路复用是一种同步机制,通过复用机制实现一个线程监视多个文件句柄,一旦某个文件句柄准备就续,就通知应用程序对文件读写操作。若没有文件句柄就续时会阻塞应用程序,交出cpu。多路是指网络连接,复用...
  • Netty系列---IO多路复用技术

    千次阅读 2019-10-24 19:41:46
    形成的原因: 如果一个I/O进来,我们就开启一个进程处理这个I/O。...所以人们提出了I/O多路复用这个模型,一个线程,通过记录I/O的状态来同时管理多个I/O,可以提高服务器的吞吐能力。 ...
  • NIO的多路复用机制

    千次阅读 2020-04-29 16:19:44
    在一次磁盘操作中,CPU不是瓶颈...一方面是因为多线程存在上下文切换(用户态和内核态的切换)消耗问题,另一方面是因为redis采用IO多路复用机制。 面试时,redis被问到了3次,每一次都是一个惨痛的教训,希望读者朋...
  • NAT与PAT端口多路复用

    千次阅读 2022-03-15 20:16:06
    文章目录 NAT是什么 NAT概述 NAT的功能与分类 NAT工作原理 NAT的优缺点 NAT实现方式 PAT端口多路复用 PAT端口多路复用概述 PAT类型 总结 NAT是什么 NAT概述 NAT(Network Address Translation):又称为网络地址转换...
  • 简单介绍:nginx 采用的是多进程(单线程) + io多路复用(epoll)模型 实现高并发 二、nginx 多进程 启动nginx 解析初始化配置文件后会 创建(fork)一个master进程 之后 这个进程会退出 master 进程会 变为...
  • 前面两篇文章我们主要了解了一下...今天我们重点分析一下ConnectIntercept拦截器中的多路复用 @Override public Response intercept(Chain chain) throws IOException { RealInterceptorChain realChain = (Rea...
  • redis 多路复用原理

    千次阅读 2018-09-27 11:40:00
    仔细说一说 I/O 多路复用机制,打一个比方:小曲在 S 城开了一家快递店,负责同城快送服务。小曲因为资金限制,雇佣了一批快递员,然后小曲发现资金不够了,只够买一辆车送快递。   经营方式一 客户每送来...
  • 1.熟悉多路复用器、加法器的工作原理。 2.学会使用VHDL 语言设计多路复用器、加法器。 3.掌握generic 的使用,设计n-1 多路复用器。 4.兼顾速度与成本,设计行波加法器和先行进位加法器。 二、实验内容 1、利用...
  • Redis的I/O多路复用机制

    千次阅读 2019-02-16 13:29:22
    都说例子举的好,原理自然就能明白,上栗子: 小曲在S城开了一家快递店,负责同城快送服务。小曲因为资金限制,雇佣了一批快递员,然后小曲发现资金不够了,只够买一辆车送快递。 经营方式一 客户每送来一份快递,...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 34,226
精华内容 13,690
热门标签
关键字:

多路复用流的原理

友情链接: uC-GUI_user.zip