消息从客户端发送而来,之前几节介绍了 客户端下 对象存储、块存储库的实现以及他们在客户端下API请求的发送过程(Ceph学习——Librados与Osdc实现源码解析 、 Ceph学习——客户端读写操作分析 、 Ceph学习——Librbd块存储库与RBD读写流程源码分析)。当请求被封装后,通过消息发送模块(Ceph学习——Ceph网络通信机制与源码分析)将请求及其相关信息发送到服务端实现真正的对数据的操作。服务端的操作模块便是由OSD、OS模块完成的,这节先介绍OSD模块。
直接上图:
同样当前最新的版本,和之前的版本有所不同,有一些模块简化了,类的名字也改了。先介绍图中涉及的相关的类,然后在对类中具体函数主要调用流程进行分析。
OSD 模块主要的类
盗图:其中ReplicatedPG 在最新的版本中去掉了,更改为PrimaryLogPG类
OSD类
OSD和OSDService是核心类,他们直接在顶层负责一个OSD节点的工作,从客户端的得到的消息,就是先到达OSD类中,通过OSD类的处理,在调用PrimaryLogPG(之前为ReplicatedPG 类)类进行处理。该类中,在读写流程中的主要工作是消息(Message)封装为 RequestOp,检查epoch (版本)是否需要更新,并获取PG句柄,并做PG相关的检查,最后将请求加入队列。
PrimaryLogPG类
该类继承自PG类,PGBackend::Listener(该类是一个抽象类)类PG类处理相关状态的维护,以及实现PG层面的功能,核心功能是用boost库的statechart状态机来做PG状态转换。它实现了PG内的数据读写等功能。
PGBackend类
该类主要功能是将请求数据通过事务的形式同步到一个PG的其它从OSD上(注意:主OSD的操作PrimaryLogPG来完成)。
他有两个子类,分别是 ReplicatedBackend和ECBackend,对应着PG的的两种类型的实现。
OSD读写函数调用流程
1)OSD::ms_fast_dispatch 函数是接收消息Message的入口函数,他被网络模块的接收线程调用。主要工作是 检查service服务 、把Message封装为OpRequest类型、获取session、获取最新的OSdMap,最后dispatch_session_waiting,进入下一步。
void OSD::ms_fast_dispatch(Message *m)
{
FUNCTRACE();
if (service.is_stopping()) {
//检查service,如果停止了直接返回
m->put();
return;
}
OpRequestRef op = op_tracker.create_request<OpRequest, Message*>(m);//把Message封装为OpRequest类型
...
...
if (m->get_connection()->has_features(CEPH_FEATUREMASK_RESEND_ON_SPLIT) ||
m->get_type() != CEPH_MSG_OSD_OP) {
// queue it directly直接调用enqueue_op处理
enqueue_op(
static_cast<MOSDFastDispatchOp*>(m)->get_spg(),
op,
static_cast<MOSDFastDispatchOp*>(m)->get_map_epoch());
} else {
Session *session = static_cast<Session*>(m->get_connection()->get_priv());//获取 session 其中包含了一个Connection的相关信息
if (session) {
{
Mutex::Locker l(session->session_dispatch_lock);
op->get();
session->waiting_on_map.push_back(*op);//将请求加如waiting_on_map的列表里
OSDMapRef nextmap = service.get_nextmap_reserved();//获取最新的OSDMAP
dispatch_session_waiting(session, nextmap);//该函数中 循环处理请求
service.release_map(nextmap);
}
session->put();
}
}
OID_EVENT_TRACE_WITH_MSG(m, "MS_FAST_DISPATCH_END", false);
}
2)OSD::dispatch_session_waiting 主要工作是循环处理队列waiting_on_map中的元素,对比OSDmap,以及获取他们的pgid,最后调用enqueue_op处理。
void OSD::dispatch_session_waiting(Session *session, OSDMapRef osdmap)
{
assert(session->session_dispatch_lock.is_locked());
auto i = session->waiting_on_map.begin();
while (i != session->waiting_on_map.end()) {
//循环处理waiting_on_map中的元素
OpRequestRef op = &(*i);
assert(ms_can_fast_dispatch(op->get_req()));
const MOSDFastDispatchOp *m = static_cast<const MOSDFastDispatchOp*>(
op->get_req());
if (m->get_min_epoch() > osdmap->get_epoch()) {
//osdmap版本不对应
break;
}
session->waiting_on_map.erase(i++);
op->put();
spg_t pgid;
if (m->get_type() == CEPH_MSG_OSD_OP) {
pg_t actual_pgid = osdmap->raw_pg_to_pg(
static_cast<const MOSDOp*>(m)->get_pg());
//osdmap->get_primary_shard(actual_pgid, &pgid)获取 pgid 该PG的主OSD
if (!osdmap->get_primary_shard(actual_pgid, &pgid)) {
continue;
}
} else {
pgid = m->get_spg();
}
enqueue_op(pgid, op, m->get_map_epoch());//获取成功则调用enqueue_op处理
}
if (session->waiting_on_map.empty()) {
clear_session_waiting_on_map(session);
} else {
register_session_waiting_on_map(session);
}
}
3)OSD::enqueue_op 的主要工作是将求情加入到op_shardedwq队列中
void OSD::enqueue_op(spg_t pg, OpRequestRef& op, epoch_t epoch)
{
...
op->osd_trace.event("enqueue op");
op->osd_trace.keyval("priority", op->get_req()->get_priority());
op->osd_trace.keyval("cost", op->get_req()->get_cost());
op->mark_queued_for_pg();
logger->tinc(l_osd_op_before_queue_op_lat, latency);
//加入op_shardedwq队列中
op_shardedwq.queue(
OpQueueItem(
unique_ptr<OpQueueItem::OpQueueable>(new PGOpItem(pg, op)),
op->get_req()->get_cost(),
op->get_req()->get_priority(),
op->get_req()->get_recv_stamp(),
op->get_req()->get_source().num(),
epoch));
}
4)OSD::dequeue_op 调用函数进行osdmap的更新,调用do_request进入PG处理流程
void OSD::dequeue_op(
PGRef pg, OpRequestRef op,
ThreadPool::TPHandle &handle)
{
...
...
logger->tinc(l_osd_op_before_dequeue_op_lat, latency);
Session *session = static_cast<Session *>(
op->get_req()->get_connection()->get_priv());
if (session) {
//调用该函数进行 osdmap的更新
maybe_share_map(session, op, pg->get_osdmap());
session->put();
}
//正在是删除、直接返回
if (pg->is_deleting())
return;
op->mark_reached_pg();
op->osd_trace.event("dequeue_op");
//调用pg的do_request处理
pg->do_request(op, handle);
// finish
dout(10) << "dequeue_op " << op << " finish" << dendl;
OID_EVENT_TRACE_WITH_MSG(op->get_req(), "DEQUEUE_OP_END", false);
}
5)PrimaryLogPG::do_request该函数 主要你检查PG的状态,以及根据消息类型进行不同处理
void PrimaryLogPG::do_request(
OpRequestRef& op,
ThreadPool::TPHandle &handle)
{
...
// make sure we have a new enough map
//检查 osdmap
auto p = waiting_for_map.find(op->get_source());
...
//是否可以丢弃
if (can_discard_request(op)) {
return;
}
...
...
//PG还没有peered
if (!is_peered()) {
// Delay unless PGBackend says it's ok