Apache APR 介绍与选型指南

系统介绍 Apache APR(Apache Portable Runtime)及其 apr-util/apr-iconv 组成、与 httpd/subversion 的依赖关系,并给出与 GLib、NSPR、POCO、ACE 等同类可移植库的选型对比,版本数据均在 Ubuntu 22.04 与 CentOS 8 实测采集。

Apache APRApache APRC/C++ApacheLinux2026/10/16

Apache APR 介绍与选型指南

目标:讲清 Apache APR(Apache Portable Runtime)是什么、由哪几个组件构成、被哪些上游软件依赖、与 GLib/NSPR/POCO 等同类可移植库相比如何选型;文中所有版本号与目录结构均已在 Ubuntu 22.04(x86_64,GCC 11.4.0)与 CentOS 8(x86_64,GCC 8.5.0)上从源码编译实测采集。

一、环境信息

本文结论基于以下两个实测环境,读者在同类系统上可复现:

  • 操作系统一:Ubuntu 22.04(x86_64),GCC 11.4.0(gcc-11.4.0-1ubuntu1~22.04.3),GNU Make 4.3;
  • 操作系统二:CentOS Linux release 8.4.2105(x86_64),GCC 8.5.0(Red Hat 8.5.0-4),GNU Make 4.2.1;
  • APR 版本:apr 1.7.6、apr-util 1.6.5(2026-10 从 apr.apache.org 官网确认的当前稳定版,官网同时列有 apr-iconv 1.2.2);
  • 源码包校验值(实测 sha256):
# 在 /root/src 下对下载好的源码包做校验(实测输出)
$ sha256sum apr-1.7.6.tar.gz apr-util-1.6.5.tar.gz
6a10e7f7430510600af25fabf466e1df61aaae910bf1dc5d10c44a4433ccc81d  apr-1.7.6.tar.gz
f43a1c8c79eef497a022ec6c99dddbdf57e42001da6ccbfae259631ed5aa2805  apr-util-1.6.5.tar.gz

💡 说明:sha256 值会随版本变化,升级时以官网下载页公布的校验值为准;上面两条摘自实测日志,仅供与你的下载结果比对格式。

二、软件概述

2.1 APR 是什么

APR(Apache Portable Runtime,Apache 可移植运行时)是 Apache 软件基金会的底层 C 语言库,作用是把操作系统差异(文件路径、进程线程、网络套接字、共享内存、锁、动态库加载、时间、随机数等)抽象成一套统一 API。它最初是 Apache HTTP Server 2.0 的底层(从 httpd 源码树抽离独立成项目),如今也是 Subversion、Tomcat Native(tomcat 的 APR 连接器)等软件的地基。

理解 APR 定位最直观的方式:上层软件只面向 APR 的 API 编程,APR 负责在 Linux、Windows、macOS 等平台上映射到对应系统调用。这样 httpd 在 Linux 上用 epoll、在 Windows 上用 IOCP,上层代码却是一套。

💡 技术拓展:APR 属于「跨平台系统级基础库」这一类软件,同类代表还有 Mozilla NSPR、GNOME GLib、POCO C++ Libraries、ACE、Qt Core 等。它们覆盖的平台与抽象层次差异较大,本文第七章给出一屏对比表;APR 的特点是「C 语言、跟随 Apache 生态、以系统调用薄封装为主」。

2.2 APR 家族的三个组件

APR 项目实际由三个组件构成,关系如下(版本号取自官网下载页,apr/apr-util 已实测安装):

组件当前版本定位是否必装
apr1.7.6核心运行时:内存池、文件/网络 I/O、线程/进程/锁、时间、DSO、字符串必装(apr-util 依赖它)
apr-util1.6.5基于 apr 的扩展库:XML(expat)、DBD(数据库)、DBM(键值库)、crypto(加密)、LDAP、URI/日期解析、memcache/redis 客户端需要扩展功能时装
apr-iconv1.2.2字符集转换模块(提供 iconv 实现,Windows 上替代系统 iconv)仅在需要其字符集表时安装(Linux 上通常用系统 iconv)

apr-util 对 apr 是严格的依赖关系:configure 时必须用 --with-apr 指向已安装 apr 的 apr-1-config 工具(实测:./configure --with-apr=/usr/local/apr/bin/apr-1-config 检测通过后 apu-1-config --version 输出 1.6.5)。apr-iconv 则是可选组件,apr-util 通过 --with-apr-iconv=DIR 关联,Linux 服务器场景一般不装。

2.3 版本模型

APR 当前稳定大版本线是 1.x(apr 1.7.6 / apr-util 1.6.5)。1.x 内部保持 ABI 兼容——已编译的旧程序可以直接替换新版本共享库运行。apr 2.x 多年来一直是 trunk 开发线,未发布稳定版,生产选型仍以 1.x 为准。

# 查看已安装版本的三种实测方式(三种结果一致:1.7.6)
$ /usr/local/apr/bin/apr-1-config --version
1.7.6
$ grep -E "APR_(MAJOR|MINOR|PATCH)_VERSION" /usr/local/apr/include/apr-1/apr_version.h
#define APR_MAJOR_VERSION       1
#define APR_MINOR_VERSION       7
#define APR_PATCH_VERSION       6
$ PKG_CONFIG_PATH=/usr/local/apr/lib/pkgconfig pkg-config --modversion apr-1
1.7.6

三、核心架构

3.1 分层结构

APR 的架构自下而上分两层:apr 核心库(libapr-1.so)提供与操作系统直接相关的基础能力;apr-util(libaprutil-1.so)在核心库之上提供纯逻辑与协议层的扩展组件。两层的关键内容如下表(能力项均可通过实测的头文件名与测试套件对号):

层子系统代表头文件(/usr/local/apr/include/apr-1/ 下实测存在)
apr 核心内存池(资源管理基石)apr_pools.h
apr 核心文件与目录 I/Oapr_file_io.h、apr_file_info.h
apr 核心网络 I/O(socket 统一抽象)apr_network_io.h
apr 核心线程/进程/信号apr_thread_proc.h
apr 核心锁(互斥/读写/条件/全局锁)apr_thread_mutex.h、apr_thread_rwlock.h、apr_global_mutex.h
apr 核心时间apr_time.h
apr 核心动态库加载(DSO)apr_dso.h
apr 核心原子操作、环境变量、命令行解析apr_atomic.h、apr_env.h、apr_getopt.h
apr-utilXML 解析apr_xml.h
apr-utilDBD(数据库连接抽象)apr_dbd.h
apr-utilDBM(键值存储抽象)apr_dbm.h
apr-utilcrypto(加密抽象,驱动式)apr_crypto.h
apr-utilLDAP、URI、Base64/MD5/SHA1apr_ldap*.h、apr_uri.h、apr_base64.h、apr_md5.h

头文件总量实测:源码编译安装后 /usr/local/apr/include/apr-1/ 下共 72 个 .h 文件(Ubuntu 实测 ls /usr/local/apr/include/apr-1/apr*.h | wc -l 输出 72,其中已含 apr-util 的 apu_* 与 apr_ldap_* 等);CentOS 8 发行版 rpm 安装的 /usr/include/apr-1/ 则有 76 个(rpm 拆包子包携带更多可选头)。

3.2 模块化:DSO 动态插件

apr-util 的重组件(加密驱动、数据库驱动、DBM 后端、LDAP)默认编译成「运行时按需加载」的 DSO 模块,而不是全部塞进主库。源码编译安装后,Ubuntu 实测的模块目录如下:

# 安装后 lib 目录实测(Ubuntu 22.04,apr 1.7.6 + apr-util 1.6.5)
$ ls /usr/local/apr/lib/
apr-util-1
apr.exp
aprutil.exp
libapr-1.a
libapr-1.la
libapr-1.so
libapr-1.so.0
libapr-1.so.0.7.6
libaprutil-1.a
libaprutil-1.la
libaprutil-1.so
libaprutil-1.so.0
libaprutil-1.so.0.6.5
pkgconfig

# DSO 模块子目录实测(configure 时带 --with-openssl --with-ldap --with-sqlite3 --with-mysql --with-berkeley-db)
$ ls /usr/local/apr/lib/apr-util-1/
apr_crypto_openssl-1.so
apr_crypto_openssl.a
apr_crypto_openssl.la
apr_crypto_openssl.so -> apr_crypto_openssl-1.so
apr_dbd_mysql-1.so
apr_dbd_sqlite3-1.so
apr_dbm_db-1.so
apr_ldap-1.so
(各自配套 .a/.la 文件与软链接,此处省略重复项)

关键差异解读:主库 libapr-1.so.0.7.6 / libaprutil-1.so.0.6.5 是程序直接链接的;apr-util-1/ 下的 apr_crypto_openssl-1.so、apr_dbd_mysql-1.so 等只在程序调用 apr_crypto_get_driver("openssl", ...)、apr_dbd_open("mysql", ...) 这类按名加载接口时才被 dlopen。好处是:不装 MySQL 客户端库的服务器上,apr-util 主库依然可以运行,只是 mysql 驱动不可用。

CentOS 8 的发行版 rpm 打包同样遵循该布局,实测 /usr/lib64/apr-util-1/ 下有 apr_crypto_openssl-1.so 与 apr_dbm_db-1.so(rpm 按 sub-package 拆分,未安装的 sub-package 对应模块就不存在)。

3.3 质量自证:自带测试套件

APR 源码树附带 test/ 目录的自测套件,编译安装后可直接运行验证库的正确性(这是评估 C 基础库成熟度的重要一手证据)。两套测试实测均全部通过:

# apr-util 测试套件实测(Ubuntu 22.04,/root/src/apr-util-1.6.5/test 下 make 后运行)
$ ./testall
testbuckets         :  SUCCESS
testpass            :  SUCCESS
testmd4             :  SUCCESS
testmd5             :  SUCCESS
testcrypto          :  SUCCESS
testldap            :  SUCCESS
testdbd             :  SUCCESS
testdate            :  SUCCESS
testmemcache        :  SUCCESS
testredis           :  SUCCESS
testxml             :  SUCCESS
testxlate           :  SUCCESS
testrmm             :  SUCCESS
testdbm             :  SUCCESS
testqueue           :  SUCCESS
testreslist         :  SUCCESS
testsiphash         :  SUCCESS
All tests passed.

# apr 核心库测试套件实测(节选尾部)
$ cd /root/src/apr-1.7.6/test && make && ./testall
testsockopt         :  SUCCESS
teststr             :  SUCCESS
teststrnatcmp       :  SUCCESS
testtable           :  SUCCESS
testtemp            :  SUCCESS
testthread          :  SUCCESS
testtime            :  SUCCESS
testud              :  SUCCESS
testuser            :  SUCCESS
testvsn             :  SUCCESS
testskiplist        :  SUCCESS
All tests passed.

⚠️ 注意:testcrypto 首次运行时若屏幕滚出多行 Crypto driver 'nss' DSO could not be opened,不是失败——那是测试用例按名探测未编译的 NSS/CommonCrypto 驱动(本环境只编了 openssl 驱动)的正常报错,套件最终仍输出 All tests passed.(实测已复核)。

四、功能特性

4.1 内存池(Pool):APR 的招牌设计

APR 最大的 API 特色是「内存池」:所有资源(内存、文件句柄、socket、子进程)挂在某个 apr_pool_t 上,池销毁时统一释放。程序因此几乎不需要逐个 free——创建请求级 pool,请求结束销毁池,杜绝泄漏。下面是最小骨架(完整可运行版本见《Apache APR 在 Ubuntu 22.04 集成使用指南》,此处摘关键行):

apr_pool_t *pool;
apr_pool_create(&pool, NULL);          /* 创建顶层池 */
char *s = apr_pstrcat(pool, "a", "b", NULL);  /* 从池分配字符串 */
apr_pool_destroy(pool);                /* 一次回收池上全部资源 */

💡 技术拓展:「池化分配」不止 APR 一家。常见方案对比——

方案思路适用场景
APR Pool资源挂池、销毁统一回收请求/任务生命周期明确的服务端程序
Arena(如 obstack)大块切小顺序分配同批对象同生共死
通用分配器(glibc malloc)逐对象 malloc/free生命周期杂乱无章的程序
jemalloc/tcmalloc线程缓存+尺寸类优化高并发通用分配瓶颈

APR 选择 Pool 的理由是其服务对象(HTTP 请求、SVN 操作)生命周期天然成批;逐对象精细释放的需求则不适合它。

4.2 统一的文件与网络 I/O

apr 把「打开一个东西」抽象成 apr_file_open(文件)与 apr_socket_create(socket),标志位语义跨平台一致(如 APR_CREATE|APR_WRITE|APR_TRUNCATE)。实测代码中写入读取 15 字节文件、内核文件描述符与权限位(APR_OS_DEFAULT)均按预期工作。

4.3 线程、进程与锁

apr 提供线程(apr_thread_create/apr_thread_join)、互斥锁(apr_thread_mutex_*)、读写锁、条件变量、全局锁与子进程(apr_proc_create/apr_proc_wait)。实测 4 线程 × 100000 次互斥锁保护累加,结果精确等于 400000(Ubuntu 与 CentOS 8 双环境一致):

# 线程+锁示例实测输出(两台环境一致)
$ ./apr_thread_demo
[+] thread 0 started
[+] thread 1 started
[+] thread 2 started
[+] thread 3 started
[=] expected 400000, got 400000 (OK)

4.4 apr-util 扩展组件

apr-util 的核心增值是「驱动式抽象」:

  • DBD:统一 MySQL/PostgreSQL/SQLite/Oracle/ODBC 连接 API,驱动为 DSO 模块;
  • DBM:统一键值存储 API,默认内置 sdbm(纯 C 实现的轻量 DBM),可选 Berkeley DB/GDBM/NDBM,实测 apu-1-config --db-version 在 sdbm-only 构建下输出 0、启用 Berkeley DB 5.3 后输出 5;
  • crypto:统一加解密 API,驱动式加载 openssl/NSS/CommonCrypto;实测加载 openssl 驱动完成 AES-256-CBC 加解密回环(21 字节明文 → 32 字节密文 → 还原一致),PBKDF2 派生密钥(口令 q1w2e3r4 + 盐)正常工作;
  • 其余:XML(expat)、URI 解析、Base64/MD4/MD5/SHA1/SIPHASH、日期解析、memcache/redis 客户端、线程池队列(apr_thread_pool)。

💡 技术拓展:DBM 这类「简单键值存储」业界有多个实现,选型对比如——

实现特点适用场景
sdbmapr-util 内置、零外部依赖、纯 C默认兜底、轻量场景
Berkeley DB (db)老牌事务型键值库、功能全需要事务/B-tree 索引
GDBMGNU 的 DBM 实现常见于 GNU 工具链
NDBM4.4BSD 传统实现,现代系统已少见兼容老代码

apr-util 默认采用 sdbm(configure 检测输出 checking for default DBM... sdbm (default)),只有显式传 --with-berkeley-db 等参数才启用其他后端。

五、依赖链:谁在用 APR

APR 的上游依赖很轻(C 编译器 + expat/apr-util 场景 + 可选 OpenSSL/LDAP/DB 库),下游用户才是它存在的意义。以 CentOS 8 实测的 rpm 反查依赖:

# 查询哪些包直接依赖 apr(CentOS 8 实测)
$ rpm -q --whatrequires apr
apr-devel-1.6.3-12.el8.x86_64
# 用完整 provides 名反查 apr-util 的直接依赖者(CentOS 8 实测,本环境未安装 httpd)
$ rpm -q --whatrequires "apr-util(x86-64)"
apr-util-bdb-1.6.1-6.el8.x86_64
apr-util-openssl-1.6.1-6.el8.x86_64
apr-util-devel-1.6.1-6.el8.x86_64

解读:apr 的直接下游是它的开发包 apr-devel(httpd 等软件经由 apr-util 间接依赖 apr,本环境未装 httpd 所以不出现);apr-util 的直接依赖者是其各 sub-package。发行版全量依赖链可用 dnf repoquery --whatrequires --alldeps apr 追溯(apr-util-openssl、apr-util-bdb 等子包名也印证了拆包设计)。主要下游软件生态如下表(按官方文档与发行版打包惯例归纳):

下游软件使用 APR 的方式
Apache httpd 2.xMPM、日志、CGI 等全部底层操作基于 apr/apr-util(编译 httpd 必先装 apr)
Apache Subversion文件系统访问库(FSFS)与仓库工具链接 apr/apr-util
Apache Tomcat(native connector)tomcat-native(tcnative)封装 APR,使 Tomcat 走 APR/OpenSSL 性能路径
serf、mod_dav 等HTTP/WebDAV 客户端与服务模块依赖 apr

⚠️ 注意:编译安装 Apache httpd 前必须先装好 apr 与 apr-util(httpd 的 configure 需要 --with-apr/--with-apr-util 指向两者的安装前缀),版本不匹配是 httpd 编译失败的常见原因。

六、适用场景与限制

适用场景:

  • 需要「一套 C 代码跨 Linux/Windows/macOS」的服务端程序(APR 抹平系统差异);
  • 编译部署 Apache httpd / Subversion / tomcat-native 的前置依赖;
  • C 程序想要现成的内存池 + 线程/锁 + 文件/网络 + DBD/DBM/crypto 组件,又不想引入 C++ 框架;
  • 与 Apache 生态集成(模块开发必须面向 apr API)。

限制与短板(基于实测与官方文档):

  • 仅 C API,无官方 C++ 绑定;异步 I/O 抽象弱(没有现代事件循环封装,epoll/kqueue 需自己基于 apr_pollset 组合);
  • apr 2.x 长期未发布稳定版,1.x 的部分设计(如 pollset、进程锁)较老;
  • DBD 驱动功能面窄(无连接池内置的高级特性),重度数据库场景通常直接用数据库官方客户端库;
  • Windows 上的完整功能依赖 apr-iconv 等附加组件,而 apr-iconv 更新缓慢。

七、同类选型对比

「选 APR 还是选别的」是 C/C++ 服务端开发的常见决策。下表横向对比主流可移植基础库(均为业界长期维护的成熟项目):

库语言许可证定位与强项相对 APR 的取舍
APR / apr-utilCApache-2.0系统调用薄抽象 + 内存池;httpd/SVN 地基最轻量;但功能面窄、无异步框架
GLibCLGPL-2.1数据结构 + 主循环 + GObject 对象系统生态更全(GNOME 系),LGPL 对静态链接不友好
NSPRCMPL-2.0NSS 的可移植层(线程/锁/时间/网络)只为配合 NSS 加密栈,单一用途
POCOC++Boost 许可网络/加密/日志全栈 C++ 框架功能强大但重,C++ 项目才合适
ACEC++ACE 自有许可高性能通信框架(Reactor/Proactor)学习曲线陡,代码侵入性强
Boost.AsioC++Boost 许可现代异步网络库(epoll/kqueue/IOCP)只覆盖网络与并发,无文件池/DBD 等系统面

选型建议(结合上表逐行看):

  • 只做 httpd/SVN/tomcat-native 的依赖或 Apache 模块开发 → 没有选择,就是 APR;
  • 纯 C、想要轻量池化 + 系统抽象、与 Apache 栈协同 → APR 是最优解;
  • C 项目需要事件驱动网络编程为主 → GLib 主循环或直接用系统 epoll 更现代;
  • C++ 项目、需要完整应用框架 → POCO/ACE;只做高并发网络 → Boost.Asio;
  • 需要 NSS 加密栈移植层 → NSPR(通常与 NSS 一起出现)。

八、参考链接