我們能用 lua 做什麼
女主宣言
lua 是一個巴西人設計的小巧的指令碼語言,它的設計目的是為了能夠嵌入到應用程式中,從而為應用程式提供靈活的擴充套件和定製功能。今天我們邀請到 360 高階開發工程師李鋼帶我們快速入門 lue。本文最先發佈於 opsdev,轉載已獲取作者授權。
PS:豐富的一線技術、多元化的表現形式,盡在“HULK一線技術雜談”,點關注哦!
前言
作為web開發工程師,我們平時主要使用的開發語言是php。這個語言提供了對html模版的強大的處理能力,也提供了十分豐富的函式庫及擴充套件,非常的適合web開發使用。那麼lua是如何進入到我們的視線中的呢?在這裡我先說下我在開發一個web產品時,會優先考慮的幾個問題:
如何保證服務的穩定性,即如何防止白屏、50x錯誤的發生。
如何提高頁面的響應速度,即讓使用者感覺頁面開啟足夠快。
那麼在用php解決這幾個問題時,是否夠用呢?
答案在我看來是否定的,為什麼這麼說呢?請聽我分別道來:
1
php在處理服務穩定性時的不足
先簡單說下由php導致的服務異常原因:
php語法錯誤、執行時的異常都會導致500錯誤,這在使用者端瀏覽器就會顯示出白屏。
在使用nginx + php-cgi這種組合提供動態web服務時,當後臺cgi程序掛掉或數量不夠用時,即會產生502錯誤;當php執行時遭遇阻塞(如連db時,db壓力過大),而在nginx中配置的超時時間到達後,通常會產生504錯誤。
這幾種異常本身都是由於php導致的,當然不能靠php去解決。
webserver如nginx提供瞭如error_page這種用於處理當服務產生異常時的後續處理機制,為了不讓使用者看到白屏或錯誤頁面,我們可以定時對正常服務時的頁面做一個快照,當遇到服務異常時,就給使用者這個歷史快照看。
但這樣做也有個侷限性:當你提供服務的頁面很多時,又或是需要根據請求的引數做一些邏輯上的處理時,顯然就很難做到了。
你也許會說,我可以寫nginx配置,讓它分析請求引數,再做相應的邏輯處理。但是這樣做的話,想想看你的nginx配置會有多麼的複雜,多麼的難以維護,而且就算你這樣做,也不能解決所有的問題。比如說我有個介面要輸出json字串,或是其它別的格式,你總不能說我再去寫個nginx擴充套件讓它支援json吧。
所以,在提高服務的穩定性方面,我們的需求是:
能用到webserver提供的錯誤處理機制。
能方便的處理請求引數,做需要的邏輯處理。
2
php在提高使用者響應速度方面的不足
php是一個阻塞式順序執行的指令碼語言,雖然支援多程序執行,但這種模式並不適合使用在併發量很高的web服務中。
想像如果一個請求的處理過程中,你需要呼叫到多處外部資源或服務(db、rest介面),那麼你的處理速度就要依賴於這些外部服務,而且是一個一個順序處理的,它們越多,處理就越慢。
php的multi_curl可以用來併發請求這些外部的rest服務,但這樣做的話,依舊需要等全部的請求都處理完成,才能返回給使用者。換句話說,如果某個外部服務很慢,那麼使用者看到頁面開啟依舊會很慢。
也可以選擇把頁面分塊,讓慢的部分用js非同步請求載入。但這樣做的話,會增加伺服器的訪問量,每增加一塊,訪問量會增大一倍。
所以,在提高頁面的響應速度方面,我們的需求是:
耗時慢的服務能夠做到非同步載入,服務端每完成一部分的計算,就讓頁面展示這部分。
不能過大的增加伺服器的壓力。
3
nginx-lua模組
最終,我們找到了nginx-lua?模組。這個模組會在每個nginx的worker_process中啟動一個lua直譯器,在nginx處理http請求的11個階段中,你可以在其中的多個階段用lua程式碼處理請求。
這二者的結合,給我們的web開發帶來了新的思路。下面我就來說下導航目前是如何使用它來解決問題的。
4
解決服務穩定性
這裡的思路很簡單,我們會在error_page指令被執行後,用lua程式碼來接受引數,處理邏輯部分,最終會返回前端和用php處理看起來一致的內容。部分程式碼如下:
nginx_conf:
這裡大家看到,當請求出現50x錯誤時,會跳到location jump_to_error_page_api中,在這裡面,content_by_lua_file指令會在content處理階段啟動指定好的lua指令碼(這裡是error_page_api.lua)來處理請求。我們再看下lua指令碼中都做了什麼:
lua example:
這裡大家可以看到,我們可以在lua指令碼中接受請求引數,做和php一樣的邏輯,最終輸出前端需要的正確的內容。
目前這套機制我們已經用在我們這邊的一個重要使用者頁面上,目前都沒有收到使用者反饋說頁面打不開,出現錯誤頁這種,效果很是明顯。
5
提高使用者頁面的響應速度
上面提到的解決響應速度的幾個需求,我們的思路是引入bigpipe的處理機制。關於bigpipe本文不做講解,大家可以自行google。這個專案目前尚處於實驗階段,但我們已經實現了一個簡單的demo:
nginx_conf:
這裡指定請求index.php會用bigpipe_index.lua處理。
lua example:
這裡在處理請求時,大致邏輯如下:
首先會先吐出首屏html部分及部分html框架程式碼。
接下來會啟動3個lua協程,在nginx-lua這個模組的排程下以非同步非阻塞的模式併發的來處理3個外部請求。
這3個外部請求各自的延時不同,但是任何一部分處理完成,都會直接返回給前端用於展示。
通過這種方式,使用者的頁面響應速度得到了明顯的提高,體驗更好。
6
結束語
正如lua官方給出的定義所說,lua很小巧,非常的適合嵌入已有的應用程式中,從而補足現有系統的一些缺憾,並擴展出新的功能。對nginx-lua模組的使用,筆者也還在研究中,但我相信更好的使用它,能為我們現有的web開發開啟一扇新的窗戶,理解更深層次的知識。
參考資料:
[1] https://www.lua.org/
[2] https://github.com/openresty/lua-nginx-module
HULK一線技術雜談
由360雲平臺團隊打造的技術分享公眾號,內容涉及雲端計算、資料庫、大資料、監控、泛前端、自動化測試等眾多技術領域,通過夯實的技術積累和豐富的一線實戰經驗,為你帶來最有料的技術分享