当前位置: 首页 > news >正文

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

相关文章:

ceph peering机制-状态机

本章介绍ceph中比较复杂的模块&#xff1a; Peering机制。该过程保障PG内各个副本之间数据的一致性&#xff0c;并实现PG的各种状态的维护和转换。本章首先介绍boost库的statechart状态机基本知识&#xff0c;Ceph使用它来管理PG的状态转换。其次介绍PG的创建过程以及相应的状…...

Java | File类

目录&#xff1a; File类File类中常用的方法&#xff1a;boolean exists( ) &#xff1a;判断此 文件/目录 是否存在boolean createNewFile( ) &#xff1a;创建一个文件boolean mkdir( ) &#xff1a;创建 “单层” 目录/文件夹boolean mkdirs( ) &#xff1a;创建 “多层” 目…...

数学建模之灰色预测

灰色预测&#xff08;Grey Forecasting&#xff09;是一种用于时间序列数据分析和预测的方法&#xff0c;通常用于处理具有较少历史数据的情况或者数据不够充分的情况。它是一种非常简单但有效的方法&#xff0c;基于灰色系统理论&#xff0c;用来估计未来的趋势。 以下是灰色…...

03_nodejd_npm install报错

npm install报错 npm ERR! code ERESOLVE npm ERR! ERESOLVE unable to resolve dependency tree npm ERR! npm ERR! While resolving: 5kcrm11.0.0 npm ERR! Found: vue2.5.17 npm ERR! node_modules/vue npm ERR! vue"2.5.17" from the root project npm ERR! np…...

three.js(二):webpack + three.js + ts

用webpackts 开发 three.js 项目 webpack 依旧是主流的模块打包工具;ts和three.js 是绝配&#xff0c;three.js本身就是用ts写的&#xff0c;ts可以为three 项目提前做好规则约束&#xff0c;使项目的开发更加顺畅。 1.创建一个目录&#xff0c;初始化 npm mkdir demo cd de…...

最小二乘法处理线性回归

最小二乘法是一种数学优化技术&#xff0c;用于查找最适合一组数据点的函数。 该方法主要用于线性回归分析&#xff0c;当然&#xff0c;也可用于非线性问题。 开始之前&#xff0c;我们先理解一下什么是回归。 回归&#xff1a;回归是一种监督学习算法&#xff0c;用于建模和…...

ModbusCRC16校验 示例代码

作者&#xff1a; Herman Ye Galbot Auromix 测试环境&#xff1a; Ubuntu20.04 更新日期&#xff1a; 2023/08/30 注1&#xff1a; Auromix 是一个机器人爱好者开源组织。 注2&#xff1a; 本文在更新日期经过测试&#xff0c;确认有效。 笔者出于学习交流目的&#xff0c; 给…...

一不留神就掉坑

乘除顺序问题 在据卡特兰数[1]公式,解决leetcode-96 不同的二叉搜索树[2]时,遇到一个非常诡异的问题, package mainimport "fmt"func main() { for i : 0; i < 40; i { fmt.Printf("第%d个卡特兰数为:%d\n", i, numTrees(i)) }}func numTrees(n int) i…...

Redis数据类型(list\set\zset)

"maybe its why" List类型 列表类型是⽤来存储多个有序的字符串&#xff0c;列表中的每个字符串称为元素&#xff08;element&#xff09;&#xff0c;⼀个列表最多可以存储个2^32 - 1个元素。在Redis中&#xff0c;可以对列表两端插⼊&#xff08;push&#xff09…...

TongWeb安装以及集成

TongWeb 安装步骤 静默安装 获取linux可执行安装包 如: Install_TWx.x.x.x_Enterprise_Linux.bin 创建安装所需配置文件 install.properties 内容如下 [root@node5 tongweb]# cat install.properties INSTALL_UI=silent USER_INSTALL_DIR=/home/tongweb SILENT_JDK_HOME=/jd…...

ScreenToGif-动图制作软件实用操作

ScreenToGif官网&#xff1a;ScreenToGif ⭕第一步&#xff1a;启动页面 ⭕第二步&#xff1a;选项 &#x1f95d;录像机-捕获频率选择手动-播放延迟1000ms(可以任意) ⭕第三步&#xff1a;录像机开始录屏 &#x1f95d;我们调整录屏的大小后&#xff0c;打开画图&#xff0c…...

sqlibs安装及复现

sqlibs安装 安装phpstudy后&#xff0c;到github上获取sqlibs源码 sqli-labs项目地址—Github获取&#xff1a;GitHub - Audi-1/sqli-labs: SQLI labs to test error based, Blind boolean based, Time based. 在phpstudy本地文件中的Apache目录中解压上方下载的源码。 将sq…...

OpenAI 创始人 Sam Altman 博客有一篇 10 年前的文章

OpenAI 创始人 Sam Altman 博客有一篇 10 年前的文章《Advice for ambitious 19 year olds》&#xff0c;给 19 岁年轻人的建议&#xff0c;从 #参考答案 看到&#xff0c;非常适合我们&#x1f923;年轻人&#xff0c;顺便用 GPT4 重新翻译了下全文。 太长不读纯摘要版本如下&…...

写的一款简易的热点词汇记录工具

项目需要对用户提交的附件、文章、搜索框内容等做热词分析。如下图&#xff1a; 公司有大数据团队。本着不麻烦别人就不麻烦别人的原则&#xff0c;写了一款简易的记录工具&#xff0c;原理也简单&#xff0c;手工在业务插入锚点&#xff0c;用分词器分好词&#xff0c;排掉字…...

算法通关村——滑动窗口高频问题

1. 无重复字符的最长子串 给定一个字符串 s &#xff0c;请你找出其中不含有重复字符的 最长子串 的长度。 示例 1: 输入: s “abcabcbb” 输出: 3 解释: 因为无重复字符的最长子串是 “abc”&#xff0c;所以其长度为 3。 1.1 滑动窗口 找到最长字串需要找到字串的首尾位置…...

mybatis源码学习-2-项目结构

写在前面,这里会有很多借鉴的内容,有以下三个原因 本博客只是作为本人学习记录并用以分享,并不是专业的技术型博客笔者是位刚刚开始尝试阅读源码的人,对源码的阅读流程乃至整体架构并不熟悉,观看他人博客可以帮助我快速入门如果只是笔者自己观看,难免会有很多弄不懂乃至理解错误…...

selenium 自动化测试——环境搭建

安装python&#xff0c;并且使用pip命令安装 selenium pip3 install selenium 然后尝试第一次使用selenium 完成一个简单的测试自动化脚本 from selenium import webdriver from selenium.webdriver.common.by import By import timedriver webdriver.Chrome() driver.get(…...

得物一面,场景题问得有点多!

题目来源&#xff1a;https://www.nowcoder.com/discuss/525371909735792640 前文 本期是【捞捞面经】系列文章的第 1 期&#xff0c;持续更新中…。 《捞捞面经》系列正式开始连载啦&#xff0c;据说看了这个系列的朋友都拿到了大厂offer~ 欢迎星标订阅&#xff0c;持续更新…...

Prompt Tuning 和instruct tuning

Prompt Tuning 是啥&#xff1f; prompt的思想是&#xff0c;把下游任务的输入转化为预训练模型的原始任务。 以bert作为举例&#xff0c;假设任务是文本分类。“今天天气很好。”我们想判断一下这句话的情感是正面还是负面 fine-tune的方法是在bert之后接一个head&#xff0…...

springboot 与异步任务,定时任务,邮件任务

异步任务 在Java应用中&#xff0c;绝大多数情况下都是通过同步的方式来实现交互处理的&#xff1b;但是在处理与第三方系统交互的时候&#xff0c;容易造成响应迟缓的情况&#xff0c;之前大部分都是使用多线程来完成此类任务&#xff0c;其实&#xff0c;在Spring 3.x之后&a…...

2022年06月 C/C++(六级)真题解析#中国电子学会#全国青少年软件编程等级考试

C/C++编程(1~8级)全部真题・点这里 第1题:小白鼠再排队2 N只小白鼠(1 < N < 100),每只鼠头上戴着一顶有颜色的帽子。现在称出每只白鼠的重量,要求按照白鼠重量从小到大的顺序输出它们头上帽子的颜色。帽子的颜色用 “red”,“blue”等字符串来表示。不同的小白鼠可…...

【C++】C++11新特性(下)

上篇文章&#xff08;C11的新特性&#xff08;上&#xff09;&#xff09;我们讲述了C11中的部分重要特性。本篇接着上篇文章进行讲解。本篇文章主要进行讲解&#xff1a;完美转发、新类的功能、可变参数模板、lambda 表达式、包装器。希望本篇文章会对你有所帮助。 文章目录 一…...

python内网环境安装第三方包

文章目录 一、问题二、解决方法三、代码实现 一、问题 内网安装第三方包的应用场景&#xff0c;一般是一些需要在没网的环境下进行开发的情况。这些环境一般仅支持本地局域网访问&#xff0c;所以只能在不下载任何第三方包的情况下艰难开发。 二、解决方法 将当前应用依赖的第…...

javaScipt

javaScipt 一、JavaScript简介二、javaScript基础1、输入输出语法2、变量3、常量4、数据类型4.1、数字型 number4.2、字符串类型 string4.3、布尔类型 boolean4.4、未定义类型 undefined4.5、null 空类型4.6、typeof 检测变量数据类型 5、数据类型转换5.1、隐式转换5.2、显示转…...

Linux(实操篇三)

Linux实操篇 Linux(实操篇三)1. 常用基本命令1.7 搜索查找类1.7.1 find查找文件或目录1.7.2 locate快速定位文件路径1.7.3 grep过滤查找及"|"管道符 1.8 压缩和解压类1.8.1 gzip/gunzip压缩1.8.2 zip/unzip压缩1.8.3 tar打包 1.9 磁盘查看和分区类1.9.1 du查看文件和…...

数学之美 — 1

为什么你会想和他人共享那些美丽的事物呢&#xff1f;因为这会让他&#xff08;她&#xff09;感到愉悦&#xff0c;也能让你在分享的过程中重新欣赏一次事物的美。 ——David Blackwell 1、感官之美&#xff0c;对于那些有规律的事物&#xff0c;你可以利用自己的视觉、触觉、…...

python中的global关键字

在Python中&#xff0c;global关键字用于在函数内部声明一个全局变量。默认情况下&#xff0c;函数内部的变量是局部变量&#xff0c;只能在函数内部访问。使用global关键字可以在函数内部创建或修改全局变量&#xff0c;使其在函数外部也可见和修改。 以下是使用global关键字…...

Matlab图像处理-幂次变换

幂次变换 如下图所示的幂次变换函数曲线图&#xff1a; 当γ <1时&#xff0c;效果和对数变换相似&#xff0c;放大暗处细节&#xff0c;压缩亮处细节&#xff0c;随着数值减少&#xff0c;效果越强。 当γ >1时&#xff0c;放大亮处细节&#xff0c;压缩暗处细节&…...

浏览器输入 URL 地址,访问主页的过程

分析&回答 浏览器解析域名&#xff1b;TCP建立连接&#xff1b;浏览器向服务器发送HTTP请求&#xff1b;服务器解析请求并返回HTTP报文&#xff1b;浏览器解析并渲染页面&#xff1b;断开连接。 反思&扩展 域名解析的流程 查找浏览器缓存——我们日常浏览网站时&am…...

每日一学————基本配置和管理

一、交换机的基本配置 配置enable口令、密码和主机名 Switch> (用户执行模式提示符) Switch>enable (进入特权模式) Switch# …...

做论坛网站的应用/外贸网站制作公司哪家好

ZLMediakit之base64 在国内的开源媒体框架中&#xff0c;ZLMediakit做的是非常 非常 非常不错的了。&#xff08;作者也是一个非常值得尊敬的高手&#xff09;是非常值得学习的&#xff0c;这里对ZML的代码做一个深度解析&#xff08;自己认为的&#xff01;&#xff01;&…...

国外免费可以做网站的服务器/百度搜索网

旧制&#xff08;中四、中五)实施九年免费教育之后&#xff0c;学生需要以校内考试成绩作评级&#xff0c;决定能否升读高中(中四、中五)。香港的中四及中五课程&#xff0c;大致上会文理分科&#xff0c;分开文、理、商科3个主流。教授科目均以香港考试及评核局举行的香港中学…...

以下可以制作二维码的网站为/网站注册账号

点击上方“Java基基”&#xff0c;选择“设为星标”做积极的人&#xff0c;而不是积极废人&#xff01;每天 14:00 更新文章&#xff0c;每天掉亿点点头发...源码精品专栏 原创 | Java 2021 超神之路&#xff0c;很肝~中文详细注释的开源项目RPC 框架 Dubbo 源码解析网络应用框…...

网站源码上传完后怎么做/推广普通话手抄报文字内容

服务器操作系统可以实现对计算机硬件与软件的直接控制和管理协调&#xff0c;任何计算机的运行离不开操作系统&#xff0c;服务器也一样&#xff0c;服务器操作系统主要分为四大流派&#xff1a;Windows Server、Netware、Unix、Linux接下来就给大家分享下常用的Windows、Linux…...

微信公众号发布wordpress/怎样注册个人网站

IDEA中git拉取代码的时出现Update canceled问题 当在IDEA中通过Git更新代码时&#xff0c;拉取失败&#xff0c;报如下错误 解决办法&#xff1a; 勾选上以后&#xff0c;点击 OK 后拉取代码&#xff1b; 然后就成功了...

网站评价/快速网站推广公司

1.服务器扫面■ HTTP TRACE Method Enabled说明&#xff1a;Apache服务器启用了TRACE Method。1.TRACE_Method是HTTP&#xff08;超文本传输&#xff09;协议定义的一种协议调试方法&#xff0c;该方法会使服务器原样返回任意客户端请求的任何内容。2. 由于该方法会原样返回客户…...