ceph 网络模块 源码分析 三

本文深入分析了Ceph网络模块SimpleMessenger的实现原理,包括Accepter监听连接、Pipe负责通信、DispatchQueue消息缓冲及处理流程,揭示了其高效稳定运行的内部机制。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

ceph 网络模块 源码分析 三

第一篇第二篇主要讲了ceph网络模块的基材框架:Messenger和Dispatcher.本篇主要讲解下Messenger的主要实现方式之一: SimpleMessenger,它也是目前最稳定的也,应用于生产环境的实现.
网络模块类图

SimpleMessenger

Messenger是一个接口,它定义的了消息发送和接受的等各种接口.SimpleMessenger是Messenger的一个具体实现,它由三个重要的模块组成:

  • Accepter: 负责监听客户端连接请求
  • DispatchQueue: 接收消息的缓冲队列
  • Pipe: 负责具体的接受和发送消息

Accepter

Accepter比较简单,它继承自Thread类,主要负责监听客户端的连接请求.它有两个重要函数bind()和entry().前者负责socket的创建,绑定和监听.后者使用poll监听socket上的请求连接,如果有新请求到来,那么调用Messenger的add_accept_pipe(sd)把socket传递给Pipe,用于数据通信.

int Accepter::bind(const entity_addr_t &bind_addr, const set<int>& avoid_ports)
{
  ...
  // create socket
  listen_sd = ::socket(family, SOCK_STREAM, 0);
  ...
  // bind socket and ip addr
  ::bind(listen_sd, (struct sockaddr *) &listen_addr.ss_addr(), listen_addr.addr_size());
 ...
  // listen
  ::listen(listen_sd, 128);
  return 0;
}
void *Accepter::entry()
{
  ...
  struct pollfd pfd;
  pfd.fd = listen_sd;
  pfd.events = POLLIN | POLLERR | POLLNVAL | POLLHUP;
  while (!done) {
	...
    int r = poll(&pfd, 1, -1);
	...
    if (pfd.revents & (POLLERR | POLLNVAL | POLLHUP))
      break;
    if (done) break;
	...
    // accept
    entity_addr_t addr;
    socklen_t slen = sizeof(addr.ss_addr());
    // 接受连接请求
    int sd = ::accept(listen_sd, (sockaddr*)&addr.ss_addr(), &slen);
    if (sd >= 0) {
   	  ...
   	  // 将accept()返回的socket传递给Pipe类,用于数据通信.
      msgr->add_accept_pipe(sd);
    }
  return 0;
}

Pipe

Pipe是SimpleMessenger中最复杂的组件,它主要包括两个线程:reader_thread和writer_thread,顾名思义,前者负责从socket中读取数据,后者负责向socket中写入数据.在ceph中,对于每一个用于数据通信的socket,或者一个连接,都会新建一个Pipe对象,并启动reader_thread线程.然后将相应的Pipe对象添加到SimpleMessenger的Pipe列表中,.这主要在Messenger的add_accept_pipe()函数中实现.但是一个连接2个线程,对于存在大量连接的系统来说,无疑是资源的浪费.

Pipe *SimpleMessenger::add_accept_pipe(int sd)
{
  lock.Lock();
  Pipe *p = new Pipe(this, Pipe::STATE_ACCEPTING, NULL);
  p->sd = sd;
  p->pipe_lock.Lock();
  p->start_reader();  // 启动Pipe的读线程接收数据
  p->pipe_lock.Unlock();
  pipes.insert(p);
  accepting_pipes.insert(p);
  lock.Unlock();
  return p;
}

Pipe的reader_thread线程读取的数据或者消息的处理有两种策略,一种是快速处理,它由reader_thread线程直接交给dispatcher.ms_fast_dispatch()进行处理,另一种是添加到缓冲队列DispatchQueue中,然后由dispatch_thread线程交给dispatcher.ms_dispatch()进行处理.这两种方式相对,前一种办法效率比较高,但是需要消息本身满足一定的条件.

DispatchQueue

DispatchQueue是用于消息缓冲的类,它主要用于缓冲Pipe中reader_thread线程接受的消息.它也定义了一个dispatch_thread线程,用于处理dispatch_queue队列中的消息.它主要是调用dispatcher.ms_dispatch()对消息进行处理,如下所示:

void DispatchQueue::entry() {
  ...
  pre_dispatch(m);
  // 调用Dispatcher的ms_dispatch处理消息
  msgr->ms_deliver_dispatch(m);
  post_dispatch(m, msize);
  ...
}

SimpleMessenger的启动

这里以monitor守护进程的启动为例.

Messenger *msgr = Messenger::create();
msgr->bind()
Monitor *mon = new Monitor();
...
msgr->start()
mon->init()
  1. 新建Messenger实例,然后bind相应的socket用于数据通信.
  2. 新建mon实例,它是Dispatcher的子类,用于实际的数据处理.
  3. 启动Messenger主要用于启动其中的readper线程,用于回收过期不用的pipe.
  4. 最后在monitor的初始化中启动以上介绍accepter线程和dispatch_queue线程.
int Monitor::init() {
	...
	messenger->add_dispatcher_tail(this);
	...
}
void Messenger::add_dispatcher_tail(Dispatcher *d) { 
    bool first = dispatchers.empty();
    dispatchers.push_back(d);
    if (d->ms_can_fast_dispatch_any())
      fast_dispatchers.push_back(d);
    if (first)
      ready();
  }
void SimpleMessenger::ready()
{
  ldout(cct,10) << "ready " << get_myaddr() << dendl;
  dispatch_queue.start();
  lock.Lock();
  if (did_bind)
    accepter.start();
  lock.Unlock();
}
  1. 最后是reader_thread和writer_thread线程的启动.读线程是服务端接受连接请求的时候启动的,即前面介绍的add_accept_pipe()中.写线程的服务端调用send_message()发送消息的过程中启动的,在connect_rank()函数中实现

SimpleMessenger的终止

msgr->wait();
delete msgr;
delete mon;

当进程中止的时候,会调用Messenger的wait()函数,它会依次关闭accepter, reader_thread, writer_thread, dispatch_thread, reaper_thread以及其他相关线程,然后delete msgr和mon即可.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值