akka actor中的FSM有限状态机的设计原则参考了erlang中的FSM(http://www.erlang.org/documentation/doc-4.8.2/doc/design_principles/fsm.html)
许多应用程序可以利用FSMs来设计,Protocol stacks是其中的一个例子。
我们可以用一个关系集合来描述一个FSM,形式如下:
State(S) x Event(E) -> Actions (A), State(S') ...这些关系可以被解释为如下含义:
If we are in state S
and the event E
occurs, we should perform the actions A
and make a transition to the state S'
. 如果当前状态处于S,且事件E发生了的话,我们应该执行行为A,并把状态转移到S'
如果用Erlang的gen_fsm行为来编程的话,上述转换规则可以用如下代码来表示:
StateName(Event, StateData) -> .. code for actions here ... {next_state, StateName', StateData'}下图用一个简单的FSM来描述”Plain Ordinary Telephony Service“(POTS, 一般的电话服务)
电话服务POTS FSM可以用以下gen_fsm行为来描述:
init(A) -> {ok, idle, A}. idle({off_hook, A}, A) -> {next_state, getting_number, {A,[]}}; idle({seize, A}, B) when A /= B -> {next_state, ringing_b_side, {B, A}); idle(_, A) -> {next_state, idle, A}. getting_number({digit, D}, {A, Seq}) -> case ... of ... -> ... {next_state, ringing_a_side, {A, B}}; ... -> ... {next_state, getting_number, {A, Seq1}}; ... -> ... {next_state, wait_on_hook, A} end; getting_number({on_hook, A}, {A,_}) -> {next_state, idle, A}. ringing_a_side({on_hook, A}, {A, B}) -> {next_state, idle, A}; ringing_a_side({answered, B}, {A, B}) -> {next_state, speech, {A,B}}. ringing_b_side({on_hook, A}, {B, A}) -> {next_state, idle, B}; ringing_b_side({off_hook, B}, {B, A}) -> {next_state, speech, {B, A}}. speech({on_hook, A}, {A, B}) -> {next_state, idle, A}; speech({on_hook, B}, {A, B}) -> {next_state, wait_on_hook, A}. wait_on_hook({on_hook, A}, A) -> {next_state, idle, A}.上述代码只是描述了状态转移。为了增加行为,需要增加以下代码:
getting_number({digit, D}, {A, Seq}) -> Seq1 = Seq ++ [D], case number_analyser:analyse(Seq1) of {user, B} -> hw:seize(B, A), {next_state, ringing_a_side, {A, B}}; get_more_digits -> {next_state, getting_number, {A, Seq1}}; invalid_number -> hw:send_nasty_tone(A, bad_number_tone), {next_state, wait_on_hook, A} end; getting_number({on_hook, A}, {A,_}) -> hw:stop_codec(A), {next_state, idle, A}.
为了完成这个例子,我们需要把FSM和时间生成器程序打包成behaviour模型,然后增加FSM相关的代码,如下所示:
-module(pots). -behaviour(gen_fsm). -export([...]). start() -> gen_fsm:start(...) stop() -> gen_fsm:send_all_state_event(...) on_hook(A) -> gen_fsm:send_event(..., {off_hook, A}). ...一个FSM的例子:
上面的POTS例子并没有包含全部的细节。下面的小例子是完整的:
-module(test1_fsm). -behaviour(gen_fsm). %% interface routines %% start us up start() -> gen_fsm:start({local, hello}, test1_fsm, [], []). %% send a hello -- this will end up in the FSM routines hello() -> gen_fsm:send_event(hello, {hello, self()}). %% send a stop this will end up in "handle_event" stop() -> gen_fsm:send_all_state_event(hello, stopit). %% -- interface end %% This initialisation routine gets called init(_) -> {ok, idle, []}. %% The state machine idle({hello, A}, []) -> {next_state, one, A}. one({hello, B}, A) -> A ! {hello, B}, B ! {hello, A}, {next_state, idle, []}. %% this handles events in *any* state handle_event(stopit, AnyState, TheStateData) -> {stop, i_shall_stop, []}. %% tell it to stop %% This is the finalisation routine - it gets called %% When we have to stop terminate(i_shall_stop, TheStateIwasIn, TheStateData) -> ok.
------------------------------华丽的分割线----------------------------------------------------------------
上面主要讲的是Erlang中的FSM的使用,下面来正式介绍Akka 中的FSM。为了展示Akka FSM特质的大部分特性,我们假设一个Actor接收和存队列大量的爆发式传过来的消息,在消息爆发过后,转发或者清除消息。
to be continued...........