Web并发模型入门


一,基本概念

并发(concurrency)

CPU划分时间片,轮流执行每个请求任务,时间片到期后,换到下一个。
这里写图片描述

并行(parallelism)

在多核服务器上,每个CPU内核执行一个任务,是真正的并行。
这里写图片描述

吞吐量(throughput)

单位时间内服务器总的请求处理量。
以 request/second 来衡量,如1200rps

  • 每个请求的处理时间latency
  • 服务器处理请求的并发workers
  • 其他因素如GC也会影响吞吐量

举例:
一个博客网站,平均每个请求的等待时间latency是200ms,总共40个workers,理论吞吐量上限 1000/200*40 = 200rps,理论每日处理动态请求上限1700万,目前实际每日处理动态请求270-330万,预估实际处理上限600万。

IO类型

  • 磁盘文件操作,例如读硬盘文件
  • 操作系统调用,例如shell命令
  • 网络操作
  • 访问数据库 MySQL, MongoDB, …
  • 访问其他Web服务,发起网络连接
  • 访问缓存服务器 Memcached, Redis

IO操作的延时远远高于CPU时钟周期和内存访问,所以一旦Web请求涉及IO操作,CPU处于wait状态,被浪费了。

IO密集型并发

并发真能提高吞吐量吗?
假设每个请求执行100ms,顺序执行10个请求共需要1s。
单核服务器并发处理10个请求,假设平均分配时间片10ms,请求1到请求10将在900ms到1000ms间执行完毕。吞吐量没有任何提高。并发越多,所有请求都变得非常缓慢。(考虑到任务的场景切换开销,吞吐量还会下降,需要超过1s才能执行完毕)。

大多数Web型应用都是IO密集型
例如,执行请求100ms当中,可能有80ms花在IO上,只有20ms消耗CPU时钟周期,最好情况下,请求1到请求10将在190ms到280ms间执行完毕,吞吐量极大提高。因为io线程并不需要CPU的参与,CPU可以执行其他线程,提高了执行效率。

IO密集型应用,大部分CPU花在等待IO上了,所以并发可以有效提高系统的吞吐量。

如下图所示,单核CPU情况下:
这里写图片描述
如果是顺序执行,顺序执行10个请求,每个请求100ms,总共1s执行完毕。
如果是并发执行,并发执行10个请求,每个请求分配10ms的时间片,仍然1s执行完毕。吞吐量没有提高,考虑到CPU的切换开销,每个请求处理时间变长。
这种情况就是典型的纯CPU密集型的应用,使用并发执行并不能提高效率。

同样是单核CPU情况下:
这里写图片描述
如果是顺序执行,顺序执行10个请求,每个请求100ms,总共1s执行完毕。
如果是并发执行,并发执行10个请求,每个请求分配10ms的时间片
200ms之后CPU处于空闲状态,190ms到280ms请求全部执行完毕
吞吐量得到极大提高,同样是因为io操作不需要CPU的参与,CPU可以切换处理其他请求。
这就是典型的IO密集型的应用,使用并发执行可以提高效率。

并发和并行

纯CPU密集型的应用
在单核上并发执行多个请求,不能提高吞吐量。
由于任务来回场景切换的开销,吞吐量反而会下降。
只有多核并行运算,才能有效提高吞吐量。

IO密集型的应用
由于请求过程中,很多时间都是外部IO操作,CPU在wait状态,所以并发执行可以有效提高系统吞吐量


二,Web并发模型

1,multi-process

常见多进程Web服务端编程模型

  • PHP
  • Python
  • Ruby

即大多数边解释边运行的脚本语言。

多进程并发

  • 每个进程可以并发处理1个请求,并发能力等于进程数量。
  • 由操作系统负责进程调度,程序无法控制。
  • 可以通过操作系统命令影响进程调度优先nice。

多进程调度
例如在一台4核服务器上,运行10个PHP进程。由操作系统负责给某个PHP进程分配某个CPU内核的时间片,实现10个并发处理。

多进程优点
1,并发模型非常简单,由操作系统调度运行稳定强壮。
2,非常容易管理
- 很容易通过操作系统方便的监控,例如每个进程CPU,内存变化状况, - 甚至可以观测到进程处理什么Web请求。
- 很容易通过操作系统管理进程,例如可以通过给进程发送signal,实现各种管理。
3,隔离性非常好
- 一个进程崩溃不会影响其他进程。
- 某进程出现问题的时候,只要杀掉它重启即可,不影响整体服务的可用性。
- 很容易实现在线热部署和无缝升级。
4,代码兼容性极好,不必考虑线程安全问题。
5,多进程可以有效利用多核CPU,实现并行处理。

多进程的监控手段

  • 监控进程CPU top –p pid。
  • 简单处理甚至可以查看进程处理的URL请求。
  • 监控进程的IO iotop –p pid。
  • 监控进程的物理内存使用 ps, /proc。
    这里写图片描述

多进程缺点
1,内存消耗大户
- 每个独立进程都需要加载完整的应用环境,内存消耗超大。(COW模式可以缓解这个问题)
- 例如每个Rails进程物理内存占用为150MB,20个workers,则需要3GB物理内存。
2,CPU消耗偏高
多进程并发,需要CPU内核在多个进程间频繁切换,而进程的场景切换(context switch)是非常昂贵的,需要大量的内存换页操作。
3,很低的I/O并发处理能力

多进程的低IO并发问题
1,多进程的并发能力非常有限
- 每个进程只能并发处理1个请求。
- 单台服务器启动的进程数有限,并发处理能力无法有效提高
Rails进程消耗内存很大,单台服务器一般启动30个Rails实例。
PHP FastCGI进程非常轻量级,但单台服务器一般启动64个。
2,只适合处理短请求,不适合处理长请求
- 每个请求都能在很短时间内执行完毕,因而不会造成进程被长期阻塞。
- 一旦某个操作特别是IO操作阻塞,就会造成进程阻塞,当大面积IO操作阻塞发生,服务器就无法响应了。
- 对于无法预知的外部IO操作,应用代码必须设置timeout参数,以防进程阻塞。

缓解多进程低IO并发问题
1,用nginx做前端Web Server。
2,凡IO操作都要设置timeout,,避免无法预知的IO挂起造成进程阻塞。
3,编写智能防火墙代码,,防止劣质爬虫和其他恶意的DOS攻击行为。
4,长请求和短请求分离开,不要放在一起。

PHP的轻量级进程
1,无虚拟机,无GC,简单解析器,内存管理简单。
2,每请求的内存管理模型:每个请求都需要初始化整个框架,请求执行完毕释放所有内存。
3,每个进程初始化一般10MB,远远小于Python/Ruby。

PHP的优点
1,每个进程占用内存少,可以多启动一些进程数量,并发处理能力高于Python/Ruby。
2,基本没有内存泄露的烦恼,。即使应用代码有内存泄露问题,每个请求执行完毕,所有内存对象全部释放,基本不会造成严重后果。

PHP的缺点
1,每请求的内存管理模型造成PHP性能低下
- 每个请求都需要初始化整个应用代码,造成bootstrap速度很缓慢。
- 重型PHP框架性能不可避免的低下:例如Drupal性能尤其差,必须依赖多种缓存机制缓解性能问题。
- PHP社区多采用轻量级框架缓解性能问题。
2,由于每请求都彻底释放内存,无法实现进程内跨请求共享资源
- 通用数据库连接池
- 内存字典表
- 其他昂贵的需要耗时建立的共享资源

PHP的大型应用
1,由于PHP的内存模型限制,很多大型应用在较早期就会不可避免的遇到PHP的瓶颈。
2,PHP的瓶颈无法解决,必须调整架构,因此在早期就会引入中间应用层(C++, Java),让PHP退化为View层模板语言。
案例:taobao,facebook,yahoo

2,multi-thread

常见多线程模型是Java VM / .net CLR,即在一个重量级进程当中启动多个线程并发处理请求。

多线程并发
1,每个线程可以并发处理1个请求,并发能力取决于线程数量。
2,线程的调度由VM负责,可以通过编程控制。

多线程优点
1,多线程并发内存消耗比较少
- 每个线程需要一个thread stack保存线程场景,thread stack一般只需要十几到几十KB内存,不像多进程,每个进程需要加载完整的应用环境,需要分配十几到上百MB内存。
- 线程可以共享资源,特别是可以共享整个应用环境,不必像多进程每个进程要加载应用环境。
2,多线程并发CPU消耗比较小
- 线程的场景切换开销小于进程的场景切换。
3,很容易创建和高效利用共享资源
- 数据库线程池
- 字典表,进程内缓存
4,IO并发能力很高
- Java VM可以轻松维护几百个并发线程的线程切换开销,远高于多进程单服务器上几十个并发的处理能力。
5,可有效利用多核CPU,实现并行运算。

多线程的缺点
1,VM的内存管理要求超高
- 对内存管理要求非常高,应用代码稍不注意,就会产生OOM(out of memory),需要应用代码长期和内存泄露做斗争
- GC的策略会影响多线程并发能力和系统吞吐量,需要对GC策略和调优有很好的经验
- 在大内存服务器上的物理内存利用率问题
2,对共享资源的操作
- 对共享资源的操作要非常小心,特别是修改共享资源需要加锁操作,很容易引发死锁问题
3, 应用代码和第三方库都必须是线程安全的
- 使用了非线程安全的库会造成各种潜在难以排查的问题
4,单进程多线程模型不方便通过操作系统管理
- 一旦出现线程死锁或者线程阻塞很容易导致整个VM进程挂起失去响应,隔离性很差

3,event IO

常见event IO编程模型
1,Nginx / Lighttpd
2,node.js

event IO原理
1,单进程单线程
2,内部维护一个事件队列
3,每个请求切成多个事件
- 每个IO调用切成一个事件
- 编程调用process.next_Tick()方法切分事件
4,单进程顺序从事件队列当中取出每个事件执行下去

event IO并发示意
这里写图片描述

event IO的优点
1,惊人的IO并发处理能力
- nginx单机可以处理50K以上的HTTP并发连接
- node.js单机可以处理几千上万个HTTP并发连接
2,极少的内存消耗
- 单一进程单一线程,无场景切换无需保存场景
3,CPU消耗偏低
- 无进程或者线程场景切换的开销

event IO的缺点
1,必须使用异步编程
- 异步编程是一种原始的编程方式
- 代码量和复杂度都会有很大的增加,提高了编程的难度,以及开发和维护成本
- 复杂的业务逻辑(例如工作流业务)会造成代码迅速膨胀,极难维护
- 异步事件流使得异常处理和调试有很大困难
2,CPU密集型的运算会阻塞住整个进程
- 需要通过编程,将密集型的任务拆分为多个事件
3,所有IO操作必须使用异步库
- 一旦不小心使用同步IO操作,会造成整个进程阻塞,库的兼容性必须非常小心
4,只能跑在1个CPU内核上,无法有效利用多核并行运算
- 运行多个进程来利用多核CPU


三,使用

Web并发模型对比列表
这里写图片描述

Web并发模型适用场景
这里写图片描述

Web并发应用场景

1,website
2,web api service
3,real-time

website
场景特点
1,业务逻辑比较复杂
2,功能点多,页面处理逻辑多,代码量比较大
4,IO并发要求不高,一般几十并发workers足够,
常用编程语言
1,Java / .net
2,PHP
3,Python / Ruby

web service
场景特点
1,业务逻辑比较简单,功能点较少
2,无页面逻辑,输出json/xml数据
3,IO并发要求比较高,往往要求几百上千并发
常用编程语言
1,Java / .net
2,node.js
3,Go
4,Python / Ruby

real-time
场景特点
1,业务逻辑很简单,功能单一
2,无页面逻辑,服务器维持长连接,持续push数据json/xml到客户端
3,IO并发要求极高,单机往往维持几千上万长连接
常用编程语言
1,node.js
2,Go

Web应用场景对比列表
这里写图片描述


四,参考资料

http://robbin.iteye.com/blog/1744725
http://litaotao.blog.51cto.com/6224470/1289790(关于event io部分有错误)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值