天天看點

akka設計模式系列-消息模型

  通過前面的文章我們總結了幾個常見的actor設計模式,但此處不得不提前介紹一下在Akka中消息的設計模式。随着對Akka的使用,我們會發現,使用Akka設計系統其實就是面向消息程式設計。actor之間消息設計的是否合理,往往意味着Akka應用設計的是否合理。那麼actor之間的消息該如何設計呢?

  指令和事件

  actor之間都是通過“消息”進行通信的,對各種各樣的“消息”進行分析,我們可以把它簡單的分為指令和事件兩類。指令,是指一個actor給另外一個actor發送指令做相關的業務邏輯;事件,則是actor對某個指令的響應結果,或者對其他事件的響應結果。簡單來說,指令是主動讓actor做出某種響應,例如發送Stop指令讓其他actor銷毀自己;事件,往往意味着某種通知,例如actor銷毀之前發送Stopped事件給它的supervisor。

  我們在用Akka開發應用時候,一定要仔細認真的把系統間的消息按照定義劃分為指令和事件。具體如何劃分可以參考這兩種消息的具體定義或DDD(領域驅動設計)相關概念。此處我們隻介紹在代碼結構上如何設計它們。

trait Message{
  // 消息建立的時間
  final val at:Long = System.currentTimeMillis()
}
trait Command extends Message
trait Event extends Message

trait MasterCommand extends Command{
  // 定義MasterCommand公有的方法或字段
}

object MasterCommand{
  case class StartWork(message:String) extends MasterCommand
}
trait MasterEvent extends Event{
  // 定義MasterEvent公有的方法或字段
}

object MasterEvent{
  case class WorkStarted(message:String) extends MasterCommand
}
      

   上面是我在開發時常用的設計消息的架構。其中Message這個trait表示對消息的抽象,其中隻有一個at字段,是消息建立的時間。Command繼承Message,表示對指令的抽象。Event也繼承Message,表示對事件的抽象。MasterCommand這個trait繼承Command标志master發出的指令。所有master發出的具體指令放在與MasterCommand這個trait同名的object中。當然也可以不同名,隻要在一個object中即可。把master具體指令放到一個object中,隻是為了對其進行名稱的限定。如果其他類型的actor也有一個StartWork指令,在同一個actor中要對這兩個StartWork同時做響應的話,會引起歧義。是以我一般都用object來進行隔離,當然是用package或者把StartWork改名也都可以對其進行隔離,這個就是個人愛好了。MasterEvent這個特質表示master收到某個指令或事件進行響應的事件,同樣也是master發出的。

  上一段話中,我用紅色字型标志出了“發出”二字。這意味着,不管是指令還是事件,都是針對發送actor而言的。具體什麼意思呢?這其實是一個代碼設計的問題,但我覺得比較重要,這裡需要單獨解釋一下。我們知道actor之間是通過消息傳遞來通信的,那麼消息傳遞一個有一個發送方和接收方。在設計代碼時,我們是把消息放到發送方的域中,還是放到接收方的域中呢。比如StartWork這個指令,如果放到masterActor中,就意味着master發出了一個StartWork這個指令,具體是誰收到該消息就不關心了,接收方一定知道該消息是masterActor發出的。如果放到workActor中,就意味着workActor一定會接收該指令,對其作出反應,具體是誰發送的就不關心了。對于事件可以做類比了解。

  具體是按照發送方還是按照接收方封裝消息,這個就仁者見仁智者見智了,隻不過我比較推崇上面代碼中的設計方案。因為我們可以圍繞actor設計上下文邊界,不會對其他領域内的對象進行引用,高内聚低耦合。

  其實我們還可以通過消息名稱來區分,比如有兩個actor都會發出StartWork的消息,那麼可以分别用StartType1Work和StartType2Work來指令。但這種方式有個問題就是沒法按照類型對不同的指令進行篩選,比如我們可以使用match來對不同類型的消息進行過濾,如果消息是用消息名稱進行區分的,在偏函數或者match時是很不友善的,關于這一點希望大家自己好好體會。

  其實對于上圖中的代碼,我一般都會優化成下面的形式。就是把master相關的指令和事件上面再加一個抽象層:master消息。其實都差不多,隻不過層次更深而已。優點是可以單獨修改公共字段或方法,缺點是設計複雜。

trait Message{
  // 消息建立的時間
  final val at:Long = System.currentTimeMillis()
}
trait Command extends Message
trait Event extends Message

trait MasterMessage extends Message{
  // 定義Master消息共有的方法或字段
}

trait MasterCommand extends MasterMessage with Command{
  // 定義MasterCommand公有的方法或字段
}
trait MasterEvent extends MasterMessage with Event{
  // 定義MasterEvent公有的方法或字段
}

object MasterCommand{
  case class StartWork(message:String) extends MasterCommand
}

object MasterEvent{
  case class WorkStarted(message:String) extends MasterCommand
}
      

   關于Akka消息的設計模式就介紹到這裡了,在Akka中消息設計的好壞往往決定着應用的品質,希望大家多多實踐,多多體會。當然了每個人的設計理念不同,這裡也隻是抛磚引玉,如果你有更好的設計模式,歡迎留言讨論。

繼續閱讀