介绍
libevent是一个用C语言编写的、轻量级的开源高性能事件通知库,由Niels Provos
和Nick Mathewson
开发。它提供了一种机制来执行事件通知,允许程序在单个线程中高效地处理多个事件源,包括IO事件、定时事件和信号事件,使得开发者能够构建出响应迅速且易于扩展的网络应用程序。特别是在需要处理大量并发连接的场景中。它支持多种平台,包括Linux、Unix和Windows,并且提供了跨平台的事件处理机制。
Github:https://github.com/libevent/libevent
核心概念
- 事件循环(Event Loop)。事件循环是libevent的核心,它不断地等待和分发事件。开发者需要创建一个事件循环,并在其中注册感兴趣的事件和相应的回调函数。当这些事件发生时,libevent会调用相应的回调函数来处理事件。
- 事件(Event)。在libevent中,事件是一个抽象的概念,代表了任何可以触发回调函数的情形。事件可以是网络IO(如可读、可写)、定时器超时或信号到达等。
- 事件基础结构(Event Base)。事件基础结构是管理事件循环和相关资源的核心结构。它负责事件的注册、分发和清理。每个event_base结构都维护着自己的事件循环和事件队列。
特性
- 跨平台:Libevent 支持多种操作系统,包括 Windows、Linux、BSD 和 macOS,提供了统一的接口来处理不同平台下的事件通知机制。
- 高性能:Libevent 基于事件驱动(event-driven)模型,采用非阻塞 I/O,对事件进行异步处理,能够高效地处理大量并发连接。
- 轻量级:专注于网络,与一些其他网络库相比,Libevent 的代码相对精炼、易读,且专注于网络功能,避免了不必要的复杂性。
- 支持多种
I/O
多路复用技术:支持select、poll、epoll、kqueue、dev/poll
等多种 I/O 多路复用技术,可以根据不同的操作系统和需求选择合适的机制。 - 支持多种事件类型:包括网络 I/O、定时器和信号等事件,用户可以注册这些事件并指定相应的回调函数来处理。
安装与编译
libevent 的安装和编译相对简单,可以通过源码编译或使用包管理器(如:apt-get
、yum
等)进行安装。对于源码编译,通常需要先下载 Libevent 的源码包,然后解压、配置、编译和安装。
查看是否已经安装
代码语言:javascript复制ls -al /usr/lib | grep libevent
下载 libevent-2.1.12-stable.tar.gz
wget https://github.com/libevent/libevent/releases/download/release-2.1.12-stable/libevent-2.1.12-stable.tar.gz
解压libevent
tar -zxvf libevent-2.1.12-stable.tar.gz
配置安装目录
代码语言:javascript复制./configure --prefix=/usr/local/libevent-2.1.12
编译安装
代码语言:javascript复制make && make install
代码语言:javascript复制如果在
libevent
安装目录make
之后会生成一个.libs/
, 里面如果没有libevent_openssl.so
说明系统没有安装openssl
库。但是如果安装了,依然没有这个文件生成,可能需要制定openssl
路径。
ln -s /usr/local/ssl/include/openssl /usr/include/openssl
查看是否安装成功
代码语言:javascript复制$ ls -al /usr/local/ | grep libevent
drwxr-xr-x 5 root root 4096 Mar 17 2024 libevent-2.1.12
安装测试
在sample
目录下会有已经编译好的服务器应用程序。
cd libevent-2.1.12-stable/sample
可以拿hello-world
程序用来测试。
ls
dns-example hello-world http-connect.c https_client-openssl_hostname_validation.o le-proxy.c signal-test.o
dns-example.c hello-world.c http-connect.o http-server le_proxy-le-proxy.o time-test
dns-example.o hello-world.o https-client http-server.c openssl_hostname_validation.c time-test.c
event-read-fifo hostcheck.c https-client.c http-server.o openssl_hostname_validation.h time-test.o
event-read-fifo.c hostcheck.h https_client-hostcheck.o include.am signal-test
event-read-fifo.o http-connect https_client-https-client.o le-proxy signal-test.c
hello-world
服务端
代码语言:javascript复制./hello-world
# 客户端连接响应如下
flushed answer
客户端
代码语言:javascript复制netcat 172.30.237.24 9995
Hello, World!
如果客户端收到Hello, World!
字符串,表示libevent
在本机可以正常使用。
http-server
当使用./http-server
命令来启动一个 HTTP 服务器。对于http-server
,你可以使用-p
或--port
参数来指定端口号。最后一个参数为服务器根目录。
具体的命令格式:
代码语言:javascript复制./http-server -p 8886 /home/www/build/libevent-2.1.12-stable/sample/
- 端口:
8886
- 根目录:
/home/www/build/libevent-2.1.12-stable/sample/
这两个命令都会启动http-server
,并让它监听在8686
端口上。你可以通过浏览器访问http://localhost:8686
来查看服务器上的内容。
在根目录:/home/www/build/libevent-2.1.12-stable/sample/
新建一个静态测试文件tinywan.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Tinywan(ShaoBo Wan)</title>
</head>
<body>
<h1>开源技术小栈</h1>
</body>
</html>
服务端
代码语言:javascript复制 ./http-server -p 8886 /home/www/build/libevent-2.1.12-stable/sample/
Listening on 0.0.0.0:8886
Got a GET request for </tinywan.html>
Got a GET request for </tinywan.html>
客户端
代码语言:javascript复制curl -i http://172.30.237.24:8886/tinywan.html
HTTP/1.1 200 OK
Content-Type: text/html
Date: Thu, 08 Aug 2024 02:12:53 GMT
Content-Length: 165
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Tinywan(ShaoBo Wan)</title>
</head>
<body>
<h1>开源技术小栈</h1>
</body>
</html>
访问首页
代码语言:javascript复制HTTP/1.1 200 OK
Content-Type: text/html
Date: Thu, 08 Aug 2024 02:16:21 GMT
Content-Length: 2206
<!DOCTYPE html>
<html>
<head>
<meta charset='utf-8'>
<title>/</title>
<base href='/'>
</head>
<body>
<h1>/</h1>
<ul>
<li><a href="time-test.o">time-test.o</a>
<li><a href="hello-world">hello-world</a>
<li><a href="http-connect.c">http-connect.c</a>
<li><a href=".deps">.deps</a>
<li><a href="openssl_hostname_validation.c">openssl_hostname_validation.c</a>
<li><a href=".libs">.libs</a>
<li><a href="http-connect">http-connect</a>
<li><a href="hostcheck.c">hostcheck.c</a>
<li><a href="dns-example.o">dns-example.o</a>
<li><a href="signal-test.o">signal-test.o</a>
<li><a href=".">.</a>
<li><a href="http-server.c">http-server.c</a>
<li><a href="time-test.c">time-test.c</a>
<li><a href="signal-test">signal-test</a>
<li><a href="http-connect.o">http-connect.o</a>
<li><a href="https_client-hostcheck.o">https_client-hostcheck.o</a>
<li><a href="signal-test.c">signal-test.c</a>
<li><a href=".dirstamp">.dirstamp</a>
<li><a href="openssl_hostname_validation.h">openssl_hostname_validation.h</a>
<li><a href="..">..</a>
<li><a href="https_client-openssl_hostname_validation.o">https_client-openssl_hostname_validation.o</a>
<li><a href="event-read-fifo.c">event-read-fifo.c</a>
<li><a href="http-server.o">http-server.o</a>
<li><a href="le-proxy.c">le-proxy.c</a>
<li><a href="http-server">http-server</a>
<li><a href="event-read-fifo.o">event-read-fifo.o</a>
<li><a href="include.am">include.am</a>
<li><a href="https_client-https-client.o">https_client-https-client.o</a>
<li><a href="le_proxy-le-proxy.o">le_proxy-le-proxy.o</a>
<li><a href="hello-world.c">hello-world.c</a>
<li><a href="tinywan.html">tinywan.html</a>
<li><a href="le-proxy">le-proxy</a>
<li><a href="hostcheck.h">hostcheck.h</a>
<li><a href="time-test">time-test</a>
<li><a href="https-client">https-client</a>
<li><a href="dns-example.c">dns-example.c</a>
<li><a href="dns-example">dns-example</a>
<li><a href="https-client.c">https-client.c</a>
<li><a href="event-read-fifo">event-read-fifo</a>
<li><a href="hello-world.o">hello-world.o</a>
</ul></body></html>
time-test
代码语言:javascript复制~/build/libevent-2.1.12-stable/sample$ ./time-test
timeout_cb called at 1723083770: 2.002 seconds elapsed.
timeout_cb called at 1723083772: 2.002 seconds elapsed.
timeout_cb called at 1723083774: 2.002 seconds elapsed.
timeout_cb called at 1723083776: 2.002 seconds elapsed.
timeout_cb called at 1723083778: 2.002 seconds elapsed.
timeout_cb called at 1723083780: 2.002 seconds elapsed.
timeout_cb called at 1723083782: 2.002 seconds elapsed.
timeout_cb called at 1723083784: 2.002 seconds elapsed.
timeout_cb called at 1723083786: 2.002 seconds elapsed.
timeout_cb called at 1723083788: 2.002 seconds elapsed.
timeout_cb called at 1723083790: 2.001 seconds elapsed.
timeout_cb called at 1723083792: 2.002 seconds elapsed.
timeout_cb called at 1723083794: 2.002 seconds elapsed.
应用场景
- Web服务器:Libevent 的高性能和异步I/O特性使其成为构建高性能Web服务器的理想选择。许多Web服务器,如Nginx的部分模块,都使用了Libevent来处理高并发连接。
- 聊天服务器:在即时通讯系统中,Libevent能够处理大量用户的实时消息推送和聊天功能。它支持非阻塞的socket I/O,使得单个线程可以管理成千上万个并发连接,非常适合构建高性能的聊天服务器。
- 游戏服务器:游戏服务器需要处理大量游戏客户端的连接请求和消息收发。Libevent能够高效地处理这些并发连接,支持大量玩家同时在线,确保游戏的流畅运行。
- 分布式系统:在分布式系统中,Libevent可以作为节点间通信的基础库,处理心跳检测、数据同步等任务。它的跨平台特性和高效的事件处理机制使得它非常适合在分布式系统中使用。
- 高性能网络客户端:除了服务器应用外,Libevent还可以用于构建高性能的网络客户端程序。通过其提供的事件驱动模型和非阻塞I/O特性,客户端程序可以高效地处理网络请求和响应。
- 缓存系统:著名的分布式缓存系统Memcached就是基于Libevent实现的。Memcached需要处理大量的并发读写请求,Libevent的高性能和异步I/O特性使其成为实现这种高性能缓存系统的关键组件。
- RPC(远程过程调用)系统:RPC系统需要高效地在不同节点之间传输数据和调用远程过程。Libevent的异步I/O和事件驱动特性使得它非常适合用于构建高性能的RPC系统。
- 其他网络应用:除了上述应用场景外,Libevent还可以用于构建各种需要高效处理并发连接的网络应用,如DNS服务器、FTP服务器、流媒体服务器等。
小结
总的来说,Libevent以其高性能、轻量级、跨平台和丰富的API等特性,在网络编程中发挥着重要作用,特别是在需要处理大量并发连接的应用场景中。