求助:基于Apache mod_slotmem_shm的跨Worker共享内存缓存POC实现
使用Apache mod_slotmem_shm实现跨Worker进程共享内存缓存POC指南
一、mod_slotmem_shm的初始化与配置
1. 模块加载
先确认mod_slotmem_shm已安装,在Apache配置文件(httpd.conf或conf.d子目录下的配置文件)中添加加载指令:
LoadModule slotmem_shm_module modules/mod_slotmem_shm.so
注意:模块路径需匹配你的Apache安装环境,比如CentOS通常为
/usr/lib64/httpd/modules/mod_slotmem_shm.so,Ubuntu为/usr/lib/apache2/modules/mod_slotmem_shm.so。
2. 共享内存段配置
用SlotMemShm指令定义共享内存区域,参数依次为内存段名称、总大小、单个slot大小、slot总数:
SlotMemShm my_shared_cache 102400 1024 100 # 解释:创建名为my_shared_cache的内存段,总容量100KB,每个slot占1KB,共100个slot
将配置放入主配置文件或虚拟主机配置块,重启Apache生效。
二、写入共享内存的代码实现(Apache模块开发方式)
mod_slotmem_shm仅支持Apache模块级调用,需编写自定义模块实现数据写入:
1. 模块核心代码
#include <httpd.h> #include <http_config.h> #include <http_protocol.h> #include <ap_slotmem.h> static ap_slotmem_instance_t *slotmem = NULL; // 模块初始化:加载共享内存段 static int my_cache_init(apr_pool_t *p, apr_pool_t *plog, apr_pool_t *ptemp, server_rec *s) { slotmem = ap_slotmem_create(p, "my_shared_cache", 0, 0, APR_SHM_NOSYNC); if (!slotmem) { ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, "Failed to initialize shared memory segment"); return HTTP_INTERNAL_SERVER_ERROR; } return OK; } // 写入数据的请求处理器 static int write_cache_handler(request_rec *r) { if (!r->handler || strcmp(r->handler, "write_cache") != 0) { return DECLINED; } if (!slotmem) { ap_rprintf(r, "Shared memory not initialized"); return HTTP_INTERNAL_SERVER_ERROR; } // 待写入的数据(需保证长度不超过单个slot大小) const char *data = "Cross-worker shared data!"; apr_size_t data_len = strlen(data) + 1; // 包含字符串终止符 // 加锁避免多进程竞争 apr_status_t rv = ap_slotmem_lock(slotmem); if (rv != APR_SUCCESS) { ap_rprintf(r, "Failed to lock shared memory"); return HTTP_INTERNAL_SERVER_ERROR; } // 获取slot 0的指针并写入数据 void *slot_ptr; rv = ap_slotmem_get(slotmem, &slot_ptr, 0); if (rv == APR_SUCCESS) { memcpy(slot_ptr, data, data_len); ap_rprintf(r, "Data written to slot 0 successfully"); } else { ap_rprintf(r, "Failed to access slot 0"); } ap_slotmem_unlock(slotmem); // 解锁 return OK; } // 注册模块钩子与处理器 static void register_hooks(apr_pool_t *p) { ap_hook_post_config(my_cache_init, NULL, NULL, APR_HOOK_MIDDLE); ap_hook_handler(write_cache_handler, NULL, NULL, APR_HOOK_MIDDLE); } module AP_MODULE_DECLARE_DATA my_cache_module = { STANDARD20_MODULE_STUFF, NULL, NULL, NULL, NULL, NULL, register_hooks };
2. 编译与配置
用apxs工具编译模块:
apxs -c -i my_cache_module.c
编译完成后,在Apache配置文件中加载自定义模块:
LoadModule my_cache_module modules/mod_my_cache.so
添加触发写入的路径配置:
<Location "/write-cache"> SetHandler write_cache </Location>
重启Apache后,访问http://你的服务器地址/write-cache即可完成数据写入。
三、跨Worker进程读取共享内存的方法
在上述自定义模块中添加读取处理器:
1. 读取处理器代码
// 读取数据的请求处理器 static int read_cache_handler(request_rec *r) { if (!r->handler || strcmp(r->handler, "read_cache") != 0) { return DECLINED; } if (!slotmem) { ap_rprintf(r, "Shared memory not initialized"); return HTTP_INTERNAL_SERVER_ERROR; } apr_status_t rv = ap_slotmem_lock(slotmem); if (rv != APR_SUCCESS) { ap_rprintf(r, "Failed to lock shared memory"); return HTTP_INTERNAL_SERVER_ERROR; } void *slot_ptr; rv = ap_slotmem_get(slotmem, &slot_ptr, 0); if (rv == APR_SUCCESS) { ap_rprintf(r, "Data from shared memory: %s", (char*)slot_ptr); } else { ap_rprintf(r, "Failed to read slot 0"); } ap_slotmem_unlock(slotmem); return OK; }
在register_hooks函数中注册该处理器:
ap_hook_handler(read_cache_handler, NULL, NULL, APR_HOOK_MIDDLE);
2. 读取路径配置
<Location "/read-cache"> SetHandler read_cache </Location>
重启Apache后,访问http://你的服务器地址/read-cache即可读取共享内存中的数据。
四、关键配置要点与注意事项
- 锁机制强制要求:必须使用
ap_slotmem_lock/ap_slotmem_unlock保证多进程/线程下的数据一致性,否则会出现脏读、数据损坏问题。 - Slot大小适配:配置时需确保单个slot大小足够容纳待存储的数据,超出部分会被截断。
- 内存段名称唯一:
SlotMemShm定义的名称需全局唯一,避免与其他模块的共享内存段冲突。 - 权限检查:Apache运行用户需拥有共享内存的读写权限,否则无法创建或访问内存段。
- 复杂数据序列化:若存储结构体等复杂数据,需自行实现序列化/反序列化逻辑,共享内存仅存储二进制原始数据。
- 异常清理:Apache正常停止时会自动释放共享内存,异常退出可能残留内存段,可通过
ipcs查看、ipcrm命令清理。
五、POC有效性验证方法
- 查看当前Apache Worker进程:
ps aux | grep httpd(Ubuntu为apache2) - 访问
/write-cache写入数据 - 手动杀掉当前处理请求的Worker进程,让Apache启动新Worker
- 访问
/read-cache,若能读取到之前写入的数据,说明跨进程共享生效 - 用多客户端并发请求
/read-cache,验证所有Worker均可读取到一致数据
内容的提问来源于stack exchange,提问作者TKS
相关产品推荐
相关产品推荐

