在我之前的博文中,已經介紹過要慎用actor的ask。這裡我們要分析一下ask的原始碼,看看它究竟是怎麼實現的。
開發時,如果要使用ask方法,必須要引入akka.pattern._,這樣才能使用ask(或者?)方法,那麼想必ask是在akka.pattern._對應的包裡面實現的。
/** implementation class of the 「ask」 pattern enrichment of actorref
*/final class askableactorref(val actorref: actorref) extends anyval "."""))
case ref: internalactorref ⇒
if (timeout.duration.length <= 0)
future.failed[any](new illegalargumentexception(s"""timeout length must be positive, question not sent to [$actorref]. sender[$sender] sent the message of type "$"."""))
else
case _ ⇒ future.failed[any](new illegalargumentexception(s"""unsupported recipient actorref type, question not sent to [$actorref]. sender[$sender] sent the message of type "$"."""))
}}
上面是通過定位ask(或?)找到的實現原始碼,我們發現,這是乙個隱式轉換,在akka.pattern.asksupport中我們找到了隱式轉換對應的函式。
implicit def ask(actorref: actorref): askableactorref = new askableactorref(actorref)
通過askableactorref原始碼我們知道最終呼叫了internalask函式,該函式有三個引數:待傳送的訊息、超時時間、訊息傳送者。函式建立了乙個promiseactorref,又把訊息原樣的通過原actor的tell函式傳送給了原actor,然後用新建立的promiseactorref作為新的sender傳遞,再用promiseactorref的result.future作為最終的future返回。下面我們來看一下promiseactorref的建立過程。
sender: actorref = actor.nosender, ontimeout: string ⇒ throwable = defaultontimeout): promiseactorref = ms]. sender[$sender] sent message of type "$"."""))
}result.future oncomplete
a}很明顯,promiseactorref持有了乙個promise[any],但是上面的**只顯示了在超時的時候通過ontimeout賦了值,並沒有不超時賦值的邏輯,且promise[any]一旦完成就呼叫promiseactorref的stop方法和cancel方法。那麼成功時賦值的邏輯應該在**呢?
如果你對ask的使用方式比較熟悉的話,一定會找出其中的端倪的。我們來梳理一下這其中的使用細節。其實,在ask的使用與tell,並沒有太大的區別,至少對於server端的actor來說沒有任何區別,都是正常的接收訊息,然後處理,最後通過sender把訊息使用tell返回。在ask時,訊息的sender是什麼,就是promiseactorref啊,那promiseactorref的!方法具體怎麼實現的呢?
override def !(message: any)(implicit sender: actorref = actor.nosender): unit = state match ))) provider.deadletters ! message}
很明顯,接收訊息的actor會通過!返回對應的訊息,訊息的處理一般會命中 case other,這其實就是給result賦值,在超時之前的賦值。如果在!方法內部給result賦值的時候,剛好已經超時或已經賦過值,會把返回的訊息傳送給deadletters。
其實result,也就是promise[any]的賦值邏輯已經解釋清楚。不過如果小夥伴對promise不熟悉的話,此處還是有點難理解的。如果說future
是乙個唯讀的,值還沒計算的佔位符。那麼promise
就是乙個可寫的,單次指派的容器,也就是說promise一旦賦值,就無法再次賦值,且與之關聯的future也就計算完畢,返回的值就是固定的。當然了通過ask(也就是?)返回的還只是乙個future,如果要取出future最終的值,還是需要await.ready等語義來支援的,這裡就不再詳細解釋了。
Akka原始碼分析 Remote 網路鏈結生命週期
remote模式下,網路鏈結的生命週期往往影響著對應actor的生命週期,那麼網路鏈結的生命週期是怎麼樣的呢?每乙個與遠端系統的鏈路都是四個狀態之一 空閒 活躍 被守護 被隔離。遠端系統的某個位址沒有任何通訊之前其關聯狀態就是idle 空閒 當第一條訊息試圖傳送給遠端系統或入站鏈結被接受,鏈路的狀態...
Junit原始碼分析 設計模式
junit的底層 主要是由觀察者模式,組合模式,模板模式,命令模式 來實現的。在junit裡首先會初始化乙個主題物件testresult物件,這個物件裡面有增加 的方法,所有的 方法都實現了 testlistener介面,這個介面會把一系統測試過程的資訊傳遞給所有的 然後 會按照它們的方式顯示給用 ...
Android原始碼設計模式分析專案
該系列文章已經根據技術發展 實戰需求以及讀者您的反饋重寫所有章節,並且加入更加深入的核心機制分析以及模式在android開發中的實戰,以便幫助大家更系統的學習。書籍已經出版,購買位址在為 android原始碼設計模式解析與實戰 另外,我們的聯絡郵箱為 coder.h gmail.com,謝謝。設計模...