akka入门-对处理器状态进行持久化

该话题涉及几个概念:
处理器、信道、事件源、日志、状态。

消息可以持久化,通常当前的状态保存在内存中(内存镜像),而事件源机制可以通过重播接收到的消息(在应用程序正常启动或崩溃后)恢复当前(或历史)的状态。

Eventsourced 实现了预写日志(write-ahead log ,WAL)用于跟踪一个Actor 所接收消息,并通过回放记录的消息来恢复其状态。

处理器是一个有状态的Actor,它将收到的消息记录在日志里(持久化)。

1.创建处理器状态对象

import java.io.Serializable;
import java.util.ArrayList;

import com.center.akka.simple.event.Event;

public class ProcessorState implements Serializable {
     private final ArrayList<String> events ;

     public ProcessorState() {
           this(new ArrayList<String>());
     }

     public ProcessorState(ArrayList<String> events) {
           this.events = events ;
     }

     public ProcessorState copy() {
           return new ProcessorState(new ArrayList<String>(events ));
     }

     public void update(Event event ) {
           events.add( event.toString());
     }

     @Override
     public String toString() {
           return events .toString();
     }
}
2.事件处理器

import akka.actor.UntypedActor;
import akka.event.Logging;
import akka.event.LoggingAdapter;

public class EventHandler extends UntypedActor {


    LoggingAdapter log = Logging.getLogger(getContext().system(), this);

    @Override
    public void onReceive(Object msg ) throws Exception {
        log.info( "Handled Event: " + msg );
    }
}

3.处理器

import java.util.UUID;

import akka.event.Logging;
import akka.event.LoggingAdapter;
import akka.japi.Procedure;
import akka.persistence.SnapshotOffer;
import akka.persistence.UntypedEventsourcedProcessor;

import com.center.akka.simple.command.Command;
import com.center.akka.simple.event.Event;

public class BaseProcessor extends UntypedEventsourcedProcessor {

  LoggingAdapter log = Logging.getLogger(getContext().system (), this );

  /**
   * The state of the processor
   */
  private ProcessorState processorState = new ProcessorState();

  /**
   * Called on restart. Loads from Snapshot first, and then replays Journal Events to update state.
   *
   * @param msg
   */
  public void onReceiveRecover(Object msg ) {
    log.info("Received Recover: " + msg);
    if (msg instanceof Event) {
      System. out.println("onReceiveRecover -- msg instanceof Event");
      System. out.println("event --- " + ((Event) msg).getData());
      processorState.update((Event) msg);

    } else if (msg instanceof SnapshotOffer) {
      System. out.println("onReceiveRecover -- msg instanceof SnapshotOffer");
      processorState = (ProcessorState) ((SnapshotOffer) msg).snapshot();
    }
  }

  /**
   * Called on Command dispatch
   *
   * @param msg
   */
  public void onReceiveCommand(Object msg ) {
    log.info("Received Command: " + msg);
    if (msg instanceof Command) {
      final String data = ((Command) msg).getData();

      // generate an event we will persist after being enriched with a uuid
      final Event event = new Event(data , UUID.randomUUID().toString());

      // persist event and THEN update the state of the processor
      persist( event, new Procedure<Event>() {
        public void apply(Event evt) throws Exception {

          processorState.update(evt );

          // broadcast event on eventstream 发布该事件
          getContext().system().eventStream().publish( evt);
        }
      });
    } else if (msg .equals("snapshot" )) {
      saveSnapshot( processorState.copy());
    } else if (msg .equals("printstate" )) {
      log.info(processorState.toString());
    }
  }
}

4.测试类

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import akka.actor.ActorRef;
import akka.actor.ActorSystem;
import akka.actor.Props;

import com.center.akka.eventsourcing_persistence.event.EventHandler;
import com.center.akka.eventsourcing_persistence.persistence.BaseProcessor;
import com.center.akka.simple.command.Command;
import com.center.akka.simple.event.Event;

public class System {

  public static final Logger log = LoggerFactory.getLogger(System.class);

  public static void main(String... args) throws Exception {

    final ActorSystem actorSystem = ActorSystem.create("actor-server");

    final ActorRef handler = actorSystem.actorOf(Props.create(EventHandler. class));
    // 订阅
    actorSystem.eventStream ().subscribe(handler , Event.class);

    Thread.sleep(5000);

    final ActorRef actorRef = actorSystem.actorOf(Props.create(BaseProcessor. class), "eventsourcing-processor" );

    actorRef.tell( new Command("CMD 1" ), null);
    actorRef.tell( new Command("CMD 2" ), null);
    actorRef.tell( new Command("CMD 3" ), null);
    actorRef.tell( "snapshot", null );//发送保存快照命令
    actorRef.tell( new Command("CMD 4" ), null);
    actorRef.tell( new Command("CMD 5" ), null);
    actorRef.tell( "printstate", null );

    Thread.sleep(5000);

    log.debug( "Actor System Shutdown Starting..." );

    actorSystem.shutdown();
  }
}
先发送三条命令进行执行,接着执行生成快照命令,此时前三条命令会保存在快照中,然后发送4、5两条命令进行执行。
如果再次执行该程序,系统会首先恢复上次处理器的状态。本程序中会首先恢复快照中的状态然后是其他的状态。
快照使得处理器的状态持久化更加高效。

5.输出结果

[INFO] [05/17/2015 18:09:56.037] [actor-server-akka.actor.default-dispatcher-2] [akka://actor-server/user/eventsourcing-processor] Received Recover: RecoveryCompleted
[INFO] [05/17/2015 18:09:56.044] [actor-server-akka.actor.default-dispatcher-2] [akka://actor-server/user/eventsourcing-processor] Received Command: Command{data='CMD 1'}
[INFO] [05/17/2015 18:09:56.272] [actor-server-akka.actor.default-dispatcher-2] [akka://actor-server/user/eventsourcing-processor] Received Command: Command{data='CMD 2'}
[INFO] [05/17/2015 18:09:56.272] [actor-server-akka.actor.default-dispatcher-3] [akka://actor-server/user/$a] Handled Event: Event{data='CMD 1', uuid='21056184-f01d-47d5-aa37-fc08a521c8bd'}
[INFO] [05/17/2015 18:09:56.273] [actor-server-akka.actor.default-dispatcher-2] [akka://actor-server/user/$a] Handled Event: Event{data='CMD 2', uuid='5dfad32f-a08c-43ac-99a5-ff644c600394'}
[INFO] [05/17/2015 18:09:56.273] [actor-server-akka.actor.default-dispatcher-3] [akka://actor-server/user/eventsourcing-processor] Received Command: Command{data='CMD 3'}
[INFO] [05/17/2015 18:09:56.275] [actor-server-akka.actor.default-dispatcher-4] [akka://actor-server/user/eventsourcing-processor] Received Command: snapshot
[INFO] [05/17/2015 18:09:56.275] [actor-server-akka.actor.default-dispatcher-3] [akka://actor-server/user/$a] Handled Event: Event{data='CMD 3', uuid='fc185f7d-898d-4cc4-af20-fcff497f7f7c'}
[INFO] [05/17/2015 18:09:56.276] [actor-server-akka.actor.default-dispatcher-4] [akka://actor-server/user/eventsourcing-processor] Received Command: Command{data='CMD 4'}
[INFO] [05/17/2015 18:09:56.276] [actor-server-akka.actor.default-dispatcher-2] [akka://actor-server/user/eventsourcing-processor] Received Command: Command{data='CMD 5'}
[INFO] [05/17/2015 18:09:56.276] [actor-server-akka.actor.default-dispatcher-4] [akka://actor-server/user/$a] Handled Event: Event{data='CMD 4', uuid='8b5c5202-7e23-480a-97af-c0841668118d'}
[INFO] [05/17/2015 18:09:56.277] [actor-server-akka.actor.default-dispatcher-4] [akka://actor-server/user/eventsourcing-processor] Received Command: printstate
[INFO] [05/17/2015 18:09:56.277] [actor-server-akka.actor.default-dispatcher-2] [akka://actor-server/user/$a] Handled Event: Event{data='CMD 5', uuid='e4a3f258-5d0b-431a-93cc-8f857a19a400'}
[INFO] [05/17/2015 18:09:56.277] [actor-server-akka.actor.default-dispatcher-4] [akka://actor-server/user/eventsourcing-processor] [Event{data='CMD 1', uuid='21056184-f01d-47d5-aa37-fc08a521c8bd'}, Event{data='CMD 2', uuid='5dfad32f-a08c-43ac-99a5-ff644c600394'}, Event{data='CMD 3', uuid='fc185f7d-898d-4cc4-af20-fcff497f7f7c'}, Event{data='CMD 4', uuid='8b5c5202-7e23-480a-97af-c0841668118d'}, Event{data='CMD 5', uuid='e4a3f258-5d0b-431a-93cc-8f857a19a400'}]
[INFO] [05/17/2015 18:09:56.283] [actor-server-akka.actor.default-dispatcher-3] [akka://actor-server/user/eventsourcing-processor] Received Command: SaveSnapshotSuccess(SnapshotMetadata(/user/eventsourcing-processor,3,1431857396276))
18:10:00.417 [main] DEBUG c.c.a.e.app.System - Actor System Shutdown Starting...

如果重启后不恢复先前的消息,只需要重写processor中的preStart()方法即可:

  @Override
  public void preStart() throws Exception {
    self().tell(Recover.create(0L), null);
  }



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值