DPS可以回答兩個(gè)問題:
How do I send a message to an actor without knowing which node it is running on?
How do I send messages to all actors in the cluster that have registered interest in a named topic?
? 類似分片、DData一樣的套路:每節(jié)點(diǎn)機(jī)有一個(gè)DistributedPubSubMediator. 我們稱之為Mediator actor,它自動(dòng)管理一個(gè)actorRef的注冊(cè)表,所有Mediators 也構(gòu)成一個(gè)分布式中介(發(fā)布訂閱)層,共同同步、維護(hù)這張表,peer監(jiān)視其中注冊(cè)的actors.?
? 你可以在任意節(jié)點(diǎn)上、通過本地mediator給任意節(jié)點(diǎn)上的另一個(gè)注冊(cè)的訂閱者即目標(biāo)actor發(fā)消息,即使目標(biāo)actor會(huì)遷移,也就是說,你可以隨時(shí)隨地發(fā)布也可以隨時(shí)隨地訂閱,發(fā)布有兩種:Publish或Send

Publish
? 典型例子就是在線聊天室,為了效率考量,發(fā)布的消息送達(dá)一臺(tái)節(jié)點(diǎn)機(jī)(that has a matching topic)只會(huì)發(fā)生一次,之后會(huì)投遞到該節(jié)點(diǎn)機(jī)上所有的訂閱者actors. DistributedPubSubMediator.Subscribe做訂閱,實(shí)際代碼:
??? DistributedPubSub(context.system).mediator ! Subscribe("主題名", self)
? 這一步即將self注冊(cè)到Mediator的注冊(cè)表上去、并且關(guān)聯(lián)一個(gè)topic. DistributedPubSubMediator.Publish做發(fā)布:
??? DistributedPubSub(context.system).mediator ! Publish("主題名", out)
? Mediator注冊(cè)表peer監(jiān)視注冊(cè)actors的含義是:Actors are automatically removed from the registry when they are terminated,注冊(cè)actor即訂閱者,當(dāng)然你也可以直接手動(dòng)解除訂閱:
??? mediator.Unsubscribe
? DistributedPubSub(context.system)是Akka Extention擴(kuò)展的用法,我們看到由于Akka的很多功能組件本身是基于actor的、這些actor往往是每節(jié)點(diǎn)都有的一種駐點(diǎn)Stub、或者說一種代理actor,要使用什么功能,就找到什么代理,這里往往使用Extention.?
? 如果有新的訂閱者,由于分布式中介層是基于gossip收斂的,所以新訂閱者可能不會(huì)第一時(shí)間發(fā)布到整個(gè)集群,由此可能會(huì)造成消息丟失;如果是已有的訂閱者下線,由于宕機(jī)下線需要判定時(shí)間,這期間的異步消息仍然會(huì)發(fā)往失效地址卻無人接收,由此也可能會(huì)造成消息丟失。所以嚴(yán)格來說DPS對(duì)消息投遞是沒有保障的,也就是 at-most-once delivery. In other words, messages can be lost over the wire.?? 要做到at-least-once投遞保障,Akka官方推薦:Kafka Akka Streams integration.
主題組
? Actors也可以用一個(gè)groupid參數(shù)訂閱topic. 使用一個(gè)groupid的訂閱actor屬于一個(gè)共同訂閱組,發(fā)布到該主題的消息如果攜帶有標(biāo)記sendOneMessageToEachGroup=true會(huì)保證發(fā)送給訂閱組的至少一個(gè)actor.? 這一個(gè)actor的選擇策略是RoutingLogic(default random) 隨機(jī)選擇。因此:
1、If all the subscribed actors have the same group id, then this works just like Send and each message is only delivered to one subscriber.
2、If all the subscribed actors have different group names, then this works like normal Publish and each message is broadcasted to all subscribers.
Send
? 點(diǎn)對(duì)點(diǎn)point-to-point發(fā)布模式,也有好處:你仍然不必知道目標(biāo)actor的實(shí)際位置,典型例子是開小窗私聊,還可以用于對(duì)一批worker分發(fā)任務(wù), like a cluster aware router where the routees dynamically can register themselves.

? The message will be delivered to one recipient with a matching path, if any such exists in the registry. If several entries match the path because it has been registered on several nodes the message will be sent via the supplied RoutingLogic(default random) to one destination. The sender of the message can specify that local affinity is preferred, i.e. the message is sent to an actor in the same local actor system as the used mediator actor, if any such exists, otherwise route to any other matching entry.
? You register actors to the local mediator with DistributedPubSubMediator.Put. The ActorRef in Put must belong to the same local actor system as the mediator. The path without address information is the key to which you send messages. On each node there can only be one actor for a given path, since the path is unique within one local actor system.
? 用send發(fā)布消息只需要通過本地Mediator向目標(biāo)actor 的 邏輯路徑path (without address information) 發(fā)布即可,所以DPS的發(fā)布目標(biāo)要么是主題名字、要么是目標(biāo)actor path,都是邏輯路徑,實(shí)現(xiàn)與物理位置徹底解耦,這也是發(fā)布訂閱這個(gè)傳統(tǒng)功能的核心意義,目標(biāo)actor代碼示例。
? 發(fā)送者actor的消息發(fā)布代碼示例:
??? mediator ! Send(path = "/user/destination", msg = out, localAffinity = true)
? 可以看到path實(shí)際上和主題named topic一樣都只是個(gè)字符串,具備這個(gè)path的actor在一個(gè)ActorSys中只會(huì)有一個(gè):On each node there can only be one such actor, since the path is unique within one local actor system.? 但是在多臺(tái)節(jié)點(diǎn)機(jī)就可以有多個(gè)同樣路徑的actors,此時(shí)把發(fā)送消息從Send改為SendToAll,即可實(shí)現(xiàn)向所有這些actor廣播消息,用途:
? Typical usage of this mode is to broadcast messages to all replicas with the same path, e.g. 3 actors on different nodes that all perform the same actions, for redundancy. You can also optionally specify a property (allButSelf) deciding if the message should be sent to a matching path on the self node or not.
消息投遞保障
? Akka幫助構(gòu)建充分并發(fā)的單機(jī)程序(“scaling up”)、并且可以平滑過渡到并行(“scaling out”). 關(guān)鍵抽象是把所有業(yè)務(wù)對(duì)象(類實(shí)例)之間的同步/阻塞方法調(diào)用變?yōu)榱薬ctors間的異步消息交互,所以消息投遞保障是十分重要的話題。本機(jī)本進(jìn)程內(nèi)的方法調(diào)用可靠性當(dāng)然很高,如果是本地actor消息交互(In-JVM)可靠性與之相當(dāng);遠(yuǎn)程則應(yīng)與傳統(tǒng)RPC的可靠性相當(dāng)。本地消息交互傳遞消息的引用,對(duì)所傳遞消息的類型沒有限制(Any),遠(yuǎn)程消息交互則會(huì)對(duì)消息尺寸有限制。在本地用!/tell操作符發(fā)消息,其可能的失敗原因與傳統(tǒng)的本地方法調(diào)用一樣:
1、StackOverflowError
2、OutOfMemoryError
3、otherVirtualMachineError
? 這些基本都是外來的系統(tǒng)原因,除此以外,還有少數(shù)Akka特有的可能失敗原因:
1、mailbox不接受消息比如說郵箱滿了 (e.g. full BoundedMailbox)
2、目標(biāo)actor處理消息異?;蛘咭呀?jīng)terminated
? 1更多是一個(gè)配置問題,2中消息的發(fā)送者將得不到feedback,異常會(huì)拋給接收者的上級(jí),對(duì)發(fā)送者來說這和消息丟失沒什么差別。
? DPS乃至Akka原生功能不保障消息可靠投遞:at-most-once = fire-and-forget,不止Akka,所有actor模式系統(tǒng)均是如此。對(duì)于投遞順序Akka的保障是:message ordering per sender–receiver pair.
? 更高的可靠投遞保障是:at-least-once—消息可能重復(fù)投遞但不會(huì)丟,通過同步ack+超時(shí)timeout重試達(dá)成;exactly-once—消息保證投遞恰好一次,必須在前者基礎(chǔ)上加上消息接收端去重才能保證。
Nobody Needs Reliable Messaging.
Erlang documentation(section 10.9 and 10.10)
情況就是這么個(gè)情況,要保障消息投遞你得自己去做。
消息投遞順序
? The rule more specifically is thatfor a given pair of actors, messages sent directly from the first to the second will not be received out-of-order.The word directly emphasizes that this guarantee only applies when sending with the tell operator to the final destination, not when employing mediators or other message dissemination features (unless stated otherwise).? DPS不能夠嚴(yán)格保障消息投遞順序。
? 要在大規(guī)模高并發(fā)業(yè)務(wù)數(shù)據(jù)分布式處理場(chǎng)景嚴(yán)格保障消息投遞次序,可能不得不在Sender端自行維護(hù)訂閱者actorRef的列表。
? 一對(duì)確定的actors之間的消息交互順序是確定的,即使這倆不在一個(gè)JVM上也就是跨網(wǎng)絡(luò),比如直接用基于TCP的Akka遠(yuǎn)程來發(fā)消息,但如下消息傳遞可能亂序:
1、節(jié)點(diǎn)機(jī)1上的Actor A發(fā)了一條M1消息給節(jié)點(diǎn)機(jī)3上的actor C
2、之后A又發(fā)了一條M2給節(jié)點(diǎn)機(jī)2上的actor B
3、之后Actor B把消息M2轉(zhuǎn)發(fā)給actor C
? Actor C收到的 M1 和 M2 可能是任意順序,也就是不保證先收到M1.? M2可能先到達(dá)C.