Linux高性能服务器编程学习记录——八、高性能服务器程序框架

1、服务器模型

目前主要是C/S(client/server)模型和P2P(peer2peer)模型,不多说。

2、服务器编程框架

服务器的基本框架如下图:
Linux高性能服务器编程学习记录——八、高性能服务器程序框架_第1张图片
I/O处理单元一般作为接入服务器,负责处理与客户端的连接,读写网络数据,实现负载均衡
逻辑单元,顾名思义,就是处理业务的模块
网络存储单元一般指数据库
请求队列是各模块之间的通信方式,如果是服务器机群的话,一般是TCP永久连接

3、I/O模型

辛辛苦苦工作了一天,回到家发现女友在打游戏,而没给你做饭,你很生(无)气(语),呵(央)斥(求)她去给你做饭,我们用接下来的几种情况来说明几种I/O模型:

(1)阻塞I/O

你喜欢看你女朋友做饭的样子,她在厨房烧饭,你啥都不干,就在门口看着她,直到她做好了。这叫阻塞I/O(从你发出指令然后一直等在门口啥都不干),
针对阻塞I/O执行的系统调用可能因为无法立即完成而别系统挂起,直到等待的事件发生为止。

(2)非阻塞I/O

让你女朋友去做饭之后,你赶紧抓紧时间坐沙发上玩起了游戏,但是你不知道饭啥时候能做好,所以你得时不时的喊一声“熟了没,老子饿了”。这种是非阻塞I/O。非阻塞I/O执行的系统调用总是立即返回,而不管事件是否已经发生,如果事件没有立即发生,这些系统调用就返回-1,和出错情况一样,此时必须根据errno来区分两种情况。

(3)同步I/O与异步I/O

饭做好后由你女朋友端到厨房门口交给你,你再端到餐桌上。这是同步。将厨房看作内核空间,将餐厅看作用户空间,饭看作数据,数据就绪后,由用户程序将数据从内核空间拷贝进用户空间称为同步。
饭做好后由你女朋友直接将饭端到餐桌上(前提是你再告诉她要做饭的时候得告诉她餐桌在哪儿)再通知你,这是异步,由内核负责将数据拷贝到用户指定区域。
可以看到阻塞非阻塞说的是程序的调用方式,同步异步指就绪的数据由谁来负责拷贝。

(4)I/O复用

I/O复用是最常使用的I/O通知机制。它指的是,应用程序通过I/O复用函数向内核注册一组事件,讷河通过I/O复用函数把其中就绪的事件通知给应用程序。Linux上常用的I/O复用函数是select、poll和epoll_wait。I/O复用函数本身是阻塞的,它们能提高程序效率的原因在于它们具有同时监听多个I/O事件的能力。

4、两种高效的事件处理模式

(1)Reactor模式

Reactor模式要求主线程(I/O处理单元,下同)只负责监听文件描述符上是否有事件发生,有的话就立即将该事件通知工作线程(逻辑单元,下同)。除此之外,主线程不做任何其他实质性的工作。读写数据,接受新的连接,以及处理客户端请求均在工作线程中完成。

(2)Proactor模式

Proactor模式将所有I/O操作都交给主线程和内核来处理,工作线程仅仅负责业务逻辑。

(3)模拟Proactor模式

可以使用同步I/O方式模拟出Proactor模式的一种方法。由上面得知Reactor模式中,读写I/O数据是由工作线程完成的,而Proactor中由内核完成,即工作线程做处理,因此从工作线程的角度考虑如果把读写I/O数据交给主线程的话,就达到了以同步方式模拟Proactor模式的效果。

5、两种高效的并发模式

(1)、半同步/半异步模式

这里的同步、异步与I/O模型中的是完全不同的概念。并发模式中的同步指的是程序完全按照代码序列的顺序执行,异步指的是程序的执行需要由系统事件来驱动,常见的系统事件包括中断、信号等。
半同步/半异步模式中,同步线程用于处理客户逻辑,异步线程用于处理I/O事件。异步线程监听到客户请求后就将其封装成请求对象并插入请求队列中,请求队列将通知工作在同步模式的工作线程来读取并处理改请求对象。
下面两张图是两种半同步/半异步模式,第一种称为半同步/半反应堆模式,异步线程只有几个,即主线程,工作线程只负责从请求队列种获取连接socket。这种模式的缺点是主线程和工作线程共享请求队列,所以在主线程往队列写数据或工作线程从队列取数据时都需要加锁,从而浪费CPU时间,另外,每个工作线程在同一时间只能处理一个客户请求。
第二种高效的半同步/半异步模式中,主线程监听到新的连接socket后就将他派发给工作线程,由工作线程负责这个连接上的I/O事件,即由工作线程自己调用epoll_wait进行处理,这样所有的线程都工作在异步模式。

(2)、领导者/追随者模式

对比上面的半同步/半异步模式,监听新连接的一直是同一个线程(主线程),它在监听到新的连接后要么自己处理I/O事件,将数据放入请求队列由工作线程去处理,要么直接将新连接丢给工作线程,让它们自己去处理剩下的事情,自己继续负责监听新连接。领导者/追随者模式也是永远只有一个线程(领导者线程)在监听新连接的到来,不同的是,它在监听到新的连接后,立即从线程集中推选出一个新的领导者来负责监听新连接,自己去处理刚刚监听到的那个连接,这样在处理I/O事件的同时又可以监听新连接,也实现了并发。

6、有限状态机

有限状态机是一种高效的编程方法。下面是书中代码清单8-3:HTTP请求的读取和分析的源码

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#define BUFFER_SIZE 4096

 typedef enum CHECK_STATE
{
   
	//主状态机的两种可能状态
	//当前正在分析请求行
	CHECK_STATE_REQUESTLINE = 0,
	//当前正在分析请求头部
	CHECK_STATE_HEADER
 } CHECK_STATE;

//从状态机状态
typedef enum LINE_STATUS
{
   
	LINE_OK = 0,		//读取到一个完整的行
	LINE_BAD,			//行出错
	LINE_OPEN			//行数据不完整
} LINE_STATUS;

//处理HTTP请求结果
typedef enum HTTP_CODE
{
   
	NO_REQUEST,					//数据不完整,需要继续读取数据
	GET_REQUEST,					//获得了一个完整的客户请求
	BAD_REQUEST,					//请求中有语法错误
	FORBIDDEN_REQUEST,			//无访问权限
	INTERNAL_ERROR,				//服务器内部错误
	CLOSED_CONNECTION,			//连接已断开
} HTTP_CODE;

//给客户端的回执
static const char* szret[] = {
    "Get a correct result\n", "Something wrong\n" };

//从状态机,用于解析出一行内容
LINE_STATUS parse_line(char* buffer, 

你可能感兴趣的:(c/c++,TCP-IP,linux,服务器,linux,网络)