存储系统02——Libevent
多路复用(Multiplexing)与多路分解(Demultiplexing)
多路复用(Multiplexing):将多个流合并到一个共享资源(某个线程)中,以便节省系统资源;
多路分解(Demultiplexing):将合并后的流分离成多个原始流,便于后续同步地、有序地处理同时接收的多个请求;
I/O多路复用(I/O Multiplexing)
Linux系统将磁盘、网络等均描述为文件(Everything is File),所以任何形式的I/O都可以称为文件IO;
同时把可以进行I/O操作的内核对象(文件描述符Fd、网络套接字Socket)称为I/O流;
阻塞I/O
当有多个进程尝试进行对同一个文件进行I/O操作时,在阻塞I/O方法中,由于每个请求都会阻塞进程/线程,因此需要为每个请求分配独立的进程或线程来处理。在高并发场景下,这种模型会消耗大量系统资源(如内存和上下文切换开销),导致性能瓶颈。
I/O多路复用
多路:多个I/O请求;
复用:通过一个线程监听多个I/O流的就绪状态,而不需要每个I/O流创建单独的线程,从而节省系统资源,多路复用主要有三种:select、poll、epoll;
I/O多路复用本质上还是一种同步I/O,即程序自己执行自己的读写操作,这个过程是阻塞的;而异步I/O则无需自己负责进行读写,进程发起I/O操作后不会被阻塞,内核会负责完成整个 I/O 操作并在完成后通知进程。
Reactor反应堆模式
为了实现事件驱动中将一个或多个客户的服务请求分离(Demultiplexing)和调度(dispatch)给程序,通常采取Reactor反应堆模式;
Reactor反应堆模式是一种用于处理并发 I/O 操作的设计模式,广泛应用于高性能服务器和网络编程中;它通过一个事件循环(Event Loop)和一组事件处理器(Handlers)来高效地处理多个并发事件;Reactor 模式的核心思想是将事件的监听分发和处理分离,从而实现高效的并发处理。
工作过程为:一个负责监听和分发事件的事件循环(Event Loop)持续检查事件源(如文件描述符)的状态变化,使用同步事件分离器(Event Demultiplexer)将检测到的事件分发给对应的事件处理器(Event_handler),事件处理器执行完成后,控制权返回到事件循环,继续监听新的事件,重复上述过程。
Reactor优势:
- 通过事件循环和事件处理器的分离,Reactor 模式能够高效地处理多个并发事件,避免了传统阻塞 I/O 模式下的线程阻塞问题;
- 事件循环通常运行在单个线程中,减少了线程上下文切换的开销;
- 可以通过增加事件处理器来处理更多类型的事件,而无需修改事件循环的逻辑。
Libevent库
Libevent 是一个用 C 语言编写的开源高性能事件通知库,主要设计用于处理网络 I/O 事件、定时事件和信号事件;基于事件循环(Event Loop),当事件(如文件描述符可读、可写、超时等)发生时,自动调用注册的回调函数。
Libevent事件实例(HTTP 服务)
使用 libevent 需要分配一个event_base结构体,每个event_base结构体持有一个事件集合,可以检测有哪些事件发生;event_base可以指定一个I/O多路复用方法(以epoll为例)
- 使用event_base_new()函数分配并且返回一个新的具有默认设置的event_base结构体;使用完event_base之后,使用 event_base_free()进行释放。
event_base *base = event_base_new(); // 实例化一个 event_base
.......
if (base)
event_base_free(base); // 释放指定 event_base
- 使用 event_new() 接口注册事件,HTTP服务器可以使用evhttp_new()接口注册evhttp上下文,返回一个被用于监测的文件httpd;使用evhttp_free(httpd)释放事件。
evhttp *httpd = evhttp_new(base); // 创建evhttp上下文
.......
if (httpd)
evhttp_free(httpd); // 释放指定 evhttp
- 创建事件完成后,使用evhttp_set_gencb()接口向事件设定回调函数GenHandler,该回调函数通过读取evhttp中不同的URL请求(Download、Upload、ListShow)并执行对应的操作。
evhttp_set_gencb(httpd, GenHandler, NULL); // 设定回调函数
- 当event_base注册了一些事件之后,就需要让 libevent 等待事件并且通知事件的发生;执行循环时,函数重复地检查是否有任何已经注册的事件被触发
event_base_dispatch(base); // 启动事件循环
以上就完成了基于libevent的一个http服务,完整代码如下:
// 初始化和启动一个基于 libevent 和 evhttp 的 HTTP 服务
bool RunModule()
{
// 初始化环境
event_base *base = event_base_new();
// 设置监听的端口和地址
sockaddr_in sin;
memset(&sin, 0, sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_port = htons(server_port_);
// http 服务器,创建evhttp上下文
evhttp *httpd = evhttp_new(base);
// 绑定端口和ip
evhttp_bind_socket(httpd, "0.0.0.0", server_port_) // 将 HTTP 服务器绑定到指定的 IP 地址和端口
// 设定回调函数
evhttp_set_gencb(httpd, GenHandler, NULL);
// 使用 event_base_dispatch 启动事件循环,开始处理事件
event_base_dispatch(base)
// 释放释放事件
if (base) event_base_free(base);
if (httpd) evhttp_free(httpd);
return true;
}