Web APP编程模型和IO策略

现代大型高性能网站诸如淘宝,京东,微博,FB,知乎等等,网站架构涉及很多知识。像业务分层,软件分割模块化,分布式部署,集群服务器,负载均衡等技术可以帮助架构师将一个大的复杂的问题切分成小的简单的问题。这篇文章着眼于解决这些切好的小问题上,单机上有哪些编程实践或者模型可以很好的做到高并发。本人web开发小白一枚,写文章是想梳理自己的思路,求得大牛斧正,希望各位多多批判。文章的内容大多来自网上的阅读加上些自己的理解,文末附上参考阅读的文章。

一个极简高并发模型

因为有数年的嵌入式领域的经验,先说一下我认为的比较高效的处理模型。

  • 硬件环境:单机30core, 1G Hz。
  • 软件环境:6Wind fastpath,每个core上都是run-to-complete的endless loop.没有操作系统。
  • 功能:一个超级简单的reverse proxy,具有load balance的简单功能。

衡量并发性能,我们看一下一个IP包从网口缓冲区收上来处理到发出去大约需要多长时间呢?

收+处理+发大概是500+1000+500=2000 cycles,时间也就是2us。单机1s内可以支持30*(1s/2us)=15,000,000 request/s的并发。屌炸天的并发能力了吧!原因有两个:

  • 没有操作系统overhead。
  • 包处理简单,IP层的处理,直接c函数调用,总共1000 cycle。

当然这是从嵌入式得来的经验,web开发中不可能这样,没有Nginx,没有web框架,没有lib没有各种open source,甚至没有linux。回到原始社会造出飞机大炮来,这不把web开发者逼疯了。软件也是一个社会化协作的过程,os,framework,lib,opensource给开发者带来极大方便的同时,也伴随着性能的开销。如何在性能和可扩展性、维护性等其他指标找到一个平衡点,如何选择合适的编程模型,合适的第三方模块达到最小的overhead,这是成长为高手的开发者都会不断思考的问题。

High Performance architecture,这篇文章总结了四个性能杀手:

  • 数据复制
  • 上下文切换
  • 动态内存分配
  • 锁竞争

上面的编程模型之所以高效,就是将CPU用到极致,尽量避免这4种情况发生。心中有这么一个极简的高效模型,后面学习其他模式的时候可以暗做对比看一下到底会有哪些额外的开销。

常用的server端linux高并发编程模型

Nginx Vs Apache

大名鼎鼎的Nginx使用了多进程模型,主进程启动时初始化,bind,监听一组sockets,然后fork一堆child processes(workers),workers共享socket descriptor。workers竞争accept_mutex,获胜的worker通过IO multiplex(select/poll/epoll/kqueue/…)来处理成千上万的并发请求。为了获得高性能,Nginx还大量使用了异步,事件驱动,non-blocking IO等技术。”What resulted is a modular, event-driven, asynchronous, single-threaded, non-blocking architecture which became the foundation of nginx code.”

Nginx 架构

对比着看一下Apache的两种常用运行模式,详见 Apache Modules

  • 1. Apache MPM prefork模式

主进程通过进程池维护一定数量(可配置)的worker进程,每个worker进程负责一个connection。worker进程之间通过竞争mpm-accept mutex实现并发和链接处理隔离。 由于进程内存开销和切换开销,该模式相对来说是比较低效的并发。

  • 2. Apache MPM worker模式

由于进程开销较大,MPM worker模式做了改进,处理每个connection的实体改为thread。主进程启动可配数量的子进程,每个进程启动可配数量的server threads和listen thread。listen threads通过竞争mpm-accept mutex获取到新进的connection request通过queue传递给自己进程所在的server threads处理。由于调度的实体变成了开销较小的thread,worker模式相对prefork具有更好的并发性能。

小结两种webserver,可以发现Nginx使用了更高效的编程模型,worker进程一般跟CPU的core数量相当,每个worker驻留在一个core上,合理编程可以做到最小程度的进程切换,而且内存的使用也比较经济,基本上没有浪费在进程状态的存储上。而Apache的模式是每个connection对应一个进程/线程,进程/线程间的切换开销,大量进程/线程的内存开销,cache miss的概率增大,都限制了系统所能支持的并发数。

IO策略

由于IO的处理速度要远远低于CPU的速度,运行在CPU上的程序不得不考虑IO在准备暑假的过程中该干点什么,让出CPU给别人还是自己去干点别的有意义的事情,这就涉及到了采用什么样的IO策略。一般IO策略的选用跟进程线程编程模型要同时考虑,两者是有联系的。

同步阻塞IO

同步阻塞IO是比较常见的IO模型,网络编程中如果创建的socket的描述符属性设置为阻塞的,当socket对应的用户空间缓冲区内尚无可读数据时,该进程/线程在系统调用read/recv socket时,会将自己挂起阻塞等待socket ready。

同步非阻塞IO和非阻塞IO同步复用

同步非阻塞IO

非阻塞IO同步复用

对比着同步阻塞IO,如果socket数据没有ready,系统调用read/recv会直接返回,进程可以继续执行不会挂起让出CPU。当然这样做对单个socket来说没有多大的意义,如果要支持大量socket的并发就很有用了,也就是IO复用。select/poll/epoll就是这样的应用,IO的read是非阻塞式调用,select是阻塞式的,同步发生在select上。程序通过select调用同时监控一组sockets,任何一个socket发生注册过的事件时,select由阻塞变为ready,函数调用返回后程序可以读取IO了。前面提到的Nginx(使用epoll)和apache(使用select)都有使用这一IO策略。select/epoll这种IO策略还有另外一个名字叫Reactor,具体他们之间的细节区别再另开一文。

异步非阻塞IO

对比同步非阻塞IO,异步非阻塞IO也有个名字—Proactor。这种策略是真正的异步,使用注册callback/hook函数来实现异步。程序注册自己感兴趣的socket 事件时,同时将处理各种事件的handler也就是对应的函数也注册给内核,不会有任何阻塞式调用。事件发生后内核之间调用对应的handler完成处理。这里暂且理解为内核做了event的调度和handler调用,具体到底是异步IO库如何做的,如何跟内核通信的,后续继续研究。

原文链接:https://segmentfault.com/a/1190000004547892

原文发布于微信公众号 - 马哥Linux运维(magedu-Linux)

原文发表时间:2016-07-11

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

3种提升云可扩展性的方法

部署在亚马逊的云服务器中被认为是实现高可扩展性的好方法,同时只需要为您所使用的计算能力支付费用。不过您要如何从技术中获得最佳的可扩展性呢?

25010
来自专栏Aloys的开发之路

Cache模拟器(CacheSim)

最近写了一个Cache的模拟器,由于平时空余时间比较分散,前前后后用了一周多的时间,基本实现的Cache的模拟功能(通过读取trace文件得到相应的命中率),能...

2757
来自专栏服务端技术杂谈

乐视支付架构

架构 乐视 订单架构 分库分表 构建一个支撑每秒十万只读系统并不复杂,无非是通过一致性哈希扩展缓存节点,水平扩展web服务器等。每秒钟数十万数据更新操作,在任何...

3678
来自专栏linux运维学习

linux学习第五十八篇: 负载均衡集群介绍,LVS介绍,LVS的调度算法,LVS NAT模式搭建

负载均衡集群介绍 主流开源软件LVS、keepalived、haproxy、nginx等 其中LVS属于4层(网络OSI 7层模型),nginx属于7层,hap...

2299
来自专栏企鹅号快讯

如何将数据库检索的结果导出?

最近很多同学询问不同的数据库的文献如何导出……老师表示很是不解,这是个很简单的小问题,上课时候也讲过,演示过,可是却是提问频率最高的问题之一。于是,今天就来大家...

3925
来自专栏北京马哥教育

马哥金牌分享 | 十分钟学会用Django快速搭建一个blog

本文是由马哥教育金牌讲师小智的文字分享《如何快速搭建一个博客》整理而来。 ---- 1.django简介 Django是一个开放源代码的Web应用框架,由Py...

3094
来自专栏数据和云

【推荐】 RAC 性能优化全攻略与经典案例剖析

ORACLE RAC凭借其卓越的容错能力和可扩展性以及对应用透明的切换能力引领了数据库高可用架构的潮流,但在实际的生产环境中,出现的性能问题非常多,对数据库的稳...

3257
来自专栏向治洪

JavaScript 的时间消耗

随着我们的网站越来越依赖 JavaScript, 我们有时会(无意)用一些不易追踪的方式来传输一些(耗时的)东西. 在这篇文章中, 我会介绍一些能让你的网站在移...

2207
来自专栏张善友的专栏

WCF服务上应用protobuf

protobuf是google提供的一个开源序列化框架,类似于XML,JSON这样的数据表示语言,其最大的特点是基于二进制,因此比传统的XML表示高效短小得多。...

2206
来自专栏linux驱动个人学习

dm-verity

一、Device Mapper: dm-verity是内核子系统的Device Mapper中的一个子模块,所以在介绍dm-verity之前先要介绍一下Devi...

4266

扫码关注云+社区

领取腾讯云代金券