akka.io包是結合spray-io
模型開發的。
I/O 為了達到極端的可伸縮性,必須有一個可以正確匹配的底層傳輸機制,完全由事件驅動,并且是非阻塞和異步的。該模塊提供了一些底層 TCP 以及套接字抽象,可以用于編寫自定義的網絡通信。從而提高了抽象的層次。
所有Akka I/O api都通過manager對象訪問。當使用I/O API時,第一步是獲取對合適的manager的引用,manager是處理底層I/O資源(選擇器、通道)并為特定事件(如監聽傳入連接)實例化worker actor。
import akka.io.{ IO, Tcp }
import context.system // implicitly used by IO(Tcp)
val manager = IO(Tcp)//查找TCPmanager并返回它的AactorRef
當manager接收到 I/O 命令以后實例化worker actor,這些worker actor會將自己的在回復上述I/O命令的消息中展現出來。
例如,在manager向TCP manager發送一個Connect
命令后,manager會創建一個actor來表示TCP連接,這個connection actor就是種worker actor,它會發送一個Connected
消息來聲明自己。然后所有與該TCP連接相關的操作都可以通過向這個connection actor發送消息來調用。
DeathWatch and Resource Management
I/O 的worker actor 負責接受命令并且發送事件(入站連接、傳入字節或寫入的確認)。通常需要一個與用戶端對應的actor來listen這些事件。這些worker會監測他們對應的listener actor,如果listener停止,與其對應的worker actor 會釋放自己掌握的所有資源。這種設計使得API更加穩健地避免資源泄露。
同樣地,也有一個負責處理connection的user actor 監測 worker actor,如果worker actor 意外終止,會得到通知。
Write models (Ack, Nack)
I/O設備的最大吞吐量限制了寫入的頻率和大小。如果一個應用程序想要推動比設備能夠處理更多的數據時,驅動程序必須緩沖字節,直到設備能夠寫入它們時。有了緩沖,可以處理短時間的密集寫入——但沒有緩沖區是無限的。為了避免壓倒性的設備緩沖器akka提供了兩種“流量控制”方式
-
Ack-based
:當寫入成功時,驅動程序會用Ack通知寫入器。 -
Nack-based
:當寫入失敗時,驅動程序會用Nack通知寫入器。
當寫入完成時,worker會將ack對象發送給寫入器。這可以用來實現基于ack的流控制,只有當舊數據被確認才發送新數據。
如果寫入(或任何其他命令)失敗,則驅動程序會通知發送命令的actor。此消息還將通知編寫失敗的writer,作為該寫入的nack。因為是異步的,失敗的寫入可能不是最近發送的,如果作者想要重新發送被刪除的消息,需要保留一個等待消息的緩沖區。
注意:Ack/Nack只是兩種流控制模式,用來表示寫入成功/失敗,但不是錯誤處理。即使所有的寫入都被確認但也不能夠保證數據不丟失。
ByteString
為了保持隔離,actor只需要跟不可變的對象進行聯系。ByteString
是不可變的字節容器。Akka的I/O系統使用它作為一個高效的、不可變的字節容器。用于JVM上的I/O,例如Array[Byte]
和ByteBuffer
。
ByteString是一個類似于rope-like的數據結構,它是不可變的,并且提供了快速的連接和切片操作(對I/O來說是完美的)。當兩個ByteStrings串聯在一起時,它們都存儲在結果的ByteString中,而不是復制到新數組中。drop
與take
這種操作返回ByteString且仍然引用初始的數組,只需改變可見的偏移量和長度。還需要特別注意確保內部數組不能被修改。每當一個潛在的不安全的數組被用來創建一個新的ByteString時,就會創建一個defensive副本。
如果你需要一個ByteString,它只會為它的內容提供必要的內存,那么使用compact
方法來獲得CompactByteString實例。
如果ByteString只表示原始數組的一部分,那么這將在該切片中復制所有字節。
ByteString繼承了IndexedSeq
的所有方法,它也有一些新的方法。要了解更多信息,請查閱akka.util.ByteString
類和它在ScalaDoc中的對象。
ByteString也有自己的優化構建器和迭代器類ByteStringBuilder
和ByteIterator
提供了額外的特性。