reactor网络模型在开源框架(redis、nginx、memcached)中的应用
Go原生网络模型(goroutine-per-connection)
其实用原生网络模型或者原生同步网络模型,都是xx自己造的词。原生网络模型 一般情况下被认为是机器学习上的一个概念
而这里的原生网络模型,是和《UNIX 网络编程》里总结归纳的 5 种 I/O 模型相区分
《UNIX 网络编程》里总结归纳的 5 种 I/O 模型:
阻塞 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 ,前面的4种都是同步模式
判断一个I/O模型是同步还是异步,主要看数据在用户和内核空间之间复制的时候是不是会阻塞当前进程,如果阻塞进程则是同步I/O,若不阻塞进程则是异步I/O。
深入理解 Go高性能网络编程模型,和Go netpoll I/O 多路复用构建原生网络模型之源码深度解析类似, 原文地址都是Andy Pan的 Go netpoller 原生网络模型之源码全面揭秘
Go中每一个请求,就会新起一个协程吗?
在 Go 语言中,每个 HTTP 请求会在服务器端启动一个新的协程(goroutine),以便可以并发处理多个请求,而不会阻塞其他的请求。
当一个 HTTP 请求到达服务器时,服务器会在一个新的 goroutine 中处理该请求,并将其路由到正确的处理器函数(handler function)。由于 goroutine 是轻量级线程,因此可以在不增加过多系统资源的情况下启动大量的协程。
当请求处理完成后,该协程会被销毁,并且在 Go 语言的运行时系统中进行垃圾回收。这种机制使得 Go 语言的 Web 服务器可以轻松地处理大量的并发请求,并提供高效的性能。
没有任何一种设计和架构是完美的,
goroutine-per-connection这种模式虽然简单高效,但是在某些极端的场景下也会暴露出问题:goroutine 虽然非常轻量,它的自定义栈内存初始值仅为 2KB,后面按需扩容;海量连接的业务场景下, goroutine-per-connection ,此时 goroutine 数量以及消耗的资源就会呈线性趋势暴涨,首先给 Go runtime scheduler 造成极大的压力和侵占系统资源,然后资源被侵占又反过来影响 runtime 的调度,导致性能大幅下降;此外,通过源码可以知道,Go netpoll 会通过 sync.Once 确保只初始化一个 epoll 实例,也就是说它是 single event-loop 模式,接收新连接和处理 I/O 事件是全部放在一个 thread 里的,所以在海量连接同时又高频创建和销毁连接的业务场景下有可能会导致性能瓶颈
https://www.jianshu.com/p/10765a74b480
【Go 夜读】第 65 期 Go 网络编程:Go 原生同步网络模型解析 vs Multi-Reactors 异步网络模型
Golang的netpoller底层事件驱动技术是基于I/O多路复用技术(I/O事件驱动技术,比如epoll/kqueue/iocp)来做封装,只不过是将这些调度和上下文切换的工作转移到了Golang运行时的调度器上,让它来负责调度goroutine,最终暴露出goroutine-per-connection这样极简的开发模式给使用者。从而实现开发者使用同步的模式去编写异步的逻辑,对于开发者来说I/O是否阻塞是无感知的,开发者无需考虑goroutine甚至更底层的线程、进程的调度和上下文切换,极大地降低了开发者编写网络引用的心智负担。
原文链接:https://blog.csdn.net/JunChow520/article/details/114212733
用同步的模式去编写异步的逻辑,极大降低了开发者编写网络应用时的心智负担,且借助于 Go runtime scheduler 对 goroutines 的高效调度,这个原生网络模型不论从适用性还是性能上都足以满足绝大部分的应用场景
事实上 Go netpoller 底层就是基于 epoll/kqueue/iocp 这些 I/O 多路复用技术来做封装的,最终暴露出 goroutine-per-connection 这样的极简的开发模式给使用者
现代的网络服务的主流已经完成从 CPU 密集型到 IO 密集型的转变,所以服务端程序对 I/O 的处理必不可少,而一旦操作 I/O 则必定要在用户态和内核态之间来回切换
https://github.com/golang/go/issues/27752
Go netpoller 通过在底层对 epoll/kqueue/iocp 的封装,从而实现了使用同步编程模式达到异步执行的效果。总结来说,所有的网络操作都以网络描述符 netFD 为中心实现。netFD 与底层 PollDesc 结构绑定,当在一个 netFD 上读写遇到 EAGAIN 错误时,就将当前 goroutine 存储到这个 netFD 对应的 PollDesc 中,同时调用 gopark 把当前 goroutine 给 park 住,直到这个 netFD 上再次发生读写事件,才将此 goroutine 给 ready 激活重新运行。显然,在底层通知 goroutine 再次发生读写等事件的方式就是 epoll/kqueue/iocp 等事件驱动机制。
通过前面对源码的分析,我们现在知道 Go netpoller 依托于 runtime scheduler,为开发者提供了一种强大的同步网络编程模式;然而,Go netpoller 存在的意义却远不止于此,Go netpoller I/O 多路复用搭配 Non-blocking I/O 而打造出来的这个原生网络模型,它最大的价值是把网络 I/O 的控制权牢牢掌握在 Go 自己的 runtime 里
https://uncledou.site/2021/goroutine-schedule-network/
https://zhuanlan.zhihu.com/p/463692291
为什么I/O 多路复用通常情况下需要和非阻塞 I/O 搭配使用?
I/O 多路复用和非阻塞 I/O 是两种常用的提高系统并发性能的技术。它们的结合可以进一步提高系统的并发性能,使得一个进程可以同时处理多个连接或请求。
具体来说,I/O 多路复用技术可以同时监听多个文件描述符上的事件,当有事件发生时,通过调用相应的处理函数来进行处理,从而避免了在一个进程中单独为每个连接或请求开辟一个线程或进程的开销。而非阻塞 I/O 技术则可以使得在一个文件描述符上的读写操作不会阻塞整个进程,从而使得系统可以同时处理多个连接或请求。
在实际应用中,通常将这两种技术结合使用,即使用 I/O 多路复用技术同时监听多个文件描述符,当有事件发生时,使用非阻塞 I/O 技术进行读写操作。这样可以避免阻塞整个进程,同时提高系统的并发性能,使得一个进程可以同时处理多个连接或请求。
需要注意的是,使用非阻塞 I/O 技术需要处理一些额外的细节,例如需要对读写操作返回的错误码进行处理,可能需要多次进行读写操作等。因此,在使用非阻塞 I/O 技术时,需要仔细处理相关细节,以保证程序的正确性和稳定性。
Reactor网络模型
Reactor模型是什么?为什么性能高
Reactor模型是一种常用的高性能I/O模型,常被用于构建高并发的网络服务器。Reactor模型基于事件驱动机制,其核心思想是将I/O操作分离到专门的I/O线程中,将事件处理和业务逻辑分离到单独的逻辑线程中,通过事件通知机制将I/O线程和逻辑线程进行协作。整个模型的流程如下:
事件注册:应用程序向I/O线程注册感兴趣的事件,例如读事件或写事件等。
事件监听:I/O线程在多路复用机制下监听这些事件。
事件分发:当某个事件就绪时,I/O线程会将其分发给相应的逻辑线程进行处理。
事件处理:逻辑线程对事件进行处理,执行业务逻辑。
事件回调:逻辑线程将处理结果返回给I/O线程,I/O线程进行回调处理。
Reactor模型的核心优势在于,相比于常规的阻塞I/O和多线程I/O模型,它可以在处理大量并发请求的情况下,保持较高的I/O效率和吞吐量。这是因为Reactor模型中,I/O线程和逻辑线程分离,每个线程只负责自己的部分,可以避免了线程上下文切换的开销,同时也减少了锁竞争的情况。此外,事件驱动机制和多路复用机制也提高了I/O操作的效率。
总之,Reactor模型通过将I/O操作和事件处理分离,将事件驱动和多路复用机制应用到网络编程中,从而实现了高效的网络I/O。
Reactor属于 5 种 I/O 模型中的哪一种?
Reactor是一种I/O模型,属于5种I/O模型中的一种,也被称为事件驱动模型。Reactor模型中,应用程序通过事件处理器向操作系统注册事件,然后操作系统会监视这些事件,并在事件发生时通知应用程序。这个模型由I/O处理器、事件处理器、事件源组成。
当一个事件发生时,操作系统会通知事件处理器,事件处理器会读取事件并将事件交给I/O处理器,I/O处理器负责处理该事件。Reactor模型是多路复用技术的一种实现方式,可以处理多个连接,提高系统的并发能力。
在Reactor模型中,应用程序主要分为两个部分:事件处理器和I/O处理器。事件处理器用来处理事件的注册和响应,I/O处理器用来执行I/O操作。事件处理器通常是单线程的,而I/O处理器可以是多线程的。Reactor模型通过事件驱动的方式实现了异步I/O操作,提高了系统的并发性能。
IO多路复用(IO multiplexing)模式也称作事件驱动IO,这句话正确吗
这句话基本上是正确的。IO多路复用模式确实也称为事件驱动IO,因为它利用了操作系统提供的事件通知机制,如select、poll、epoll等,使得应用程序可以在等待IO事件时不阻塞线程,并在事件就绪时及时进行处理。通过这种方式,IO多路复用模式可以有效提高应用程序的并发处理能力和性能,被广泛应用于网络编程等领域。
https://zhuanlan.zhihu.com/p/161357177
IO多路复用和阻塞IO看起来貌似没有什么区别,因为两个阶段都是阻塞的,事实上还更差一些,因为这里需要使用两个系统调用(select和recvfrom),而阻塞IO只调用了一个系统调用(recvfrom)。但是,用select的优势在于它可以同时处理多个连接。
所以,如果处理的连接数不是很高的话,使用select/poll/epoll的web server不一定比使用multi-threading + blocking IO的web server性能更好,select/epoll的优势并不是对于单个连接能处理得更快,而是在于能处理更多的连接。
关于信号驱动IO:信号驱动IO的简介
当前Go社区一些 基于Multi-Reactor模型的第三方库
字节-golang网络框架netpoll(Multi-Reactor模型)核心源码分析
I/O 事件发生之后,netpoller 是通过什么方式唤醒那些在 I/O wait 的 goroutine 的?答案是通过 runtime.netpoll
jaydenwen123: https://www.bilibili.com/video/BV12U4y167sf
(wxf,boltdb的作者,夜读讲师)
原文链接: https://dashen.tech/2020/01/30/Go高性能网络编程模型/
版权声明: 转载请注明出处.