1. 项目概述:从零上手 libhv 的 HTTP 服务器
如果你正在寻找一个高性能、易上手且功能强大的 C/C++ 网络库来构建你的下一个网络服务,那么libhv绝对值得你花时间深入了解。今天我们不谈那些复杂的底层原理和架构设计,就从最实际、最直观的一步开始:运行官方内置的 HTTP 服务器示例bin/httpd。这个看似简单的操作,实际上是你打开libhv世界大门的钥匙。通过它,你不仅能瞬间体验到一个功能完备的 HTTP 服务器的强大,更能直观地理解libhv在设计上的便捷性和实用性。无论你是想快速搭建一个内网测试接口、一个轻量级的文件服务器,还是为你的 C++ 后端服务提供一个高性能的 HTTP 接入层,这个httpd程序都能在几分钟内帮你搞定。接下来,我将带你从环境准备、编译运行,到深入配置和功能探索,手把手让你掌握这个强大的工具。
2. 环境准备与项目编译
在运行bin/httpd之前,我们首先需要把libhv的源代码编译成可执行文件。这个过程本身也是了解一个开源项目构建系统的好机会。libhv提供了多种构建方式,包括经典的Makefile、跨平台的CMake,以及Bazel、vcpkg、xmake等,对开发者非常友好。为了覆盖最广泛的环境,我们这里以 Linux/macOS 系统下最通用的Makefile方式为例,Windows 用户可以使用CMake生成 Visual Studio 项目,流程大同小异。
2.1 获取源代码与基础环境确认
第一步是获取libhv的源代码。推荐使用git克隆,这样可以方便地切换到特定版本或跟进最新更新。
git clone https://github.com/ithewei/libhv.git cd libhv在编译之前,请确保你的系统已经安装了基础的编译工具链,例如gcc/g++或clang,以及make工具。对于大多数 Linux 发行版,可以通过包管理器安装。例如,在 Ubuntu/Debian 上:
sudo apt update sudo apt install build-essential cmake git对于 macOS 用户,确保已安装 Xcode Command Line Tools:
xcode-select --install2.2 配置与编译过程详解
进入libhv根目录后,你会发现一个configure脚本。这个脚本的作用是检测当前系统环境,并生成对应的Makefile。直接运行它即可:
./configure这个命令会输出一系列检测信息,包括编译器类型、支持的特性(如 OpenSSL)等。它本质上是一个autoconf风格的配置脚本,你可以通过./configure --help查看所有可配置选项。例如,如果你需要 SSL/TLS 支持以启用 HTTPS,可以运行./configure --with-openssl。不过对于初次体验,使用默认配置完全足够。
配置完成后,就可以开始编译了:
makemake命令会读取生成的Makefile,编译整个libhv库以及所有的示例程序。编译过程可能会花费几分钟时间,具体取决于你的机器性能。如果一切顺利,你会在bin/目录下看到生成的可执行文件,其中就包括我们今天的主角httpd,以及配套的工具如curl、wrk等。
注意:如果在编译过程中遇到关于
openssl的错误,而你暂时不需要 HTTPS 功能,可以重新运行./configure而不带--with-openssl参数。反之,如果你明确需要 HTTPS,请确保系统已安装 OpenSSL 开发库(如libssl-dev)。
2.3 编译后的目录结构解析
编译成功后,让我们快速浏览一下几个关键的目录和文件,这对后续的理解和使用有帮助:
bin/: 存放所有编译好的可执行文件。httpd是 HTTP 服务器,curl是仿cURL的命令行 HTTP 客户端,wrk是 HTTP 压测工具。lib/: 存放编译生成的libhv静态库(.a)和动态库(.so或.dylib)。include/: 存放libhv的所有头文件,是你未来自己开发时需要引用的。etc/: 存放一些示例配置文件,如httpd.conf是httpd服务器的配置文件模板。examples/: 大量的 C 和 C++ 示例代码,是学习libhv各种 API 用法的绝佳资料。
至此,你的libhv开发环境就已经准备就绪了。接下来,我们就可以启动这个内置的 HTTP 服务器了。
3. 首次运行与基础功能体验
编译完成后,直接运行bin/httpd是最快的体验方式。这个程序设计得非常贴心,它内置了一个功能丰富的 HTTP 服务,无需任何额外代码,开箱即用。我们先以最简单的交互模式启动它。
3.1 启动服务器与查看帮助
在终端中,进入libhv目录,直接运行:
./bin/httpd你会看到服务器启动,并默认监听在0.0.0.0:8080端口。这意味着它接受来自任何网络接口的连接。在浏览器中访问http://localhost:8080或http://127.0.0.1:8080,你应该能看到一个简单的欢迎页面,或者一个目录列表(取决于htdocs目录下的内容)。这就是一个最基本的 HTTP 服务在运行了。
不过,直接运行只是使用了默认配置。httpd提供了丰富的命令行参数。使用-h参数查看帮助:
./bin/httpd -h帮助信息会列出所有可用参数,例如:
-p或--port: 指定监听端口,默认为 8080。-h或--host: 指定监听主机,默认为0.0.0.0。-d或--daemon: 以守护进程(后台)模式运行。-c或--conf: 指定配置文件路径。-s或--signal: 向已运行的守护进程发送信号(如 stop, restart, reload)。
例如,如果你想在 9090 端口启动服务,可以这样运行:
./bin/httpd -p 90903.2 体验内置的多种 HTTP 服务
libhv的httpd不仅仅是一个简单的演示,它内置了多种实用的 HTTP 服务模式,我们可以使用配套的bin/curl工具来一一测试。这些测试能让你立刻感受到libhv处理 HTTP 协议的完整能力。
1. 静态文件服务:这是最基本的功能。httpd默认会将当前目录下的htdocs文件夹作为静态文件根目录。如果htdocs不存在,它会列出libhv根目录的内容。尝试访问一个文件:
./bin/curl -v localhost:8080/LICENSE-v参数表示显示详细过程,你可以看到完整的 HTTP 请求和响应头,以及文件内容。响应头中的Content-Type会根据文件后缀自动设置。
2. 目录列表服务:如果请求的是一个目录路径,且目录下没有index.html等默认索引文件,httpd会生成一个简单的 HTML 页面来列出目录内容。
./bin/curl -v localhost:8080/3. 内置的 API 接口(Endpoint):这是httpd示例最精彩的部分,它预定义了一系列路由,用于演示如何处理不同类型的 HTTP 请求和数据类型。我们逐一测试:
基础健康检查
/ping:./bin/curl -v localhost:8080/ping服务器会返回
text/plain类型的"pong"。这常用于微服务的健康检查。回显服务
/echo:./bin/curl -v localhost:8080/echo -d "hello,world!"-d参数用于发送 POST 请求的 body 数据。服务器会将接收到的 body 原样返回。查询参数解析
/query:./bin/curl -v "localhost:8080/query?page_no=1&page_size=10"注意 URL 中的
&在 shell 中有特殊含义,所以最好用引号包裹整个 URL。服务器会解析查询字符串,并以 JSON 格式返回。处理表单数据
/kv:./bin/curl -v localhost:8080/kv -H "Content-Type:application/x-www-form-urlencoded" -d 'user=admin&pswd=123456'这里我们模拟了网页表单提交,
Content-Type设置为application/x-www-form-urlencoded。服务器会解析键值对并返回 JSON。处理 JSON 数据
/json:./bin/curl -v localhost:8080/json -H "Content-Type:application/json" -d '{"user":"admin","pswd":"123456"}'服务器不仅能解析 JSON 请求体,还能验证其有效性,并同样以 JSON 格式回应。
处理文件上传
/upload:./bin/curl -v localhost:8080/upload -F "file=@LICENSE"-F参数用于模拟multipart/form-data表单,常用于文件上传。这里我们将本地的LICENSE文件上传。服务器会保存文件(默认在/tmp目录)并返回上传信息。RESTful 风格路径参数:
./bin/curl -v -X DELETE localhost:8080/group/test/user/123这里演示了 RESTful API 中常见的路径参数。路由模式为
/group/:group_name/user/:user_id。服务器会解析出group_name=test和user_id=123,并响应 DELETE 方法。
实操心得:在测试这些 API 时,强烈建议始终加上
-v参数。观察完整的 HTTP 交互过程(请求头、响应头、状态码)是理解和调试 HTTP 服务的最佳方式。libhv的curl工具输出非常清晰,比很多图形化工具更能让你理解底层细节。
4. 深入配置:使用配置文件定制你的 HTTP 服务
命令行参数适合简单的配置,但对于一个正式的服务,我们通常使用配置文件。libhv的httpd支持通过-c参数指定一个 JSON 格式的配置文件。etc/目录下已经有一个httpd.conf的模板,我们可以基于它进行修改。
4.1 配置文件结构与核心参数解析
让我们先查看一下默认的配置文件:
cat etc/httpd.conf你会看到一个 JSON 对象,里面包含了大量的配置项。我们来解读几个最核心的:
{ "host": "0.0.0.0", "port": 8080, "worker_processes": 0, "worker_threads": 4, "document_root": "./htdocs", "index_files": ["index.html", "index.htm"], "error_pages": { "404": "./htdocs/404.html" }, "keepalive_timeout": 75, "max_connections": 1024, "max_body_size": 10485760, "max_header_size": 8192, "static_file_cache": 3600, "enable_gzip": true, "log_file": "/tmp/httpd.log", "log_level": "INFO", "ssl": false, "ssl_cert": "", "ssl_key": "", "routes": [ { "method": "GET", "path": "/ping", "handler": "ping_handler" }, { "method": "*", "path": "/echo", "handler": "echo_handler" } // ... 更多路由 ] }host/port: 监听地址和端口。worker_processes: 工作进程数。默认为 0,表示不 fork 子进程,仅在主进程内运行。如果设置为大于 0 的数(如 2),则会以多进程模式运行,适合利用多核 CPU。通常,一个进程绑定一个 CPU 核心。worker_threads: 每个工作进程中的事件循环线程数。默认为 4。这是libhv高性能的关键,它使用多线程 Reactor 模型来处理并发连接。通常设置为与 CPU 逻辑核心数相当。document_root: 静态文件服务的根目录。keepalive_timeout: HTTP Keep-Alive 连接的超时时间(秒)。保持连接复用可以极大提升性能。max_connections: 最大并发连接数限制,用于防止资源耗尽。max_body_size: 单个 HTTP 请求体的最大尺寸限制(字节),防止恶意超大请求。enable_gzip: 是否启用 GZIP 压缩响应正文,对文本类资源(HTML, CSS, JS, JSON)可以显著减少传输体积。routes: 这是核心配置项,定义了 URL 路径到处理函数(handler)的映射。示例中已经预置了/ping,/echo等路由。
4.2 创建并应用自定义配置
现在,我们来创建一个自己的配置文件。复制一份模板并修改:
cp etc/httpd.conf my_httpd.conf用文本编辑器打开my_httpd.conf,进行一些个性化修改:
- 将端口改为
9090。 - 将
document_root改为一个你希望提供服务的绝对路径,例如"/home/yourname/www"。 - 在
routes数组的末尾,添加一个你自己的简单路由:
{ "method": "GET", "path": "/hello", "handler": "hello_handler" }注意,这里我们引用了一个名为"hello_handler"的处理器。这个处理器名称需要与httpd程序内注册的处理函数对应。由于我们无法修改已编译的httpd二进制文件来添加新的 C++ 处理函数,所以这个自定义路由在标准httpd中暂时无法工作。但这展示了配置文件的扩展能力。实际上,libhv的强大之处在于你可以编写自己的程序,像examples/http_server_test.cpp那样,轻松地注册任意处理函数。
保存配置文件后,使用它启动服务:
./bin/httpd -c my_httpd.conf -d-d参数让服务在后台以守护进程模式运行。你可以使用ps aux | grep httpd查看进程。现在,你的 HTTP 服务就运行在 9090 端口,并且使用了你指定的文档根目录。
4.3 服务管理:启动、停止与重启
当以守护进程模式运行(-d)时,httpd会将进程 ID(PID)写入一个文件(默认是/tmp/httpd.pid)。我们可以通过-s信号参数来管理这个进程。
停止服务:
./bin/httpd -c my_httpd.conf -s stop这个命令会读取 PID 文件,并向该进程发送
SIGTERM信号,使其优雅退出。重启服务:
./bin/httpd -c my_httpd.conf -s restart这相当于先
stop再start。在更新了配置文件或程序后非常有用。重新加载配置:
./bin/httpd -c my_httpd.conf -s reload向进程发送
SIGHUP信号。一个设计良好的服务进程应该捕获这个信号,并重新读取配置文件,实现不中断服务的配置热更新。需要注意的是,标准httpd示例可能没有实现完整的配置热重载逻辑,但你可以参考这个模式在自己的服务中实现。
注意事项:使用配置文件启动守护进程时,务必确保配置文件中
log_file的路径是可写的,否则服务可能因无法记录日志而启动失败。同时,在生产环境中,建议将日志级别(log_level)设置为"WARN"或"ERROR",以减少不必要的日志输出对磁盘 I/O 的影响。
5. 性能初探与压力测试
一个网络库好不好,性能是硬指标。libhv在性能上做了大量优化,其事件循环和多线程模型设计得非常高效。我们可以使用内置的wrk工具进行一个简单的压力测试,感受一下它的吞吐能力。
5.1 使用内置 wrk 进行基准测试
首先,确保你的httpd服务正在运行(默认端口 8080)。然后,在另一个终端中,使用libhv自带的压测工具:
./bin/wrk -c 100 -t 4 -d 10s http://127.0.0.1:8080/ping这个命令的含义是:
-c 100: 模拟 100 个并发 HTTP 连接。-t 4: 使用 4 个线程来发起请求。-d 10s: 测试持续时间为 10 秒。- 目标 URL 是
http://127.0.0.1:8080/ping,我们测试最简单的ping接口。
命令执行后,你会看到类似下面的输出:
Running 10s test @ http://127.0.0.1:8080/ping 4 threads and 100 connections Thread Stats Avg Stdev Max +/- Stdev Latency 1.23ms 200.15us 12.33ms 90.12% Req/Sec 20.34k 1.67k 23.45k 69.50% Latency Distribution 50% 1.21ms 75% 1.27ms 90% 1.35ms 99% 1.79ms 811200 requests in 10.10s, 100.88MB read Requests/sec: 80316.04 Transfer/sec: 9.99MB这个结果非常惊人!在本地环境下,一个简单的ping接口,QPS(每秒请求数)可以达到 8 万以上,平均延迟仅 1.23 毫秒。这充分证明了libhv事件循环的高效性。Latency Distribution部分显示了延迟分布,99% 的请求都在 1.79 毫秒内完成,说明性能非常稳定。
5.2 测试静态文件服务性能
静态文件服务涉及磁盘 I/O,性能指标会有所不同。我们测试一下获取一个小文件(比如LICENSE文件):
./bin/wrk -c 100 -t 4 -d 10s http://127.0.0.1:8080/LICENSE你会发现 QPS 会下降,因为增加了磁盘读操作。同时,观察Transfer/sec(每秒传输字节数),这个值更能反映网络吞吐能力。libhv在处理静态文件时,使用了高效的sendfile系统调用(如果系统支持),可以将文件内容直接从内核缓冲区发送到网卡,避免了用户空间和内核空间之间的多次数据拷贝,这也是其高性能的原因之一。
5.3 性能调优思路与参数影响
从压力测试结果中,我们可以思考几个影响性能的关键配置点:
worker_threads参数:这个参数对应了libhv事件循环线程的数量。理论上,最佳值是你的 CPU 逻辑核心数。设置过少,无法充分利用多核;设置过多,线程间切换反而会增加开销。你可以修改配置文件中的worker_threads,分别设置为 2, 4, 8,然后重新进行压测,观察 QPS 和延迟的变化,找到你硬件上的甜点。TCP 内核参数:对于高并发场景,可能需要调整操作系统级别的 TCP 参数,例如
net.core.somaxconn(监听队列长度)、net.ipv4.tcp_tw_reuse(TIME_WAIT 套接字重用)等。这需要根据实际部署环境进行优化。日志输出:在生产环境压测时,务必将
log_level设置为"ERROR"或直接关闭日志输出到文件(log_file置空)。频繁的磁盘 I/O 日志写入会是巨大的性能瓶颈。Keep-Alive:确保配置中
keepalive_timeout是一个合理的正值(如 75)。HTTP 持久连接可以避免频繁的 TCP 握手/挥手,对提升高并发下的性能至关重要。在wrk测试中,它默认就使用了 Keep-Alive。
实操心得:压测工具
wrk本身也可能成为瓶颈。如果wrk所在机器的 CPU 已经跑满,那么测出来的就是客户端的极限,而非服务端的。此时可以尝试减少wrk的线程数-t,或者使用多台机器作为压测客户端。另外,压测最好在隔离的网络环境中进行,避免其他网络流量干扰。
6. 从示例到实践:理解 httpd 背后的代码逻辑
运行现成的bin/httpd很方便,但如果你想真正掌握libhv并用于自己的项目,就必须理解它背后的代码是如何组织的。httpd的源码实现分散在几个文件中,但其核心逻辑清晰易懂。
6.1 核心代码文件追踪
httpd的主程序入口在src/apps/httpd.c(C版本)或src/apps/httpd.cpp(C++版本)。我们以 C++ 版本为例,因为它更直观。你可以用编辑器打开这个文件看看。
它的main函数主要做了以下几件事:
- 解析命令行参数:处理
-p,-h,-c,-d等我们之前用到的参数。 - 加载配置文件:读取 JSON 配置文件,并填充到一个
HttpService对象的配置中。 - 注册内置路由处理器:这是关键步骤。代码中会创建一个
HttpService对象(名为router),然后通过router.GET(),router.POST()等方法,将路径(如"/ping")与对应的 C++ 函数(如ping_handler)绑定起来。 - 创建并配置 HttpServer:使用上一步的
router创建一个HttpServer实例,设置端口、线程数等参数。 - 启动服务器:调用
server.run()或server.start()。
真正的业务逻辑,藏在那些处理器函数里。例如,ping_handler可能就是一个简单的 lambda 函数:
router.GET("/ping", [](HttpRequest* req, HttpResponse* resp) { resp->content_type = "text/plain"; resp->body = "pong"; return 200; // HTTP 状态码 });6.2 如何借鉴并创建自己的 HTTP 服务
你不必修改httpd.cpp来添加功能。libhv鼓励你创建自己的项目。最简单的方式是参考examples/http_server_test.cpp。我们来快速创建一个属于自己的迷你 HTTP 服务。
创建一个新文件my_server.cpp:
#include "HttpServer.h" using namespace hv; int main() { // 1. 创建路由服务 HttpService router; // 2. 注册你的业务路由 // 一个简单的 JSON API router.GET("/api/time", [](HttpRequest* req, HttpResponse* resp) { resp->json["timestamp"] = (long long)time(NULL); resp->json["message"] = "Hello from libhv!"; return 200; }); // 一个处理 POST 数据的 API router.POST("/api/echo", [](const HttpContextPtr& ctx) { // ctx->request() 获取请求对象 // ctx->response() 获取响应对象 // 直接回传请求体 return ctx->send(ctx->body(), ctx->type()); }); // 3. 创建服务器 HttpServer server(&router); server.setPort(8888); // 监听 8888 端口 server.setThreadNum(2); // 使用 2 个IO线程 // 4. 设置静态文件目录(可选) server.setDocumentRoot("./www"); // 静态文件根目录 // 5. 运行服务器 server.run(); // 阻塞运行,直到进程被终止 return 0; }编译这个程序需要链接libhv库。假设你已经将libhv安装到了系统路径(sudo make install),可以这样编译:
g++ -std=c++11 my_server.cpp -o my_server -lhv如果libhv库在本地,则需要指定头文件和库路径:
g++ -std=c++11 -I./include my_server.cpp -o my_server -L./lib -lhv -lpthread运行./my_server,你就拥有了一个完全受控的、带有自定义 API 的 HTTP 服务。你可以在此基础上,连接数据库、调用其他服务,构建复杂的业务逻辑。
6.3 关键对象:HttpRequest 与 HttpResponse
在编写处理器时,你最常打交道的两个对象是HttpRequest和HttpResponse(或通过HttpContext访问)。
HttpRequest:包含了客户端请求的所有信息。req->path: 请求的路径,如/api/time。req->method: 请求方法(GET, POST, PUT, DELETE等)。req->headers: 一个unordered_map,存放所有的请求头。req->query_params: 解析好的查询参数(URL中?后面的部分)。req->body: 请求体数据。req->content_type: 请求体的内容类型。req->json: 如果请求体是 JSON 格式,这个对象会自动解析成nlohmann::json(需要编译时开启 JSON 支持)。
HttpResponse:用于构建返回给客户端的响应。resp->status_code: 设置 HTTP 状态码,如 200, 404, 500。resp->headers: 可以添加自定义响应头。resp->content_type: 设置响应体的内容类型,如"application/json"。resp->body: 设置响应体的原始数据。resp->json: 直接设置一个nlohmann::json对象,库会自动将其序列化为字符串并设置content_type。resp->File(): 一个便捷方法,用于发送一个文件内容。
理解这两个对象,你就掌握了使用libhv处理 HTTP 请求响应的核心。
7. 常见问题与故障排查实录
在实际操作中,你可能会遇到一些问题。这里我总结了一些常见的情况和排查思路,希望能帮你快速定位。
7.1 编译与链接问题
问题1:编译时找不到hv/HttpServer.h等头文件。
- 原因:编译器没有找到
libhv的头文件路径。 - 解决:使用
-I参数指定头文件目录。例如,如果libhv在/home/user/libhv,则添加-I/home/user/libhv/include。
问题2:链接时报告未定义的引用(undefined reference),错误信息包含hv::HttpServer等。
- 原因:链接器没有找到
libhv的库文件。 - 解决:
- 确保
libhv已成功编译,在lib/目录下存在libhv.a(静态库)或libhv.so(动态库)。 - 使用
-L指定库目录,如-L/home/user/libhv/lib。 - 使用
-l指定库名,如-lhv。 - 如果使用动态库,还需确保运行时库路径正确,或将动态库路径加入
LD_LIBRARY_PATH环境变量。
- 确保
问题3:运行自己编译的程序时,报错error while loading shared libraries: libhv.so.1: cannot open shared object file。
- 原因:系统在运行时找不到
libhv的动态链接库(.so 文件)。 - 解决:
- 临时方案:设置环境变量
export LD_LIBRARY_PATH=/path/to/libhv/lib:$LD_LIBRARY_PATH。 - 永久方案:将库文件复制到系统库目录(如
/usr/local/lib),然后运行sudo ldconfig更新缓存。或者将库路径添加到/etc/ld.so.conf.d/下的配置文件中。
- 临时方案:设置环境变量
7.2 服务器运行时问题
问题4:启动httpd时提示bind: Address already in use。
- 原因:你指定的端口(默认 8080)已经被其他进程占用。
- 解决:
- 换一个端口:
./bin/httpd -p 8081。 - 找出并停止占用端口的进程:在 Linux 上使用
sudo lsof -i:8080或sudo netstat -tlnp | grep :8080查看进程 ID,然后kill它。
- 换一个端口:
问题5:客户端(浏览器或curl)无法连接到服务器。
- 排查步骤:
- 检查服务器是否在运行:
ps aux | grep httpd。 - 检查监听端口:
sudo netstat -tlnp | grep httpd。确认httpd进程是否在预期的 IP 和端口上监听(如0.0.0.0:8080)。 - 检查防火墙:如果从其他机器访问,确保服务器防火墙放行了该端口(例如,
sudo ufw allow 8080/tcp)。 - 本地测试:在服务器本机用
curl http://127.0.0.1:8080测试。如果本地通而外部不通,问题很可能在防火墙或网络配置上。
- 检查服务器是否在运行:
问题6:上传大文件失败,或者 POST 大数据时连接被重置。
- 原因:可能触发了配置中的
max_body_size限制,或者系统 TCP 缓冲区设置过小。 - 解决:
- 在配置文件中增大
max_body_size的值(单位是字节)。 - 检查操作系统对单个进程打开文件描述符的限制(
ulimit -n),并发连接或大文件传输可能需要提高此限制。
- 在配置文件中增大
7.3 功能与行为问题
问题7:静态文件服务返回 403 Forbidden。
- 原因:
httpd进程对document_root目录或其下的某个文件没有读取权限。 - 解决:检查目录和文件的权限(
ls -la),确保运行httpd的用户(可能是你自己)有rx(读取和执行)权限。对于目录,需要执行权限才能进入。
问题8:自定义的处理器函数没有被调用。
- 原因:路由注册顺序或路径匹配问题。
- 排查:
libhv的路由匹配是顺序的。确保你的路由注册在更通用的路由(如通配符*)之前。- 检查路径字符串是否完全匹配,包括开头的
/。 - 开启调试日志,查看请求是否到达服务器以及匹配到了哪个路由。可以在代码中设置
hv::setLogLevel(LOG_LEVEL_DEBUG);,或者在配置文件中设置"log_level": "DEBUG"。
问题9:如何启用 HTTPS(SSL/TLS)?
- 解决:
- 编译
libhv时启用 OpenSSL 支持:./configure --with-openssl。 - 准备你的 SSL 证书(.crt 或 .pem 文件)和私钥文件(.key)。
- 在配置文件中设置:
"ssl": true, "ssl_cert": "/path/to/your/cert.pem", "ssl_key": "/path/to/your/key.pem" - 重启服务,它将在同一端口同时提供 HTTP 和 HTTPS 服务(取决于客户端请求)。或者,你可以专门监听一个 HTTPS 端口。
- 编译
7.4 性能相关问题
问题10:压测时 QPS 上不去,或者出现大量错误。
- 排查思路:
- 客户端瓶颈:用
top或htop查看wrk进程的 CPU 使用率。如果接近 100%,说明压测工具本身成了瓶颈。可以尝试减少wrk的线程数-t,或者使用多台客户端。 - 服务端瓶颈:查看
httpd进程的 CPU 使用率。如果不高,可能是遇到了其他限制,如带宽、磁盘 I/O(如果是文件服务)、或max_connections限制。 - 系统限制:检查
ulimit -n(文件描述符数),并发连接数不能超过此限制。可以使用sudo sysctl -w fs.file-max=100000和修改/etc/security/limits.conf来提高限制。 - 日志影响:确保生产环境压测时没有开启
DEBUG或INFO级别的日志输出到控制台或文件,这会造成严重的 I/O 阻塞。
- 客户端瓶颈:用
掌握这些排查方法,你就能独立解决使用libhvhttpd时遇到的大部分常见问题。记住,清晰的日志和系统监控工具(如top,netstat,ss)是你最好的朋友。