第8章 高性能服务器框架
Posted 小乌龟的笔记本
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了第8章 高性能服务器框架相关的知识,希望对你有一定的参考价值。
第8章 高性能服务器框架
这一章是全书的核心,也是后续章节的总览。在这一章中,我们按照服务器程序的一般原理, 将服务器结构为如下三个主要模块:
-
I/O处理单元。本章将介绍I/O处理单元的四种I/O模型和两种高效时间处理模式。
-
逻辑单元。本章将介绍逻辑单元的两种高效并发模式,以及高效的逻辑处理方式----有限状态机。
-
存储单元。本书不讨论存储单元,因为它只是服务器程序的可选模块,而且其内容与网络编程本身无关。
最后,本章还介绍了提高服务器性能的其他建议。
8.1 服务器类型
8.1.1 C/S 模型
TCP服务器和TCP客户端的工作流程(图)
C/S模型非常适合资源相对几重的场合,并且它的实现也很简单,但其缺点也很明显: 服务器是通信的中心,当访问量过大时,可能所有客户端都将得到很慢的响应。
8.1.2 P2P模型
P2P模型使得每台机器在消耗服务的同事也给别人提供服务,这样自由能够充分、自由地共享。 P2P模型的缺点也很明显:当用户之间传输的请求过多时,网络的负载将加重。
P2P模型还存在一个显著的问题,即主机之间很难互相发现。所以实际使用的P2P模型通常带有一个专门的发现服务器。 这个发现服务器通常还提供查找服务,使每个客户都能尽快地找到自己需要的资源。
8.2 服务器编程框架
-
I/O 处理单元
-
请求队列
-
逻辑单元
-
请求队列
-
网络存储单元(可选)
I/O 处理单元是服务器管理客户连接的模块。
一个逻辑单元通常是一个进程或线程。
网络存储单元可以是数据库、缓存和文件。
请求队列是个单元之间的通信方式的抽象。
8.3 I/O 模型
-
阻塞 I/O
-
I/O 复用
-
SIGIO 信号
-
异步 I/O
8.4两种高效的事件处理模式
服务器通常要处理3类事件:I/O事件、信号及定时事件。
8.4.1 Reactor 模式
Reactor是这样一种模式,它要求主线程(I/O处理单元,下同)只负责监听文件描述上是否有事件发生, 有的话就立即将该事件通知工作线程(逻辑单元,下同)。除此之外,主线程不做任何其他实质性的工作。 读写数据,接收新的连接,以及处理客户端请求均在工作线程中完成。
8.4.2 Proactor 模式
Proactor模式将所有I/O操作都交给主线程和内核来处理,工作线程仅仅负责业务逻辑。
8.5 两种高效的并发模式
从实现上来说,并发编程主要有多进程和多线程两种方式。
并发模式是指I/O处理单元和多个逻辑单元之间协调完成任务的方法。
服务器主要有两种并发编程模式:
- 半同步/半异步(half-sync/half-async)模式
- 领导者/追随者(Leader/Followers)模式
8.5.1 半同步/半异步模式
在I/O模型中,“同步”和“异步”区分的是内核向应用程序通知的是何种I/O事件(是就绪事件还是完成事件), 以及该由谁来完成I/O读写(是应用程序还是内核)
在并发模式中,“同步”指的是程序完全按照代码序列的顺序指向;“异步”指的是程序的执行需要由系统事件来驱动。 常见的系统事件包括中断、信号等。
半同步/半异步模式中,同步线程用于处理客户逻辑,异步线程用于处理I/O事件。
8.5.2 领导者/追随者模式
领导者/追随者模式是多个工作线程轮流获得事件源集合,轮流监听、分发并处理事件的一种模式。 在任意时间点,程序都仅有一个领导者线程,它负责监听I/O事件。 而其他线程则都是追随者,它们休眠在线程池中等待成为新的领导者。 当前领导者如果检测到I/O事件,首先要从线程池中推选出新的领导者线程,然后处理I/O事件。 此时新的领导者等待新的I/O事件,而原来的领导者则处理I/O事件,二者实现了并发。
领导者/追随者模式包含如下几个组件:
- 句柄集(HandleSet)
- 线程集(ThreadSet)
- 事件处理器(EventHandler)
- 具体的事件处理器(ConcreateEventHandler)
8.6有限状态机
前面两节探讨的是服务器的I/O处理单元、请求队列和逻辑单元之间协调完成任务的各种模式, 这一节我们介绍逻辑单元内部的一种高效编程方法:有限状态机(finite state machine)
// code:8.1 STATE_MACHINE( Package _pack ) { PackageType _type = _pack.GetType(); switch( _type ) { case type_A: process_package_A( _pack ); break; case type_B: process_package_B( _pack ); break; } } // code:8.2 STATE_MACHINE() { State cur_State = type_A; while( cur_State != type_C ) { Package _pack = getNewPackage(); switch( cur_State ) { case type_A: process_package_state_A( _pack ); cur_State = type_B; break; case type_B: process_package_state_B( _pack ); cur_State = type_C; break; } } }
8.7提高服务器性能的其他建议
8.7.1池
根据不同的资源类型,池可分为很多种,常见的有内存池、进程池、线程池和连接池。
- 内存池通常用于socket的接收缓存和发送缓存
- 进程池和线程池都是并发编程常用的“伎俩”
- 连接池通常用于服务器或服务器机群的内部永久连接
8.7.2数据复制
高性能服务器应该避免不必要的数据复制,尤其是当数据复制发生在用户代码和内核之间的时候。
8.7.3上下文切换和锁
并发程序必须考虑上下文切换(context switch)的问题,即进程切换或线程切换导致的系统开销。
并发程序需要考虑的另外一个问题是共享资源的枷锁保护。锁通常被认为是导致服务器效率低下的一个因素, 因为由它引入的代码不仅不处理任何业务逻辑,而且需要访问内核资源。 如果必须使用“锁”,则可以考虑减小锁的粒度。
这里只是整理大概内容,更详细内容,请看书??《Linux高性能服务器编程》 游双 著,机械工业出版社]
以上是关于第8章 高性能服务器框架的主要内容,如果未能解决你的问题,请参考以下文章
EasySwoole+ElasticSearch打造高性能小视频服务系统