ceph:消息通信机制小记--研读

目录

Messenger的创建

创建Worker和Epoll句柄

启动线程

加入监听事件

等待并处理事件

服务端bind

服务端listen

处理连接

AsyncConnection的状态转换

AsyncMessenger启动接收线程

AsyncMessenger类图

Send IO

Receive IO


原文: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,流程如下

创建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方式来实现,比如EpollKqueue,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);   // 等待并处理事件
    
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值