模块化分发机制
在文章 "sylar源码解析------RPC模块之Rock协议" 的最后,我们分析了 RockServer::handleClient
的核心逻辑。在这部分代码中,Sylar 并未直接将请求分发给固定的处理函数,而是通过模块化机制实现了灵活可扩展的 RPC 分发框架。具体而言,当有客户端连接建立、断开或发送请求、通知时,RockServer
会借助 ModuleManager
遍历当前系统中注册的所有 ROCK 类型模块,并依次调用它们的回调函数(如 onConnect
、handleRequest
等)。这种"模块驱动式"的设计,使得 RPC 服务的扩展和接入不再依赖服务器核心逻辑,而是通过注册模块插件即可实现业务隔离、协议抽象与运行时热插拔。
因此,接下来我们将深入分析 Sylar 的模块化机制,重点理解 Module
、ModuleManager
、RockModule
以及其与 RockServer
的协作关系,进而揭示整个 RPC 系统的可插拔服务治理架构是如何实现的。
cpp
void RockServer::handleClient(Socket::ptr client) {
SYLAR_LOG_DEBUG(g_logger) << "handleClient " << *client;
sylar::RockSession::ptr session(new sylar::RockSession(client));
session->setWorker(m_worker);
// 通知所有 ROCK 类型模块有连接建立
ModuleMgr::GetInstance()->foreach(Module::ROCK,
[session](Module::ptr m) {
m->onConnect(session);
});
// 设置连接断开时的回调
session->setDisconnectCb([](AsyncSocketStream::ptr stream) {
ModuleMgr::GetInstance()->foreach(Module::ROCK,
[stream](Module::ptr m) {
m->onDisconnect(stream);
})
});
// 设置请求处理函数
// 当客户端发来一个 RockRequest(ROCK协议的请求包)时,调用这个 lambda。
// 遍历所有注册的 ROCK 模块:
// 调用每个模块的 handleRequest() 来处理请求。
// 如果有一个模块返回了 true(表示处理了该请求),就不再继续调用其他模块(短路逻辑)。
session->setRequestHandler([](sylar::RockRequest::ptr req, sylar::RockResponse::ptr rsp, sylar::RockStream::ptr conn)->bool {
bool rt = false;
ModuleMgr::GetInstance()->forearch(Module::ROCK, [&rt, req, rsp, conn](Module::ptr m) {
if (rt) {
return;
}
rt = m->handleRequest(req, rsp, conn);
});
return rt;
})
// 设置通知处理函数
session->setNotifyHandler([](sylar::RockNotify::ptr nty, sylar::RockStream::ptr conn)->bool {
SYLAR_LOG_INFO(g_logger) << "handleNty " << nty->toString()
<< " body=" << nty->getBody();
bool rt = false;
ModuleMgr::GetInstance()->foreach(Module:; ROCK, [&rt, nty, conn](Module::ptr m) {
if (rt) return;
rt = m->handleNotify(nty, conn);});
return rt;
}
);
// 启动会话,开始异步读写
// 会在设置的m_worker中持续进行
session->start();
}
library.h
核心类
Library
该类是是 Sylar 框架中专门用于动态加载模块(Module)插件的核心工具类,它通过封装 dlopen
/ dlsym
等系统调用机制,实现了对 .so
动态库的加载、模块对象的创建以及资源的自动释放管理。在模块化架构中,每一个模块都被构建为独立的共享库文件,Library::GetModule(path)
会在运行时打开该 .so
文件,提取出符合约定的 CreateModule()
与 DestoryModule()
函数,并以智能指针封装返回模块实例。这使得模块可以按需加载、热插拔部署、与主程序解耦,是 Sylar 实现模块化治理、插件式扩展的关键支撑组件。
仿函数ModuleCloser
用于在智能指针释放 Module* 时执行自定义清理逻辑。
cpp
typedef void (*destory_module)(Module*);
class ModuleCloser {
public:
ModuleCloser(void* handle, destory_module d)
: m_handle(handle), m_destory(d) {
}
// 重载()运算符 仿函数
void operator()(Module* module) {
// 当 std::shared_ptr<Module> 被销毁时会调用这个 operator() 来做清理工作。
std::string name = module->getName();
std::string version = module->getVersion();
std::string path = module->getFilename();
m_destory(module);
int rt = dlclose(m_handle);
if (rt) {
SYLAR_LOG_ERROR(g_logger) << "dlclose handle fail handle="
<< m_handle << " name=" << name
<< " version=" << version
<< " path=" << path
<< " error=" << dlerror();
}
else {
SYLAR_LOG_INFO(g_logger) << "destory module=" << name
<< " version=" << version
<< " path=" << path
<< " handle=" << m_handle
<< " success";
}
}
private:
// 动态库句柄(由dlopen返回)
void* m_handle;
// Module的销毁函数
destory_module m_destory;
};
GetModule()
从指定路径加载一个动态模块(.so
共享库),并返回其对应的 Module 对象智能指针。
注意:
在 Sylar 框架中,每个模块(.so 文件)必须按照统一的接口格式实现下面两个函数:
extern "C" sylar::Module* CreateModule(); 用于创建Module对象
extern "C" void DestoryModule(sylar::Module* m); 用于销毁Module对象
cpp
typedef Module* (create_module)();
typedef void (*destory_module)(Module*);
Module::ptr Library::GetModule(const std::string& path) {
// RTLD_NOW 表示立即解析出所有符号(函数、变量),不延迟
void* handle = dlopen(path.c_str(), RTLD_NOW);
if (!handle) {
SYLAR_LOG_ERROR(g_logger) << "cannot load library path="
<< path << " error=" << dlerror();
return nullptr;
}
// 从共享库中获取函数指针CreateModule
create_module create = (create_module)dlsym(handle, "CreateModule");
if (!create) {
SYLAR_LOG_ERROR(g_logger) << "cannot load symbol CreateModule in "
<< path << " error=" << dlerror();
dlclose(handle);
return nullptr;
}
// 从共享库中获取销毁函数
destory_module destory = (destory_module)dlsym(handle, "DestoryModule");
if (!destory) {
SYLAR_LOG_ERROR(g_logger) << "cannot load symbol DestoryModule in "
<< path << " error=" << dlerror();
dlclose(handle);
return nullptr;
}
Module::ptr module(create(), ModuleCloser(handle, destory));
module->setFilename(path);
SYLAR_LOG_INFO(g_logger) << "load module name=" << module->getName()
<< " version=" << module->getVersion()
<< " path=" << module->getFilename()
<< " success";
// 重新加载配置目录
//强制重新加载配置目录(. / conf 或用户通过 - c 参数指定的路径)。
// 原因:
// 加载模块后,模块可能会注册新的配置项;
// 因此需要重新读取.yml 文件,以确保新模块的配置生效。
Config::LoadFromConfDir(sylar::EnvMgr::GetInstance()->getConfigPath(), true);
// 该module已经具备自动销毁和资源释放的机制
return module;
}
module.h
核心类
Module
该类是 Sylar 框架里用来管理各个功能模块的基础类,它统一了模块的加载、卸载、连接管理和请求处理接口。它既能支持普通模块,也能专门用来做基于 Rock 协议的 RPC 模块,是实现模块化和插件化的基础。
枚举Type
用来区分模块的类型
cpp
enum Type {
MODULE = 0, // 通用模块
ROCK = 1, // 支持ROCK协议的模块
};
成员变量
cpp
std::string m_name; // 模块名称
std::string m_version; // 模块版本
std::string m_filename; // 模块对应的.so文件路径
std::string m_id; // 模块唯一id
uint32_t m_type; // 模块类型(MODULE/ROCK)
成员函数
cpp
/**
* @brief 参数解析前的钩子函数
* @param argc 命令行参数个数
* @param argv 命令行参数数组
*
* 可以在这里做参数检查、预处理等操作
*/
virtual void onBeforeArgsParse(int argc, char** argv);
/**
* @brief 参数解析后的钩子函数
* @param argc 命令行参数个数
* @param argv 命令行参数数组
*
* 可以在这里做参数确认、日志初始化等操作
*/
virtual void onAfterArgsParse(int argc, char** argv);
// 模块加载时的回调函数,返回是否加载成功
virtual bool onLoad();
// 模块卸载时的回调函数,返回是否卸载成功
virtual bool onUnload();
/**
* @brief 有新连接时的回调函数(适用于 ROCK 模块)
* @param stream 与客户端的连接对象
* @return 是否处理成功
*/
virtual bool onConnect(sylar::Stream::ptr stream);
/**
* @brief 连接断开时的回调函数(适用于 ROCK 模块)
* @param stream 与客户端的连接对象
* @return 是否处理成功
*/
virtual bool onDisconnect(sylar::Stream::ptr stream);
/**
* @brief 服务器初始化完成(即将启动)时的回调函数
* @return 是否启动准备成功
*/
virtual bool onServerReady();
/**
* @brief 服务器已经完全启动时的回调函数
* @return 是否启动成功
*/
virtual bool onServerUp();
/**
* @brief 处理请求消息(适用于 ROCK 模块)
* @param req 请求消息对象
* @param rsp 响应消息对象
* @param stream 与客户端的连接对象
* @return 是否处理成功
*/
virtual bool handleRequest(sylar::Message::ptr req,
sylar::Message::ptr rsp,
sylar::Stream::ptr stream);
/**
* @brief 处理通知消息(适用于 ROCK 模块)
* @param notify 通知消息对象
* @param stream 与客户端的连接对象
* @return 是否处理成功
*/
virtual bool handleNotify(sylar::Message::ptr notify,
sylar::Stream::ptr stream);
registerService()
注册服务信息(通常用于服务发现),向服务发现中心(如 ZooKeeper)注册服务节点。
这个模块对外提供服务,外部访问时是通过模块底层服务器监听的IP和端口来连接的。因此,我们需要把模块底层监听的IP和端口信息,连同我们自己传入的域名和服务名称,一起注册到服务发现模块里。这里传入的域名和服务名称,就是用来标识这个模块底层服务器具体提供了什么服务。通过这种注册,外部才能找到并访问到这个模块对应的服务。
cpp
void Module::registerService(const std::string& server_type,
const std::string& domain, const std::string& service) {
// 获取当前应用中的服务发现模块ServiceDiscovery实例
auto sd = Application::GetInstance()->getServiceDiscovery();
if (!sd) return;
std::vector<TcpServer::ptr> svrs;
// 获取指定类型(如 "http")的所有 TcpServer 对象。
if (!Application::GetInstance()->getServer(server_type, svrs)) {
return;
}
// 遍历所有同类型的服务器(一个类型可能启动了多个端口或监听多个地址)。
// 一个 TcpServer 通常会有多个 socket,是因为它可能监听多个地址(IP 或端口),而不是因为有多个客户端连接。
for (auto& i : svrs) {
auto socks = i->getSocks();
for (auto& s : socks) {
// 拿到它的监听地址(服务器端)。
auto addr = std::dynamic_pointer_cast<IPv4Address>(s->getLocalAddress());
if (!addr) {
continue;
}
auto str = addr->toString();
if (str.find("127.0.0.1") == 0) {
// 如果地址是 127.0.0.1,即本地环回地址,不注册(其他服务访问不到它)。
continue;
}
std::string ip_and_port;
if (str.find("0.0.0.0") == 0) {
// 如果监听地址是 0.0.0.0(即监听所有 IP),那就用本机外网 IP 替代 0.0.0.0。
// 不能直接注册 0.0.0.0:8080,服务消费者无法使用这个地址。必须转换为真实 IP
ip_and_port = sylar::GetIPv4() + ":" + std::string(addr->getPort());
} else {
ip_and_port = addr->toString();
}
sd->registerServer(domain, service, ip_and_port, server_type);
}
}
}
RockModule
该类是对 Module 基类的扩展,专门用于处理基于 Rock 协议的通信模块。它在 Module 的基础上,新增了处理 Rock 协议请求和通知消息的纯虚函数,使得继承它的模块必须实现具体的请求和通知处理逻辑。同时,它重载了基类的通用请求和通知处理接口,将底层的 Message 类型消息转换为 Rock 协议的具体请求或通知对象,并调用对应的处理函数,从而实现了对 Rock 协议消息的专门分发和处理。
cpp
// 处理 Rock 协议的请求消息
virtual bool handleRockRequest(sylar::RockRequest::ptr request,
sylar::RockResponse::ptr response,
sylar::RockStream::ptr stream) = 0;
// 处理 Rock 协议的通知消息
virtual bool handleRockNotify(sylar::RockNotify::ptr notify,
sylar::RockStream::ptr stream) = 0;
// 重载基类 Module 的 handleRequest
// 将 Message 转换为 RockRequest/RockResponse 并分发给 handleRockRequest
virtual bool handleRequest(sylar::Message::ptr req,
sylar::Message::ptr rsp,
sylar::Stream::ptr stream) override;
// 重载基类 Module 的 handleNotify
// 将 Message 转换为 RockNotify 并分发给 handleRockNotify
virtual bool handleNotify(sylar::Message::ptr notify,
sylar::Stream::ptr stream) override;
bool RockModule::handleRequest(sylar::Message::ptr req
,sylar::Message::ptr rsp
,sylar::Stream::ptr stream) {
auto rock_req = std::dynamic_pointer_cast<sylar::RockRequest>(req);
auto rock_rsp = std::dynamic_pointer_cast<sylar::RockResponse>(rsp);
auto rock_stream = std::dynamic_pointer_cast<sylar::RockStream>(stream);
return handleRockRequest(rock_req, rock_rsp, rock_stream);
}
bool RockModule::handleNotify(sylar::Message::ptr notify
,sylar::Stream::ptr stream) {
auto rock_nty = std::dynamic_pointer_cast<sylar::RockNotify>(notify);
auto rock_stream = std::dynamic_pointer_cast<sylar::RockStream>(stream);
return handleRockNotify(rock_nty, rock_stream);
}
ModuleManage
该类是 Sylar 框架中用于统一管理系统模块的核心类,它负责对所有模块(Module 实例)进行注册、注销、查找、分类和遍历等操作。该类支持通过模块名称或模块类型(如 MODULE
或 ROCK
)进行访问,同时在连接建立或断开等关键时机,自动调用各个模块的相关钩子函数(如 onConnect
、onDisconnect
),从而实现模块间的事件驱动协作。
成员变量
cpp
// 模块名 -> 模块对象的映射表。
std::unordered_map<std::string, Module::ptr> m_modules;
// 按模块类型分类的模块映射:
// 第一层 key 是模块类型(如 MODULE = 0, ROCK = 1)
// 第二层 key 是模块名,value 是模块对象
std::unordered_map<uint32_t, std::unordered_map<std::string, Module::ptr>> m_type2Modules;]
成员函数
对内部维护的Module集合进行增删改查操作
cpp
Module::ptr ModuleManager::get(const std::string& name) {
RWMutexType::ReadLock lock(m_mutex);
auto it = m_modules.find(name);
return it == m_modules.end() ? nullptr : it->second;
}
void ModuleManager::add(Module::ptr m) {
del(m->getId());
RWMutexType::WriteLock lock(m_mutex);
m_modules[m->getId()] = m;
m_type2Modules[m->getType()][m->getId()] = m;
}
void ModuleManager::del(const std::string& name) {
Module::ptr module;
RWMutexType::WriteLock lock(m_mutex);
auto it = m_modules.find(name);
if (it == m_modules.end()) {
return;
}
module = it->second;
m_module.erase(it);
m_type2Modules[module->getType()].erase(module->getId());
if (m_type2Modules[module->getType()].empty()) {
m_type2Modules.erase(module->getType());
}
lock.unlock();
module->onUnload();
}
void ModuleManager::delAll() {
RWMutexType::ReadLock lock(m_mutex);
auto tmp = m_modules;
lock.unlock();
for(auto& i : tmp) {
del(i.first);
}
}
void ModuleManager::listAll(std::vector<Module::ptr>& ms) {
RWMutexType::ReadLock lock(m_mutex);
for(auto& i : m_modules) {
ms.push_back(i.second);
}
}
void ModuleManager::listByType(uint32_t type, std::vector<Module::ptr>& ms) {
RWMutexType::ReadLock lock(m_mutex);
auto it = m_type2Modules.find(type);
if(it == m_type2Modules.end()) {
return;
}
for(auto& i : it->second) {
ms.push_back(i.second);
}
}
init()/initModule()
从指定的模块目录中加载所有 .so
动态库文件,并将其中定义的模块注册到系统中。init()
函数会先获取模块目录路径,然后扫描该目录下所有以 .so
结尾的文件,并对其排序后逐个调用 initModule()
进行加载。initModule()
则使用 Library::GetModule()
动态加载模块对象,如果加载成功就调用 add()
方法将其添加到模块管理器中,从而完成模块的注册与统一管理。
cpp
void ModuleManager::init() {
auto path = EnvMgr::GetInstance()->getAbsolutePath(g_module_path->getValue());
std::vector<std::string> files;
sylar::FSUtil::ListAllFile(files, path, ".so");
std::sort(files.begin(), files.end());
for(auto& i : files) {
initModule(i);
}
}
void ModuleManager::initModule(const std::string& path) {
Module::ptr m = Library::GetModule(path);
if(m) {
add(m);
}
}
foreach()
对指定类型模块的统一遍历操作,接收一个模块类型(如 Module::ROCK
)和一个回调函数 cb
,首先通过 listByType
获取该类型下的所有模块列表,然后逐个调用回调函数 cb(i)
对每个模块执行指定逻辑。
cpp
void ModuleManager::foreach(uint32_t type, std::function<void(Module::ptr)> cb) {
std::vector<Module::ptr> ms;
listByType(type, ms);
for(auto& i : ms) {
cb(i);
}
}
onConnect()/onDisconnect()
这两个函数用于在连接建立或断开时,统一通知系统中所有已加载的模块。
onConnect
会遍历所有模块,并调用它们的 onConnect
方法,传入新建立的 stream
连接对象;
onDisconnect
则在连接断开时进行同样的处理,调用模块的 onDisconnect
方法。
cpp
void ModuleManager::onConnect(Stream::ptr stream) {
std::vector<Module::ptr> ms;
listAll(ms);
for(auto& m : ms) {
m->onConnect(stream);
}
}
void ModuleManager::onDisconnect(Stream::ptr stream) {
std::vector<Module::ptr> ms;
listAll(ms);
for(auto& m : ms) {
m->onDisconnect(stream);
}
}
在前文我们梳理了 Sylar 框架中模块化机制的核心组成部分,包括 Module
、RockModule
、ModuleManager
、Library
等关键类及其职责和交互方式。每一个模块都可以被动态加载为 .so
文件,并注册到 ModuleManager
中进行统一管理。当有连接建立、断开或收到请求/通知时,ModuleManager
会统一分发事件到所有相关模块中,从而实现模块驱动式的运行时可插拔服务治理架构。
接下来我们结合上面分析的机制,继续解读 RockServer::handleClient()
这一函数的核心逻辑,看看模块系统如何真正参与 RockServer 的请求分发和连接管理过程:
cpp
void RockServer::handleClient(Socket::ptr client) {
SYLAR_LOG_DEBUG(g_logger) << "handleClient " << *client;
sylar::RockSession::ptr session(new sylar::RockSession(client));
session->setWorker(m_worker);
// 通知所有 ROCK 类型模块有连接建立
ModuleMgr::GetInstance()->foreach(Module::ROCK,
[session](Module::ptr m) {
m->onConnect(session);
});
// 设置连接断开时的回调
session->setDisconnectCb([](AsyncSocketStream::ptr stream) {
ModuleMgr::GetInstance()->foreach(Module::ROCK,
[stream](Module::ptr m) {
m->onDisconnect(stream);
})
});
// 设置请求处理函数
// 当客户端发来一个 RockRequest(ROCK协议的请求包)时,调用这个 lambda。
// 遍历所有注册的 ROCK 模块:
// 调用每个模块的 handleRequest() 来处理请求。
// 如果有一个模块返回了 true(表示处理了该请求),就不再继续调用其他模块(短路逻辑)。
session->setRequestHandler([](sylar::RockRequest::ptr req, sylar::RockResponse::ptr rsp, sylar::RockStream::ptr conn)->bool {
bool rt = false;
ModuleMgr::GetInstance()->forearch(Module::ROCK, [&rt, req, rsp, conn](Module::ptr m) {
if (rt) {
return;
}
rt = m->handleRequest(req, rsp, conn);
});
return rt;
})
// 设置通知处理函数
session->setNotifyHandler([](sylar::RockNotify::ptr nty, sylar::RockStream::ptr conn)->bool {
SYLAR_LOG_INFO(g_logger) << "handleNty " << nty->toString()
<< " body=" << nty->getBody();
bool rt = false;
ModuleMgr::GetInstance()->foreach(Module:; ROCK, [&rt, nty, conn](Module::ptr m) {
if (rt) return;
rt = m->handleNotify(nty, conn);});
return rt;
}
);
// 启动会话,开始异步读写
// 会在设置的m_worker中持续进行
session->start();
}
该函数是 RockServer
在接收到客户端连接时负责处理的回调入口,它的整体流程如下:
1.建会话并绑定 worker
这段代码会将当前客户端连接包装成一个 RockSession
,并绑定到指定的 IO 协程调度器 m_worker
上,后续该会话的读写处理都会交给这个线程池去调度执行。
cpp
sylar::RockSession::ptr session(new sylar::RockSession(client));
session->setWorker(m_worker);
2.通知所有 ROCK 模块:有新连接建立
在连接刚建立后,系统会遍历所有已注册的 ROCK 类型模块 (即继承了 RockModule
的模块),并调用它们的 onConnect()
方法,将当前 session
传入。模块可以借此机会进行一些会话级初始化操作,比如绑定用户上下文、创建会话状态、打印日志等。
这正体现了模块驱动式的设计思想:服务模块自己定义连接建立后的行为,而不是写死在服务器逻辑里。
cpp
ModuleMgr::GetInstance()->foreach(Module::ROCK, [session](Module::ptr m) {
m->onConnect(session);
});
3.设置连接断开时的回调
当该连接断开时,会调用 setDisconnectCb()
设置的 lambda,系统同样会遍历所有 ROCK 模块,并依次调用它们的 onDisconnect()
方法。模块可以在此处进行资源回收、状态清理、断连通知等逻辑处理。
cpp
session->setDisconnectCb([](AsyncSocketStream::ptr stream) {
ModuleMgr::GetInstance()->foreach(Module::ROCK, [stream](Module::ptr m) {
m->onDisconnect(stream);
});
});
4.设置请求处理函数(handleRequest)
这是处理客户端 Rock 请求的核心逻辑:
-
客户端发送过来的
RockRequest
会被解析并传入这个 lambda。 -
然后,系统遍历所有 ROCK 类型模块,依次调用每个模块的
handleRequest()
方法。 -
如果某个模块返回了
true
,代表该请求已成功处理,后续模块就不再继续处理(即 短路机制)。
这种方式允许多个模块响应不同类型的业务请求,而不需要改动核心服务器代码,极大增强了系统的扩展性和可插拔性。
cpp
session->setRequestHandler([](sylar::RockRequest::ptr req,
sylar::RockResponse::ptr rsp,
sylar::RockStream::ptr conn) -> bool {
bool rt = false;
ModuleMgr::GetInstance()->foreach(Module::ROCK, [&rt, req, rsp, conn](Module::ptr m) {
if (rt) return;
rt = m->handleRequest(req, rsp, conn);
});
return rt;
});
5.设置通知处理函数(handleNotify)
同理,对于 Rock 协议中的通知类型(RockNotify
),也通过遍历模块的方式分发处理。每个模块可以根据 notify
消息内容决定是否处理;一旦某个模块处理了,就停止继续分发。
cpp
session->setNotifyHandler([](sylar::RockNotify::ptr nty,
sylar::RockStream::ptr conn) -> bool {
bool rt = false;
ModuleMgr::GetInstance()->foreach(Module::ROCK, [&rt, nty, conn](Module::ptr m) {
if (rt) return;
rt = m->handleNotify(nty, conn);
});
return rt;
});
6.启动异步会话
调用 start()
开启这个 RockSession
,进入真正的异步事件处理流程。它会持续监听 socket 上的数据,并根据收到的请求/通知分发给上面注册的处理函数(即模块逻辑)。
cpp
session->start();
至此,sylar RPC框架的"模块驱动式 RPC 分发机制"介绍完毕。
更详细的sylar注释:jinkezhen/sylar: for self study