sproto rpc 的用法
原文 http://blog.codingnow.com/2015/04/sproto_rpc.html
sproto 是我自己設計, 用在我們新項目中取代過去用到的 google protocol buffers 的東西。
為什么不用 protobuf ? 這個問題我有足夠的發言權。在 lua 語言為主的項目中,sproto 更合適。google 官方并沒有給 protobuf 加入 lua 支持。現在在網上流傳的 protobuf lua 方案,被人用的最多的兩種,一個是 pbc 的 lua binding ,另一個是 protoc-gen-lua 。前者是我在開發維護,并使用了多年;后者是在我過去的項目中,項目中的同事因為需要而開發的。
另外,在我的項目的副產品中,還有開源的 protobuffer 的 as3 庫以及 erlang 庫,都有許多用戶。所以,我相信我對 protobuf 有足夠長時間的使用經驗以及對它有足夠的了解。這也是放棄 protobuf 而轉向自己設計的 sproto 的底氣所在。
在這一篇 blog 中,不想討論 protobuf 的優劣,只談談 sproto 中如何使用 rpc 的 api 。這是 sproto 的 api 文檔中沒有寫明,而很多想用它的同學問起的問題。
對于 request/response 的 RPC 方案,除了消息本身打包外,還有兩個重要的信息需要傳輸。它們分別是請求的類型以及請求的 session 。
不要把請求的類型和消息的類型混為一談。因為不同的請求可以用相同的消息類型,所以在 sproto 中,需要對 rpc 請求額外編碼。你也不一定為每個請求額外設計一個消息類型,可以直接在定義 rpc 協議時內聯寫上請求(以及回應)的消息結構。
通常,我們用數字作為消息類型的標識,當然你也可以使用字符串。在用類 json 的無 schema 的協議中使用字符串多一些,但在 sproto 這種帶 schema 的協議中,使用數字會更高效。同樣,session 作為一條消息的唯一標識,你也可以用數字或字符串。而生成唯一數字 session 更容易,編碼也更高效。
所以,每當我們發送一次遠程請求,需要傳輸的數據就有三項:請求的類型、一個請求方自己保證唯一的 session id 以及請求的數據內容。
服務方收到請求后,應根據請求的類型對請求的數據內容解碼,并根據類型分發給相應的處理器。同時應該把 session id 記錄下來。等處理器處理完畢后,根據類型去打包回應的消息,并附加上 session id ,發送回客戶端。
注意:回應是不需要傳輸消息類型的。這是因為 session id 就唯一標識了這是對哪一條請求的回應。而 session id 是客戶端保證唯一的,它在產生 session id 時,就保存了這個 session 對應的請求的類型,所以也就有能力對回應消息解碼。
btw ,如果只是單向推送消息(也就是 publish/subscribe 模式),直接省略 session 就可以了,也不需要回應。
sproto 提供的第一種 rpc 模式封裝了上面的流程。
你需要定義一個叫做 package 的消息類型,里面包含 type 和 session 兩項。
對于每個包,都以這個 package 開頭,后面接上 (padding)消息體。最后連在一起,用 sproto 自帶的 0-pack 方式壓縮。
你可以用 sproto:host 這個 api 生成一個消息分發器 host ,用來處理上面這種 rpc 消息。
默認每個 rpc 處理端都有處理請求和處理回應的能力。也就是每個 rpc 端都同時可以做服務器和客戶端。所以 host:dispatch 這個 api 可以處理消息包,返回它是請求還是回應,以及具體的內容。
如果 host 要對外發送請求,它可以用 host:attach 生成一個打包函數。這個生成的函數可以將 type session content 三者打包成一個串,這個串可以被對方的 host:dispatch 正確處理。
具體的說明,我放在了 skynet 的 wiki 頁 上。
sproto 另外還提供了一組沒有那么多封裝的 rpc api 。它們分別是:
- sproto:request_encode
- sproto:request_decode
- sproto:response_encode
- sproto:response_decode
顧名思義,這組 api 不會幫你處理 type session 這些信息,而是留給你處理。它只是在你知道一條消息的內容在已知是請求還是回應包時,可以調用對應的 api 來編解碼。
比如,當你的服務器只處理請求的話,就只需要調用sproto:request_decode。當然,你得自己先知道請求的 type 是什么(編碼在額外的地方),也需要自己保存下 session ;需要回應請求時,調用sproto:response_decode即可。不過附帶上前面保存的 session 也是你的責任。
設計這組 api 是源于 skynet 的 message server 模板。因為這個模板已經封裝好了 session 。在模板上使用 sproto 的前一種 api 就不太合適了。