http://wiki.jikexueyuan.com/project/openresty/ngx_lua/whats_cosocket.html
怎樣理解 cosocket
筆者認為,cosocket 是 OpenResty 世界中技術、實用價值最高部分。讓我們可以用非常低廉的成本,優雅的姿勢,比傳統 socket 編程效率高好幾倍的方式進行網絡編程。無論資源占用、執行效率、并發能力都非常出色。
魯迅有句名言“其實世界上本沒有路,走的人多了便有了路”,其實對于 cosocket 的中文翻譯貌似我也碰到了類似的問題。當我想給大家一個正面解釋,爬過了官方 wiki 發現,原來作者本人(章亦春)也沒有先給出 cosocket 定義。
看來只能通過一些側面信息,從而讓這條路逐漸的清晰起來。
cosocket = coroutine + socket
coroutine:協同程序(后面簡稱:協程) socket:網絡套接字
OpenResty 中的 cosocket 不僅需要協程特性支撐,它還需 Nginx 非常最重要的“事件循環回調機制”,兩部分結合在一起最終達到了 cosocket 效果,外加 Nginx 自身對各種資源的“小氣”,LuaJIT 的執行效率,最終加分不少。在 Lua 世界中調用任何一個有關 cosocket 網絡函數內部關鍵調用如圖所示:

從該圖中我們可以看到,用戶的 Lua 腳本每觸發一個網絡操作,都會有協程的 yield 以及 resume,因為請求的 Lua 腳本實際上都運行在獨享協程之上,可以在任何需要的時候暫停自己(yield),也可以在任何需要的時候被喚醒(resume)。
暫停自己,把網絡事件注冊到 Nginx 監聽列表中,并把運行權限交給 Nginx。當有 Nginx 注冊網絡事件達到觸發條件時,喚醒對應的協程繼續處理。
以此為藍板,封裝實現 connect、read、recieve 等操作,形成了大家目前所看到的 cosocket API。
可以看到,cosocket 是依賴 Lua 協程 + Nginx 事件通知兩個重要特性拼的。
從 0.9.9 版本開始,cosocket 對象是全雙工的,也就是說,一個專門讀取的 "light thread",一個專門寫入的 "light thread",它們可以同時對同一個 cosocket 對象進行操作(兩個 "light threads" 必須運行在同一個 Lua 環境中,原因見上)。但是你不能讓兩個 "light threads" 對同一個 cosocket 對象都進行讀(或者寫入、或者連接)操作,否則當調用 cosocket 對象時,你將得到一個類似 "socket busy reading" 的錯誤。
所以東西總結下來,到底什么是 cosocket,中文應該怎么翻譯,筆者本人都開始糾結了。我們不妨從另外一個角度來審視它,它到底給我們帶來了什么。
同步與異步解釋: 同步:做完一件事再去做另一件; 異步:同時做多件事情,某個事情有結果了再去處理。
阻塞與非阻塞解釋: 阻塞:不等到想要的結果我就不走了; 非阻塞:有結果我就帶走,沒結果我就空手而回,總之一句話:爺等不起。
異步/同步是做事派發方式,阻塞/非阻塞是如何處理事情,兩組概念不在同一個層面。
無論 ngx.socket.tcp()、ngx.socket.udp()、ngx.socket.stream()、ngx.req.socket(),它們基本流程都是一樣的,只是一些細節參數上有區別(比如 TCP 和 UDP 的區別)。下面這些函數,都是用來輔助完成更高級的 socket 行為控制:
- connect
- sslhandshake
- send
- receive
- close
- settimeout
- setoption
- receiveuntil
- setkeepalive
- getreusedtimes
它們不僅完整兼容 LuaSocket 庫的 TCP API,而且還是 100% 非阻塞的。
這里給大家 show 一個例子,對 cosocket 使用有一個整體認識。
location /test { resolver 114.114.114.114; content_by_lua_block { local sock = ngx.socket.tcp() local ok, err = sock:connect("www.baidu.com", 80) if not ok then ngx.say("failed to connect to baidu: ", err) return end local req_data = "GET / HTTP/1.1\r\nHost: www.baidu.com\r\n\r\n" local bytes, err = sock:send(req_data) if err then ngx.say("failed to send to baidu: ", err) return end local data, err, partial = sock:receive() if err then ngx.say("failed to recieve to baidu: ", err) return end sock:close() ngx.say("successfully talk to baidu! response first line: ", data) } }
可以看到,這里的 socket 操作都是同步非阻塞的,完全不像 node.js 那樣充滿各種回調,整體看上去非常簡潔優雅,效率還非常棒。
對 cosocket 做了這么多鋪墊,到底他有多么重要呢?直接看一下官方默認綁定包有多少是基于 cosocket 的:
效仿這些基礎庫的實現方法,可以完成不同系統或組件的對接,例如 syslog、beanstalkd、mongodb 等,直接 copy 這些組件的通訊協議即可。
posted on 2018-06-22 09:17
思月行云 閱讀(2984)
評論(0) 編輯 收藏 引用 所屬分類:
Nginx\Openresty