W3Cschool
恭喜您成為首批注冊(cè)用戶(hù)
獲得88經(jīng)驗(yàn)值獎(jiǎng)勵(lì)
筆者認(rèn)為,cosocket 是 OpenResty 世界中技術(shù)、實(shí)用價(jià)值最高部分。讓我們可以用非常低廉的成本,優(yōu)雅的姿勢(shì),比傳統(tǒng) socket 編程效率高好幾倍的方式進(jìn)行網(wǎng)絡(luò)編程。無(wú)論資源占用、執(zhí)行效率、并發(fā)能力都非常出色。
魯迅有句名言“其實(shí)世界上本沒(méi)有路,走的人多了便有了路”,其實(shí)對(duì)于 cosocket 的中文翻譯貌似我也碰到了類(lèi)似的問(wèn)題。當(dāng)我想給大家一個(gè)正面解釋?zhuān)肋^(guò)了官方 wiki 發(fā)現(xiàn),原來(lái)作者本人(章亦春)也沒(méi)有先給出 cosocket 定義。
看來(lái)只能通過(guò)一些側(cè)面信息,從而讓這條路逐漸的清晰起來(lái)。
cosocket = coroutine + socketcoroutine:協(xié)同程序(后面簡(jiǎn)稱(chēng):協(xié)程) socket:網(wǎng)絡(luò)套接字
OpenResty 中的 cosocket 不僅需要協(xié)程特性支撐,它還需 Nginx 非常最重要的“事件循環(huán)回調(diào)機(jī)制”,兩部分結(jié)合在一起最終達(dá)到了 cosocket 效果,外加 Nginx 自身對(duì)各種資源的“小氣”,LuaJIT 的執(zhí)行效率,最終加分不少。在 Lua 世界中調(diào)用任何一個(gè)有關(guān) cosocket 網(wǎng)絡(luò)函數(shù)內(nèi)部關(guān)鍵調(diào)用如圖所示:
從該圖中我們可以看到,用戶(hù)的 Lua 腳本每觸發(fā)一個(gè)網(wǎng)絡(luò)操作,都會(huì)有協(xié)程的 yield 以及 resume,因?yàn)檎?qǐng)求的 Lua 腳本實(shí)際上都運(yùn)行在獨(dú)享協(xié)程之上,可以在任何需要的時(shí)候暫停自己(yield),也可以在任何需要的時(shí)候被喚醒(resume)。
暫停自己,把網(wǎng)絡(luò)事件注冊(cè)到 Nginx 監(jiān)聽(tīng)列表中,并把運(yùn)行權(quán)限交給 Nginx。當(dāng)有 Nginx 注冊(cè)網(wǎng)絡(luò)事件達(dá)到觸發(fā)條件時(shí),喚醒對(duì)應(yīng)的協(xié)程繼續(xù)處理。
以此為藍(lán)圖,封裝實(shí)現(xiàn) connect、read、receive 等操作,形成了大家目前所看到的 cosocket API。
可以看到,cosocket 是依賴(lài) Lua 協(xié)程 + Nginx 事件通知兩個(gè)重要特性拼的。
從 0.9.9 版本開(kāi)始,cosocket 對(duì)象是全雙工的,也就是說(shuō),一個(gè)專(zhuān)門(mén)讀取的 "light thread",一個(gè)專(zhuān)門(mén)寫(xiě)入的 "light thread",它們可以同時(shí)對(duì)同一個(gè) cosocket 對(duì)象進(jìn)行操作(兩個(gè) "light threads" 必須運(yùn)行在同一個(gè) Lua 環(huán)境中,原因見(jiàn)上)。但是你不能讓兩個(gè) "light threads" 對(duì)同一個(gè) cosocket 對(duì)象都進(jìn)行讀(或者寫(xiě)入、或者連接)操作,否則當(dāng)調(diào)用 cosocket 對(duì)象時(shí),你將得到一個(gè)類(lèi)似 "socket busy reading" 的錯(cuò)誤。
所以東西總結(jié)下來(lái),到底什么是 cosocket,中文應(yīng)該怎么翻譯,筆者本人都開(kāi)始糾結(jié)了。我們不妨從另外一個(gè)角度來(lái)審視它,它到底給我們帶來(lái)了什么。
同步與異步解釋?zhuān)?同步:做完一件事再去做另一件; 異步:同時(shí)做多件事情,某個(gè)事情有結(jié)果了再去處理。阻塞與非阻塞解釋?zhuān)?阻塞:不等到想要的結(jié)果我就不走了; 非阻塞:有結(jié)果我就帶走,沒(méi)結(jié)果我就空手而回,總之一句話(huà):爺?shù)炔黄稹?/blockquote>異步/同步是做事派發(fā)方式,阻塞/非阻塞是如何處理事情,兩組概念不在同一個(gè)層面。
無(wú)論 ?
ngx.socket.tcp()
?、?ngx.socket.udp()
?、?ngx.socket.stream()
?、?ngx.req.socket()
?,它們基本流程都是一樣的,只是一些細(xì)節(jié)參數(shù)上有區(qū)別(比如 TCP 和 UDP 的區(qū)別)。下面這些函數(shù),都是用來(lái)輔助完成更高級(jí)的 socket 行為控制:
- connect
- sslhandshake
- send
- receive
- close
- settimeout
- setoption
- receiveuntil
- setkeepalive
- getreusedtimes
它們不僅完整兼容 LuaSocket 庫(kù)的 TCP API,而且還是 100% 非阻塞的。
這里給大家 show 一個(gè)例子,對(duì) cosocket 使用有一個(gè)整體認(rèn)識(shí)。
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 receive from baidu: ", err) return end sock:close() ngx.say("successfully talk to baidu! response first line: ", data) } }
可以看到,這里的 socket 操作都是同步非阻塞的,完全不像 node.js 那樣充滿(mǎn)各種回調(diào),整體看上去非常簡(jiǎn)潔優(yōu)雅,效率還非常棒。
對(duì) cosocket 做了這么多鋪墊,到底他有多么重要呢?直接看一下官方默認(rèn)綁定包有多少是基于 cosocket 的:
- ngx_stream_lua_module Nginx "stream" 子系統(tǒng)的官方模塊版本(通用的下游 TCP 對(duì)話(huà))。
- lua-resty-memcached 基于 ngx_lua cosocket 的庫(kù)。
- lua-resty-redis 基于 ngx_lua cosocket 的庫(kù)。
- lua-resty-mysql 基于 ngx_lua cosocket 的庫(kù)。
- lua-resty-upload 基于 ngx_lua cosocket 的庫(kù)。
- lua-resty-dns 基于 ngx_lua cosocket 的庫(kù)。
- lua-resty-websocket 提供 WebSocket 的客戶(hù)端、服務(wù)端,基于 ngx_lua cosocket 的庫(kù)。
效仿這些基礎(chǔ)庫(kù)的實(shí)現(xiàn)方法,可以完成不同系統(tǒng)或組件的對(duì)接,例如 syslog、beanstalkd、mongodb 等,直接 copy 這些組件的通訊協(xié)議即可。
Copyright©2021 w3cschool編程獅|閩ICP備15016281號(hào)-3|閩公網(wǎng)安備35020302033924號(hào)
違法和不良信息舉報(bào)電話(huà):173-0602-2364|舉報(bào)郵箱:jubao@eeedong.com
掃描二維碼
下載編程獅App
編程獅公眾號(hào)
聯(lián)系方式:
更多建議: