You are viewing an old version of this page. View the current version.

Compare with Current View Page History

« Previous Version 19 Next »

스냅샷은 앞장 이벤트소싱에서도 부분적으로 사용이되었습니다. 스냅샷은 성능상의 목적으로 다양한곳에서 혼합되어 사용될수 있으며

그 컨셉은 간단합니다. 무수히 발생하는 이벤트로 인해 우리가 설계한 어떠한 객체의 상태는 지속적으로 변경된다는 점이며

모든 상태변화를 기록하는것은 불필요할수도 있으며, 필요한 순간의 청사진만 찍어서 그것을 활용할수 있다란 것입니다.


상태를 기록하는 범위주기에따라 세가지로 구분되며, 그 차이를통해 스냅샷을 이해할수가 있습니다.

  • 이벤트저장 : 상태복원은 연속된 이벤트 재생만을 통해가능하며 리플레이와같은 구현을 위해 이벤트 자체를 모두 기록
  • 상태 히스토리 저장 : 모든 이벤트를 기록할필요없이 특정시점의 상태를 복원을 하면되기 때문에, 원하는 만큼 상태를 기록함
  • 마지막 상태만 저장 : 마지막 저장값만 보장함으로, 왜 변경되었고 바로 이전으로 돌리는것은 어렵습니다. 중앙DB를 이용한 일반적 개발 방식입니다.


AKKA에서의 스냅샷은 모든것을 저장해야 하는것과, 중요한 1건은 꼭 저장해야 하는 Persitence 각기 다른 요구 요건에서

중간 조율을할수 있는 장치쯤으로 정의해봅니다.

연관 키워드 : difference between redo and snapshot


스냅샷 구현

@Component
@Scope("prototype")
public class SnapShotActor extends AbstractPersistentActor {
	
	private final LoggingAdapter log = Logging.getLogger(getContext().system(), "AbstractPersistentActor");	  
		
	private Object state;
	private int snapShotInterval = 5;
	
	private int msgCnt = 0;
	
    @Override
    public String persistenceId() { return "ExamplePersistentActor-id-1"; }
    
    @Override public Receive createReceiveRecover() {
	  return receiveBuilder().
	    match(SnapshotOffer.class, s -> {
	      state = s.snapshot();	//상태복원
	      log.info("상태복원");
	      // ...
	    }).
	    match(String.class, s -> {/* ...*/}).build();
	}
	 
	// 스냅샷을 지원하는 메시지 정의
	@Override public Receive createReceive() {
	  return receiveBuilder().
	    match(SaveSnapshotSuccess.class, ss -> {
	      SnapshotMetadata metadata = ss.metadata();	      
	      // ...
	    }).
	    match(SaveSnapshotFailure.class, sf -> {
	      SnapshotMetadata metadata = sf.metadata();
	      // ...
	    }).	      
	    match(String.class, cmd -> {
	    	log.info("EventFired:"+cmd);
	    	if(cmd.indexOf("print") ==0 ) {
	    		log.info("상태확인:"+state);	    		
	    	}else {
	    		msgCnt++;
	    		if (msgCnt % snapShotInterval == 0 ) {
		    		//이벤트가 ?회 발생할때마다,상태를 변경하고 스냅샷을 찍음 ( 테스트를 위한 임의 조건)	        		        	  		    		
		    		state = cmd + "을 먹은 상태";
		    		log.info("SaveSnapShot:" + state);
		    		saveSnapshot(state);	    		
		    	}	    		
	    	}    	
	    })	    
	    .build();
	}
}

이벤트를 받게되면, 상태가 변경되게되며 자신이 원하는 타이밍에

카메라 셔터(saveSnapshot) 를 누르기만 하면됩니다.

여기서는 수많은 이벤트를 모두 저장하는것은 비효율적이니, 매 5번째 상태의 스냅샷정보를

유지하기로 하였다고 가정하였습니다.


스냅샷 복원

private Object state;

@Override public Receive createReceiveRecover() {
  return receiveBuilder().
    match(SnapshotOffer.class, s -> {
      state = s.snapshot();
      // ...
    }).
    match(String.class, s -> {/* ...*/}).build();
}


또는


@Override
public Recovery recovery() {
  return Recovery.create(
    SnapshotSelectionCriteria
      .create(457L, System.currentTimeMillis()));
}

마지막 상태를 복원(SnapshotOffer)할수도 있고,

각 시간대별 스냅샷은 고유 아이디가 존재함으로

특정 스냅샷(SnapshotSelectionCriteria)을 복원할수도 있습니다.


스냅샷 테스트

	protected void persistenceSnapShot()  {
	    new TestKit(system) {{
	    	ActorRef probe = getRef();	    	
	    	
			Props snapShotActorProp = ext.props("snapShotActor");			
			System.out.println("snapShotActor 액터생성");
			ActorRef snapShotActor = system.actorOf(snapShotActorProp, "snapShotActor");
			
			System.out.println("event 생성");
			snapShotActor.tell("커피", ActorRef.noSender());
			snapShotActor.tell("사탕", ActorRef.noSender());
			snapShotActor.tell("커피", ActorRef.noSender());
			snapShotActor.tell("스테이크", ActorRef.noSender());
			snapShotActor.tell("라면", ActorRef.noSender()); // <-- 복구기대 상태
			snapShotActor.tell("사탕", ActorRef.noSender());  
			snapShotActor.tell("커피", ActorRef.noSender());

			System.out.println("상태확인");
			snapShotActor.tell( "print" , ActorRef.noSender());			
			expectNoMessage(java.time.Duration.ofSeconds(1));
			
			System.out.println("snapShotActor 종료또는 비정상종료");
			snapShotActor.tell( akka.actor.PoisonPill.getInstance() , ActorRef.noSender());			
			expectNoMessage(java.time.Duration.ofSeconds(1));
			
			System.out.println("snapShotActor 마지막 상태 확인");
			ActorRef snapShotActo2 = system.actorOf(snapShotActorProp, "eventActor");
			
			System.out.println("상태 복원확인");
			snapShotActo2.tell( "print" , ActorRef.noSender());
			
			expectNoMessage(java.time.Duration.ofSeconds(1));				       
			
	    }};
	}

snapShotActor 액터생성
event 생성
상태확인
[INFO] [06/17/2018 15:43:10.283] [AkkaTestApp-akka.actor.default-dispatcher-5] [AbstractPersistentActor] 상태복원
[INFO] [06/17/2018 15:43:10.398] [AkkaTestApp-akka.actor.default-dispatcher-4] [AbstractPersistentActor] EventFired:커피
[INFO] [06/17/2018 15:43:10.400] [AkkaTestApp-akka.actor.default-dispatcher-4] [AbstractPersistentActor] EventFired:사탕
[INFO] [06/17/2018 15:43:10.400] [AkkaTestApp-akka.actor.default-dispatcher-4] [AbstractPersistentActor] EventFired:커피
[INFO] [06/17/2018 15:43:10.400] [AkkaTestApp-akka.actor.default-dispatcher-4] [AbstractPersistentActor] EventFired:스테이크
[INFO] [06/17/2018 15:43:10.403] [AkkaTestApp-akka.actor.default-dispatcher-5] [AbstractPersistentActor] EventFired:라면
[INFO] [06/17/2018 15:43:10.403] [AkkaTestApp-akka.actor.default-dispatcher-5] [AbstractPersistentActor] SaveSnapShot:라면을 먹은 상태
[INFO] [06/17/2018 15:43:10.405] [AkkaTestApp-akka.actor.default-dispatcher-5] [AbstractPersistentActor] EventFired:사탕
[INFO] [06/17/2018 15:43:10.405] [AkkaTestApp-akka.actor.default-dispatcher-5] [AbstractPersistentActor] EventFired:커피
[INFO] [06/17/2018 15:43:10.406] [AkkaTestApp-akka.actor.default-dispatcher-5] [AbstractPersistentActor] EventFired:print
[INFO] [06/17/2018 15:43:10.406] [AkkaTestApp-akka.actor.default-dispatcher-5] [AbstractPersistentActor] 상태확인:라면을 먹은 상태
snapShotActor 종료또는 비정상종료
snapShotActor 마지막 상태 확인
상태 복원확인
[INFO] [06/17/2018 15:43:11.887] [AkkaTestApp-akka.actor.default-dispatcher-2] [AbstractPersistentActor] 상태복원
[INFO] [06/17/2018 15:43:11.889] [AkkaTestApp-akka.actor.default-dispatcher-3] [AbstractPersistentActor] EventFired:print
[INFO] [06/17/2018 15:43:11.889] [AkkaTestApp-akka.actor.default-dispatcher-3] [AbstractPersistentActor] 상태확인:라면을 먹은 상태

샘플은 모든 이용자가 먹을때마다 이벤트가 발생하고,그 시식 결과에의해 어떠한 상태로 변경이 됩니다.

이벤트드리븐 의 예를 간단하게 설명하는것으로 대단한 기능이 있는것은 아닙니다. 

단지 이벤트는 무수히 발생하며 모든것을 기록하고 이용하기 어려우니, 스냅샷은 특정한 이벤트 조건에따라(여기서는 단순하게 5번째)

사진을 찍게되며 , 모든 사용자의 청사진을 최근 몇개까지 운영할것인가를 성능과 복원과을 고려하여 설계에 반영할수 있다란것입니다..

스냅샷 시도에따른 반응 메시지

MethodSuccessFailure message
saveSnapshot(Any)SaveSnapshotSuccessSaveSnapshotFailure
deleteSnapshot(Long)DeleteSnapshotSuccessDeleteSnapshotFailure
deleteSnapshots(SnapshotSelectionCriteria)DeleteSnapshotsSuccessDeleteSnapshotsFailure

스냅샷은 다양한 이유로(디스크풀및 메모리풀등) 실패가 있을수 있으며

그에 대응하는 코드작성도 가능합니다. 


원문 참고 :




  • No labels