OpenResty實戰-Lua入門

OpenResty實戰-Lua入門

簡介

OpenResty(也稱為 ngx_openresty) 是一個全功能的 Web 應用服務器。它打包了標準的Nginx 核心,很多的常用的第三方模塊,以及它們的大多數依賴項。

通過揉和眾多設計良好的 Nginx 模塊,OpenResty 有效地把 Nginx 服務器轉變為一個強大的Web 應用服務器,基於它開發人員可以使用 Lua 編程語言對 Nginx 核心以及現有的各種Nginx C 模塊進行腳本編程,構建出可以處理一萬以上併發請求的極端高性能的 Web 應用。

OpenResty 致力於將你的服務器端應用完全運行於 Nginx 服務器中,充分利用 Nginx 的事件模型來進行非阻塞 I/O 通信。不僅僅是和 HTTP 客戶端間的網絡通信是非阻塞的,與MySQL、PostgreSQL、Memcached 以及 Redis 等眾多遠方後端之間的網絡通信也是非阻塞的。

因為 OpenResty 軟件包的維護者也是其中打包的許多 Nginx 模塊的作者,所以 OpenResty可以確保所包含的所有組件可以可靠地協同工作。

OpenResty 最早是雅虎中國的一個公司項目,起步於 2007 年 10 月。當時興起了 OpenAPI的熱潮,用於滿足各種 Web Service 的需求,就誕生了 OpenResty。

在公司領導的支持下,最早的 OpenResty 實現從一開始就開源了。最初的定位是服務於公司外的開發者,像其他的OpenAPI 那樣,但後來越來越多地是為雅虎中國的搜索產品提供內部服務。

這是第一代的OpenResty,當時的想法是,提供一套抽象的 web service,能夠讓用戶利用這些 webservice 構造出新的符合他們具體業務需求的 Web Service 出來,所以有些“meta webservie”的意味,包括數據模型、查詢、安全策略都可以通過這種 meta web service 來表達和配置。同時這種 web service 也有意保持 REST 風格。

與這種概念相對應的是純 AJAX 的web 應用,即 web 應用幾乎都使用客戶端 JavaScript 來編寫,然後完全由 web service 讓web 應用“活”起來。用戶把 .html, .js, .css, .jpg 等靜態文件下載到 web browser 中,然後 js開始運行,跨域請求雅虎提供的經過站長定製過的 web service,然後應用就可以運行起來。

不過隨著後來的發展,公司外的用戶畢竟還是少數,於是應用的重點是為公司內部的其他團隊提供 web service,比如雅虎中國的全能搜索產品,及其外圍的一些產品。

從那以後,開發的重點便放在了性能優化上面。章亦春在加入淘寶數據部門的量子團隊之後,決定對OpenResty 進行重新設計和徹底重寫,並把應用重點放在支持像量子統計這樣的 web 產品上面,所以量子統計 3.0 開始也幾乎完全是 web service 驅動的純 AJAX 應用。

這是第二代的 OpenResty,一般稱之為 ngx_openresty,以便和第一代基於 Perl 和 Haskell實現的 OpenResty 加以區別。章亦春和他的同事王曉哲一起設計了第二代的 OpenResty。在王曉哲的提議下,選擇基於 nginx 和 lua 進行開發。

為什麼要取 OpenResty 這個名字呢?OpenResty 最早是順應 OpenAPI 的潮流做的,所以Open 取自“開放”之意,而Resty便是 REST 風格的意思。雖然後來也可以基於ngx_openresty 實現任何形式的 web service 或者傳統的 web 應用。

也就是說 Nginx 不再是一個簡單的靜態網頁服務器,也不再是一個簡單的反向代理了。第二代的 openresty 致力於通過一系列 nginx 模塊,把nginx擴展為全功能的 web 應用服務器。

ngx_openresty 是用戶驅動的項目,後來也有不少國內用戶的參與,從 openresty.org 的點擊量分佈上看,國內和國外的點擊量基本持平。

ngx_openresty 目前有兩大應用目標:

1. 通用目的的 web 應用服務器。在這個目標下,現有的 web 應用技術都可以算是和OpenResty 或多或少有些類似,比如 Nodejs, PHP 等等。ngx_openresty 的性能(包括內存使用和 CPU 效率) 算是最大的賣點之一。

2. Nginx 的腳本擴展編程,用於構建靈活的 Web 應用網關和 Web 應用防火牆。有些類似的是 NetScaler。其優勢在於 Lua 編程帶來的巨大靈活性。

ngx_openresty 從一開始就是公司實際的業務需求的產物。在過去的幾年中的大部分開發工作也是由國內外許多公司和個人的實際業務需求驅動的。這種模型在實踐中工作得非常好,可以確保我們做的就是大家最迫切需要的。在此過程中,慢慢形成了 ngx_openresty 的兩大應用方向,也就是前面提到的那兩大方向。是我們的用戶幫助我們確認了這兩個方向,事實上,這並不等同於第一代 OpenResty 的方向,而是變得更加底層和更加通用了。

開源精神的核心是分享而非追求流行。畢竟開源界不是娛樂圈,也不是時尚圈。如果我們的開源項目有越來越多的人開始使用,只是一個“happy accident”,我們自然會很高興,但這並不是我們真正追求的。

開放源碼只是開源項目生命週期中的“萬里長征第一步”,國內的許多開源項目止步於開放源碼,而沒有後續投入長期的時間和精力去跟進響應用戶的各種需求和反饋,但不免夭折。這種現象在國外的不少開源項目中也很常見。

國外成功的開源項目比較多,或許跟許多發達國家的程序員們的精神狀態有關係。比如我認識的一些國外的黑客都非常心思單純,熱情似火。他們在精神上的束縛非常少,做起事來多是不拘一格。有的人即便長期沒有工作單純靠抵押和捐贈過活,也會不遺餘力地投身於開源項目。而我接觸到的國內許多程序員的精神負擔一般比較重,經濟上的壓力也比較大,自然難有“玩開源”的心思。

不過,國內也是有一些程序員擁有國外優秀黑客的素質的,而且他們通過網絡和全球的黑客緊密聯繫在一起,所以我們完全可以期待他們未來有振奮人心的產出。在互聯網時代的今天,或許按國界的劃分來討論這樣的問題會變得越來越不合時宜。


Lua 入門

Lua 是一個小巧的腳本語言。是巴西里約熱內盧天主教大學(Pontifical Catholic University of Rio de Janeiro) 裡的一個研究小組,由 Roberto Ierusalimschy、Waldemar Celes 和 Luiz Henrique de Figueiredo 所組成並於 1993 年開發。其設計目的是為了嵌入應用程序中,從而為應用程序提供靈活的擴展和定製功能。Lua 由標準 C 編寫而成,幾乎在所有操作系統和平臺上都可以編譯、運行。Lua 並沒有提供強大的庫,這是由它的定位決定的。所以 Lua 不適合作為開發獨立應用程序的語言。Lua 有一個同時進行的 JIT 項目,提供在特定平臺上的即時

編譯功能。

Lua 腳本可以很容易的被 C/C++ 代碼調用,也可以反過來調用 C/C++ 的函數,這使得 Lua在應用程序中可以被廣泛應用。不僅僅作為擴展腳本,也可以作為普通的配置文件,代替XML、ini 等文件格式,並且更容易理解和維護。標準 Lua 5.1 解釋器由標準 C 編寫而成,代碼簡潔優美,幾乎在所有操作系統和平臺上都可以編譯和運行;一個完整的標準 Lua 5.1 解釋器不足 200KB。而本書推薦使用的 LuaJIT 2 的代碼大小也只有不足 500KB,同時也支持大部分常見的體系結構。在目前所有腳本語言引擎中,LuaJIT 2 實現的速度應該算是最快的之

一。這一切都決定了 Lua 是作為嵌入式腳本的最佳選擇。

Lua 語言的各個版本是不相兼容的。因此本書只介紹 Lua 5.1 語言,這是為標準 Lua 5.1 解釋器和 LuaJIT 2 所共同支持的。LuaJIT 支持的對 Lua 5.1 向後兼容的 Lua 5.2 和 Lua 5.3 的特性,我們也會在方便的時候予以介紹。


Lua 簡介

這一章我們簡要地介紹 Lua 語言的基礎知識,特別地,我們會有意將討論放置於 OpenResty的上下文中。同時,我們並不會迴避 LuaJIT 獨有的新特性;當然,在遇到這樣的獨有特性時,我們都會予以說明。我們會關注各個語言結構和標準庫函數對性能的潛在影響。在討論性能相關的問題時,我們只會關心 LuaJIT 實現。

Lua 是什麼?

1993 年在巴西里約熱內盧天主教大學(Pontifical Catholic University of Rio de Janeiro in Brazil)誕生了一門編程語言,發明者是該校的三位研究人員,他們給這門語言取了個浪漫的名字—— Lua ,在葡萄牙語裡代表美麗的月亮。事實證明她沒有糟蹋這個優美的單詞,Lua 語言正如它名字所預示的那樣成長為一門簡潔、優雅且富有樂趣的語言。

Lua 從一開始就是作為一門方便嵌入(其它應用程序)並可擴展的輕量級腳本語言來設計的,因此她一直遵從著簡單、小巧、可移植、快速的原則,官方實現完全採用 ANSI C 編寫,能以 C程序庫的形式嵌入到宿主程序中。LuaJIT 2 和標準 Lua 5.1 解釋器採用的是著名的 MIT 許可協議。

正由於上述特點,所以 Lua 在遊戲開發、機器人控制、分佈式應用、圖像處理、生物信息學等各種各樣的領域中得到了越來越廣泛的應用。其中尤以遊戲開發為最,許多著名的遊戲,比如 Escape from Monkey Island、World of Warcraft、大話西遊,都採用了 Lua 來配

合引擎完成數據描述、配置管理和邏輯控制等任務。即使像 Redis 這樣中性的內存鍵值數據庫也提供了內嵌用戶 Lua 腳本的官方支持。

作為一門過程型動態語言,Lua 有著如下的特性:

1. 變量名沒有類型,值才有類型,變量名在運行時可與任何類型的值綁定;

2. 語言只提供唯一一種數據結構,稱為表(table),它混合了數組、哈希,可以用任何類型的值作為 key 和 value。提供了一致且富有表達力的表構造語法,使得 Lua 很適合描述複雜的數據;

3. 函數是一等類型,支持匿名函數和正則尾遞歸(proper tail recursion);

4. 支持詞法定界(lexical scoping)和閉包(closure);

5. 提供 thread 類型和結構化的協程(coroutine)機制,在此基礎上可方便實現協作式多任務;

6. 運行期能編譯字符串形式的程序文本並載入虛擬機執行;

7. 通過元表(metatable)和元方法(metamethod)提供動態元機制(dynamic metamechanism),從而允許程序運行時根據需要改變或擴充語法設施的內定語義;

8. 能方便地利用表和動態元機制實現基於原型(prototype-based)的面向對象模型;

9. 從 5.1 版開始提供了完善的模塊機制,從而更好地支持開發大型的應用程序;


Lua 的語法類似 PASCAL 和 Modula 但更加簡潔,所有的語法產生式規則(EBNF)不過才 60幾個。熟悉 C 和 PASCAL 的程序員一般只需半個小時便可將其完全掌握。而在語義上 Lua 則與 Scheme 極為相似,她們完全共享上述的 1 、3 、4 、6 點特性,Scheme 的 continuation與協程也基本相同只是自由度更高。

最引人注目的是,兩種語言都只提供唯一一種數據結構:Lua 的表和 Scheme 的列表(list)。正因為如此,有人甚至稱 Lua 為“只用表的 Scheme”。

Lua 和 LuaJIT 的區別

Lua 非常高效,它運行得比許多其它腳本(如 Perl、Python、Ruby)都快,這點在第三方的獨立測評中得到了證實。儘管如此,仍然會有人不滿足,他們總覺得“嗯,還不夠快!”。LuaJIT就是一個為了再榨出一些速度的嘗試,它利用即時編譯(Just-in Time) 技術把 Lua 代碼編譯成本地機器碼後交由 CPU 直接執行。

LuaJIT 2 的測評報告表明,在數值運算、循環與函數調用、協程切換、字符串操作等許多方面它的加速效果都很顯著。憑藉著 FFI 特性,LuaJIT 2在那些需要頻繁地調用外部 C/C++ 代碼的場景,也要比標準 Lua 解釋器快很多。目前LuaJIT 2 已經支持包括 i386、x86_64、ARM、PowerPC 以及 MIPS 等多種不同的體系結構。

LuaJIT 是採用 C 和彙編語言編寫的 Lua 解釋器與即時編譯器。LuaJIT 被設計成全兼容標準的 Lua 5.1 語言,同時可選地支持 Lua 5.2 和 Lua 5.3 中的一些不破壞向後兼容性的有用特性。因此,標準 Lua 語言的代碼可以不加修改地運行在 LuaJIT 之上。

LuaJIT 和標準 Lua 解釋器的一大區別是,LuaJIT 的執行速度,即使是其彙編編寫的 Lua 解釋器,也要比標準 Lua5.1 解釋器快很多,可以說是一個高效的 Lua 實現。另一個區別是,LuaJIT 支持比標準 Lua5.1 語言更多的基本原語和特性,因此功能上也要更加強大。

若無特殊說明,我們接下來的文章都是基於 LuaJIT 進行介紹的。

下一篇將介紹Lua的環境搭建。

後續計劃內容:

Lua

Nginx

OpenResty

LuaRestyRedisLibrary

LuaCjsonLibrary

PostgresNginxModule

LuaNginxModule

LuaRestyDNSLibrary

LuaRestyLock

stream_lua_module

balancer_by_lua

OpenResty 與 SSL

測試

Web服務

火焰圖

OpenResty周邊

零碎知識點


分享到:


相關文章: