马可波罗网站做外贸,天眼查企业查询公司,公司做自己的网站,惠州哪个房地产网站做的比较好本章介绍ceph中比较复杂的模块#xff1a;
Peering机制。该过程保障PG内各个副本之间数据的一致性#xff0c;并实现PG的各种状态的维护和转换。本章首先介绍boost库的statechart状态机基本知识#xff0c;Ceph使用它来管理PG的状态转换。其次介绍PG的创建过程以及相应的状…本章介绍ceph中比较复杂的模块
Peering机制。该过程保障PG内各个副本之间数据的一致性并实现PG的各种状态的维护和转换。本章首先介绍boost库的statechart状态机基本知识Ceph使用它来管理PG的状态转换。其次介绍PG的创建过程以及相应的状态机创建和初始化。然后详细介绍peering机制三个具体的实现阶段GetInfo、GetLog、GetMissing。
statechart状态机 1.1 状态 1.2 事件 1.3 状态机的响应 1.4 状态机的定义 1.5 context函数 1.6 事件的特殊处理 1.7 PG状态机 1.8 PG状态机的总体状态转换图 1.9 OSD启动加载PG状态机转换 1.10 PG创建后状态机的状态转换 1.11 PG在触发Peering过程时机 1. statechart状态机 Ceph在处理PG的状态转换时使用了boost库提供的statechart状态机。因此先简单介绍一下statechart状态机的基本概念和涉及的相关知识以便更好地理解Peering过程PG的状态机转换流程。下面例举时截取了PG状态机的部分代码。
1.1 状态
没有子状态情况下的状态定义 在statechart里一个状态的定义方式有两种 struct Reset : boost::statechart::state Reset, RecoveryMachine , NamedState { ... };
这里定义了状态Reset它需要继承boost::statechart::state类。该类的模板参数中第一个参数为状态自己的名字Reset第二个参数为该状态所属状态机的名字表明Reset是状态机RecoveryMachine的一个状态。
有子状态情况下的状态定义 struct Start; struct Started : boost::statechart::state Started, RecoveryMachine, Start , NamedState { ... } struct Start : boost::statechart::state Start, Started , NamedState { };
状态Started也是状态机RecoveryMachine的一个状态模板参数中多了一个参数Start它是状态Started的默认初始子状态。 这里定义的Start是状态Started的子状态。第一个模板参数是自己的名字第二个模板参数是该子状态所属父状态的名字。 综上所述一个状态要么属于一个状态机要么属于一个状态成为该状态的子状态。其定义的模板参数是自己第二个参数是拥有者第三个参数是它的起始子状态。 1.2 事件
状态能够接收并处理事件。事件可以改变状态促使状态发生转移。在boost库的statechart状态机中定义事件的方式如下所示 struct QueryState : boost::statechart::event QueryState { Formatter *f; explicit QueryState(Formatter *f) : f(f) {} void print(std::ostream *out) const { *out Query; } }; };
QueryState为一个事件需要继承boost::statechart::event类模板参数为自己的名字。
1.3 状态机的响应
在一个状态内部需要定义状态机处于当前状态时可以接受的事件以及如何处理这些事件的方法 #define TrivialEvent(T) struct T : boost::statechart::event T { \ T() : boost::statechart::event T () {} \ void print(std::ostream *out) const { \ *out #T; \ } \ }; TrivialEvent(Initialize) TrivialEvent(Load) TrivialEvent(GotInfo) TrivialEvent(NeedUpThru) TrivialEvent(NullEvt) TrivialEvent(FlushedEvt) TrivialEvent(Backfilled) TrivialEvent(LocalBackfillReserved) TrivialEvent(RemoteBackfillReserved) TrivialEvent(RejectRemoteReservation) TrivialEvent(RemoteReservationRejected) TrivialEvent(RemoteReservationCanceled) TrivialEvent(RequestBackfill) TrivialEvent(RequestRecovery) TrivialEvent(RecoveryDone) TrivialEvent(BackfillTooFull) TrivialEvent(RecoveryTooFull) TrivialEvent(MakePrimary) TrivialEvent(MakeStray) TrivialEvent(NeedActingChange) TrivialEvent(IsIncomplete) TrivialEvent(IsDown) TrivialEvent(AllReplicasRecovered) TrivialEvent(DoRecovery) TrivialEvent(LocalRecoveryReserved) TrivialEvent(RemoteRecoveryReserved) TrivialEvent(AllRemotesReserved) TrivialEvent(AllBackfillsReserved) TrivialEvent(GoClean) TrivialEvent(AllReplicasActivated) TrivialEvent(IntervalFlush) struct Initial : boost::statechart::state Initial, RecoveryMachine , NamedState { explicit Initial(my_context ctx); void exit(); typedef boost::mpl::list boost::statechart::transition Initialize, Reset , boost::statechart::custom_reaction Load , boost::statechart::custom_reaction NullEvt , boost::statechart::transition boost::statechart::event_base, Crashed reactions; boost::statechart::result react(const Load); boost::statechart::result react(const MNotifyRec); boost::statechart::result react(const MInfoRec); boost::statechart::result react(const MLogRec); boost::statechart::result react(const boost::statechart::event_base) { return discard_event(); } };
状态机的7种事件处理方法 上述代码列出了状态RecoveryMachine/Initial可以处理的事件列表和处理对应事件的方法
1 通过boost::mpl::list定义该状态可以处理多个事件类型。本例中可以处理Initialize、Load、NullEvt和event_base事件。
2 简单事件处理
boost::statechart::transition Initialize, Reset
定义了状态Initial接收到事件Initialize后无条件直接跳转到Reset状态
3 用户自定义事件处理 当接收到事件后需要根据一些条件来决定状态如何转移这个逻辑需要用户自己定义实现
boost::statechart::custom_reaction Load
custom_reaction 定义了一个用户自定义的事件处理方法必须有一个react()的处理函数处理对应该事件。状态转移的逻辑需要用户自己在react函数里实现
boost::statechart::result react(const Load); 4)NullEvt事件用户自定义处理但是没有实现react()函数来处理最终事件匹配了boost::statechart::event_base事件直接调用函数discard_event把事件丢弃掉。 boost::statechart::custom_reaction NullEvt boost::statechart::result react(const boost::statechart::event_base) { return discard_event(); }
1.4 状态机的定义 RecoveryMachine为定义的状态机需要继承boost::statechart::state_machine类 struct Initial; class RecoveryMachine : public boost::statechart::state_machine RecoveryMachine, Initial { RecoveryState *state; public: PG *pg; }
模板参数第一个参数为自己的名字第二个参数为状态机默认的初始状态Initial。
状态机的基本操作有两个 RecoveryMachine machine; PG *pg; explicit RecoveryState(PG *pg) : machine(this, pg), pg(pg), orig_ctx(0) { machine.initiate();//a--- } void handle_event(const boost::statechart::event_base evt, RecoveryCtx *rctx) { start_handle(rctx); machine.process_event(evt);//b--- end_handle(); } void handle_event(CephPeeringEvtRef evt, RecoveryCtx *rctx) { start_handle(rctx); machine.process_event(evt-get_event());/b--- end_handle(); } a.状态机的初始化
initiate()是继承自boost::statechart::state_machine的成员函数。
b.函数process_event()用来向状态机投递事件从而触发状态机接收并处理该事件
process_event()也是继承自boost::statechart::state_machine的成员函数。
1.5 context函数 context是状态机的一个比较有用的函数它可以获取当前状态的所有祖先状态的指针。通过它可以获取父状态以及祖先状态的一些内部参数和状态值。context()函数是实现在boost::statechart::state_machine中的
context()函数在boost::statechart::simple_state中有实现 //boost_1_73_0/boost/statechart/simple_state.hpp 234 template class OtherContext 235 OtherContext context() 236 { 237 typedef typename mpl::if_ 238 is_base_of OtherContext, MostDerived , 239 context_impl_this_context, 240 context_impl_other_context 241 ::type impl; 242 return impl::template context_impl OtherContext ( *this ); 243 } 244 245 template class OtherContext 246 const OtherContext context() const 247 { 248 typedef typename mpl::if_ 249 is_base_of OtherContext, MostDerived , 250 context_impl_this_context, 251 context_impl_other_context 252 ::type impl; 253 return impl::template context_impl OtherContext ( *this ); 254 }
从simple_state的实现来看context()可以获取当前状态的祖先状态指针也可以获取当前状态所属状态机的指针。
例如状态Started是RecoveryMachine的一个状态状态Start是Started状态的一个子状态那么如果当前状态是Start就可以通过该函数获取它的父状态Started的指针
Started * parent context Started ();
同时也可以获取其祖先状态RecoveryMachine的指针
RecoveryMachine *machine context RecoveryMachine ();
在状态机实现中大量了使用该函数来获取相应的指针。Eg PG *pg context RecoveryMachine ().pg; context RecoveryMachine ().get_cur_transaction(), context RecoveryMachine ().get_on_applied_context_list(), context RecoveryMachine ().get_on_safe_context_list());
综上所述context()函数为获取当前状态的祖先状态上下文提供了一种方法。
span id “1.6事件的特殊处理”/span
1.6 事件的特殊处理 事件除了在状态转移列表中触发状态转移或者进入用户自定义的状态处理函数还可以有下列特殊的处理方式
在用户自定义的函数里可以直接调用函数transit来直接跳转到目标状态。例如 boost::statechart::result PG::RecoveryState::Initial::react(const MLogRec i) { PG *pg context RecoveryMachine ().pg; assert(!pg-is_primary()); post_event(i); return transit Stray ();//go--- }
可以直接跳转到状态Stray。在用户自定义的函数里可以调用函数post_event()直接产生相应的事件并投递给状态机 PG::RecoveryState::Start::Start(my_context ctx) : my_base(ctx), NamedState(context RecoveryMachine ().pg-cct, Start) { context RecoveryMachine ().log_enter(state_name); PG *pg context RecoveryMachine ().pg; if (pg-is_primary()) { dout(1) transitioning to Primary dendl; post_event(MakePrimary());//go--- } else { //is_stray dout(1) transitioning to Stray dendl; post_event(MakeStray());//go--- } }
在用户的自定义函数里调用函数discard_event()可以直接丢弃事件不做任何处理 boost::statechart::result PG::RecoveryState::Primary::react(const ActMap) { dout(7) handle ActMap primary dendl; PG *pg context RecoveryMachine ().pg; pg-publish_stats_to_osd(); pg-take_waiters(); return discard_event();//go--- }
在用户的自定义函数里调用函数forward_event()可以把当前事件继续投递给状态机 boost::statechart::result PG::RecoveryState::WaitUpThru::react(const ActMap am) { PG *pg context RecoveryMachine ().pg; if (!pg-need_up_thru) { post_event(Activate(pg-get_osdmap()-get_epoch())); } return forward_event(); }
结合 1.3 状态机的响应 的3种事件响应大概有7种事件响应处理的方法。
1.7 PG状态机 在类PG的内部定义了类RecoveryState该类RecoveryState的内部定义了PG的状态机RecoveryMachine和它的各种状态。 class PG{ class RecoveryState{ class RecoveryMachine{ }; }; };
在每个PG创建时在构造函数里创建一个新的RecoveryState类的对象并创建相应的RecoveryMachine类的对象也就是创建了一个新的状态机。每个PG类对应一个独立的状态机来控制该PG的状态转换。 PG::PG(OSDService *o, OSDMapRef curmap, const PGPool _pool, spg_t p) : recovery_state(this){ } class RecoveryState{ public: explicit RecoveryState(PG *pg) : machine(this, pg), pg(pg), orig_ctx(0) { machine.initiate(); } };
上面machine.initiate()调用的是boost::statechart::state_machine中的initiate()方法。1.8 PG状态机的总体状态转换图
下图为PG状态机的总体状态转换图简化版 1.9 OSD启动加载PG状态机转换 当OSD重启时调用函数OSD::init()该函数调用load_pgs()加载已经存在的PG其处理过程和以下创建PG的过程相似。 int OSD::init() { // load up pgs (as they previously existed) load_pgs(); } void OSD::load_pgs() { ... PG::RecoveryCtx rctx(0, 0, 0, 0, 0, 0); pg-handle_loaded(rctx);//go-- ... } void PG::handle_loaded(RecoveryCtx *rctx) { dout(10) handle_loaded dendl; Load evt; recovery_state.handle_event(evt, rctx); } struct Initial : boost::statechart::state Initial, RecoveryMachine , NamedState { typedef boost::mpl::list boost::statechart::transition Initialize, Reset , boost::statechart::custom_reaction Load , boost::statechart::custom_reaction NullEvt , boost::statechart::transition boost::statechart::event_base, Crashed reactions; boost::statechart::result react(const Load); } boost::statechart::result PG::RecoveryState::Initial::react(const Load l) { PG *pg context RecoveryMachine ().pg; // do we tell someone were here? pg-send_notify (!pg-is_primary()); pg-update_store_with_options(); pg-update_store_on_load(); return transit Reset ();//go--- }
1.10 PG创建后状态机的状态转换 void PG::handle_create(RecoveryCtx *rctx) { dout(10) handle_create dendl; rctx-created_pgs.insert(this); Initialize evt; recovery_state.handle_event(evt, rctx); ActMap evt2; recovery_state.handle_event(evt2, rctx); rctx-on_applied-add(make_lambda_context([this]() { update_store_with_options(); })); }
当PG创建后同时在该类内部创建了一个属于该PG的RecoveryMachine类型的状态机该状态机的初始化状态为默认初始化状态Initial。
在PG创建后调用函数pg-handle_create(rctx)来给状态机投递事件
该函数首先向RecoveryMachine投递了Initialize类型的事件。接收到Initialize类型的事件后直接转移到Reset状态。其次向RecoveryMachine投递了ActMap事件。 boost::statechart::result PG::RecoveryState::Reset::react(const ActMap) { PG *pg context RecoveryMachine ().pg; if (pg-should_send_notify() pg-get_primary().osd 0) { context RecoveryMachine ().send_notify( pg-get_primary(), pg_notify_t( pg-get_primary().shard, pg-pg_whoami.shard, pg-get_osdmap()-get_epoch(), pg-get_osdmap()-get_epoch(), pg-info), pg-past_intervals); } pg-update_heartbeat_peers(); pg-take_waiters(); return transit Started ();//a--- }
a. 在自定义的react函数里直接调用了transit函数跳转到Started状态。 struct Start; struct Started : boost::statechart::state Started, RecoveryMachine, Start , NamedState {//这里直接进入默认子状态Start ... } /*-------Start---------*/ PG::RecoveryState::Start::Start(my_context ctx) : my_base(ctx), NamedState(context RecoveryMachine ().pg, Start) { context RecoveryMachine ().log_enter(state_name); PG *pg context RecoveryMachine ().pg; if (pg-is_primary()) { ldout(pg-cct, 1) transitioning to Primary dendl; post_event(MakePrimary());//go--- } else { //is_stray ldout(pg-cct, 1) transitioning to Stray dendl; post_event(MakeStray());//go--- } } struct Start : boost::statechart::state Start, Started , NamedState { explicit Start(my_context ctx); void exit(); typedef boost::mpl::list boost::statechart::transition MakePrimary, Primary , boost::statechart::transition MakeStray, Stray reactions; }; struct Primary : boost::statechart::state Primary, Started, Peering , NamedState {//这里直接进入Primary的默认子状态Peering。 ... } struct Stray : boost::statechart::state Stray, Started , NamedState { ... }
1.进入状态RecoveryMachine/Started后就进入RecoveryMachine/Started的默认的子状态RecoveryMachine/Started/Start中。 由以上代码可知在Start状态的构造函数中根据本OSD在该PG中担任的角色不同分别进行如下处理
(1)如果是主OSD就调用函数post_event()抛出事件MakePrimary进入主OSD的默认子状态Primary/Peering中
(2)如果是从OSD就调用函数post_event()抛出事件MakeStray进入Started/Stray状态 对于一个OSD的PG处于Stray状态是指该OSD上的PG副本目前状态不确定但是可以响应主OSD的各种查询操作。它有两种可能一种是最终转移到状态ReplicaActive处于活跃状态成为PG的一个副本另一种可能的情况是如果是数据迁移的源端可能一直保持Stray状态该OSD上的副本可能在数据迁移完成后PG以及数据就都被删除了。 1.11 PG在触发Peering过程时机 1.当系统初始化时OSD重新启动导致PG重新加载。 2.PG新创建时PG会发起一次Peering的过程 3. 当有OSD失效OSD的增加或者删除等导致PG的acting set发生了变化该PG就会重新发起一次Peering过程。 参考link https://ivanzz1001.github.io/records/post/ceph/2019/02/01/ceph-src-code-part10_1