多进程模型(PPC)与多线程模型(TPC)的性能优化 | 关于服务器高性能优化的思考


基础知识

线程与进程

  • 概念
    线程:程序执行流的最小单元,系统独立调度和分配CPU(独立运行)的基本单位。
    进程:资源分配的基本单位,至少包含一个线程。
  • 区别
    线程与资源分配无关,它属于某一个进程,并与进程内的其他线程一起共享进程资源。
    每个进程都有自己一套独立的资源(数据),供其内部的所有线程共享。
    不论大小,开销线程都要更“轻量级”。
    同一个进程内的线程通信,要比进程间的通信更快速、高效。

单服务器高性能

单服务器高性能的关键之一,就是服务器采取的并发模型,并发模型有如下两个设计关键点:

  • 服务器如何管理连接
  • 服务器如何处理请求

以上两个设计点,最终都和操作系统的I/O模型及进程模型有关。

  • I/O模型:阻塞、非阻塞、同步、异步
  • 进程模型:单进程、多进程、多线程

PPC模型

PPC 是 Process Per Connection 的缩写,又称做Apache模型,是通过多进程来实现业务并发。具体操作就是每次有新的连接,就去创建一个进程来处理这个连接的请求,这也是传统的UNIX服务器所采取的模型。
基本的流程图是:
PPC模型

  1. 父进程接受连接(图中 accept)
  2. 父进程‘fork’子进程
  3. 子进程处理连接的读写请求(图中子进程 read、业务处理、write)
  4. 子进程关闭连接(图中的子进程close)

PPC 模型实现简单,但是每次连接都需要创建进程,开销较大,适合服务器的连接数没那么多的情况。
当服务器的并发量较大时,PPC模型的弊端主要体现在如下几个方面:

  1. fork代价高:站在操作系统的角度,创建一个进程的成本是很高的,需要单独分配内核资源,同时需要将内存映像从父进程复制到子进程。
  2. 父子进程间通信复杂:父进程‘fork’子进程时,文件描述符可以通过内存映像复制从父进程传到子进程,但fork完成后,父子进程间通信就比较麻烦了,需要采用IPC(Interprocess Communication)之类的进程通信方案。
  3. 支持的并发连接数量有限:如果每个连接存活的时间比较长,而新的连接又源源不断,则同时开启的进程会越来越多,操作系统进程调度和切换的频率会越来越高,系统的压力会越来越大。所以PPC模型一般可以处理的最大并发连接数也就几百左右。

prefork模式

在PPC模型中,当连接进来时才去fork子进程来处理连接请求,由于fork时成本比较高,速度比较慢,所以用户访问时可能会比较卡顿,prefork模式的出现,就是为了解决这个问题。
prefork 模式是指在系统启动时就预先创建好进程,然后再接收用户请求,当有新的连接进来时,省去fork过程,提升用户访问速度。
prefork模式基本示意图:
prefork模式
prefork模式的实现关键是多个进程都 accept 同一个 socket ,当有新的连接进入时,操作系统保证只有一个进程最后 accept 成功。
prefork 模式和 PPC 模式一样,存在父子进程间通信复杂、支持的并发数量少等问题,因此目前的实际应用并不多。Apache服务器提供了MPM prefork模式,可以在需要可靠性或者旧软件兼容的站点上使用这种模式,默认最大支持256个并发连接。

TPC模型

TPC 是 Thread Per Connection 的缩写,是一种多线程并发处理模型。具体操作就是每次有新的连接就新建一个线程去专门处理这个连接的请求。与进程相比,线程要更轻量级,创建线程消耗的资源要少的多,而且线程间共用进程的存储空间,通信比较简单。因此,TPC实际上是弱化了PPC fork 代价高的问题和父子进程间通信复杂的问题。
TPC的基本操作流程:
TPC模型

  1. 父进程接收连接(图中的accept)
  2. 父进程创建子线程(图中的pthread)
  3. 子线程处理请求(图中子线程read、业务处理、write)
  4. 子线程关闭连接(图中子线程中的close)

注意,和PPC相比,TPC中的主进程不用再close连接了。原因是子线程是共享主进程的进程空间的,连接的文件描述符并没有被复制,因此只需要close一次就可以了。
TPC虽然解决了 fork 代价高和子进程间通信复杂的问题,但也引入了新的问题,具体表现在:

  1. 创建线程虽然比进程代价低,但并不是没有代价,在高并发时(例如每秒上万连接),还是有性能问题。
  2. 无需进程间通信,但是线程间的互斥和共享又引入了复杂度,可能一不小心又导致了死锁问题。
  3. 多线程会出现互相影响的情况,某个线程出现异常时,可能会导致整个进程退出(例如内存越界)。

除了引入了新的问题,TPC还是存在CPU线程调度和切换代价的问题。因此,TPC方案和PPC方案本质上基本类似,在并发几百连接的场景下,反而更多的采用PPC方式,因为PPC方案不会有死锁的风险,也不会多进程相互影响,稳定性更高。

prethread模式

TPC模型中,当连接进来时才创建线程来处理连接请求,虽然创建线程比创建进程更轻量级,但还是有一定的代价, prethread 模式就是为了解决这一问题。
和prefork模式类似,prethread模式会预先创建线程,然后再接收用户请求,当有新的用户请求进来时,就可以省掉创建线程的操作,响应速度更快、用户体验更好。
常见的prethread实现方式:

  1. 主进程 accept,然后将连接交给某个线程处理
  2. 子线程都尝试去 accept ,最后只有一个线程可以 accept 成功。

示意图:
prethread模式
Apache服务器的MPM worker模式本质上就是一种prethread方案,但稍微做了改进。Apache服务器会首先创建多个进程,每个进程里面再创建多个线程,这么做主要是为了保证稳定性,即使某个子进程里面的某个线程异常导致整个子进程退出,还会有其他子进程继续提供服务,不会导致整个服务器全部挂掉。
prethread 理论上可以比 prefork 支持更多的并发连接,Apache 服务器 MPM worker 模式默认支持 16 × 25 = 400 个并发处理线程。

参考链接

本文发表于2018年09月27日 11:23
阅读 6672 讨论 1 喜欢 4

抢先体验

扫码体验
趣味小程序
文字表情生成器

闪念胶囊

你要过得好哇,这样我才能恨你啊,你要是过得不好,我都不知道该恨你还是拥抱你啊。

直抵黄龙府,与诸君痛饮尔。

那时陪伴我的人啊,你们如今在何方。

不出意外的话,我们再也不会见了,祝你前程似锦。

这世界真好,吃野东西也要留出这条命来看看

快捷链接
网站地图
提交友链
Copyright © 2016 - 2021 Cion.
All Rights Reserved.
京ICP备2021004668号-1