2014-10-11 19 views
4

雖然在一個相當大的Akka應用程序上工作,但當使用普通方法和非Akka類時,我遇到了一個非常簡單的結構,但在使用Akka時實際上很難釘,這就是爲什麼我來這裏要問你建議什麼是解決這個問題的最好方法。Akka Design Principals

所以問題是這樣的,我有一個父角色,我們稱他爲「連接器」,連接器具有描述接收ConnectCommand實例時應該做什麼的行爲。首先使用HttpClient提交表單,然後轉到一對URL以檢查某些會話參數,最後將發送方(稱爲「消費者」)發送一條連接消息,其中包含使用該API所需的所有內容。

現在,我是一個忠告,不是太多的拉/問,所以實施這個在我看來是一個艱鉅的任務。我們來回顧一下。所有由HttpClientActor返回的響應都是一個Response實例,所以首先想到的是在連接過程的某個步驟完成後,我的演員中定義了多個行爲並逐漸增加,並將行爲更改爲下一步。

private final PartialFunction<Object, BoxedUnit> inital = ReceiveBuilder 
    .match(ConnectCommand.class, c -> this.startConnection()) 
    .matchAny(this::unhandled) 
    .build(); 

private final PartialFunction<Object, BoxedUnit> stage1 = ReceiveBuilder 
    .match(Response.class, this::stage1) 
    .matchAny(this::unhandled) 
    .build(); 

private final PartialFunction<Object, BoxedUnit> stage2 = ReceiveBuilder 
    .match(Response.class, this::stage2) 
    .matchAny(this::unhandled) 
    .build(); 

private final PartialFunction<Object, BoxedUnit> stage3 = ReceiveBuilder 
    .match(Response.class, this::stage3) 
    .matchAny(this::unhandled) 
    .build(); 

private final PartialFunction<Object, BoxedUnit> stage4 = ReceiveBuilder 
    .match(Response.class, this::stage4) 
    .matchAny(this::unhandled) 
    .build(); 

private final PartialFunction<Object, BoxedUnit> stage5 = ReceiveBuilder 
    .match(Response.class, this::stage5) 
    .matchAny(this::unhandled) 
    .build(); 

private final PartialFunction<Object, BoxedUnit> stage6 = ReceiveBuilder 
    .match(Response.class, this::stage6) 
    .matchAny(this::unhandled) 
    .build(); 

private final PartialFunction<Object, BoxedUnit> stage7 = ReceiveBuilder 
    .match(Response.class, this::stage7) 
    .matchAny(this::unhandled) 
    .build(); 

這樣做的好處是它使用了tell,而不是問,但是存在代碼變得非常不可讀的主要缺點。

現在我在這個點上,我覺得這個演員需要一些改變,但在我看來有兩種選擇。

第一個涉及將每個HttpRequest和Response分解成一個單獨的Actor並聚合Connector actor中的結果。這具有非常可讀的優點,使用說明並且不應該傷害表演,因爲Akka是爲處理大量演員而設計的。唯一的缺點是我需要爲這些需要從Stage5Actor傳遞給Connector actor的狀態部分創建許多容器類。這會造成大量內存開銷(如果我錯了,請糾正我)。

第二種方法是使用Ask模式將步驟連接在一起。這將導致一個單一的連接角色,因爲Spray也是這樣做的,因爲它是Http客戶端,我認爲這可能是一個有效的解決方案。唯一的缺點是,因爲一切都在外部Http API之上,超時可能成爲一個問題。如果這種方法是由Akka團隊推薦的,那麼如何處理完全不可預測的所有超時。

請注意,此實施需要能夠使用監督策略,因爲我們現在的整個方法都是基於此。

如果您覺得有比我提到的更好的解決方案,請告訴我!我真的很享受Akka a.t.m.並且我得到的每一條建議都是對經驗和知識的一種獲益,不僅對我,而且對整個社區來說都是如此:D。另外我認爲這是一個偶爾遇到的問題。

在此先感謝,並非常感謝Akka團隊製作這樣一個真棒圖書館!

PS。這個問題首先在Akka github本身提出,但我決定將它發佈在這裏,因爲這與Actor相關的問題與Akka相關的問題一樣多。

鏈接到該問題在GitHub上:https://github.com/akka/akka/issues/16080

回答

3

在我看來,像你不一定需要N級收集的響應。如果您知道您等待的反應有多少,您可以在單一行爲下收集所有反應。

此外,在您使用ask的任何場景中,您可以輕鬆地將其替換爲中介Actor以保存此上下文,並通過tell傳遞所有消息。這實際上就是ask,不過主要的區別在於你不必爲每個ask指定超時時間(當然,對於整個請求你應該仍然有超時),你可以將所有未完成的階段打包在一個單一的演員,而不是每個ask的額外演員。

傑米艾倫對Extra and Cameo PatternsEffective Akka中的描述非常好。

所以考慮到這一點的所有,你也許可以跟隨沿着線的東西:

  • 當消費者將消息發送到連接器,連接器可以爲此請求上下文創建一個新的演員(客串) 。您必須捕捉此演員中的sender消費者。
  • Cameo演員可以通過tell s啓動隨後的請求。此時,您可以將Cameo或連接器作爲主管,這樣您的監督策略仍可以按照您的要求工作。
  • 該Cameo中的Receive塊可以等待來自Connections的響應。這不一定是ask上的await。只需接受receive中的消息,然後更新您的內部狀態。
  • 當所有連接完成後,您可以通過tell對原始消費者作出響應。
+0

問題在於,我沒有真正的方法來區分Response對象。對此有任何建議? – Martijn 2014-10-12 19:45:50

+0

你是什麼意思?您是否想要將響應映射到請求? – 2014-10-12 20:34:15

+2

實際上,如果您使用@CemCatikkas建議的中間角色,則可以通過這種方式跟蹤回覆。也就是說,對於要從連接器發送給客戶端角色的每條消息,請創建一個新的子actor,它將發送實際消息,然後等待響應。該中間actor可以包含足夠的狀態來知道響應正在回覆哪個請求,並將該信息傳回給父actor(連接器)。 – 2014-10-13 03:38:58