目录
原文:https://zhuanlan.zhihu.com/p/111914519
分布式存储系统,需要一个稳定的网络通信机制,来实现客户端和服务端的消息通信。ceph有三种消息通信框架:simple,xio和async。目前只研究了async。
Messenger的创建
消息的收发和处理都是异步的,所以必须需要单独的模块来处理。所有模块(mon,osd, mds等)在启动阶段都会创建一个Messenger。既然是async的通信框架,那必须有单独的线程来处理消息收发,所以在创建Messenger时必须启动多个线程。以ceph-mon为例,创建Messenger
Messenger *msgr = Messenger::create(g_ceph_context, public_msgr_type,entity_name_t::MON(rank),
"mon", 0, Messenger::HAS_MANY_CONNECTIONS)
Messenger::create
Messenger *Messenger::create(CephContext *cct, const string &type, entity_name_t name, string lname,
uint64_t nonce, uint64_t cflags)
{ // name = entity_name_t::MON(rank), lname = "mon"
...
else if (r == 1 || type.find("async") != std::string::npos)
return new AsyncMessenger(cct, name, type, std::move(lname), nonce);
...
}
AsyncMessenger中的几个重要成员
class AsyncMessenger : public SimplePolicyMessenger {
private:
NetworkStack *stack; // 起线程
std::vector<Processor*> processors; // 主要用来监听连接
DispatchQueue dispatch_queue; // 用来分发消息
ceph::unordered_map<entity_addr_t, AsyncConnectionRef> conns // 保存已建立的连接
...
}
接下来就是new AsyncMessenger的过程。
AsyncMessenger::AsyncMessenger(CephContext *cct, entity_name_t name,
const std::string &type, string mname, uint64_t _nonce)
: SimplePolicyMessenger(cct, name,mname, _nonce), dispatch_queue(cct, this, mname), ...
{
...
StackSingleton *single;
// 创建一个StackSingleton的单例
cct->lookup_or_create_singleton_object<StackSingleton>(single, "AsyncMessenger::NetworkStack::"+transport_type);
single->ready(transport_type);
stack = single->stack.get();
stack->start();
// 获取worker
local_worker = stack->get_worker();
local_connection = new AsyncConnection(cct, this, &dispatch_queue, local_worker);
init_local_connection();
reap_handler = new C_handle_reap(this);
unsigned processor_num = 1;
if (stack->support_local_listen_table())
processor_num = stack->get_num_worker();
for (unsigned i = 0; i < processor_num; ++i)
processors.push_back(new Processor(this, stack->get_worker(i), cct));
}
AsyncMessenger初始化的过程很简单,先将几个重要的成员初始化。这里面比较重要的是stack的初始化,先是创建一个StackSingleton的单例
struct StackSingleton {
CephContext *cct;
std::shared_ptr<NetworkStack> stack;
StackSingleton(CephContext *c): cct(c) {}
void ready(std::string &type) {
if (!stack)
stack = NetworkStack::create(cct, type);
}
}
然后通过single->ready --> NetworkStack::create来创建NetwrokStack,流程如下
![](https://i-blog.csdnimg.cn/blog_migrate/b587ce7945c51b6d16f612eef9948a4b.png)
创建Worker和Epoll句柄
NetworkStack::create
std::shared_ptr<NetworkStack> NetworkStack::create(CephContext *c, const string &t)
{
if (t == "posix")
return std::make_shared<PosixNetworkStack>(c, t);
...
}
NetworkStack和子类PosixNetworkStack中几个重要成员如下:
class NetworkStack : public CephContext::ForkWatcher {
std::string type; // type = "posix"
unsigned num_workers = 0; // num_workers = 3
...
std::function<void ()> add_thread(unsigned i);
protected:
CephContext *cct;
vector<Worker*> workers; // 存着三个PosixWorker
...
}
class PosixNetworkStack : public NetworkStack {
vector<int> coreids;
vector<std::thread> threads; // threads存着三个add_thread()中的匿名函数
...
}
NetworkStack中的workers用来保存多个Worker,每个Worker都会创建一个Epoll(大多的网络编程中,都会使用基于事件通知的异步网络IO方式来实现,比如Epoll和Kqueue,ceph的网络模块使用的是Epoll)。在NetworkStack的构造函数中,会创建三个Worker
NetworkStack::NetworkStack(CephContext *c, const string &t): type(t), started(false), cct(c)
{
const uint64_t InitEventNumber = 5000;
num_workers = cct->_conf->ms_async_op_threads; // num_workers = 3
for (unsigned i = 0; i < num_workers; ++i) {
Worker *w = create_worker(cct, type, i);
w->center.init(InitEventNumber, i, type);
workers.push_back(w);
}
cct->register_fork_watcher(this);
}
NetworkStack::create_worker如下
Worker* NetworkStack::create_worker(CephContext *c, const string &type, unsigned i)
{
if (type == "posix")
return new PosixWorker(c, i);
...
}
Worker和PosixWorker中几个重要成员如下:
class Worker : public Thread {
...
EventCenter center;
...
}
class PosixWorker : public Worker {
NetHandler net;
...
}
EventCenter(事件中心)是一个处理事件的数据结构,相当于一个事件处理的容器。它本身并不真正去处理事件,通过回调函数的方式来完成事件的处理。同样,如何获取需要处理的事件也不是事件中心来完成的,它只负责处理,具体对需要处理的事件的获取是通过EventDriver来完成的。EventDriver是一个接口类,其实现主要是由EpollDriver、KqueueDriver和SelectDriver三个类操作的。Ceph支持多种操作系统的使用,如果使用的是Linux操作系统,使用EpollDriver,如果是BSD,使用KqueueDriver,如果都不是的情况下再使用SelectDriver(系统定义为最坏状况下)。EpollDriver封装了epoll的接口,事件驱动的执行主要依赖于epoll的方式,其中主要有三个函数:epoll_create,创建epoll句柄; epoll_ctl,将被监听的描述符fd添加到epoll句柄或从epoll句柄中删除或者对监听事件进行修改;epoll_wait,等待事件触发(观察就绪列表里面有没有数据,并进行提取和清空就绪列表,非常高效)。
class EpollDriver : public EventDriver {
int epfd;
struct epoll_event *events;
CephContext *cct;
int size;
...
}
启动线程
在AsyncMessenger的构造函数中创建Worker后,就该启动Worker中的线程,在启动线程的过程中,重要的有两点:1,加入监听事件;2,等待并处理事件。
从stack->start()开始,代码如下
void NetworkStack::start()
{
...
for (unsigned i = 0; i < num_workers; ++i) {
if (workers[i]->is_init())
continue;
std::function<void ()> thread = add_thread(i); // add_thread返回一个匿名函数
spawn_worker(i, std::move(thread));
}
...
}
遍历workers,执行spawn_worker,在spawn_worker中执行std::thread(func)启动线程。
void spawn_worker(unsigned i, std::function<void ()> &&func) override {
threads.resize(i+1);
threads[i] = std::thread(func);
}
线程执行的函数func如下
[this, w]() {
char tp_name[16];
sprintf(tp_name, "msgr-worker-%u", w->id);
ceph_pthread_setname(pthread_self(), tp_name);
const uint64_t EventMaxWaitUs = 30000000;
w->center.set_owner(); // 加入监听事件
w->initialize();
w->init_done();
while (!w->done) {
ceph::timespan dur;
int r = w->center.process_events(EventMaxWaitUs, &dur); // 等待并处理事件