Ceph网络模块使用案例:OSD心跳检测机制

ceph是一个分布式的文件系统。对于一个分布式系统,需要一个稳定的底层网络通信模块(消息模块),用于组件之间的互联互通。

 

ceph的组件主要包括,OSD,monitor,mgr,osdclient,client等,这些模块内部的通信,以及模块间的通信都使用了,后面我们直接把这些组件称为网络模块的使用者。使用者在使用网络模块的时候,主要涉及到2个角色,一个是messenger,一个是dispatcher。messenger相当于一个消息管理器(网络模块的核心,消息的发送和接收都是messenger通过底层的类实现),dispatcher相当于一个消息的处理器。

 

-messenger

  • ① 将dispatcher移交给它的信息发送给其它节点(节点,这里节点是一个逻辑单元,比如osd.0就算是一个节点,osd.1算一个新的节点)处理
  • ② 从其它节点的messenger获取消息移交给本节点的dispatcher。

-dispatcher

  • ① 对接收到的消息(有messenger移交给它)进行处理
  • ② 把需要发送的消息移交给本节点的messenger

每个ceph组件都会注册多个messenger和多个dispatcher用于处理不同类型的消息。以OSD组件为例,OSD的守护进程启动的时候会注册7个messenger来管理消息,每个messenger的用途不一样。这部分代码在ceph osd守护进程启动中 /src/ceph_osd.cc。 每一个OSD都有一个守护进程(OSD deamon)。这个deamon负责完成OSD的所有逻辑功能,包括与monitor和其他OSD(事实上是其他OSD的deamon)通信以维护更新系 统状态,与其他OSD共同完成数据的存储和维护,与client通信完成各种数据对象操作等等。

 

例子:一个OSD模块会注册7个messenger和2个dispatcher。

 

 

编号

Messenger实例名称

作用

1

*ms_public

用来处理OSD和Client之间的消息

2

*ms_cluster

用来处理OSD和集群之间的消息

3

*ms_hb_front_client

用来向其它OSD发送心跳的消息

4

*ms_hb_back_client

用来向其它OSD发送心跳的消息

5

*ms_hb_back_server

用来接收其他OSD的心跳消息

6

*ms_hb_front_server

用来接收其他OSD的心跳消息

7

*ms_objecter

用来处理OSD和Objecter之间的消息

 

编号

Dispatcher实例名称

作用

1

*OSD

可以处理部分osd节点的消息

2

*heartbeat_dispatcher

处理心跳连接

1 消息模块的使用框架

1.1 发送消息

本节描述ceph中的应用(osd、mgr、monitor等)是如何使用网络模块发送消息的。在实际的应用中,有两种常见的发送消息的方式,当然这两种方式只是看起来有些不同,的底层实现都是相同,都是调用AsyncConnection::send_message(Message *m)把消息发送出去。

1)方式一:使用AsyncMessenger::send_message(Message *m, const entity_inst_t& dest))

其中Message *m是要发送的消息,dest是目的地址。

send_message会首先去判断自己和目标地址之前是不是已经存在链接Connection,如果没有就创建一个,conn->send_message(m)发送消息

-----------------------------------------------------------

  conn->send_message(m)

  -----------------------------------------------------------

2) 方式二:

① 首先要通过Messenger类,获取对应的Connection:

  ------------------------------------------------------------

  conn = messenger->get_connection(dest_server);

  ------------------------------------------------------------

  get_connection过程是这样的,如果dest.addr是my_inst.addr,就直接返回local_connection。

  如果链接不存在就新建一个。

② 当获得一个Connection之后,就可以调用Connection的发送函数来发送消息。

  -----------------------------------------------------------

  conn->send_message(m)

  -----------------------------------------------------------

具体发送的实现过程依赖于选择的消息模式,simple、async等实现方式都不同。在另一个文章里我会讲到具体实现过程,这里不多做解释。

1.2 消息的接收

消息的接收过程简言之就是通过监听socket判断是否有消息到来,如果有就接收。这个过程是个很复杂的过程,涉及到了连接建立、错误处理等等。具体的实现依赖于选择的消息模式,比如,SimpleMessenger是使用一个read线程来实现;AsyncMessenger是使用基于事件的机制实现。接收的过程对应用层都是透明的,本章不做解释。

1.3 消息的处理

消息接收完成后,就进入消息的处理。首先判断消息m是否可以fast_dispatch,如果可以,调用注册fast_dispatcher函数处理消息。如果不能fast_dispatch,调用函数in_q->enqueue,将接收到的消息加入到DispatchQueue的mqueue队列中,排队等待处理。

2 ceph OSD心跳检测与网络模块 

下面我们具体举一个OSD心跳检测的例子来讲解,通过心跳检测机制来了解网络模块的使用。在ceph中需要通过心跳检测来判断OSD是不是在线,因为这部分的功能比较简单独立。

2.1 Messenger & Dispatcher的注册

在OSD模块注册的7个Messenger和2个Dispatcher中,4个Messenger都和心跳检测相关,一个heartbeat_dispatcher用来处理心跳连接。

 

编号

Messenger实例名称

作用

1

*ms_public

用来处理OSD和Client之间的消息

2

*ms_cluster

用来处理OSD和集群之间的消息

3

*ms_hb_front_client

用来向其它OSD发送心跳的消息

4

*ms_hb_back_client

用来向其它OSD发送心跳的消息

5

*ms_hb_back_server

用来接收其他OSD的心跳消息

6

*ms_hb_front_server

用来接收其他OSD的心跳消息

7

*ms_objecter

用来处理OSD和Objecter之间的消息

对应代码在ceph_osd.cc中

-------------------------------------------------------------------------------------------------------

-HeartBeatMessenger

在ceph守护进程启动过程中(ceph-osd.cc),创建了4个messenger用于心跳检测。

 

在ceph守护进程创建osd的时候将这些messenger传给了osd, 注意这些messenger在osd中被重命名了。

重命名为了:

hb_front_client_messenger

hb_back_client_messenger

hb_front_server_messenger

hb_back_server_messenger

 

---------------------------------------------------------------------------------------------------

 

编号

Dispatcher实例名称

作用

1

*OSD

可以处理部分osd节点的消息

2

*heartbeat_dispatcher

处理心跳连接

对应代码在osd.cc中

-------------------------------------------------------------------------------------------------------

  hb_front_client_messenger->add_dispatcher_head(&heartbeat_dispatcher);

  hb_back_client_messenger->add_dispatcher_head(&heartbeat_dispatcher);

  hb_front_server_messenger->add_dispatcher_head(&heartbeat_dispatcher);

  hb_back_server_messenger->add_dispatcher_head(&heartbeat_dispatcher);

-------------------------------------------------------------------------------------------------------

2.2 心跳机制介绍

OSD 中有一个heartbeat_thread,这个heartbeat_thread的作用就是不断的发送ping请求给其他节点。在Ceph中,OSD的地位都是对等的,每一个OSD在向其他OSD发送ping消息的同时,也会收到其他OSD发来ping消息。OSD收到ping消息后会发送一个回复消息reply message。在部署ceph的时候,通常会使用两张网卡front和back,将流量分开。所以OSD使用两对messenger和来分别发送和监听front额back的ping心跳。图中展示了一个3个OSD的ceph集群的心跳检测过程,只画出了front网卡的心跳检测,back雷同。

 

2.3 心跳检测中网络模块的使用

 

我们着重看两个osd节点间如何通信。上图展示了OSD A向OSD B发送Ping消息(hb_front_client_messenger(A) ->ms_hb_front_server(B)),然后OSD B收到消息后交给dispatcher进行处理,然后发送回复消息给OSD A (hb_front_client_messenger(A) <-ms_hb_front_server(B))的过程。具体的可以拆分成5个步骤:

  • ① 连接(connection)建立
  • ② Ping消息发送
  • ③ Ping消息接受及处理
  • ④ 回复消息
  • ⑤ 处理PING_REPLY

 

下面对这五个步骤进行一一介绍。

① 连接(connection)建立

获取目标 messenger B的链接connection

conn =hb_front_client_messenger(A)->get_connection(dest_server_B),如果没有链接就创建一个。

monitor中的osdmap记录了每一个osd的front地址和back地址,这个是在osd启动的时候就告诉monitor的。

OSDService::get_con_osd_hb(),首先先获取osdmap,获取目标osdB的front地址,然后在A的messenger hb_front_client_messenger中创建一个connection。

pair<ConnectionRef,ConnectionRef> OSDService::get_con_osd_hb(int peer, epoch_t from_epoch)
{
  OSDMapRef next_map = get_nextmap_reserved();
  // service map is always newer/newest
  assert(from_epoch <= next_map->get_epoch());

  pair<ConnectionRef,ConnectionRef> ret;
  if (next_map->is_down(peer) ||
      next_map->get_info(peer).up_from > from_epoch) {
    release_map(next_map);
    return ret;
  }
  ret.first = osd->hb_back_client_messenger->get_connection(next_map->get_hb_back_inst(peer));
  if (next_map->get_hb_front_addr(peer) != entity_addr_t())
    ret.second = osd->hb_front_client_messenger->get_connection(next_map->get_hb_front_inst(peer));
  release_map(next_map);
  return ret;
}osd->hb_back_client_messenger->get_connection(next_map->get_hb_back_inst(peer));
  if (next_map->get_hb_front_addr(peer) != entity_addr_t())
    ret.second = osd->hb_front_client_messenger->get_connection(next_map->get_hb_front_inst(peer));
  release_map(next_map);
  return ret;
}

 

② Ping消息发送

在OSD::heartbeat()中,对记录了osd连接信息map进行遍历,每一个heartbeatinfo中记录了一个目标osd的链接信息(connection),通过这些conns把消息发送出去。重点的就是红色部分。

 

void OSD::heartbeat()
{
  ......
  // send heartbeats
  for (map<int,HeartbeatInfo>::iterator i = heartbeat_peers.begin();
       i != heartbeat_peers.end();
       ++i) {
    int peer = i->first;
    i->second.last_tx = now;
    if (i->second.first_tx == utime_t())
      i->second.first_tx = now;
    dout(30) << "heartbeat sending ping to osd." << peer << dendl;
    i->second.con_back->send_message(new MOSDPing(monc->get_fsid(),
                      service.get_osdmap()->get_epoch(),
                      MOSDPing::PING, now,
                      cct->_conf->osd_heartbeat_min_size));

    if (i->second.con_front)
      i->second.con_front->send_message(new MOSDPing(monc->get_fsid(),
                         service.get_osdmap()->get_epoch(),
                         MOSDPing::PING, now,
                      cct->_conf->osd_heartbeat_min_size));
  ......
  }  
}i->second.con_back->send_message(new MOSDPing(monc->get_fsid(),
                      service.get_osdmap()->get_epoch(),
                      MOSDPing::PING, now,
                      cct->_conf->osd_heartbeat_min_size));

    if (i->second.con_front)
      i->second.con_front->send_message(new MOSDPing(monc->get_fsid(),
                         service.get_osdmap()->get_epoch(),
                         MOSDPing::PING, now,
                      cct->_conf->osd_heartbeat_min_size));
  ......
  }  
}

③ Ping消息接受及处理

OSD B收到消息,Messenger B内部的dispatch线程会调用事先键入的dispatcher对消息进行处理。HeartbeatDispatcher会将message交给osd->heartbeat_dispatch()处理。

 

struct HeartbeatDispatcher : public Dispatcher {
    OSD *osd;
    explicit HeartbeatDispatcher(OSD *o) : Dispatcher(o->cct), osd(o) {}

    bool ms_dispatch(Message *m) override {
      return osd->heartbeat_dispatch(m);
    }
    ......
}heartbeat_dispatcher
bool OSD::heartbeat_dispatch(Message *m)
{
  dout(30) << "heartbeat_dispatch " << m << dendl;
  switch (m->get_type()) {

  case CEPH_MSG_PING:
    dout(10) << "ping from " << m->get_source_inst() << dendl;
    m->put();
    break;

  case MSG_OSD_PING:
    handle_osd_ping(static_cast<MOSDPing*>(m));
    break;

  default:
    dout(0) << "dropping unexpected message " << *m << " from " << m->get_source_inst() << dendl;
    m->put();
  }
  return true;
}osd->heartbeat_dispatch(m);
    }
    ......
}heartbeat_dispatcher
bool OSD::heartbeat_dispatch(Message *m)
{
  dout(30) << "heartbeat_dispatch " << m << dendl;
  switch (m->get_type()) {

  case CEPH_MSG_PING:
    dout(10) << "ping from " << m->get_source_inst() << dendl;
    m->put();
    break;

  case MSG_OSD_PING:
    handle_osd_ping(static_cast<MOSDPing*>(m));
    break;

  default:
    dout(0) << "dropping unexpected message " << *m << " from " << m->get_source_inst() << dendl;
    m->put();
  }
  return true;
}

heartbeat_dispatch()根据消息的type进行处理,因为消息的type是MSG_OSD_PING,调到OSD::handle_osd_ping(MOSDPing *m)进行处理, 进入case MOSDPing::PING:

void OSD::handle_osd_ping(MOSDPing *m)
{
  switch (m->op) {
  case MOSDPing::PING:
    {
      //做了一系列处理

      ...
      //发送回复包
      Message *r = new MOSDPing(monc->get_fsid(),
                curmap->get_epoch(),
                MOSDPing::PING_REPLY, m->stamp,
                cct->_conf->osd_heartbeat_min_size);
      m->get_connection()->send_message(r);
      ...
     }
  case MOSDPing::PING_REPLY:
    {
     // 更新时间戳,避免心跳超时
// osd有专门的tick线程进行周期性的检查,如果发现有心跳超时的,就会上报monitor

      }
   }case MOSDPing::PING:
    {
      //做了一系列处理

      ...
      //发送回复包
      Message *r = new MOSDPing(monc->get_fsid(),
                curmap->get_epoch(),
                MOSDPing::PING_REPLY, m->stamp,
                cct->_conf->osd_heartbeat_min_size);
      m->get_connection()->send_message(r);
      ...
     }
  case MOSDPing::PING_REPLY:
    {
     // 更新时间戳,避免心跳超时
// osd有专门的tick线程进行周期性的检查,如果发现有心跳超时的,就会上报monitor

      }
   }

④ 回复消息

第③步中,在OSD B的dispatcher中对消息做一系列处理后,会封装一个回复消息PING_REPLY,然后发送OSD A。

------------------------------------------------------------------------

      Message *r = new MOSDPing(monc->get_fsid(),

                curmap->get_epoch(),

                MOSDPing::PING_REPLY, m->stamp,

                cct->_conf->osd_heartbeat_min_size);

      m->get_connection()->send_message(r);

------------------------------------------------------------------------

这个过程和前面类似,不同的是这次是OSD B的Messenger去获取connection链接,(这个链接就是之前OSD A建立的链接,通过看Asyncmessenger.cc 可以看到在执行第②步中AsyncConnection::send_message(Message *m)时,通过 m->set_connection(this);将connection赋给了message,所以说A向B发送消息和B向A发送回复消息都是用的同一条链接connection),发送回复消息给OSD A。

 

⑤ 处理PING_REPLY

OSD A 的Messenger(hb_front_client_messenger)监听到了回复消息,交给自己的Dispatcher处理。

 

还是先进入osd->heartbeat_dispatch(m)的MSG_OSD_PING,然后使用OSD::handle_osd_ping(MOSDPing *m)根据 消息的type做相应处理,这次是进入ping reply

 

void OSD::handle_osd_ping(MOSDPing *m)
{
  switch (m->op) {
  case MOSDPing::PING:
    {
      ...
      //发送回复包
      Message *r = new MOSDPing(monc->get_fsid(),
                curmap->get_epoch(),
                MOSDPing::PING_REPLY, m->stamp,
                cct->_conf->osd_heartbeat_min_size);
      m->get_connection()->send_message(r);
      ...
     }
  case MOSDPing::PING_REPLY:
    {
     // 更新时间戳,避免心跳超时
// osd有专门的tick线程进行周期性的检查,如果发现有心跳超时的,就会上报monitor

      }
}case MOSDPing::PING_REPLY:
    {
     // 更新时间戳,避免心跳超时
// osd有专门的tick线程进行周期性的检查,如果发现有心跳超时的,就会上报monitor

      }
}

可以看出,心跳的发送流程是很简单的,也是很独立的。在设计分布式系统的时候,为了保证集群的内部状态正确,应尽量不要引入过多复杂的因素影响心跳的流程。 毕竟心跳快速正确的处理是确保集群运转正常的最基本条件。

 

 参考文章【reference】:

整理中。。。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值