1. 程式人生 > >skynet源碼分析:Socket

skynet源碼分析:Socket

fork tps chan type 操作 err mongo hub ESS

skynet 的 C API 采用異步讀寫,你可以使用 C 調用,監聽一個端口,或發起一個 TCP 連接。但具體的操作結果要等待 skynet 的事件回調。skynet 會把結果以 PTYPE_SOCKET 類型的消息發送給發起請求的服務。(參考skynet_socket.h)

在處理實際業務中,這樣的 API 很難使用,所以又提供了一組阻塞模式的 lua API 用於 TCP socket 的讀寫。它是對 C API 的封裝。

所謂阻塞模式,實際上是利用了 lua 的 coroutine 機制。當你調用 socket api 時,服務有可能被掛起(時間片被讓給其他業務處理),待結果通過 socket 消息返回,coroutine 將延續執行。

SocketChannel


請求回應模式是和外部服務交互時所用到的最常用模式之一。通常的協議設計方式有兩種。

  1. 每個請求包對應一個回應包,由 TCP 協議保證時序。redis 的協議就是一個典型。每個 redis 請求都必須有一個回應,但不必收到回應才可以發送下一個請求。

  2. 發起每個請求時帶一個唯一 session 標識,在發送回應時,帶上這個標識。這樣設計可以不要求每個請求都一定要有回應,且不必遵循先提出的請求先回應的時序。MongoDB 的通訊協議就是這樣設計的。

對於第一種模式,用 skynet 的 Socket API 很容易實現,但如果在一個 coroutine 中讀寫一個 socket 的話,由於讀的過程是阻塞的,這會導致吞吐量下降(前一個回應沒有收到時,無法發送下一個請求)。

對於第二種模式,需要用 skynet.fork 開啟一個新線程來收取回應包,並自行和請求對應起來,實現比較繁瑣。

所以、skynet 提供了一個更高層的封裝:socket channel 。

關於 socket channel 的具體用法除了閱讀 lualib/socketchannel.lua (同時這也是理解 socket 模塊的好材料)的實現外,也可以閱讀 lualib/redis.lua 和 lualib/mongo.lua 這兩個為 skynet 編寫的數據庫 driver 。

mysql

在這個 fork https://github.com/chfg007/skynet 裏,實現了 mysql 的 driver (改自 OpenResty)。

主要文件為 lualib/mysql.lua 和 3rd/lua-mysqlaux

local status, err = pcall(mysqldb.query,mysqldb,sqlstr) 記得捕獲錯誤,有可能查詢的時候鏈接已經斷開。

https://github.com/cloudwu/skynet/wiki/SocketChannel

skynet源碼分析:Socket