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