早年间, ⽀持多个⽤户并发访问的服务应⽤,往往采⽤多进程⽅式,即针对每⼀个 TCP ⽹络连接创建⼀个服务进程。在 2000 年左右,⽐较流⾏使⽤ CGI ⽅式编写 Web 服务,当时⼈们⽤的⽐较多的 Web 服务器是基于多进程模式开发的 Apache1.3.x 系列,因为进程占⽤系统资源较多,所以⼈们开始使⽤多线程⽅式编写 Web 应用服务,线程占⽤的资源更少,这使单台服务器⽀撑的⽤户并发度提⾼了,但依然存在资源浪费的问题。因为在多进程或多线程编程⽅式下,均采⽤了阻塞通信⽅式,对于慢连接请求,会使服务端的进程或线程因『等待』客户端的请求数据⽽不能做别的事情,⽩⽩浪费了操作系统的调度时间和系统资源。这种⼀对⼀的服务⽅式在⼴域⽹的环境下显示变得不够廉价,于是⼈们开始采⽤⾮阻塞⽹络编程⽅式来提升服务端网络并发度,⽐较著名的 Web 服务器 Nginx 就是⾮阻塞通信服务的典型代表,另外还有象 Java Netty 这样的⾮阻塞⽹络开发库。
⾮阻塞⽹络编程⼀直以⾼并发和⾼难度⽽著称,这种编程⽅式虽然有效的提升了服务器的利⽤率和处理能力,但却对⼴⼤程序员提出了较⼤挑战,因为⾮阻塞 IO 的编程⽅式往往会把业务逻辑分隔的⽀离破碎,需要在通信过程中记录⼤量的中间状态,⽽且还需要处理各种异常情况,最终带来的后果就是开发周期⻓、复杂度⾼,⽽且难于维护。
阻塞式⽹络编程实现容易但并发度不⾼,⾮阻塞⽹络编程并发度⾼但编写难,针对这两种⽹络编程⽅式的优缺点,⼈们提出了使⽤协程⽅式编写⽹络程序的思想。其实协程本身并不是⼀个新概念,早在2000年前Windows NT 上就出现了『纤程』的 API,号称可以创建成千上万个纤程来处理业务,在 BSD Unix 上可以⽤来实现协程切换的 API <ucontext.h> 在 2002 年就已经存在了,当然另外⽤于上下⽂跳转的 API<setjmp.h> 出现的更早(1993年)。虽然协程的概念出现的较早,但⼈们终不能发现其广泛的应⽤场景,象『longjmp』这些 API 多⽤在⼀些异常跳转上,如 Postfix(著名的邮件MTA)在处理⽹络异常时⽤其实现程序跳转。直到 Russ Cox 在 Go 语⾔中加⼊了协程(Goroutine)的功能,使⽤协程进⾏⾼并发⽹络编程才变得的简单易⾏。
Russ Cox 早在 2002 年就编写了⼀个简单的⽹络协程库 libtask(https://swtch.com/libtask/ ),代码量不多,却可以使我们⽐较清晰地看到『通过使⽹络 IO 协程化,使编写⾼并发⽹络程序变得如此简单』。