本节我们从基础出发,来研究ceph peering这一复杂的过程,期望对其工作原理有更深入的理解。
1. OSD中网络消息的处理
在前面的ceph网络通信章节中,我们介绍了SimpleMessenger网络通信框架。这里OSD实现了Dispatcher接口:
下面我们来看其对Dispatcher接口各函数的具体实现。
1) ms_can_fast_dispatch()的实现
从上面我们看到,对于case中所列举的消息,是可以进行fast_dispatch()进行处理的。
2) ms_can_fast_dispatch_any()的实现
上面说明,会将OSD这个Dispatcher加入到Messenger的fast_dispatchers列表中的。
3) ms_fast_dispatch()的实现
从上面可以看出,其会调用dispatch_session_waiting()来进行处理:
在dispatch_op_fast()函数中就会对ms_can_fast_dispatch()所指定的消息进行处理。
4) ms_fast_preprocess()的实现
从上面的代码我们看到,对于来自于其他OSD发送过来的CEPH_MSG_OSD_MAP消息,则将session.received_map_epoch设置为收到的OSDMap中的最新版本。
5) ms_dispatch()的实现
ms_dispatch()函数实现对普通消息的分发处理。现在我们来看一下_dispatch()函数:
上面我们看到了最重要的对CEPH_MSG_OSD_MAP消息的处理。
6) ms_handle_connect()的实现
OSD只会主动向Monitor以及其他OSD发起连接。这里处理向其他Monitor发起连接的请求回调。
7) ms_handle_fast_connect()的实现
OSD只会主动向Monitor以及其他OSD发起连接。这里处理向其他OSD主动发起连接的回调请求。
8) ms_handle_accept()的实现
在OSD中没有对ms_handle_accept()函数进行重新实现。
9)ms_handle_fast_accept()的实现
由于ms_can_fast_dispatch_any()永远返回true,因此OSD会接受Monitor,RadosGw以及其他OSD的连接。通过上面的代码,我们看到当RadosGW和其他OSD向OSD发起新的连接时,会构造生成一个新的Session,将其作为connection的private值保存起来。
10) ms_handle_reset()的实现
当连接被reset时,回调此函数清空该连接对应的session信息
11)ms_handle_remote_reset()的实现
上面对ms_handle_remote_reset()的实现为空。
12) ms_get_authorizer()的实现
上面可以看到其调用build_authorizer()来构建AuthAuthorizer。
13)ms_verify_authorizer()的实现
实现对incomming连接的校验。
2. OSD模块消息
下面介绍一下OSD模块所使用到的一些消息:
-
CEPH_MSG_OSD_OP:客户端进行读写请求时会构造此消息,primary OSD会收到
-
MSG_OSD_REPOP:在进行数据修改操作时,Replicated OSD会收到此类消息,由Primary OSD发送
-
MSG_OSD_REPOPREPLY: 针对MSG_OSD_REPOP的响应
-
MSG_OSD_SUBOP:Primary与Replicas之间针对objects的一些内部操作,主要用于object recovery的时候。
-
MSG_OSD_SUBOPREPLY:针对MSG_OSD_SUBOP的响应
-
CEPH_OSD_OP_WRITE: 写部分对象
-
CEPH_OSD_OP_WRITEFULL: 写一个完整对象
2.1 ms_fast分发的消息类型
2.2 普通分发的消息类型
-
MSG_OSD_MARK_ME_DOWN
-
CEPH_MSG_PING
-
CEPH_MSG_OSD_MAP
-
MSG_PGSTATSACK
-
MSG_MON_COMMAND
-
MSG_COMMAND
-
MSG_OSD_SCRUB
-
MSG_OSD_PG_CREATE
-
MSG_OSD_PG_NOTIFY
-
MSG_OSD_PG_QUERY
-
MSG_OSD_PG_LOG
-
MSG_OSD_PG_REMOVE
-
MSG_OSD_PG_INFO
-
MSG_OSD_PG_TRIM
-
MSG_OSD_PG_MISSING:
-
MSG_OSD_BACKFILL_RESERVE
-
MSG_OSD_RECOVERY_RESERVE
3. ms_fast_dispatch()代码分析
通过上文分析,我们知道对于有以下消息,会调用ms_fast_dispatch()来进行分发,下面我们来看一下该函数的实现:
首先我们这里调用service.get_nextmap_reserved()来获得nextmap,我们来看其实现:
从上面的注释中我们看到,service.osdmap是指当前已经发布的最新的OSDMap,而service.next_osdmap是将要发布的OSDMap。我们会使用next_osdmap来发送消息和初始化连接,但前提是目标target与当前用户工作在相同的OSDMap epoch。
下面我们从代码中来看一下service.osdmap与service.next_osdmap的区别:
1) OSDService::publish_map()
查找publish_map()函数,发现只有两个地方调用:
- OSD::init()时调用publish_map()
在OSD初始化时,首先加载superblock中所指定的OSDMap版本作为当前的初始化osdmap,然后再调用consume_map()来消费该osdmap,这可能触发启动时PG的第一次peering操作。
除了上面介绍的在OSD init阶段会触发调用consume_map(),还会在保存完一个新的OSDMap之后触发调用consume_map(),下面我们来看:
从上面代码我们看出,要等到reserved_maps都消费完成之后,osdmap才会真正发布。如下图所示:
2) OSDService::pre_publish_map()
查找pre_publish_map()函数发现也只有两个地方调用:
- _committed_osd_maps()中调用pre_publish_map()
当接收到MOSDMap消息,如果有符合条件的新OSDMaps,则会将其打包到一个Transaction中,之后再将该Transaction持久化到硬盘上。当持久化成功,会回调_committed_osd_map()函数。如上代码所示,当前OSD会遍历MOSDMap消息中的所有新OSDMap,然后调用service.pre_publish_map()将去标记为预发布状态。
- OSD::consume_map()中调用pre_publish_map
在_committed_osd_map()函数中还会调用OSD::consume_map():
在consume_map()中会触发发布新接收到的OSDMap,之后再触发相应PG的peering操作。
3.1 几个相关变量
在进一步分析ms_fast_dispatch()之前,我们先来分析一下如下几个重要的变量:
如下图所示:
- Session::waiting_on_map: 等待在OSDMap上的请求。通常来说,在peering过程中当发现要创建PG,或者Peering过程中发现该PG有分裂出子PG,则可能会把相关的一些请求放入到session对应的waiting_on_map中;
-
Session::waiting_for_pg: 等待在指定PG上的请求。通常来说,当获取不到指定的PG时(比如当前并没有获得到最新的OSDMap,从而导致PG找不到),就会将请求放入到waiting_for_pg中;
-
OSD::session_waiting_for_map: 保存等待在OSDMap上的Session;
-
OSD::session_waiting_for_pg:保存等待在指定PG上的session。
-
OSD::waiting_for_osdmap: 保存等待在OSDMap上的请求
注:一般具有session状态、需要等待响应的消息会加入到session_waiting_for_map中;而无状态的需要等待osdmap的请求加入到waiting_for_osdmap中
3.1 函数update_waiting_for_pg()
主要处理有PG分裂情况下,更新session的waiting_for_pg。
注:PG的分裂会造成Monitor更新OSDMap
3.2 dispatch_session_waiting()函数
dispatch_session_waiting()就是将session上waiting_on_map里面的请求,调用dispatch_op_fast()转发出去。
4. ms_dispatch()分析
此函数用于分发非fast消息。我们来看,这里有一个osd_lock
,这是一把十分大的锁。我们知道一个DispatchQueue会有dispatch_thread以及local_delivery_thread这两个线程来进行分发,这就存在竞争关系。OSD作为一个Dispatcher,使用osd_lock来保证同一时刻,只能有一个线程调用到此函数。
4.1 do_waiters()函数
do_waiters()主要用于处理当前阻塞在waiting_for_osdmap上的请求。比如有些请求需要new osdMap,那么就会先将这些请求放入waiting_for_osdmap上。然后在新的OSDMap准备好后,就会调用take_waiters()将其加入到finished列表中:
有如下两种情况会将请求加入到waiting_for_osdmap上:
4.2 _dispatch()分发消息
_dispatch()会分发如下消息,其中有一些不需要依赖OSDMap,另外一些则需要:
1) 无需OSDMap的消息
-
CEPH_MSG_PING
-
CEPH_MSG_OSD_MAP
-
MSG_MON_COMMAND
-
MSG_COMMAND
-
MSG_OSD_SCRUB
2) 需要OSDMap的消息
注: Peering过程中,RecoveryCtx::send_notify()发送的就是此消息
- MSG_OSD_PG_QUERY:replica(stray)上用于处理来自于primary的PG查询。
注:Peering过程中GetInfo就是通过发送此消息来查询的
注:Peering过程中,发现有missing object时,就通过PG::search_for_missing()来发送此查询消息
-
MSG_OSD_PG_TRIM
-
MSG_OSD_PG_MISSING
-
MSG_OSD_BACKFILL_RESERVE
-
MSG_OSD_RECOVERY_RESERVE
5. OSD运行状态
下面我们简要介绍一下OSD运行中的几个状态,以便更好的理解peering:
[参看]
-
Ceph OSD
-
Ceph pg分裂流程及可行性分析