网络编程的演进——从Apache到Nginx

Apache

Apache HTTP服务器是 Robert McCool 在1995年写成,并在1999年开始在Apache软件基金会的 框架下进行开发。由于Apache HTTP服务器是基金会最开始的一个项目也是最为有名的一个项目, 所以通常大家提到Apache这个词都是说的Apache HTTP Server。
Apache web服务器从1996年开始就是互联网上最为流行的HTTP服务器。Apache之所以这么流行 很大程度上是由于相比其他的软件项目,在Apache基金会的精心维护下他的文档十分的详尽还有 集成的支持服务。
Apache由于其可变性、高性能和广泛的支持,经常是系统管理员的首选。他可以通过一系列 的语言相关的扩展模块支持很多解释型语言的后端,而不需要连接一个独立的后端程序。
Apache软件基金会也是利用开源软件盈利的一个范本。时至今日,Apache软件基金会 已经枝繁叶茂,在基金会名下的开源项目我们耳熟能详的有:
Apache HTTP Server
Ant(Java的编译工具)
ActiveMQ(MQ集群)
Cassandra(强一致的分布式KV数据库)
CloudStack(OpenStack的劲敌)
CouchDB(KV数据库)
Flume(日志收集工具)
Hadoop、Hbase、Hive
Kafka(流式计算)
Lucene(开源搜索引擎)
Maven(Java编译&依赖管理工具)
Mesos(分布式协调)
OpenNLP(开源自然语言处理库)
OpenOffice(开源的类Office工具)
Perl(Perl语言)
Spark(分布式计算集群)
Storm(流式计算)
Structs(Java SSH框架的第二个S)
Subversion(SVN,你懂的)
Tcl(Tcl语言)
Thrift(Java网络框架)
Tomcat(大名鼎鼎的Java容器)
ZooKeeper(分布式协调集群)

完整的Apache基金会的项目列表参见:Welcome to The Apache Software Foundation!**

Nginx

2002年,一个叫Igor Sysoev的俄罗斯哥们儿(貌似俄罗斯叫Igor的人挺多的) 写出了一个叫Nginx(和Engine X谐音,取引擎之义)。 那时候有一个时代背景,当时C10K(Concurrency 10K,1万并发)问题还是困扰绝大多数 web服务器的一个难题。Nginx利用异步事件驱动的架构写成,是C10K问题的一个很好的答卷。 Nginx的第一个公开发行版是在2004年发布的,之前都是作为俄罗斯访问量第二的网站Rambler 的内部使用。
Nginx的主要优势在于“轻、快、活”:

很低的资源占用,甚至能在很多嵌入式设备上运行。

响应速度超快,几乎不会由于高并发影响响应速度。

配置灵活,广泛的模块支持。

网上关于Apache和Nginx性能比较的文章非常多,基本上有如下的定论:
Nginx在并发性能上比Apache强很多,如果是纯静态资源(图片、JS、CSS)那么Nginx是不二之选。
Apache有mod_php、在PHP类的应用场景下比Nginx部署起来简单很多。一些老的PHP项目用Apache 来配置运行非常的简单,例如Wordpress。
对于初学者来说Apache配置起来非常复杂冗长的类XML语法,甚至支持在子目录放置.htaccess 文件来配置子目录的属性。Nginx的配置文件相对简单一点。
Nginx的模块比较容易写,可以通过写C的mod实现接口性质的服务,并且拥有惊人的性能。 分支OpenResty,可以配合lua来实现很多自定义功能,兼顾扩展性和性能。

这里我们要着重讨论的是为什么Nginx在并发性能上比Apache要好很多。
想要了解这个问题,不得不先做一些铺垫,讲讲并发网络编程的一些历史:
壹 最原始
最原始的网络编程的伪代码大致是这样:

listen(port)  # 监听在接收服务的端口上 
while True:   # 一直循环 
  conn = accept()    # 接收连接 
  read_content = read(conn) # 读取连接发送过来的请求 
  response = process(conn) # 执行业务逻辑,并得到给客户端回应的内容 
  conn.write(response) # 将回应写回给连接

我们需要了解,最原始的Linux中accept、read、write调用都是 阻塞的(现在,阻塞也是这些调用的默认行为)。这就导致了以上代码只能同时 处理一个连接,所以就有了下面的方法:

贰 每个连接开一个进程

后来,大家想到了办法:

listen(port)  # 监听在接收服务的端口上 
while True:   # 一直循环 
  conn = accept()    # 接收连接 
  if fork() == 0:
    # 子进程 
    read_content = read(conn) # 读取连接发送过来的请求 
    response = process(conn) # 执行业务逻辑,并得到给客户端回应的内容 
    conn.write(response) # 将回应写回给连接

用子进程来处理连接,父进程继续等待连接进来。但这种方式有如下两个明显的缺陷:
fork()调用比较费时,需要对进程进行内存拷贝。即使现在的Linux普遍 引入了COW(Copy On Write)技术(fork的时候不做内存拷贝,只有其中一个 副本发生了write的时候才进行copy)加速了fork的效率,但fork依旧是个 比较“重”的系统调用。
较多的内存占用,也是由于上述的内存复制造成的。

叁 引入线程

得益于之前提过的Linux对于线程的引入,上面例子的开进程,被换成了开线程, 这样,上一小节说的两个缺陷都大大的被缓解了。

肆 进程/线程池

计算机领域有很多算法或者是方法都会用到一种智慧:“空间换时间”。 即用使用更多内存的方式换取更快的运行速度:事先创建出很多进程/线程 ,就像一个池子,这样虽然会浪费一部分的内存,但连接过来的时候就省去了 开启进程/线程的时间。
但这种方式会有一个比较显著的缺陷:当并发数大于进程/线程池的大小的时候 性能就会发生很大的下滑,退化成“贰”的情况。

伍 非阻塞&事件驱动

那么,是不是想要达到高性能就一定要付出高系统资源占用呢? 答案是否定的,如果我们注意观察生活中的一个细节,肯德基和麦当劳的不同 服务方式:
肯德基服务员在前台问:“先生/小姐,有什么可以帮你?”
顾客,思考一下点什么比较好:“我要,xxxxx”
服务员去后台配餐、取餐,3分钟过去了:“您的餐齐了,下一位”

麦当劳服务员在前台问:“先生/小姐,有什么可以帮你?”
顾客:“我要,xxxxx”。如果顾客思考超过5秒:“后面的顾客请先点”; 点完餐,前台服务员继续为下一位顾客点餐。后台有别的服务员完成配餐。

可以思考一下,这两种运作方式那种比较好:
在肯德基,如果遇到需要纠结半天吃什么的客户。服务员和后面的顾客 都会陷入较长时间的等候。原因就是如果最前面的客户先让后面的顾客点餐 ,他想好了还需要较长时间的等候。相比之下,麦当劳就更胜一筹。
在麦当劳,后面配餐的服务员如果发现有两个订单都要了可乐。他可以 智能地把两个订单的可乐一次性灌好,这样会大大的提高效率。各个岗位上 的服务员可以灵活的采用各种方式优化自己的工作效率。

这里,肯德基的服务方式就是古老的进程/线程池;麦当劳的服务方式 就是一个简单的非阻塞&事件驱动。
那么,非阻塞&事件驱动这么好,为什么大家没有一开始就采用这种方式呢? 原因有二:
非阻塞&事件驱动需要系统的支持,提供non-blocking版的整套 系统调用。
非阻塞&事件驱动编程难度较大,需要很高的抽象思维能力, 把整个任务拆解;采用有限状态机编程才能实现。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,602评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,442评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,878评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,306评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,330评论 5 373
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,071评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,382评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,006评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,512评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,965评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,094评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,732评论 4 323
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,283评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,286评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,512评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,536评论 2 354
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,828评论 2 345

推荐阅读更多精彩内容