系统服务 - 技术专题 - 并发模型粗浅分析探讨

基本概念
并发 concurrency
并行 parallelism
吞吐量 throughput
并发请求

并行请求

吞吐量
单位时间内服务器总的请求处理量
以 request/second 来衡量,如 1200rps
每个请求的处理时间 latency(延迟时间)
服务器处理请求的并发 workers
其他因素如 GC 也会影响吞吐量
举例: CSDN new bbs
平均每个请求的 latency = 200ms
总共 40 个 workers
理论吞吐量上限 (1000/200)*40 = 200rps
理论每日处理动态请求上限 1700 万,目前实际每日处理动态请求 270-330 万,预估实际处理上限 600 万
IO 类型
磁盘文件操作,例如读硬盘文件
操作系统调用,例如 shell 命令
网络操作
访问数据库 MySQL, MongoDB, ...
访问其他 Web 服务,发起网络连接
访问缓存服务器 Memcached, Redis
典型 IO 密集请求

IO 密集型并发
并发真能提高吞吐量吗?
场景
假设每个请求执行 100ms,顺序执行 10 个请求共需要 1s,单核服务器并发处理 10 个请求,假设平均分
配时间片 10ms,请求 1 到请求 10 将在 900ms 到 1000ms 间执行完毕。吞吐量没有任何提高。
计算密集型
计算密集型的场景下,并发越多,所有请求都变得非常缓慢。(考虑到任务的场景切换开销,吞吐量还会下降,需要超过 1s 才能执行完毕)。
大多数 Web 型应用都是 IO 密集型执行请求 100ms 当中,可能有 80ms 花在 IO 上,只有 20ms 消耗 CPU 时钟周期,最好情况下,请求 1 到请求 10 将在 190ms 到 280ms 间执行完毕,吞吐量极大提高。
IO 密集型
IO 密集型应用,大部分 CPU 花在等待 IO 上了,所以并发可以有效提高系统的吞吐量


并发和并行
纯 CPU 密集型的应用
在单核上并发执行多个请求,不能提高吞吐量
由于任务来回场景切换的开销,吞吐量反而会下降
只有多核并行运算,才能有效提高吞吐量
IO 密集型的应用
由于请求过程中,很多时间都是外部 IO 操作,CPU 在 wait 状态,所以并发执行可以有效提高系统吞吐量
Web 并发模型
multi-process
multi-thread
multi-process + multi-thread(GIL)
event I/O(相应时间)
Coroutine(协程)
multi-process
常见多进程 Web 服务端编程模型
PHP
Python
Ruby
多进程的管理
多进程并发
每个进程可以并发处理 1 个请求,并发能力等于进程数量
由操作系统负责进程调度,程序无法控制
可以通过操作系统命令影响进程调度优先 nice
多进程调度
例如在一台 4 核服务器上,运行 10 个 PHP 进程。由操作系统负责给某个 PHP 进程分配某个 CPU 内核的时间片,实现 10 个并发处理
多进程优点
并发模型非常简单,由操作系统调度运行稳定强壮
非常容易管理
很容易通过操作系统方便的监控,例如每个进程 CPU,内存
变化状况,甚至可以观测到进程处理什么 Web 请求
很容易通过操作系统管理进程,例如可以通过给进程发送
signal,实现各种管理: unicorn
隔离性非常好
一个进程崩溃不会影响其他进程
某进程出现问题的时候,只要杀掉它重启即可,不影响整体服务的可用性
很容易实现在线热部署和无缝升级
代码兼容性极好,不必考虑线程安全问题
多进程可以有效利用多核 CPU,实现并行处理
多进程的监控手段

多进程缺点
内存消耗大户
每个独立进程都需要加载完整的应用环境,内存消耗超大。(COW 模式可以缓解这个问题)
例如每个 Rails 进程物理内存占用为 150MB,20 个 workers,则需要 3GB 物理内存。
CPU 消耗偏高
多进程并发,需要 CPU 内核在多个进程间频繁切换,而进程的场景切换(context switch)是非常昂贵的,需要大量的内存换页操作。
很低的 I/O 并发处理能力
多进程的低 IO 并发问题
多进程的并发能力非常有限
每个进程只能并发处理 1 个请求
单台服务器启动的进程数有限,并发处理能力无法有效提高
Rails 进程消耗内存很大,单台服务器一般启动 30 个 Rails 实例
PHP FastCGI 进程非常轻量级,但单台服务器一般启动 64 个
只适合处理短请求,不适合处理长请求
每个请求都能在很短时间内执行完毕,因而不会造成进程被长期阻塞
一旦某个操作特别是 IO 操作阻塞,就会造成进程阻塞,当大面积 IO 操作阻塞发生,服务器就无法响应了
对于无法预知的外部 IO 操作,应用代码必须设置 timeout 参数,以防进程阻塞
缓解多进程低 IO 并发问题
用 nginx 做前端 Web Server
适当增大 proxy buffer size,避免多进程 request/response buffer IO 开销
使用 X-sendfile,避免多进程读取大文件 IO 开销
凡 IO 操作都要设置 timeout
避免无法预知的 IO 挂起造成进程阻塞
编写智能防火墙代码
防止劣质爬虫和其他恶意的 DOS 攻击行为
长请求和短请求分离开,不要放在一起
评论