OpenResty的現狀、趨勢、使用及學習方法

jopen 8年前發布 | 56K 次閱讀

溫銘,奇虎 360 企業安全服務端架構師,OpenResty 社區咨詢委員會成員。一直在互聯網安全公司從事服務端的開發和架構工作,致力于用互聯網技術幫助企業提高安全防護。曾負責開發過木馬云查殺和反釣魚系統。

我個人之前主要是用 Python 來完成開發工作,包括云查殺和反釣魚系統,都是 Python 完成的。在 2011 年左右接觸到 nginx 的 C Module 開發,被異步的高性能顛覆了三觀,只是門檻太高,一直想找一個像 Python 一樣簡單,像 nginx C Module 一樣高效的技術。

所以在 2012 年,得知 OpenResty 這個項目的時候,我就在企業安全一個新項目里面,使用它作為服務端的主要技術。

在今年上半年開始在 Github 上面,把積累的經驗寫成一本電子書《OpenResty最佳實踐》,并在剛過去的 11 月 14 號,以社區名義組織了 OpenResty 的第一次技術大會。

1. OpenResty 是什么,適合什么場景下使用

和大部分知名開源軟件誕生在歐美國家不同,OpenResty 自身和依賴的主要組件都是金磚國家的開發者發明的,這點還挺有意思。

Nginx 是俄羅斯人發明的, Lua 是巴西幾個教授發明的,中國人章亦春把 LuaJIT VM 嵌入到 Nginx 中,實現了 OpenResty 這個高性能服務端解決方案。

通過 OpenResty,你可以把 nginx 的各種功能進行自由拼接, 更重要的是,開發門檻并不高,這一切都是用強大輕巧的 Lua 語言來操控。

它主要的使用場景主要是:

在 Lua 中揉和和處理各種不同的 nginx 上游輸出(Proxy,Postgres,Redis,Memcached 等)

在請求真正到達上游服務之前,Lua 可以隨心所欲的做復雜的訪問控制和安全檢測

隨心所欲的操控響應頭里面的信息

從外部存儲服務(比如 Redis,Memcached,MySQL,Postgres)中獲取后端信息,并用這些信息來實時選擇哪一個后端來完成業務訪問

在內容 handler 中隨意編寫復雜的 Web 應用,使用 同步但依然非阻塞 的方式,訪問后端數據庫和其他存儲

在 rewrite 階段,通過 Lua 完成非常復雜的 URL dispatch

用 Lua 可以為 nginx 子請求和任意 location,實現高級緩存機制

組織 OpenResty 技術大會之前,我一直認為自己是一個孤獨的 OpenResty 使用者,覺得自己在使用一個冷門的技術。

雖然大家都聽說過 OpenResty 或者 ngx_lua,但感覺用在生產環境中使用的卻少之又少,除了幾個 CDN 公司外,好像沒有聽說過哪家知名互聯網公司在使用。而 CDN 行業之所以使用,很多是受到 cloudflare 技術棧的影響,OpenResty 的作者也在國外這家 CDN 公司。

但辦完這個大會,我發現使用者真的挺多,奇虎360的所有服務端團隊都在使用,京東、百度、魅族、知乎、優酷、新浪這些互聯網公司都在使用。有用來寫 WAF、有做 CDN 調度、有做廣告系統、消息推送系統,還有像我們部門一樣,用作 API server 的。有些還用在非常關鍵的業務上,比如開濤在高可用架構分享的京東商品詳情頁,是我知道的 ngx_lua 最大規模的應用。

2. 奇虎企業安全服務端技術選型的標準

先說下 3 年多前做架構選型的時候,我為什么會選擇 OpenResty?

其實架構如何設計并不重要,因為每家公司,每個團隊,他們的公司文化和技術背景各不相同,生搬硬套會適得其反。重要的是當初為什么這么選擇,中途為什么調整。

我們的產品要求單機上面,服務端提供高性能的 API 接口, QPS 至少過萬,未來需要支撐到 10 萬。我們并沒有急于去使用 PHP 、 Python 或者其他的語言來實現功能,而是先勾勒出一個理想化的技術模型。

這個模型應該具備:

非阻塞的訪問網絡IO。在連接 MySQL 、Redis 和發起 HTTP 請求時,工作進程不能傻傻的等待網絡IO的返回,而是需要支持事件驅動,用協程的方式讓 CPU 資源更有效的去處理其他請求。很多語言并不具備這樣的能力和周邊庫。

有完備的緩存機制。不僅需要支持 Redis 、Memcached 等外部緩存,也應該在自己的進程內有緩存系統。我們希望大部分的請求都能在一個進程中得到數據并返回,這樣是最高效的方法,一旦有了網絡IO和進程間的交互,性能就會受到很大影響。

同步的寫代碼邏輯,不要讓開發者感知到回調和異步。這個也很重要,程序員也是人,代碼應該更符合人的思維習慣,顯式的回調和異步關鍵字,會打斷思路,也給調試帶來困難。

最好是站在巨人肩上,基于成熟的技術上搭建。采用一門全新誕生的語言和技術,需要經歷語言自身發展期頻繁調整的陣痛,還可能站錯隊。

不僅支持 Linux 平臺,還需要支持 Windows 平臺,這個是我們產品很特別的需求,很多中小企業用戶還是習慣 Windows 的操作,不具備 Linux 的維護能力。

基于以上幾點的考慮,考察了當時的一些方案,選擇了 OpenResty 。

首先,它最大的特點就是用同步的代碼邏輯實現非阻塞的調用,其次它有單進程內的 LRU cache 和進程間的 share DICT cache,而且它是揉合 nginx 和 LuaJIT 而產生的。而且 nginx 有 Windows 版本,雖然有非常多的限制,但這些限制都是可以解決的, nginx 官方 Windows 版本中不支持的特性,我們開源出來的版本都解決了。

第一次看到這樣的方案,我覺得它肯定會顛覆高性能服務端的開發。為什么呢?在我之前的公司里,每天會有近百億次的查詢請求,而服務器只用了十臺。

我們采用了 nginx C 模塊 + 內置在 nginx 中的 K-V 數據庫(自己開發的),來實現所有的業務邏輯,達到這個目標。聽上去很簡單,但是過程非常艱辛,兩三個十幾年工作經驗的大牛做了一年多才穩定下來。絕大部分開發能力不足,只能望塵莫及。而且后續的調試和維護,也會花費不少精力。

但是 OpenResty 的出現改變了這一切, OpenResty 非常的 pythonic ,適合人類的正常思維。新手經過一兩個月的學習,做出來的 API, 就可以達到 nginx C 模塊的性能,而且代碼量大大減少,也方便調試。

3. 以奇虎和新浪為例,如何在項目中引入新技術

技術選型只是第一步,如何才能在一個產品或者項目中引入 OpenResty 這個新的技術呢?我拿奇虎企業安全和新浪移動這兩家公司真實發生的案例給大家看看。我和新浪移動的周晶,都是在一個有成熟產品的部門,用一兩個人的力量,把一個新技術,替換掉了原有的技術架構。但由于企業產品和個人產品的不同,方法有很大的不一樣。

先說我所在奇虎企業安全。我在 2012 年初加入這個部門,當時產品主打免費,目標用戶是小企業。所以架構設計上面,只考慮了幾十點、幾百點的終端請求,使用了非常強綁定的 Windows 平臺技術,而且傾向于不用開源軟件,自己新做一個更適合自己的框架。包括自己用 C++ 開發的 Web server,自己寫的 PHP 路由和框架,數據存儲在 sqlite 里面。

我幫忙修改了兩個月 PHP 的 bug,看明白了技術架構的思路之后,就去新開的一個產品線了。這是一個實驗性的產品,主要面對央企和專用網,一個網絡中有上百萬的終端。

剛開始沒有什么人關注,我就直接采用了 Linux + OpenResty + Redis + Postgres 的開源組件,性能測試甩之前的N條街。后面這個實驗性的產品,和之前的產品,合并為一個產品,技術上面就割裂為兩套架構。老功能用老架構,新功能用新架構。

隨著越來越多大用戶的增加,原有的技術架構開始捉襟見肘,技術債務越積壓越多。隨著用戶的抱怨,sqlite 被拋棄,全面換成 Postgres。但對于自己開發的框架還是有些敝帚自珍。

期間通過對比測試、OpenResty 培訓還有多次用戶性能問題排查,讓開發同學們都知道這門技術的優勢。快被加班壓垮的開發同學,逐漸開始選擇使用 OpenResty 而不是自研的框架,來進行新功能的開發,以及舊功能的遷移,來避免加班。

在產品重構的時候,之前自研的服務端框架被完全拋棄,服務端開發的同學從 8 、9 個人減少到 3 個人。在新技術的引入過程中,我們沒有采用強制的舉措,因為企業產品需要穩定,用戶處部署的版本更新很慢。

而新浪移動周晶的實踐,對大家更有參考意義。新浪移動最開始是基于 Apache,用 PHP 來處理用戶請求。Apache 是同步多進程模型,在并發請求不多的情況下沒有問題。

但是總是會有突發新聞,比如馬航失聯、文章出軌等,突發的高流量把后臺壓垮了幾次。而且可以預見世界杯的流量也會很大,所以周晶花幾個月時間,用 nginx 替換了 Apache,使用 nginx 的 fast_cgi_cache,QPS 提升了一個數量級。

新浪移動后臺的接口都是使用 PHP 來實現的,在高并發下有些力不從心。而 nginx 簡單的緩存雖然能滿足性能,但不能滿足業務精細化和數據一致性的要求,需要找 PHP 之外的解決方案,前提是讓 PHP 的開發能夠舒適的使用。 node.js 的回調地獄、Go 的調試不方便,都是一個阻礙。

他們最后選擇了 OpenResty,而且基于 OpenResty 開源了一個 Web 框架 Vanilla(香草),模仿了 Yaf 的使用習慣,讓 PHP 的開發更容易接受和上手。 Vanilla 已經在新浪移動開始使用,一些核心業務,比如高清圖和體育直播,正在向這個框架遷移中。

4. 入門痛點,以及學習的正確方法

我和周晶的入門,都是自己摸著石頭過河。當時除了 Python 社區「大媽」的那篇使用文章外,找不到其他的資料。

奇虎和新浪都用 OpenResty 成功替換了之前的技術,但問題還是挺明顯,就是大家都認為自己是孤獨的使用者,同事中基本沒有人認同。在關鍵和支撐業務上,使用 OpenResty 有些不放心,都會在邊緣業務上先做嘗試和驗證。

雖然 OpenResty 的性能做的很棒,比肩或者超過其他所有的高性能解決方案,但是擔心沒有學習資料、擔心招不到人、擔心沒人交流,可能還擔心作者章亦春哪天撂挑子不干了,這個項目就黃了。

高可用架構群里的各位都是架構師,是技術決策者,在引入一門新技術的時候,肯定會考慮到這些風險。比如小米科技馬利超在高可用架構的分享,他們在搶購系統中曾經使用過 ngx_lua,雖然性能滿足需求,但是團隊里面熟悉的人少,最后還是改成了 Go 語言實現。

如何解決這些擔憂? 社區是有過思考和討論的,我們放在分享最后講。先從一個嘗試使用這門技術的開發者的角度看,OpenResty 不少基礎工作沒有完善,友好程度不夠:

只能從源碼安裝,沒有 apt-get、brew 等軟件倉庫安裝方法;安裝第三方庫沒有 PIP、NPM 之類的包管理工具,需要去先谷歌,然后拷貝代碼文件到指定的目錄下,才能 require 使用。

代碼編寫需要修改 nginx.conf 和對應的 lua 代碼,即使是 hello world 也是如此。當然你可以把代碼寫在 nginx 的配置文件里面,但是生產環境肯定是要分離的。這種編寫代碼的方式,不像是一個編程語言,和常規的編程方式不同。

有獨特的執行階段概念,因為 OpenResty 是基于 nginx 的,所以也繼承它的這種概念。你的代碼邏輯,可能需要放在不同的階段里面運行,才能獲取你想要的預期。而這些階段間信息如何傳遞,以及哪些 API 不能在某些階段使用,就會經常攔住新手。

遇到問題只有郵件列表這一種方式來溝通,而郵件列表是被墻的。文檔也只有英文版本,導致很多新手的問題無法被解決。

沒有系統學習 OpenResty 的手段,大都是業務需要實現什么功能,就去文檔和 API 里面去找。至于方式對不對,能不能優化,就不知道了。

而 Lua 語言自身也有一些特別的地方:

下標從 1 開始,這個是和其他編程語言很大的不同。

不區分 array 和 dict ,會導致處理 json 的時候,無法區分 array 和 object。

默認全局變量,需要在所有變量前加 local,忘記的話,可能導致各種難查的 bug。

自帶的字符串正則匹配規則和通常的 PCRE 不同,使用的話,學習成本較高。

Lua 標準庫和周邊庫,都是阻塞的,需要自己甄別哪些可以和 OpenResty 搭配使用。新手很容易使用了阻塞的庫,而導致性能急劇下降。

有沒有好的入門方法?

我們團隊正在做這方面的努力,盡量在現有的基礎上,降低學習的門檻。 對于新手,可以看 StuQ 上面 OpenResty 的系列視頻教程 (http://www.stuq.org/course/detail/1015),我們計劃有 4 季,分別是入門、進階、實戰和源碼分析。現在第一季已經上線,第二季正在后期制作。看完前兩季,基本上就可以在項目里面用了。

對于已經使用了 OpenResty 的開發者,我們把這兩三年遇到的坑,都記錄在 GitHub 的OpenResty最佳實踐,大家可以當做 cookbook 來使用。

5. nginScript 這樣的嘗試會替代 OpenResty 嗎?

nginScript 是今年 nginx 大會上,Nginx 官方推出的一個新的配置語言。它是模仿了 OpenResty 的做法,把 JavaScript VM 嵌入到 nginx 中,提供簡單的 nginx 配置功能。

我們看下它的 hello world:

locationi / {
        js_run "
            var res;
            res = $r.response;
            res.status = 200;
            res.send("hello world!");
            res.finish();
        "
    }

再對比下 OpenResty 的 hello world:

location / {
        content_by_lua_block {
            ngx.say("hello world")
        }
    }

看上去差不多,只是 OpenResty 簡潔一些。根據 nginx 官方的說明,nginScript 只是想提供一種更方便配置 nginx 的方法,并不想取代 ngx_lua。

考慮到 JavaScript 本身的流行和開發社區的強大,如果未來兩三年它從一個簡單的 nginx 配置語言,逐漸演變成類似 ngx_lua 這樣功能非常完備的開發語言,甚至替代 OpenResty 也是有可能的。

當然,這個前提是 OpenResty 停滯不前。現在 OpenResty 已經有的功能,和計劃開發的功能,傾向于覆蓋 nginx Plus 的功能。所以 nginx 和 OpenResty 之間,有一個良性的競爭關系,這是大家都樂意看到的。

6. 未來重點解決的問題和新增特性

短期內的目標,是想降低入門的難度:

提供官方二進制發布包。類似于 docker 的安裝方法,一行命令,下載一個sh腳本,增加一個源地址,不用手工解決依賴,不用源碼編譯,直接就可以試用。
而且會發布 Windows 的二進制包,方便這個平臺的開發者本機做一些測試。

增加包管理。命令行工具叫 iresty,可以從 iresty.org 上面搜索、安裝需要的 lua resty 庫,避免找錯庫或者放錯目錄。

寫一本書《 OpenResty 編程》,這本書會成為官方的入門書籍,框架和關鍵內容由作者春哥直接操刀,我和社區的其他同學幫助一起完成。

做完上面3點,OpenResty 的入門難度會降低到和其他編程語言一樣。

在功能上面,會增加很多激動人心的新特性:

支持 TCP 和 UDP 。Nginx 最新的 stream 子系統已經支持了 TCP,OpenResty 的 ngx_stream_lua 模塊正在開發中,會擁有和現有的 nginx http modlue 相同的 lua API,所以很多應用和庫,可以不加修改的運行在一個新的子系統上面。

更好的支持推送場景。增加 shared list 共享內存的隊列,可以用于 worker 間的通訊;增加 semaphore 特性,用于 ngx_lua 輕量級線程間的通訊。酷狗音樂的推送服務就是基于這些實現的,這些改動點會在這個月并入 master。可以邀請酷狗音樂的同學,來給大家詳細分享下里面的細節。

建立一個開源的 WAF 平臺。現在阿里云和 cloudflare 的 WAF 做的都很棒,經受住了很多實際的考驗。但是都沒有開源,我們希望最好的 WAF 是開源的,而且是基于 OpenResty 的。

在 OpenResty 中增加內存數據庫。可以有持久化,或者就是全內存的,支持 SQL 的查詢。這個也是出于極致性能的考慮,有時候我們還是需要使用 SQL 來做一些復雜的查詢,但有不想使用那么重的關系型數據庫,而且數據是可以丟失的。那么這個就可以排上用場。

實現 PHP、Python 等方言,讓 PHP、Python 等程序員可以用自己喜歡的語言寫 OpenResty 的代碼,底層轉換為 LuaJIT 的字節碼。

春哥在 OpenResty 技術大會上面說了非常多的新特性,包括 streaming RegEx 正則引擎等等,非常高端,我挑了幾個我覺得有意思的做介紹。

7. 開源社區建設

OpenResty 誕生于 2011 年,大多數時間都是春哥主力在維護這個項目,當然也有很多開發者提交 feature 和 bugfix ,但基本上算單打獨斗。

社區有 github 和郵件列表,大部分還是提問的。春哥每天會花費很多的時間,來詳細的回答各種基礎問題。

今年新增了 QQ 群和微信群, QQ 群的質量很高,每天都會有很多提問,非技術問題是被禁止的。而且還有了自己的技術大會,能給大家面對面交流的機會。

我們翻譯了 ngx_lua 的英文文檔,能讓大家更方便的查找資料;我們搭建了一個不用KX上網就能訪問的論壇: bbs.iresty.com,用作提問和知識積累的地方。后面會把谷歌郵件列表的內容同步過來。

只有上面這些是不夠的,在 OpenResty 技術大會的第二天,我們召集了一個很小規模的閉門會議,決定成立 OpenResty 咨詢委員會。

這個委員會,是以個人名字參加的,成員來自奇虎 360、新浪、又拍云、酷狗音樂等公司和社區的開發者,希望把國內社區的核心使用者和開發者團結在一起,促進 OpenResty 的發展。

同時,OpenResty 軟件基金會也開始籌備工作,我們希望走規范的非盈利組織的模式,來保證 OpenResty 長期穩定發展。給開發者和使用者信心,敢于在關鍵業務上面使用 OpenResty。

Q & A

1、請問 OpenResty 的定位是什么,從分享來看似乎全棧了?

定位主要是高性能,所有的新功能和優化,都是針對性能的。 但是也有人拿來做頁面,比如京東;也有人拿來替代 PHP 做 Web server,比如新浪。 我覺得它越來越像一個獨立的開發語言。

2、請問 Lua 是不是可以實現動態配置 location?比如動態切流量?

balancer_by_lua 可能是你需要的,你可以用 Lua 來定義自己的負載均衡器,可以在每個請求的級別上去定義,當前訪問的后端的節點地址、端口,還可以定制很細力度的訪問失敗之后的重試策略。

3、OpenResty 是可以拿到 nginx 請求里面的所有信息?那是不是可以做一些更復雜的轉發操作?能介紹一下 OpenResty 在 cdn 里面的應用場景嗎?

可以看下 iresty.com 的分享,又拍的張聰非常詳細的介紹了 OpenResty 在又拍 CDN 的使用。

4、OpenResty 是否修改了 nginx 的源碼,還是和 nginx 完全可剝離開的?Nginx 版本升級,OpenResty 也跟著升級嗎?例如 nginx 修復漏洞 bug 等情況。

OpenResty 不修改 nginx 的源碼,可以跟隨 nginx 無痛升級。 如果你覺得 OpenResty 升級慢了, 你可以只拿 ngx_lua 出來,當做 nginx 的一個模塊來編譯。實際上,OpenResty 在測試過程中,發現了很多 nginx 自身的 bug 。

5、軟 WAF nginx + Lua 是主流和未來方向么?

我覺得 WAF 應該基于 nginx,不管是性能還是流行程度。而 OpenResty 具有更靈活操控 nginx 的能力,所以我覺得 OpenResty 在 WAF 領域非常合適。cloudflare 的 WAF 就是基于 OpenResty。

6、看樣子未來可能有各種 ngx_xx,最有可能的是 js,不知道這方面有什么前沿的動向?

我們組在嘗試把 PHP 嵌入到 nginx 中,當然性能肯定不如 LuaJIT,但是會方便很多 PHP 同學,有進展的話,我們會開源出來 :)

7、OpenResty 目前看似乎是一個 proxy 的配置框架(糅合了 nginx + Lua),但以后的發展是什么樣子?會不會以后更進一步,比如做一個 API gateway 之類的。

OpenResty其實是希望大家忽略 nginx 的存在,直接使用 ngx_lua 提供的 API 實現自己的業務邏輯。更像一門獨立的開發語言,只不過底層使用 nginx 的網絡庫而已。你可以按照你的想法搭建任何好玩的服務端應用出來。

來自: https://segmentfault.com/a/1190000004113020

 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!