【ceph】AsyncMessenger 通信模块基础A1--编辑中

本文详细解析了Ceph的三种网络通信模式,重点介绍了async模式的Epoll+EventCenter实现和xio模式的加速通信库Accelerio应用,以及它们在处理大规模并发和性能优化中的作用。还涵盖了Messenger实例及其在OSD角色中的分工和EventCenter的工作原理。

ceph网络通信三种实现方式


ceph的网络通信的源码在src/msg下,有Ceph Messager的三个实现:simple,async,xio。

simple模式:2021:基本不用了,不建议看了)


simple模式相对比较简单,一个链接的每一端都有两个线程,分别负责读和写,当集群规模很大的时候,会有大量线程,占用大量资源,限制了网络性能。(SimpleMessenger是网络层最早的实现,当时的网络编程模型还是线程当道,因此simple采用的就是这种简单粗暴的线程模型)

async模式:2021:当前主要模式)


async模式基于Epoll + EventCenter,这也是当前的默认消息机制。(async主要是国内ceph大神麦子迈 王豪迈大神的实现的。随着linux提供epoll这样的系统调用,可以提供基于事件的IO多路服用,越来越多的网络开始使用该系统调用来实现高并发通信。比如libevent。async也是如此。)

xio模式(2021:不太了解)


xio模式基于开源网络通信库accelio来实现,目前处于试验阶段。(xio使用了开源网络通信模块accelio,需要依赖第三方的库,目前也在实验阶段。)

从Messenger.cc 这个类中可以看出在create Messager实例的时候根据type选择网络框架是simple/async/xio.其中选择xio的话需要的话需要定义HAVE_XIO 这个宏,并且xio支持tcp/ip。infiniband 这两种协议,而前两种支持tcp/ip 协议。


AsyncMessenger

相关的类

message数据格式

见:https://blog.csdn.net/bandaoyu/article/details/121315682

Messenger实例名称

在main()函数中首先创建Messenger,然后对注册的Messenger进行bind,绑定后start消息模块进行工作,不同的角色会启动相应的守护进程,如果OSD在main()函数中注册了6个Messenger的实例,如下表所示。

编号    Messenger实例名称    作用
1    *ms_public    用来处理OSD和Client之间的消息
2    *ms_cluster    用来处理OSD和集群之间的消息
3    *ms_hbclient    用来处理OSD和其它OSD保持心跳的消息
4    *ms_hb_back_server    用来处理OSD接收心跳消息
5    *ms_hb_front_server    用来处理OSD发送心跳消息
6    *ms_objecter    用来处理OSD和Objecter之间的消息

EventCenter


worker 线程 while循环,执行EventCenter::process_events。这里事件有三类:

  • time_events:定时事件,比如connection的定时函数AsyncConnection::tick。
  • external_events:外部事件,放进去里面立马唤醒线程执行,send_message就是外部事件。
  • file事件:epoll监听的事件,与文件(如socket连接fd)绑定,当fd可读or可写时执行。

例子如下:

time_events:定时事件,比如connection的定时函数AsyncConnection::tick。

register_time_events.insert(center->create_time_event(delay_period*1000000, this));


external_events:外部事件,放进去里面立马唤醒线程执行,send_message就是外部事件。

Messenger::send_message(Message *m, dest)
AsyncMessenger::send_message(Message *m, dest)
--|AsyncMessenger::_send_message(Message *m, dest)
----|conn=_lookup_conn(dest.addr)  
----|AsyncMessenger::submit_message(Message *m,conn,dest,...)
------|conn->send_message(m) # AsyncConnection::send_message(Message *m)
--------|out_q[priority].emplace_back(std::move(bl),m)  #放入队列

//回调操作(write_handler= new C_handle_write(this))放入event中心,wakeup线程执行
--------|EventCenter::dispatch_event_external(write_handler) 
----------|external_event.push_back(write_handler) //放入事件中,就可以唤醒线程去执行
----------|wakeup()
            |
            |
 w->center.process_events
             |
         cb = event->read_cb;
         cb->do_request()
             |C_handle_write
             |
             |
--|write_handler = new C_handle_write(this)


file事件:epoll监听的事件,与文件(如socket连接fd)绑定,当fd可读or可写时执行,如

int r = create_file_event(notify_receive_fd, EVENT_READABLE, notify_handler);

notify_receive_fd可读时,process_events将执行回调notify_handler


http://t.csdn.cn/l41qs

建立连接的过程

Ceph 数据IO会通过AsyncMessenger 收发数据,所以以OSD的是数据收发为切入点,学习AsyncMessenger

ceph版本10.2.5

一、client如何与osd进行建立通信的呢?

  1. osd是运行在服务器上的进程,进程的主体在ceph_osd.cc中的main函数开始,并且申请一些用于服务的线程。比如这里用于接收客户端连接的线程Accepter。如果配对连接成功后,会把连接成功的socket交给SimpleMessager来处理。
//ceph_osd.cc
Messenger *ms_public = Messenger::create(g_ceph_context, g_conf->ms_type,
					   entity_name_t::OSD(whoami), "client",
					   getpid());
  1. 在ceph_osd.cc 中main函数中 会申请一个Messager 消息管理的类,该类是一个基类,最终会选择SimpleMessager类实例,SimpleMessager类中会有一个叫做Accepter的成员,会申请该对象,并且初始化。

然后在main中把它绑定一个固定的ip,这个ip根据配置文件来获取。3.这个ip最终会绑定在Accepter中。然后在Accepter->bind函数中,会对这个ip初始化一个socket,并且保存为listen_sd。接着会启动Accepter->start(),这里会启动Accepter的监听线程,这个线程做的事情放在Accepter->entry()函数中。接下来看看这个函数中实现了什么。

//SimpleMessenger.cc
int SimpleMessenger::bind(const entity_addr_t &bind_addr)
{
  // bind to a socket
  set<int> avoid_ports;
  int r = accepter.bind(bind_addr, avoid_ports);
}

//Accepter.cc
int Accepter::bind(const entity_addr_t &bind_addr, const set<int>& avoid_ports)
{
  /* socket creation */
  listen_sd = ::socket(family, SOCK_STREAM, 0);
	...
	rc = ::bind(listen_sd, (struct sockaddr *) &listen_addr.ss_addr(), listen_addr.addr_size());
	...
  // listen!
  rc = ::listen(listen_sd, 128);
}

//Accepter.cc
int Accepter::start()
{
  // start thread
  create("ms_accepter"); //转到线程创建函数,自动调用entry()
}

//Accepter.cc
void *Accepter::entry()
{
  while (!done) {//开始一个循环,线程不能结束。直到停止这个线程时。
    int r = poll(&pfd, 1, -1);//poll监听一个端口,等待客户端来进行连接。
    // accept
	......
    int sd = ::accept(listen_sd, (sockaddr*)&addr.ss_addr(), &slen);//如果有客户端来连接时,如果连接成功则进行下面的处理
    if (sd >= 0) {
      /*注意这一句,add_accept_pipe,即创建专门的pipe负责此次通信,而线程继续accept,而不是停下来处理通信请求*/
      msgr->add_accept_pipe(sd); //msgr 是具体实现类为SimpleMessager,跟踪到实现的函数中去
    } 
  }
}

//SimpleMessenger.cc
Pipe *SimpleMessenger::add_accept_pipe(int sd)
{
  Pipe *p = new Pipe(this, Pipe::STATE_ACCEPTING, NULL);//当接收到一个连接时,这时需要建立一个数据通道pipe,。
  p->sd = sd;//告知pipe 处理socket为sd。也就是从这个sd读取数据。
  p->pipe_lock.Lock();
  p->start_reader();//启动pipe的读线程,从sd中读取message信息
  p->pipe_lock.Unlock();
  pipes.insert(p);
  accepting_pipes.insert(p);//将pipe加入到SimpleMessager中的accepting_pipes,便于管理。
  lock.Unlock();
  return p;
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.

二、消息的传递

  1. 接下来已经建立了pipe的通道,马上就要开始进行message的接收, 目前接收message的工作交给了pipe的reader线程。接下来看看pipe->reader()做了什么事情。

pipe->reader()中会先 read_message(),将传递的消息解析出来,然后把这个消息放入in_q->fast_dispatch(m)中处理,继续发送到OSD::ms_fast_dispatch中处理,自此转到OSD模块处理,在ms_fast_dispatch中会将message 转化为OpRequestRef op,后续直接对这个op进行处理。继续经过dispatch_session_waiting()、dispatch_op_fast()、handle_op()。handle_op()中开始将这个处理由OSD转化为PG的处理。

// Pipe.cc
void Pipe::reader()
{
if (tag == CEPH_MSGR_TAG_ACK) {
...
}
else if (tag == CEPH_MSGR_TAG_MSG) {
      ldout(msgr->cct,20) << "reader got MSG" << dendl;
      Message *m = 0;
      int r = read_message(&m, auth_handler.get());//接收消息
	}
	m->set_connection(connection_state.get());//创建了一个connection
	in_q->fast_preprocess(m);//void OSD::ms_fast_preprocess(Message *m)
	
	if (delay_thread) {
	...
	}
	} else {
        if (in_q->can_fast_dispatch(m)) {//判断消息是否可以快速处理
          in_q->fast_dispatch(m);// 主调用函数,后依次展开。
	  }
        } else {
          in_q->enqueue(m, m->get_priority(), conn_id);// in_q:接收到消息的队列, out_q:准备发送的消息队列
        }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.

备注:
通过调用函数in_q->can_fast_dispatch(m)判断消息是否可以快速处理,如果可以进行fast_dispatch,那么调用in_q->fast_dispatch(m)进行处理,最终调用(*p)->ms_fast_dispatch(m);如果不可以,那么调用函数in_q->enqueue(m, m->get_priority(), conn_id),把接收到的消息加入到DispatchQueue的mequeue队里中,用DispatchQueue的分发线程ms_dispatch进行处理。
注意:ms_dispatch和ms_fast_dispatch两种处理区别在于:ms_dispatch是由DispatchQueue的线程处理的,是一个单线程,阻塞式处理;ms_fast_dispatch是Pipe的接收线程ms_pipe_read直接调用处理的,它的性能高于前者。

// DispatchQueue.cc
void DispatchQueue::fast_dispatch(Message *m)
{
  uint64_t msize = pre_dispatch(m);
  msgr->ms_fast_dispatch(m);//---
  post_dispatch(m, msize);
}

// Messenger.h
  void ms_fast_dispatch(Message *m) {
    for (list<Dispatcher*>::iterator p = fast_dispatchers.begin();
	 p != fast_dispatchers.end();
	 ++p) {// 循环fast_dispatchers,下发到osd端
      if ((*p)->ms_can_fast_dispatch(m)) {
	(*p)->ms_fast_dispatch(m);// 这里调用osd.cc 中的ms_fast_dispatch函数
	return;
      }
    }
  }

//OSD类继承Dispatcher类
class OSD : public Dispatcher类, public md_config_obs_t 
			{
			
			}

//OSD.cc 读写消息的入口函数。客户端读写IO都是从该函数开始,而不是ms_dispatch
void OSD::ms_fast_dispatch(Message *m)
{
OpRequestRef op = op_tracker.create_request<OpRequest, Message*>(m);//message - > OpRequest
OSDMapRef nextmap = service.get_nextmap_reserved();//最新的osdmap
Session *session = static_cast<Session*>(m->get_connection()->get_priv());//conn - > session
  if (session) {
    {
      update_waiting_for_pg(session, nextmap);// 更新session里保存的osdmap信息
      session->waiting_on_map.push_back(op);// 把op放入 session 的waiting_on_map list列表中
      dispatch_session_waiting(session, nextmap);//处理等待发送的session
    }
    session->put();
  }
}

//OSD.cc
void OSD::dispatch_session_waiting(Session *session, OSDMapRef osdmap)
{
  for (list<OpRequestRef>::iterator i = session->waiting_on_map.begin();
       i != session->waiting_on_map.end() && dispatch_op_fast(*i, osdmap);
       session->waiting_on_map.erase(i++));// 如果session->waiting_on_map list中有op,就调用dispatch_op_fast函数进行处理
	......
}

//OSD.cc
bool OSD::dispatch_op_fast(OpRequestRef& op, OSDMapRef& osdmap)
{
switch(op->get_req()->get_type()) {//message::header.type
// client ops
case CEPH_MSG_OSD_OP:
    handle_op(op, osdmap);//go---
    break;
    // for replication etc.
 case MSG_OSD_SUBOP:
    handle_replica_op<MOSDSubOp, MSG_OSD_SUBOP>(op, osdmap);
    break;
  case MSG_OSD_REPOP: //如果是副本请求,那么直接调用这里,最终会和handle_op()函数一样,调用enqueue_op,然后和主副本请求流程一致,直到下述ReplicatedPG::do_request中调用函数handle_message。
    handle_replica_op<
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值