单服务器高性能模式

高性能模式涉及磁盘,操作系统,CPU,内存,缓存,网络,编程语言和平台,架构。很具有挑战性。

高性能架构设计:

提高单服务器性能,将单机性能发挥到极致

如果单机服务无法支撑,设计集群方案

架构设计决定了性能的上限,实现细节决定了性能的下限。

并发模型:

如何管理连接

如何处理请求

并发模型设计IO模型及进程模型

IO模型:阻塞,非阻塞,异步,同步

进程模型:单进程,多进程,多线程

PPC

每次有新连接,fork()新的进程来处理连接请求。早期Unix网络模型,实现简单,适用于连接数不高的场景。

父进程接收连接

父进程“fork”子进程

子进程处理连接的读写请求

子进程关闭连接

弊端体现在:

fork代价高,需要分配很多内核资源,还要拷贝父进程的内存映像

父子进程通信复杂,需要IPC来尽心信息传递

并发数量有限最大几百, 如果连接持续时间很长,新连接不断接入,进程调度和切换越来越高,系统压力很大

Prefork

prefork采用预先fork,在启动时就预先创建好进程,然后才开始接受用户请求,省去了fork的耗时操作。多个进程,accept同一个socket,最终只有一个成功,存在“惊群”现象,即多个子进程都被唤醒去争取,但只有一个成功,增加了不必要的进程调度和上下文切换。Linux 2.6以后已经解决accept惊群问题。

prefork仍然存在父子进程通信复杂,支持并发数量有限的问题。

TPC

每个新连接都创建一个新线程去处理这个连接的请求,线程比进程轻量级,创建消耗少。多线程共享进程空间,通信更简单。

父进程接受新连接

创建子线程

子线程处理连接的读写数据

子线程关闭连接

注:TPC模式下,子线程不会复制文件描述符,TPP下会复制文件描述符

也有一些缺点:

线程创建仍然有代价

多线程共享进程内存,需要引用互斥,处理不好有死锁,并发复杂

多线程会相互影响,特殊情况会导致主进程退出,exit,内存越界。

相比TPP,TPC更简单,但同样无法TPC也无法承载太多的并发。

Prethread同Prefork,但实现灵活。海量连接都不适合TPC和TPP。

Reactor和Proactor

针对TPC和TPP用完释放提出资源复用,不为单独的连接创建单独的线程或者进程,创建一个进程池,将连接分配给其中一个进程。一个进程也可以处理多个连接。连接没数据可读是,会被阻塞到该连接上。将read改为非阻塞的,针对一个进程处理多个连接的话, 采用不断轮询,但如果连接数太多,挨个轮询很消耗CPU,并且效率很低。

IO多路复用:

多连接共用一个阻塞对象,只需要在一个阻塞对象上等待,无需轮序所有连接,epoll,poll,kqueue

某个连接有数据可以处理时,OS通知进程,进程从阻塞态返回,开始业务处理

Reactor就是IO多路复用结合线程池,事件反应,通俗理解是:来了一个事件我就有相应的反应。我就是reactor,根据不同的时间类型,调用不同的逻辑。IO多路复用统一监听事件,收到事件再Dispatch给某个进程。

Reactor模式的核心组成部分是Reactor和进程或者线程池。Ractor负责事件监听和分配,线程池负责具体的事件处理。

Reactor有以下实现方式体现在:

Reactor的数量,可以一个或者多个

资源池的数量,一个或者多个

理论上四个组合,但多Reactor,单进程或线程比单Reactor和单进程或线程无性能优势,还复杂,实际不使用。

资源池是线程还是进程根据具体语言和平台,Nginx多进程,Java多线程,C进程和线程均可。

1.单Reactor和单进程

Reactor监听事件

是新连接建立,accept处理,创建Handler处理

非连接建立,Reactor直接调用handler处理

Handler完成raead-- 业务处理--send 整个流程

模式简单,无IPC以及竞争,但无法发挥多核性能,必须等待Handler挨个处理。性能有瓶颈。

2.单Reactor和多线程

主线程Reactor通过select监听连接接事件,收到后通过dispatch分发

不是连接事件,Acceptor通过Accept接受连接,并创建Handler处理后续连接

不是连接事件,Ractor会调用对应的Handler处理

Handler只负责响应事件,不进行业务处理,read到数据后,发给processor处理

processor在独立线程中完成真正处理,Handler收到响应后把结果通过send给client

能够充分利用多核多CPU的处理能力,但多线程数据共享和访问较复杂,Ractor承担所有事件的监听和响应,瞬间高并发存在性能瓶颈。单Ractor多进程,IPC通信复杂,使用不多。

3.多Reactor多线程

mainReactor通过select监听建立事件,收到事件后通过Acceptor接受,将新的连接分配给某个线程

subReactor将mainRactor分配的连接加入连接队列,进行监听,创建Handler用于处理连接的各种事件

新事件发生,subRactor来调用对应的Handler进行相应

Handler完成read-->业务处理-->send的完成业务流程

看起来比单Ractor和多线程池复杂,单实现其实更简单:

父子进程职责明确,父进程只负责新连接建立,子进程负责后续业务处理

交互简单,父进程直接把新连接给子进程,无需返回数据

字父进程间的文件连接互相独立,无需同步共享处理

Proactor

Ractor是非阻塞同步网络模型,read和send都需要用户进程同步操作。如果将read和send同步操作改为异步的,就成了异步网络模型Proactor。

Reactor可以理解为:来了事件通知你,你来处理。Proactor可以理解为:来了事件,我处理好了告诉你。这里我是指操作系统内核,事件就是新的连接,数据可读,可写等I/O事件,你指事件的回调。

理论上Proactor模型比Reactor效率高,异步性能更高,但系统层面并没有对异步的完全支持,Boost.Asio linux下都是用epoll模拟出来的异步模型。

  • 发表于:
  • 原文链接https://kuaibao.qq.com/s/20180624G00DI400?refer=cp_1026
  • 腾讯「云+社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 yunjia_community@tencent.com 删除。

扫码关注云+社区

领取腾讯云代金券