ceph peering机制-状态机

本章介绍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里,一个状态的定义方式有两种:

 
  1. struct Reset : boost::statechart::state< Reset, RecoveryMachine >, NamedState {

  2. ...

  3. };

这里定义了状态Reset,它需要继承boost::statechart::state类。该类的模板参数中,第一个参数为状态自己的名字Reset,第二个参数为该状态所属状态机的名字,表明Reset是状态机RecoveryMachine的一个状态。

有子状态情况下的状态定义

 
  1. struct Start;

  2. struct Started : boost::statechart::state< Started, RecoveryMachine, Start >, NamedState {

  3. ...

  4. }

  5. struct Start : boost::statechart::state< Start, Started >, NamedState {

  6. };

状态Started也是状态机RecoveryMachine的一个状态,模板参数中多了一个参数Start,它是状态Started的默认初始子状态。
这里定义的Start是状态Started的子状态。第一个模板参数是自己的名字,第二个模板参数是该子状态所属父状态的名字。

综上所述,一个状态,要么属于一个状态机,要么属于一个状态,成为该状态的子状态。其定义的模板参数是自己,第二个参数是拥有者,第三个参数是它的起始子状态。

 1.2 事件

状态能够接收并处理事件。事件可以改变状态,促使状态发生转移。在boost库的statechart状态机中定义事件的方式如下所示:

 
  1. struct QueryState : boost::statechart::event< QueryState > {

  2. Formatter *f;

  3. explicit QueryState(Formatter *f) : f(f) {}

  4. void print(std::ostream *out) const {

  5. *out << "Query";

  6. }

  7. };

  8. };

QueryState为一个事件,需要继承boost::statechart::event类,模板参数为自己的名字。

1.3 状态机的响应

在一个状态内部,需要定义状态机处于当前状态时,可以接受的事件以及如何处理这些事件的方法:

 
  1. #define TrivialEvent(T) struct T : boost::statechart::event< T > { \

  2. T() : boost::statechart::event< T >() {} \

  3. void print(std::ostream *out) const { \

  4. *out << #T; \

  5. } \

  6. };

  7. TrivialEvent(Initialize)

  8. TrivialEvent(Load)

  9. TrivialEvent(GotInfo)

  10. TrivialEvent(NeedUpThru)

  11. TrivialEvent(NullEvt)

  12. TrivialEvent(FlushedEvt)

  13. TrivialEvent(Backfilled)

  14. TrivialEvent(LocalBackfillReserved)

  15. TrivialEvent(RemoteBackfillReserved)

  16. TrivialEvent(RejectRemoteReservation)

  17. TrivialEvent(RemoteReservationRejected)

  18. TrivialEvent(RemoteReservationCanceled)

  19. TrivialEvent(RequestBackfill)

  20. TrivialEvent(RequestRecovery)

  21. TrivialEvent(RecoveryDone)

  22. TrivialEvent(BackfillTooFull)

  23. TrivialEvent(RecoveryTooFull)

  24. TrivialEvent(MakePrimary)

  25. TrivialEvent(MakeStray)

  26. TrivialEvent(NeedActingChange)

  27. TrivialEvent(IsIncomplete)

  28. TrivialEvent(IsDown)

  29. TrivialEvent(AllReplicasRecovered)

  30. TrivialEvent(DoRecovery)

  31. TrivialEvent(LocalRecoveryReserved)

  32. TrivialEvent(RemoteRecoveryReserved)

  33. TrivialEvent(AllRemotesReserved)

  34. TrivialEvent(AllBackfillsReserved)

  35. TrivialEvent(GoClean)

  36. TrivialEvent(AllReplicasActivated)

  37. TrivialEvent(IntervalFlush)

 
  1. struct Initial : boost::statechart::state< Initial, RecoveryMachine >, NamedState {

  2. explicit Initial(my_context ctx);

  3. void exit();

  4. typedef boost::mpl::list <

  5. boost::statechart::transition< Initialize, Reset >,

  6. boost::statechart::custom_reaction< Load >,

  7. boost::statechart::custom_reaction< NullEvt >,

  8. boost::statechart::transition< boost::statechart::event_base, Crashed >

  9. > reactions;

  10. boost::statechart::result react(const Load&);

  11. boost::statechart::result react(const MNotifyRec&);

  12. boost::statechart::result react(const MInfoRec&);

  13. boost::statechart::result react(const MLogRec&);

  14. boost::statechart::result react(const boost::statechart::event_base&) {

  15. return discard_event();

  16. }

  17. };

状态机的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把事件丢弃掉。

 
  1. boost::statechart::custom_reaction< NullEvt >

  2.     

  3. boost::statechart::result react(const boost::statechart::event_base&) {

  4.     return discard_event();

  5.       }

1.4 状态机的定义
RecoveryMachine为定义的状态机,需要继承boost::statechart::state_machine类:

 
  1.     struct Initial;

  2.     class RecoveryMachine : public boost::statechart::state_machine< RecoveryMachine, Initial > {

  3.       RecoveryState *state;

  4.     public:

  5.       PG *pg;

  6.       }

模板参数第一个参数为自己的名字,第二个参数为状态机默认的初始状态Initial。

状态机的基本操作有两个:   

 
  1. RecoveryMachine machine;

  2.     PG *pg;

  3.     explicit RecoveryState(PG *pg)

  4.       : machine(this, pg), pg(pg), orig_ctx(0) {

  5.       machine.initiate();//a---

  6.     }

  7.     void handle_event(const boost::statechart::event_base &evt,

  8.               RecoveryCtx *rctx) {

  9.       start_handle(rctx);

  10.       machine.process_event(evt);//b---

  11.       end_handle();

  12.     }

  13.     void handle_event(CephPeeringEvtRef evt,

  14.               RecoveryCtx *rctx) {

  15.       start_handle(rctx);

  16.       machine.process_event(evt->get_event());/b---

  17.       end_handle();

  18.     }


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中有实现:

 
  1. //boost_1_73_0/boost/statechart/simple_state.hpp

  2. 234     template< class OtherContext >

  3. 235     OtherContext & context()

  4. 236     {

  5. 237       typedef typename mpl::if_<

  6. 238         is_base_of< OtherContext, MostDerived >,

  7. 239         context_impl_this_context,

  8. 240         context_impl_other_context

  9. 241       >::type impl;

  10. 242       return impl::template context_impl< OtherContext >( *this );

  11. 243     }

  12. 244      

  13. 245     template< class OtherContext >

  14. 246     const OtherContext & context() const

  15. 247     {

  16. 248       typedef typename mpl::if_<

  17. 249         is_base_of< OtherContext, MostDerived >,

  18. 250         context_impl_this_context,

  19. 251         context_impl_other_context

  20. 252       >::type impl;

  21. 253       return impl::template context_impl< OtherContext >( *this );

  22. 254     }

从simple_state的实现来看,context()可以获取当前状态的祖先状态指针,也可以获取当前状态所属状态机的指针。

例如状态Started是RecoveryMachine的一个状态,状态Start是Started状态的一个子状态,那么如果当前状态是Start,就可以通过该函数获取它的父状态Started的指针:

Started * parent = context< Started >();

同时也可以获取其祖先状态RecoveryMachine的指针:

RecoveryMachine *machine = context< RecoveryMachine >();

在状态机实现中,大量了使用该函数来获取相应的指针。Eg:

 
  1.   PG *pg = context< RecoveryMachine >().pg;

  2.   context< RecoveryMachine >().get_cur_transaction(),

  3.   context< RecoveryMachine >().get_on_applied_context_list(),

  4.   context< RecoveryMachine >().get_on_safe_context_list());

综上所述,context()函数为获取当前状态的祖先状态上下文提供了一种方法。

<span id = “1.6事件的特殊处理”></span>

1.6 事件的特殊处理
事件除了在状态转移列表中触发状态转移,或者进入用户自定义的状态处理函数,还可以有下列特殊的处理方式:

在用户自定义的函数里,可以直接调用函数transit来直接跳转到目标状态。例如:

 
  1. boost::statechart::result PG::RecoveryState::Initial::react(const MLogRec& i)

  2. {

  3.   PG *pg = context< RecoveryMachine >().pg;

  4.   assert(!pg->is_primary());

  5.   post_event(i);

  6.   return transit< Stray >();//go---

  7. }

可以直接跳转到状态Stray。在用户自定义的函数里,可以调用函数post_event()直接产生相应的事件,并投递给状态机

 
  1. PG::RecoveryState::Start::Start(my_context ctx)

  2.   : my_base(ctx),

  3.     NamedState(context< RecoveryMachine >().pg->cct, "Start")

  4. {

  5.   context< RecoveryMachine >().log_enter(state_name);

  6.   PG *pg = context< RecoveryMachine >().pg;

  7.   if (pg->is_primary()) {

  8.     dout(1) << "transitioning to Primary" << dendl;

  9.     post_event(MakePrimary());//go---

  10.   } else { //is_stray

  11.     dout(1) << "transitioning to Stray" << dendl; 

  12.     post_event(MakeStray());//go---

  13.   }

  14. }

在用户的自定义函数里,调用函数discard_event()可以直接丢弃事件,不做任何处理

 
  1. boost::statechart::result PG::RecoveryState::Primary::react(const ActMap&)

  2. {

  3.   dout(7) << "handle ActMap primary" << dendl;

  4.   PG *pg = context< RecoveryMachine >().pg;

  5.   pg->publish_stats_to_osd();

  6.   pg->take_waiters();

  7.   return discard_event();//go---

  8. }

在用户的自定义函数里,调用函数forward_event()可以把当前事件继续投递给状态机

 
  1. boost::statechart::result PG::RecoveryState::WaitUpThru::react(const ActMap& am)

  2. {

  3.   PG *pg = context< RecoveryMachine >().pg;

  4.   if (!pg->need_up_thru) {

  5.     post_event(Activate(pg->get_osdmap()->get_epoch()));

  6.   }

  7.   return forward_event();

  8. }

结合 1.3 状态机的响应 的3种事件响应,大概有7种事件响应处理的方法。

1.7 PG状态机
在类PG的内部定义了类RecoveryState,该类RecoveryState的内部定义了PG的状态机RecoveryMachine和它的各种状态。

 
  1. class PG{

  2.     class RecoveryState{

  3.         class RecoveryMachine{

  4.         };

  5.     };

  6. };

在每个PG创建时,在构造函数里创建一个新的RecoveryState类的对象,并创建相应的RecoveryMachine类的对象,也就是创建了一个新的状态机。每个PG类对应一个独立的状态机来控制该PG的状态转换。

 
  1. PG::PG(OSDService *o, OSDMapRef curmap,

  2.        const PGPool &_pool, spg_t p) :

  3.     recovery_state(this){

  4. }

  5. class RecoveryState{

  6. public:

  7.     explicit RecoveryState(PG *pg)

  8.       : machine(this, pg), pg(pg), orig_ctx(0) {

  9.       machine.initiate();

  10.     }

  11. };

上面machine.initiate()调用的是boost::statechart::state_machine中的initiate()方法。
1.8 PG状态机的总体状态转换图

下图为PG状态机的总体状态转换图简化版

1.9 OSD启动加载PG状态机转换
当OSD重启时,调用函数OSD::init(),该函数调用load_pgs()加载已经存在的PG,其处理过程和以下创建PG的过程相似。

 
  1. int OSD::init()

  2. {

  3.   // load up pgs (as they previously existed)

  4.   load_pgs();

  5. }

  6. void OSD::load_pgs()

  7. {

  8. ...

  9.     PG::RecoveryCtx rctx(0, 0, 0, 0, 0, 0);

  10.     pg->handle_loaded(&rctx);//go--

  11. ...

  12. }

  13. void PG::handle_loaded(RecoveryCtx *rctx)

  14. {

  15.   dout(10) << "handle_loaded" << dendl;

  16.   Load evt;

  17.   recovery_state.handle_event(evt, rctx);

  18. }

  19. struct Initial : boost::statechart::state< Initial, RecoveryMachine >, NamedState {

  20.     typedef boost::mpl::list <

  21.     boost::statechart::transition< Initialize, Reset >,

  22.     boost::statechart::custom_reaction< Load >,

  23.     boost::statechart::custom_reaction< NullEvt >,

  24.     boost::statechart::transition< boost::statechart::event_base, Crashed >

  25.     > reactions;

  26.     

  27.     boost::statechart::result react(const Load&);

  28. }

  29. boost::statechart::result PG::RecoveryState::Initial::react(const Load& l)

  30. {

  31.   PG *pg = context< RecoveryMachine >().pg;

  32.   // do we tell someone we're here?

  33.   pg->send_notify = (!pg->is_primary());

  34.   pg->update_store_with_options();

  35.   pg->update_store_on_load();

  36.   return transit< Reset >();//go---

  37. }

1.10 PG创建后状态机的状态转换
 

 
  1. void PG::handle_create(RecoveryCtx *rctx)

  2. {

  3.   dout(10) << "handle_create" << dendl;

  4.   rctx->created_pgs.insert(this);

  5.   Initialize evt;

  6.   recovery_state.handle_event(evt, rctx);

  7.   ActMap evt2;

  8.   recovery_state.handle_event(evt2, rctx);

  9.   rctx->on_applied->add(make_lambda_context([this]() {

  10.     update_store_with_options();

  11.   }));

  12. }

当PG创建后,同时在该类内部创建了一个属于该PG的RecoveryMachine类型的状态机,该状态机的初始化状态为默认初始化状态Initial。

在PG创建后,调用函数pg->handle_create(&rctx)来给状态机投递事件

该函数首先向RecoveryMachine投递了Initialize类型的事件。接收到Initialize类型的事件后直接转移到Reset状态。其次,向RecoveryMachine投递了ActMap事件。

 
  1. boost::statechart::result PG::RecoveryState::Reset::react(const ActMap&)

  2. {

  3.   PG *pg = context< RecoveryMachine >().pg;

  4.   if (pg->should_send_notify() && pg->get_primary().osd >= 0) {

  5.     context< RecoveryMachine >().send_notify(

  6.       pg->get_primary(),

  7.       pg_notify_t(

  8.     pg->get_primary().shard, pg->pg_whoami.shard,

  9.     pg->get_osdmap()->get_epoch(),

  10.     pg->get_osdmap()->get_epoch(),

  11.     pg->info),

  12.       pg->past_intervals);

  13.   }

  14.   pg->update_heartbeat_peers();

  15.   pg->take_waiters();

  16.   return transit< Started >();//a---

  17. }

a. 在自定义的react函数里直接调用了transit函数跳转到Started状态。   

 
  1. struct Start;

  2.     struct Started : boost::statechart::state< Started, RecoveryMachine, Start >, NamedState {//这里直接进入默认子状态Start

  3.     ...

  4.     }

  5.     /*-------Start---------*/

  6.     PG::RecoveryState::Start::Start(my_context ctx)

  7.       : my_base(ctx),

  8.         NamedState(context< RecoveryMachine >().pg, "Start")

  9.     {

  10.       context< RecoveryMachine >().log_enter(state_name);

  11.     

  12.       PG *pg = context< RecoveryMachine >().pg;

  13.       if (pg->is_primary()) {

  14.         ldout(pg->cct, 1) << "transitioning to Primary" << dendl;

  15.         post_event(MakePrimary());//go---

  16.       } else { //is_stray

  17.         ldout(pg->cct, 1) << "transitioning to Stray" << dendl;

  18.         post_event(MakeStray());//go---

  19.       }

  20.     }

  21.     

  22.     struct Start : boost::statechart::state< Start, Started >, NamedState {

  23.       explicit Start(my_context ctx);

  24.       void exit();

  25.     typedef boost::mpl::list <

  26.     boost::statechart::transition< MakePrimary, Primary >,

  27.     boost::statechart::transition< MakeStray, Stray >

  28.     > reactions;

  29.     };    

  30.     

  31.     struct Primary : boost::statechart::state< Primary, Started, Peering >, NamedState {//这里直接进入Primary的默认子状态Peering。

  32.     ...

  33.     }

  34.     

  35.     struct Stray : boost::statechart::state< Stray, Started >, NamedState {

  36.     ...

  37.     }    

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/94423.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

敦煌网(DHgate)高成功率的下单流程(养号优势)

1打开敦煌官网 http://www.dhgate.com/ 2点击右上角的注册账号&#xff0c;输入账号信息 3注册完成后打开需要购买的商品页面 点击buy it now 4输入收货地址 5输入银行卡信息 6点击confirm to pay 确认购买 7购买成功&#xff0c;可以在订单页面确认到信息 敦煌网、卖全球、买…

13、Vue3 大事件管理系统

一、大事件项目介绍 和 创建 1.1 Vue3 大事件管理系统 在线演示&#xff1a; https://fe-bigevent-web.itheima.net/login 接口文档: https://apifox.com/apidoc/shared-26c67aee-0233-4d23-aab7-08448fdf95ff/api-93850835 基地址&#xff1a; http://big-event-vue-api-t.i…

解锁安全高效办公——私有化部署的WorkPlus即时通讯软件

在当今信息时代&#xff0c;高效的沟通与协作对于企业的成功至关重要。然而&#xff0c;随着信息技术的发展&#xff0c;保护敏感信息和数据安全也变得越来越重要。为了满足企业对于安全沟通和高效办公的需求&#xff0c;我们隆重推出私有化部署的WorkPlus即时通讯软件&#xf…

爬虫逆向实战(二十五)--某矿采购公告

一、数据接口分析 主页地址&#xff1a;某矿 1、抓包 通过抓包可以发现数据接口是cgxj/by-lx-page 2、判断是否有加密参数 请求参数是否加密&#xff1f; 通过查看“载荷”模块可以发现有一个param的加密参数 请求头是否加密&#xff1f; 无响应是否加密&#xff1f; 无c…

抖音矩阵,矩阵账号开发,抖音矩阵源码搭建

抖音矩阵&#xff0c;矩阵账号开发&#xff0c;抖音矩阵源码搭建&#xff1a; 1、账号矩阵系统搭建首先需要注意的是支持多平台&#xff0c;多账号&#xff0c;可以实现流量互通&#xff0c;账号矩阵多个账号联动形成账号矩阵形式分发开发。 2、账号矩阵系统需要可以查看分发…

vue中html引入使用<%= BASE_URL %>变量

首先使用src相对路径引入 注意&#xff1a; js 文件放在public文件下 不要放在assets静态资源文件下 否则 可能会报错 GET http://192.168.0.113:8080/src/assets/js/websockets.js net::ERR_ABORTED 500 (Internal Server Error) 正确使用如下&#xff1a;eg // html中引…

初识Java 2-1 操作符

目录 优先级 赋值 递减和递增操作符 关系操作符 逻辑操作符 字面量 字面量中的下划线 科学记数法 按位操作符 移位操作符 三元操作符 字符串操作符和 类型转换操作符 截尾和舍入 本笔记参考自&#xff1a; 《On Java 中文版》 Java的操作符大多继承自C&#xff0…

ThreeJS 模型中内嵌文字

之前有过模型中内嵌html网页&#xff0c;地址☞threeJS 模型中加载html页面_threejs 加载dom元素_小菜花29的博客-CSDN博客 这次是纯粹的在模型中嵌入文本信息&#xff0c;进行简单的文字展示 展示效果图 1. 使用FontLoader文字加载器 引入文本json文件&#xff0c;代码如下…

掉了无数头发成地中海后,我整理出了这套40+的大屏模板,快收藏!

最近又有不少粉丝后台问我接不接做可视化大屏&#xff0c;看来可视化大屏是越来越火啦&#xff0c;但老李还是要说一下&#xff0c;老李本身工作就很忙&#xff0c;实在是顾不过来&#xff0c;但老李会在自己体验过后为大家挑选合适的工具和模板&#xff0c;提升大家做大屏的效…

江西武功山旅游攻略(周末两日游)

一、 往返路线 1: 出发路线 周五晚上上海出发坐火车&#x1f684;到江西萍乡(11.5小时,卧铺550左右) 打车到江西武功山景区,120-150元左右,人均30元,1小时10分左右到达 或者 &#x1f697;到达萍乡北之后 出站后步行200米到长途汽车站&#xff0c;乘旅游巴士直达武功山游…

2023高教社杯数学建模思路 - 案例:FPTree-频繁模式树算法

文章目录 算法介绍FP树表示法构建FP树实现代码 建模资料 ## 赛题思路 &#xff08;赛题出来以后第一时间在CSDN分享&#xff09; https://blog.csdn.net/dc_sinor?typeblog 算法介绍 FP-Tree算法全称是FrequentPattern Tree算法&#xff0c;就是频繁模式树算法&#xff0c…

leetcode 1022.从根到叶的二进制数之和

⭐️ 题目描述 &#x1f31f; leetcode链接&#xff1a;https://leetcode.cn/problems/sum-of-root-to-leaf-binary-numbers/description/ 代码&#xff1a; class Solution { public:int sum (TreeNode* root , int num 0) {if (root nullptr) {return 0;}int cur num r…

无涯教程-分类算法 - 多项式逻辑回归模型函数

Logistic逻辑回归的另一种有用形式是多项式Lo​​gistic回归&#xff0c;其中目标或因变量可以具有3种或更多可能的unordered类型&#xff0c;即没有定量意义的类型。 用Python实现 现在&#xff0c;无涯教程将在Python中实现上述多项式逻辑回归的概念。为此&#xff0c;使用…

算法通关村第8关【白银】| 二叉树的深度和高度问题

1.最大深度问题 思路&#xff1a;递归三部曲 第一步&#xff1a;确定参数和返回值 题目要求求二叉树的深度&#xff0c;也就是有多少层&#xff0c;需要传递一个root从底层向上统计 int maxDepth(TreeNode root) 第二步&#xff1a;确定终止条件 当递归到null时就说明到底了…

【MCU】SD NAND芯片之国产新选择

文章目录 前言传统SD卡和可贴片SD卡传统SD卡可贴片SD卡 实际使用总结 前言 随着目前时代的快速发展&#xff0c;即使是使用MCU的项目上也经常有大数据存储的需求。可以看到经常有小伙伴这样提问&#xff1a; 大家好&#xff0c;请问有没有SD卡芯片&#xff0c;可以直接焊接到P…

科技探究之旅--亲子研学活动

2023年8月26日&#xff0c;广州市从化区齐家社会工作服务中心&#xff08;以下简称“齐家”&#xff09;的“星乐园-乡村儿童公益辅导服务项目”组织了新开村及西湖村助学点24对亲子到广州市白云区文搏3D打印基地进行“科技探究之旅--亲子研学”活动&#xff0c;旨在发现、点燃…

长胜证券:沪指高开回落涨1.13%,地产、券商等板块强势

28日&#xff0c;A股两市早盘大幅高开&#xff0c;盘中有所回落&#xff0c;沪指、深证成指涨幅收窄至1.5%以内。截至收盘&#xff0c;沪指涨1.13%报3098.64点&#xff0c;深成指涨1.01%&#xff0c;创业板指涨0.96%&#xff0c;两市合计成交11266亿元。盘面上&#xff0c;渔业…

面试中的代码写作:如何撰写清晰、高效的示例代码

&#x1f337;&#x1f341; 博主猫头虎 带您 Go to New World.✨&#x1f341; &#x1f984; 博客首页——猫头虎的博客&#x1f390; &#x1f433;《面试题大全专栏》 文章图文并茂&#x1f995;生动形象&#x1f996;简单易学&#xff01;欢迎大家来踩踩~&#x1f33a; &a…

leetcode刷题(字符串相加、包含每个查询的最小区间、模拟行走机器人、环形子数组的最大和、满足不等式的最大值、四数之和、树中距离之和)

目录 1、字符串相加 2、包含每个查询的最小区间 3、模拟行走机器人 4、环形子数组的最大和 5、满足不等式的最大值 6、四数之和 7、 树中距离之和 1、字符串相加 class Solution:def addStrings(self, num1: str, num2: str) -> str:i len(num1) - 1 # num1的末…

HarmonyOS Codelab 优秀样例——购物应用,体验一次开发多端部署魅力

一. 样例介绍 本篇Codelab基于自适应布局和响应式布局&#xff0c;实现购物应用在手机、折叠屏、平板不同屏幕尺寸设备上按不同设计显示。通过三层工程结构组织代码&#xff0c;实现一次开发&#xff0c;多端部署 。 手机运行效果如图所示&#xff1a; 折叠屏运行效果图&#x…