注意
本文档适用于 Ceph 的开发版本。
RADOS 客户端协议
这非常不完整,但必须从某个地方开始。
基础
请求是 MOSDOp 消息。回复是 MOSDOpReply 消息。
对象请求的目标是一个 hobject_t,它包括一个池、哈希值、对象名、放置键(通常为空)和 snapid。
哈希值是一个 32 位哈希值,通常通过对对象名进行哈希处理生成。但是,hobject_t 可以任意构造,具有任何哈希值和名称。请注意,在 MOSDOp 中,这些组件分布在多个字段中,而不是在实际的 hobject_t 成员中逻辑组装(主要是历史原因)。
请求也可以以 PG 为目标。在这种情况下,ps 值匹配特定的 PG,对象名为空,并且(希望)请求中的操作是 PG 操作。
无论哪种方式,请求最终都以 PG 为目标,要么通过使用显式的 pgid,要么通过将哈希值折叠到池中当前的 pg 数量上。客户端将请求发送到相关联 PG 的主 OSD。
每个请求都分配有一个唯一的 tid。
重发
如果连接断开,客户端将重发任何未完成的请求。
任何时候发生 PG 映射更改导致主 OSD 更改时,客户端都有责任重发请求。请注意,尽管从 OSD 的角度来看可能存在间隔更改(触发 PG 对等),但如果主 OSD 未更改,则客户端无需重发。
此规则有几个例外
OSDMap 中 pg_pool_t 中有一个 last_force_op_resend 字段。如果此字段发生更改,则强制客户端重发任何未完成的请求。(例如,在分层调整时会发生这种情况。)
某些请求的重发条件是发生任何 PG 间隔更改,如 pg_interval_t 的 is_new_interval() 所定义(与 OSD 中对等使用的标准相同)。
如果设置和取消设置了 PAUSE OSDMap 标志。
每次将请求发送到 OSD 时,attempt 字段都会递增。第一次为 0,下一次为 1,依此类推。
退避
通常,OSD 只会将无法立即处理的任何请求排队到内存中,直到可以处理为止。这可能会产生问题,因为 OSD 限制了传入消息消耗的总 RAM 量:如果达到消息数量或字节数的任一阈值,则不会从网络套接字读取新消息,从而导致通过网络的背压。
但在某些情况下,OSD 知道或预期 PG 或对象将在一段时间内不可用,并且不希望通过排队请求来消耗内存。在这些情况下,它可以向客户端发送 MOSDBackoff 消息。
退避请求有四个属性
操作码(block、unblock 或 ack-block)
id,在此会话中分配的唯一 id
hobject_t begin
hobject_t end
有两种类型的退避:PG 退避将在客户端阻塞所有以整个 PG 为目标的请求,如哈希/hobject_t 空间 [begin,end) 的范围所描述,而对象退避将阻塞所有以单个对象为目标的请求(begin == end)。
当客户端收到 block 退避消息时,它现在负责不发送退避所描述的 hobject_ts 的任何请求。退避保持有效,直到退避被清除(通过 'unblock' 消息)或 OSD 会话关闭。立即将 ack_block 消息发送回 OSD 以确认收到退避。
收到 unblock 时,它将引用客户端先前已阻塞的特定 id。但是,unblock 所描述的范围可能小于原始范围,因为 PG 可能已在 OSD 上拆分。unblock 应该只解除 unblock 消息中指定的范围。重新检查落在 unblock 请求范围内的任何请求,如果没有其他已安装的退避适用,则重发。
在 OSD 上,退避也跨哈希空间的范围进行跟踪,并存在于三种状态中
new
acked
deleting
新安装的退避设置为 new,并向客户端发送一条消息。当收到 ack-block 消息时,它会更改为 acked 状态。OSD 可能会处理处于 new 状态的退避所涵盖的来自客户端的其他消息,但一旦退避处于 acked 状态,它就不应该再看到被阻塞的请求,除非存在错误。
如果 OSD 想要删除处于 acked 状态的退避,它可以简单地将其删除并通知客户端。如果退避处于 new 状态,它必须将其移动到 deleting 状态,并继续使用它来丢弃客户端请求,直到收到 ack-block 消息,此时它才能最终被删除。这对于保留 OSD 处理的操作顺序是必要的。