0%

Tikv的BatchSystem

BatchSystem是Tikv实现multi-raft的基石,本文介绍BatchSystem的实现。BatchSystem本身是一个抽象出来的通用的模块,不牵涉业务逻辑(multi-raft),方便单独介绍。

总体结构 (1)

FsmBasicMailboxScheduler (2)

Fsm是一个trait,由业务逻辑来实现。Fsm的运转是靠Message来驱动的,所以就有了BasicMailbox。一个Fsm和一个BasicMailbox绑定:BasicMailbox用于接收驱动FsmMessageFsm是发到BasicMailboxMessage的owner。

1
2
3
4
pub struct BasicMailbox<Owner: Fsm> {
sender: mpsc::LooseBoundedSender<Owner::Message>,
state: Arc<FsmState<Owner>>,
}

其中sender提供发送Message的功能,state里面实际上包裹了一个Fsm(除了Fsm本身还包含Fsm的状态)。可以从BasicMailbox中把Fsm拿出来(take_fsm函数),也可以还回去(release函数)。

当需要驱动Fsm的时候,就通过以下两个函数向关联的BasicMailbox发一个Message

1
2
3
4
5
6
7
8
9
10
11
12
13
impl<Owner: Fsm> BasicMailbox<Owner> {
pub fn force_send<S: FsmScheduler<Fsm = Owner>>(
&self,
msg: Owner::Message,
scheduler: &S,
) -> Result<(), SendError<Owner::Message>> {...}

pub fn try_send<S: FsmScheduler<Fsm = Owner>>(
&self,
msg: Owner::Message,
scheduler: &S,
) -> Result<(), TrySendError<Owner::Message>> {...}
}

发送到BasicMailboxMessage立即驱动Fsm吗?不是的,Fsm经过调度,才能最终被Message驱动。这也是force_sendtry_send的第二个参数的用途。这两个函数是这样工作的:把msg放入sender;把FsmBasicMailbox里拿出来,扔给Scheduler,这时Fsm处于NOTIFYSTATE_NOTIFIED状态(调度之前,FsmBasicMailbox中的时候处于NOTIFYSTATE_IDLE状态。将来,FsmMessage之后就会被还回BasicMailbox,那时又回到NOTIFYSTATE_IDLE状态。

Scheduler里面包含一个channel的发送端。当BasicMailbox接收到一个Message的时候,就把对应的Fsm扔到这个channel里。下文再说channel的接收端以及如何消费里面的Fsm

Router (3)

Router包含上文介绍过的3个组件:

  • BasicMailbox: 一个Control BasicMailbox和若干个Normal BasicMailboxRouter提供的接口是:向Control BasicMailbox或某个(由参数addr指定)Normal BasicMailbox发送一个Message;除此之外,还有广播,即向把Message发给所有BasicMailbox
  • Fsm: 每个BasicMailbox都关联一个Fsm,即有一个Control Fsm和若干个Normal Fsm
  • Scheduler: 一个Control Scheduler和一个Normal Scheduler;当一个Message发到一个BasicMailbox的时候,就把对应的Fsm拿出来,让Scheduler调度(即放进Schedulerchannel里);

PollerPollHandler (4)

前文说过,每个Scheduler有一个channel,有Message发给一个Fsm(发送到其关联的BasicMailbox)的时候,Fsm就被放进channelPoller就是这个channel的消费者:

1
2
3
4
5
6
struct Poller<N: Fsm, C: Fsm, Handler> {
router: Router<N, C, NormalScheduler<N, C>, ControlScheduler<N, C>>,
fsm_receiver: channel::Receiver<FsmTypes<N, C>>,
handler: Handler,
max_batch_size: usize,
}

其中fsm_receiver就是channel的接收端。Poller的工作就是从fsm_receiver中接收Fsm(这些Fsm上有Message),然后处理它们,处理的逻辑是业务层的事,所以这里由一个trait表示,即PollHandler。从fsm_receiver接收到的可能是Control Fsm也可能是Normal Fsm,所以PollHandler定义了对应的handle_controlhandle_normal接口。

另外,Pollerfsm_receiver中一下接收一批Fsm,由结构体Batch表示。

BatchSystem (5)

BatchSystem就是把上述东西组合起来,其spawn函数的工作就是创建一批Poller实例,并为它们一一放进线程里运行起来。

写的不错,有赏!