前言
Application 模块化,全网分析的文章不多。
我觉得这个算是较为基础的 Application 设计了,实现了类似于 Spring 的设计。(业务实现和基础架构分离。)
application 模块化动态加载
关键流程 ⭐
- 按照约定,每个动态加载的模块,都要继承
Module基类,实现onLoad模块装载,onUnload模块卸载,onServerReady服务器启动前。其中onServerReady中 负责 获取 全局变量下的 服务器,为其注册Servlet路由;按照约定,还需要,使用extern C对外暴露 两个函数符号,分别是CreateModule和DestoryModule。CreateModule函数负责 创建并返回Module基类指针指向 派生类对象。 - 应用初始化(
Application::init())阶段,ModuleManager模块管理器 扫描指定模块路径下的所有.so文件; - 对每个
.so文件,通过dlopen加载动态库,找到CreateModule(创建模块实例)和DestoryModule(销毁模块实例)符号。创建模块实例,并注册到ModuleManager管理器 中。 - 在 服务器 配置完成 后,在正式
start启动前,调用Module相应的onServerReady(),把相关的Servlet操作加载到对应的HTTP Server实例中。
项目里除了基础的 Module,还有 RockModule,NameServerModule。
接口
- Module 基类:模块接口标准化
- 核心作用:定义所有模块必须实现的接口,规范模块的初始化、销毁、事件处理等行为。
- 关键接口:
- 生命周期管理:
onLoad()(加载时初始化)、onUnload()(卸载时清理); - 服务器事件回调:
onServerReady()(服务器就绪前)、onServerUp()(服务器启动后); - 连接事件处理:
onConnect()(连接建立时)、onDisconnect()(连接断开时); - 消息处理:
handleRequest()(处理请求消息)、handleNotify()(处理通知消息)。
- 生命周期管理:
- ModuleManager:模块全局管理器
- 单例模式:通过
ModuleMgr单例全局访问,负责模块的注册、卸载、查询与事件分发。 - 核心功能:
- 模块加载:
init()扫描配置的模块路径(默认module/),加载所有.so动态库; - 模块管理:
add()注册模块、del()卸载模块(触发onUnload())、get()按名称查询模块; - 事件分发:连接建立 / 断开、服务器状态变更等事件,通过
onConnect()、onServerUp()等方法同步通知所有模块; - 类型分类:按模块类型(type)维护映射表,支持按类型遍历模块(
foreach())。
- 模块加载:
- 线程安全:使用读写锁(
RWMutex)保护模块容器(m_modules、m_type2Modules),确保多线程环境下的安全访问。
- Library:动态库加载工具
- 核心作用:封装
dlopen、dlsym等系统调用,实现动态库的加载与模块实例的创建。 - 加载流程:
a. 通过
dlopen加载指定路径的 .so 动态库,获取句柄; b. 查找动态库中导出的CreateModule(创建模块实例)和DestoryModule(销毁模块实例)符号; c. 调用CreateModule生成Module实例,通过自定义删除器(ModuleCloser)管理生命周期(销毁时调用DestoryModule并dlclose释放句柄); d. 加载成功后自动读取配置文件,更新模块相关配置。
dlopen 加载过程⭐
- 查找库文件:绝对路径 > 环境变量 > 项目下的缓存库 > 默认系统目录
- 加载到内存:内核通过
mmap把.so文件的代码段,数据段映射到当前进程的虚拟地址空间 - 重定向:
- 动态链接库
ld.so解析.so中的符号引用,把函数地址,全局变量地址修正到正确的位置。 - 如果使用
RTLD_LAZY,符号地址在第一次调用时才解析
- 动态链接库
- 指向初始化代码
- a. 调用库里的
.init_array段(构造函数),完成全局对象初始化。
- a. 调用库里的
- 返回句柄,用户通过返回的
void*句柄,同dlsym查找符号地址,调用函数。