Diese Präsentation wurde erfolgreich gemeldet.
Wir verwenden Ihre LinkedIn Profilangaben und Informationen zu Ihren Aktivitäten, um Anzeigen zu personalisieren und Ihnen relevantere Inhalte anzuzeigen. Sie können Ihre Anzeigeneinstellungen jederzeit ändern.
Akka Persistent FSMを使ってみる
2016/03/04 「Akkaを語る会」
@satoshi_m8a
課題
• 複数の集約ををまたがる処理を順序良く実行したい。
• 境界づけられたコンテキストをまたがる処理を順序良く実
行したい。
• それらの処理がどこまで進行しているのかを記録したい。
• クラッシュしたら途中からやり直したい。もしくは最初の...
目次
• Sagaについて
• PersistentFSM
• 実装
Sagaについて
Saga…
• Saga(サーガ)と呼ぶと混乱を招く
サーガ?佐賀?Sa・Ga?
• Long-Lived Transaction (H Garcaa-Molrna 著 “Sagas” )
• Long-Running Process
• Process Manager
Long-lived Transaction
• Sagaの最初期の説明
• DBMSの用語
• 複数のDBトランザクションを一つのトランザクシ
ョンにまとめる
Long-Running Process (長期プロセス)
• 複数の処理を同時に開始しそれらの処理を追跡する。
• 条件分岐はない
• AllPhoneNumbersCounted、MatchedPhoneNumbersCountedなどのク...
Process Manager
• Aggregateや境界づけられたコンテキストをまたいでメッセージを仲介しながら、ひとつのビジネスプ
ロセスを遂行するための仕組み
• コンテキスト内のAggregateだけでなく、コンテキスト外のAggre...
参考:CQRS Journey
https://msdn.microsoft.com/en-us/library/jj554200.aspx
例:カンファレンス予約システム
• Process Manager自体はビジネスロジックをもたず、状態に応じてメッセージをルーティングするだけ
。
https://msdn.microsoft.com/en-us/library/jj55420...
例:カンファレンス予約システム
• CreateOrder → MakeReservation → MakePayment
の順に実行する。
• Reservationが出来なければ、MakePayment は実行
せずに、WaitListに登...
PersistentFSM
Akka Persistence
• Actorの内部状態を永続化できるようになる。
• CQRSとイベントソーシングに使える。
• at least once delivery
PersistentFSM
• experimental(2016/03/04時点)
• FSM(有限状態機械)のように振る舞うPersistentActor
PersistentFSM
• whenで状態ごとのハンドラとデフォルトタイムアウト
を定義する
• gotoによって状態遷移する
• forMaxで次の状態のタイムアウトをセットする
• applyingでイベントを元に内部データを更新
• ...
実装
例:カンファレンス予約システム
https://msdn.microsoft.com/en-us/library/jj554200.aspx
実装(1)
object OrderProcessManager {
def props(receiver: ActorRef, order: ActorRef, reservation: ActorRef, payment: ActorRef...
実装(2)
object Commands {
sealed trait OrderProcessCommand extends Command[OrderProcessId]
case class PlaceOrder(id: OrderPr...
実装(3)
class OrderProcessManager(receiver: ActorRef, order: ActorRef, reservation: ActorRef, payment: ActorRef, waitList: A...
実装(4)
startWith(Untouched, OrderProcessData(None, None, None, None))
when(Untouched, 10 seconds) {
case Event(PlaceOrder(i...
実装(5)
when(ReservationProcessing, 10 seconds) {
case Event(SeatReserved(reservationId, seatId), _) =>
val paymentId = Paym...
実装(6)
when(PaymentProcessing, 10 seconds) {
case Event(PaymentAccepted(paymentId), OrderProcessData(Some(oid), _, _, _)) =...
実装(7)
when(CancellationProcessing, 10 seconds) {
case Event(PaymentCanceled(paymentId), _) =>
stay applying PaymentRemoved...
実装(8)
private def cancelAll(d: OrderProcessData) = {
d.paymentId.foreach {
id =>
payment ! CancelPayment(id)
}
d.waitListI...
まとめと考察
• PersistentFSMでProcess Managerを実装した
• become/unbecomeとの違い
• 集約に実装するか、プロセスマネージャーを起動するか、集約がプ
ロセスマネージャーを起動するか。
• 各集約に...
参考
• http://doc.akka.io/docs/akka/snapshot/scala/persiste
nce.html
• http://www.enterpriseintegrationpatterns.com/pattern
...
Nächste SlideShare
Wird geladen in …5
×

Akka Persistent FSMを使ってみる

990 Aufrufe

Veröffentlicht am

Akk Persistent FSM を使って Process Manager を実装した話

Veröffentlicht in: Software
  • Als Erste(r) kommentieren

Akka Persistent FSMを使ってみる

  1. 1. Akka Persistent FSMを使ってみる 2016/03/04 「Akkaを語る会」 @satoshi_m8a
  2. 2. 課題 • 複数の集約ををまたがる処理を順序良く実行したい。 • 境界づけられたコンテキストをまたがる処理を順序良く実 行したい。 • それらの処理がどこまで進行しているのかを記録したい。 • クラッシュしたら途中からやり直したい。もしくは最初の 状態に戻したい。 • 状態によっては別の処理を実行したい。
  3. 3. 目次 • Sagaについて • PersistentFSM • 実装
  4. 4. Sagaについて
  5. 5. Saga… • Saga(サーガ)と呼ぶと混乱を招く
  6. 6. サーガ?佐賀?Sa・Ga? • Long-Lived Transaction (H Garcaa-Molrna 著 “Sagas” ) • Long-Running Process • Process Manager
  7. 7. Long-lived Transaction • Sagaの最初期の説明 • DBMSの用語 • 複数のDBトランザクションを一つのトランザクシ ョンにまとめる
  8. 8. Long-Running Process (長期プロセス) • 複数の処理を同時に開始しそれらの処理を追跡する。 • 条件分岐はない • AllPhoneNumbersCounted、MatchedPhoneNumbersCountedなどのクエリに応答するよう なイベントが発行されている。 実践ドメイン駆動設計 ヴァーン・ヴァーノン (著), 高木 正弘 (翻訳) から引用
  9. 9. Process Manager • Aggregateや境界づけられたコンテキストをまたいでメッセージを仲介しながら、ひとつのビジネスプ ロセスを遂行するための仕組み • コンテキスト内のAggregateだけでなく、コンテキスト外のAggregateを束ねた処理も作れる。 • 集約からのメッセージによって、条件分岐することもある。(動的なルーティング) • 順序立てても良いし、同時に実行するプロセスがあってもよい。 • 汎用性が高い http://www.enterpriseintegrationpatterns.com/patterns/messaging/ProcessManager.html
  10. 10. 参考:CQRS Journey https://msdn.microsoft.com/en-us/library/jj554200.aspx
  11. 11. 例:カンファレンス予約システム • Process Manager自体はビジネスロジックをもたず、状態に応じてメッセージをルーティングするだけ 。 https://msdn.microsoft.com/en-us/library/jj554200.aspx
  12. 12. 例:カンファレンス予約システム • CreateOrder → MakeReservation → MakePayment の順に実行する。 • Reservationが出来なければ、MakePayment は実行 せずに、WaitListに登録する。 • CreateOrder, MakeReservation, MakePayment が失 敗やタイムアウトしたら全ての処理を取り消す。
  13. 13. PersistentFSM
  14. 14. Akka Persistence • Actorの内部状態を永続化できるようになる。 • CQRSとイベントソーシングに使える。 • at least once delivery
  15. 15. PersistentFSM • experimental(2016/03/04時点) • FSM(有限状態機械)のように振る舞うPersistentActor
  16. 16. PersistentFSM • whenで状態ごとのハンドラとデフォルトタイムアウト を定義する • gotoによって状態遷移する • forMaxで次の状態のタイムアウトをセットする • applyingでイベントを元に内部データを更新 • イベントのシーケンスが保存される。
  17. 17. 実装
  18. 18. 例:カンファレンス予約システム https://msdn.microsoft.com/en-us/library/jj554200.aspx
  19. 19. 実装(1) object OrderProcessManager { def props(receiver: ActorRef, order: ActorRef, reservation: ActorRef, payment: ActorRef, waitList: ActorRef) = Props(new OrderProcessManager(receiver,order, reservation, payment, waitList)) object States { sealed trait OrderProcessState extends FSMState case object Untouched extends OrderProcessState { override def identifier: String = "untouched" } case object OrderProcessing extends OrderProcessState { override def identifier: String = "order-processing" } case object ReservationProcessing extends OrderProcessState { override def identifier: String = "reservation-processing" } case object PaymentProcessing extends OrderProcessState { override def identifier: String = "payment-processing" } case object AddToWaitListProcessing extends OrderProcessState { override def identifier: String = "add-to-wait-list-processing" } case object OrderCompleted extends OrderProcessState { override def identifier: String = "order-completed" } case object CancellationProcessing extends OrderProcessState { override def identifier: String = "cancellation-processing" } } }
  20. 20. 実装(2) object Commands { sealed trait OrderProcessCommand extends Command[OrderProcessId] case class PlaceOrder(id: OrderProcessId) extends OrderProcessCommand case class CancelOrderProcess(id: OrderProcessId) extends OrderProcessCommand } object Events { sealed trait OrderProcessEvent extends DomainEvent case class OrderStarted(orderId: OrderId) extends OrderProcessEvent case class OrderAdded(orderId: OrderId) extends OrderProcessEvent case class ReservationAdded(reservationId: ReservationId, seatId: SeatId) extends OrderProcessEvent case class WaitListAdded(waitListId: WaitListId) extends OrderProcessEvent case class PaymentAdded(paymentId: PaymentId) extends OrderProcessEvent case class OrderConfirmed(orderId: OrderId) extends OrderProcessEvent case class PaymentRemoved(paymentId: PaymentId) extends OrderProcessEvent case class ReservationRemoved(reservationId: ReservationId) extends OrderProcessEvent case class WaitListRemoved(waitListId: WaitListId) extends OrderProcessEvent case class OrderRemoved(orderId: OrderId) extends OrderProcessEvent } object Data { case class OrderProcessData(orderId: Option[OrderId], reservationId: Option[ReservationId], waitListId: Option[WaitListId], paymentId: Option[PaymentId]) { def isAllCanceled = orderId.isEmpty && reservationId.isEmpty && waitListId.isEmpty && paymentId.isEmpty } }
  21. 21. 実装(3) class OrderProcessManager(receiver: ActorRef, order: ActorRef, reservation: ActorRef, payment: ActorRef, waitList: ActorRef) extends PersistentFSM[OrderProcessState, OrderProcessData, OrderProcessEvent] { override def domainEventClassTag: ClassTag[OrderProcessEvent] = scala.reflect.classTag[OrderProcessEvent] override def persistenceId: String = self.path.parent.name + "-" + self.path.name override def applyEvent(domainEvent: OrderProcessEvent, currentData: OrderProcessData): OrderProcessData = { domainEvent match { case OrderStarted(orderId) => currentData.copy(orderId = Some(orderId)) case OrderAdded(orderId) => currentData.copy(orderId = Some(orderId)) case ReservationAdded(reservationId, seatId) => currentData.copy(reservationId = Some(reservationId)) case WaitListAdded(waitListId) => currentData.copy(waitListId = Some(waitListId)) case PaymentAdded(paymentId) => currentData.copy(paymentId = Some(paymentId)) case PaymentRemoved(_) => currentData.copy(paymentId = None) case ReservationRemoved(_) => currentData.copy(reservationId = None) case WaitListRemoved(_) => currentData.copy(waitListId = None) case OrderRemoved(_) => currentData.copy(orderId = None) case OrderConfirmed(_) => currentData } }
  22. 22. 実装(4) startWith(Untouched, OrderProcessData(None, None, None, None)) when(Untouched, 10 seconds) { case Event(PlaceOrder(id), _) => val orderId = OrderId(UUID.randomUUID().toString) goto(OrderProcessing) applying OrderStarted(orderId) forMax (5 seconds) andThen { case OrderProcessData(Some(oid), _, _, _) => order ! CreateOrder(orderId) } } when(OrderProcessing, 10 seconds) { case Event(OrderCreated(orderId), _) => val reservationId = ReservationId(UUID.randomUUID().toString) goto(ReservationProcessing) applying OrderAdded(orderId) forMax (5 seconds) andThen { case OrderProcessData(Some(oid), _, _, _) => reservation ! MakeReservation(reservationId) } case Event(StateTimeout, _) => goto(CancellationProcessing) andThen { case d: OrderProcessData => cancelAll(d) } }
  23. 23. 実装(5) when(ReservationProcessing, 10 seconds) { case Event(SeatReserved(reservationId, seatId), _) => val paymentId = PaymentId(UUID.randomUUID().toString) goto(PaymentProcessing) applying ReservationAdded(reservationId, seatId) forMax (5 seconds) andThen { case OrderProcessData(Some(_), Some(rid), _, _) => payment ! MakePayment(paymentId) } case Event(SeatNotReserved(reservationId, waitListId), _) => goto(AddToWaitListProcessing) applying WaitListAdded(waitListId) forMax (5 seconds) andThen { case OrderProcessData(Some(_), None, Some(wid), _) => waitList ! AddSeatToWaitList(waitListId) } case Event(StateTimeout, _) => goto(CancellationProcessing) andThen { case d: OrderProcessData => cancelAll(d) } } when(AddToWaitListProcessing, 10 seconds) { case Event(SeatAdded(waitListId), OrderProcessData(Some(oid), _, _, _)) => receiver ! OrderConfirmed(oid) goto(OrderCompleted) }
  24. 24. 実装(6) when(PaymentProcessing, 10 seconds) { case Event(PaymentAccepted(paymentId), OrderProcessData(Some(oid), _, _, _)) => receiver ! OrderConfirmed(oid) goto(OrderCompleted) applying PaymentAdded(paymentId) case Event(StateTimeout, _) => goto(CancellationProcessing) andThen { case d: OrderProcessData => cancelAll(d) } } when(OrderCompleted, 10 seconds) { case Event(CancelOrderProcess(id), OrderProcessData(Some(oid), Some(rid), wid, pid)) => goto(CancellationProcessing) andThen { case d: OrderProcessData => cancelAll(d) } }
  25. 25. 実装(7) when(CancellationProcessing, 10 seconds) { case Event(PaymentCanceled(paymentId), _) => stay applying PaymentRemoved(paymentId) andThen { case d: OrderProcessData if d.isAllCanceled => goto(Untouched) } case Event(WaitListCanceled(waitListId), _) => stay applying WaitListRemoved(waitListId) andThen { case d: OrderProcessData if d.isAllCanceled => goto(Untouched) } case Event(ReservationCanceled(reservationId), _) => stay applying ReservationRemoved(reservationId) andThen { case d: OrderProcessData if d.isAllCanceled => goto(Untouched) } case Event(OrderCanceled(orderId), _) => stay applying OrderRemoved(orderId) andThen { case d: OrderProcessData if d.isAllCanceled => goto(Untouched) } case Event(StateTimeout, _) => stay andThen { case d: OrderProcessData => cancelAll(d) } }
  26. 26. 実装(8) private def cancelAll(d: OrderProcessData) = { d.paymentId.foreach { id => payment ! CancelPayment(id) } d.waitListId.foreach { id => waitList ! CancelWaitList(id) } d.reservationId.foreach { id => reservation ! CancelReservation(id) } d.orderId.foreach { id => order ! CancelOrder(id) } }
  27. 27. まとめと考察 • PersistentFSMでProcess Managerを実装した • become/unbecomeとの違い • 集約に実装するか、プロセスマネージャーを起動するか、集約がプ ロセスマネージャーを起動するか。 • 各集約にはAtLeastOnceDeliveryでコマンドを送るか、Timeoutで何 度もリトライするか。 • 各集約からイベントはEventBusやDistributed Pub-Subでトピック を購読するか。
  28. 28. 参考 • http://doc.akka.io/docs/akka/snapshot/scala/persiste nce.html • http://www.enterpriseintegrationpatterns.com/pattern s/messaging/ProcessManager.html • https://msdn.microsoft.com/en- us/library/jj554200.aspx

×