基于协程方式编写高并发、高性能的服务

一、概述

      在编写网络应用服务时,在追求高并发、高性能还是追求易编程、好维护之间一直是争论的话题,为了达到高并发、高性能的目的,于是出现了很多异步IO编程框架,诸如 JAVA 中的 Netty,C++ 中的 ASIO,C 中的 libevent,以及 acl 库中的 aio 模块。这些库底层基本都是基于操作系统所提供的 IO 事件接口(如:Windows 下的 iocp,Linux 下的 epoll,Bsd 下的 kqueue,Solaris 下的 devpoll,以及 UNIX 平台中的 poll,select),对 IO 过程进行二次封装,然后通过回调的方式调用使用者的函数过程。此类异步 IO 过程的典型应用如:nginx,redis,squid,ircd,lighttpd 等,这些异步 IO 编程都有着共同的缺点:异步回调过程是反人类的,不符合人类顺序思维模式,造成了编程复杂度高、容易出错等问题。相对于此,另一个极端是另一帮人通过提供线程池、进程池模式来满足并发的需求,使服务器模块可以同时满足多个客户端的连接请求(典型的应用如:mysql,mongodb,apache,postfix,tomcat 等),这些应用服务的最大问题只能满足有限的并发,因为它们大多采用一个连接绑定一个线程或进程,但系统的线程/进程资源毕竟是有限的,即使在当今多核的时代,CPU 资源依赖是非常宝贵的,那些天天鼓吹多核了就不必考虑高并发瓶颈的言论简直是在误导观众、不负责任。

      争论虽然一直存在,但技术总得要进步才成。acl 的服务器开发框架中提供了一种半非阻塞半阻塞的线程池模型(参考:使用 acl::master_threads 类编写多进程多线程服务器程序),可谓是一种中间态,同时具有上述二者的部分优点(在启动有限线程数的前提下能满足一定程度的高并发,同时编程方式基本是顺序方式的)。所谓部分优点,那就说还有一些优点是无法具备的。

      当两种势力互不相让的时候,Go 语言出现了,该语言的协程编程模式一下子使人们眼睛顿时一亮:程序员采用协程方式,可以很容易以顺序编程方式编写高并发、高性能的服务程序。于是人们开始变得不够淡定了,尤其是很多 C/C++ 程序员开始转投 Go 语言阵营了(比如:七牛的许式伟等人开始用 Go 开发他们的云存储)。

      一直以来,本人也曾经以自己能以非阻塞模式开发高并发、高性能的应用为荣,在看了一些有关 Go 语言的资料后,便陷入深深的沉思:是不是也该放弃多年的 c/c++ 经验,转投 Go 了?难道要用 Go 语言把 acl 库中的功能都实现一遍?这简直是不可想象的。“迎着问题上”,于是乎,开始查与协程相关的资料和开源项目,积累了一定的经验后决定自己重新设计一套基于 C 的协程库。

      在 Go 出现之前,协程其实并不是一个新鲜事物,操作系统早就提供底层的 API 支持,如 UNIX 下的 getcontext/makecontext/swapcontext 就是用来切换协程的上下文的,只是没有引人们的足够重视而已。其实 Go 语言的作者之一 Russ Cox 早在 05 年就用 C 语言实现了一个协程的模型库:libtask,里面实现了协程调度、协程锁、协程管道等功能。这些功能点在 Go 语言也能看到一些影子。在参考了众多 C/C++ 的协程库实现后,决定改造 libtask,形成一套真正独立的协程库,可方便程序员方便快捷地编写网络协程程序。

发布了125 篇原创文章 · 获赞 7 · 访问量 7万+

猜你喜欢

转载自blog.csdn.net/zsxxsz/article/details/88349458