OpenResty:將 NGINX 轉變為成熟的應用服務器

OpenResty:將 NGINX 轉變為成熟的應用服務器我們重新發布會議報告的文字記錄 高負載++ 2016年,去年7月8日至XNUMX日在莫斯科附近的斯科爾科沃舉行。 弗拉基米爾·普羅塔索夫 講述如何使用 OpenResty 和 Lua 擴展 NGINX 功能。

大家好,我叫 Vladimir Protasov,在 Parallels 工作。 我會告訴你一些關於我自己的事情。 我一生四分之三的時間都在寫代碼。 我成為了一名真正的程序員:我有時會在夢中看到代碼。 人生四分之一的時間都花在工業開發上,編寫直接投入生產的代碼。 你們中有些人使用但不知道的代碼。

讓你知道情況有多糟糕。 當我還是大三的時候,我進來了,他們給了我這兩個 TB 的數據庫。 現在它已經為大家帶來了高負載。 我去參加會議並問:“伙計們,告訴我,你們有大數據嗎?一切都很酷嗎? 你們那裡有多少個基地? 他們回答我:“我們有100GB!” 我說:“酷啊,100GB!” 我心裡想著如何巧妙地挽回撲克臉。 你認為,是的,這些人很酷,然後你回來修改這些多 TB 的數據庫。 這就是初級。 你能想像它有多受歡迎嗎?

我知道 20 多種編程語言。 這是我在工作中必須弄清楚的。 他們給你用 Erlang、C、C++、Lua、Python、Ruby 或其他語言編寫的代碼,你必須把它們全部刪掉。 一般來說,我不得不這樣做。 無法計算出確切的數字,但大約有 20 個數字丟失了。

由於這裡的每個人都知道 Parallels 是什麼以及我們做什麼,所以我不會談論我們有多酷以及我們做什麼。 我只告訴你,我們在全球有13個辦事處,300多名員工,發展在莫斯科、塔林和馬耳他。 如果你願意,你可以搬到馬耳他,如果冬天很冷,你需要溫暖你的背部。

具體來說,我們部門是用Python 2編寫的。我們是做生意的,沒有時間引入流行的技術,所以我們很痛苦。 我們有 Django,因為它擁有一切,我們把多餘的扔掉了。 還有 MySQL、Redis 和 NGINX。 我們還有很多其他很酷的東西。 我們有 MongoDB,我們有兔子跑來跑去,我們只是什麼都沒有——但它不是我的,而且我不這樣做。

OpenResty

我講述了我自己。 讓我們看看今天我要講什麼:

  • OpenResty 是什麼?它和什麼一起吃?
  • 當我們擁有 Python、NodeJS、PHP、Go 和其他人人都滿意的很酷的東西時,為什麼要重新發明輪子呢?
  • 以及一些現實生活中的例子。 我不得不把報告刪減很多,因為我花了3,5個小時才拿到它,所以例子很少。

OpenResty 是 NGINX。 感謝他,我們擁有了一個成熟的網絡服務器,它寫得很好,運行速度很快。 我認為我們大多數人在生產中使用 NGINX。 你們都知道他又快又酷。 他們在其中做了很酷的同步 I/O,所以我們不需要像 Python 中 gevent 循環那樣循環任何東西。 Gevent 很酷,很酷,但是如果你編寫 C 代碼並且 gevent 出了問題,你會瘋狂地調試它。 我有經驗:花了整整兩天的時間才弄清楚那裡出了什麼問題。 如果有人沒有在幾週前挖掘,發現問題,把它寫在互聯網上,而谷歌也沒有找到它,那麼我們就會徹底瘋了。

NGINX 已經做了緩存和靜態內容。 你不需要擔心如何人為地做到這一點,這樣你就不會在某個地方放慢速度,這樣你就不會在某個地方丟失描述符。 Nginx部署起來非常方便,你不需要考慮拿什麼——WSGI、PHP-FPM、Gunicorn、Unicorn。 Nginx 已安裝,並交給管理員,他們知道如何使用它。 Nginx 以結構化的方式處理請求。 我稍後會討論這個問題。 簡而言之,他有一個階段,當他剛剛接受請求時,當他處理時,當他將內容提供給用戶時。

Nginx 很酷,但有一個問題:儘管人們在配置中加入了所有這些很酷的功能,但它仍然不夠靈活,儘管它可以定制。 這個力量還不夠。 所以,淘寶的人,我記得是八年前,就內置了Lua。 他給什麼?

  • 大小。 它很小。 LuaJIT 提供了大約 100-200 KB 的內存開銷和最小的性能開銷。
  • 速度。 LuaJIT解釋器在很多情況下都接近C,在某些情況下它輸給了Java,在某些情況下它超過了它。 有一段時間,它被認為是最先進的、最酷的 JIT 編譯器。 現在有更酷的,但它們很重,例如相同的V8。 一些 JS 解釋器和 Java HotSpot 在某些點上速度更快,但在某些點上仍然會丟失。
  • 簡單易學。 比如說,如果您有 Perl 代碼庫並且您不是 Booking,那麼您將找不到 Perl 程序員。 因為他們不在,都被帶走了,教他們又漫長又困難。 如果你想要程序員做其他事情,他們可能還需要重新培訓或找到。 對於 Lua 來說,一切都很簡單。 Lua任何一個小學生三天就能學會。 我花了大約兩個小時才弄清楚。 兩個小時後,我已經在生產環境中編寫代碼了。 大約一周後,他直接投入生產並離開。

結果,它看起來像這樣:

OpenResty:將 NGINX 轉變為成熟的應用服務器

這裡有很多。 OpenResty 組裝了一堆模塊,包括 luash 和引擎。 您已準備好一切 - 已部署並可運行。

Примеры

歌詞已經說得夠多了,讓我們繼續看代碼。 這是一個小Hello World:

OpenResty:將 NGINX 轉變為成熟的應用服務器

還有什麼? 這是發動機位置。 我們不擔心,我們不會編寫自己的路由,我們不會採用一些現成的路由 - 我們已經在 NGINX 中擁有了它,我們生活得很好,也很懶惰。

content_by_lua_block 是一個塊,表示我們正在使用 Lua 腳本提供內容。 我們採用一個引擎變量 remote_addr 並將其滑入 string.format. 這與 sprintf,只有在Lua中,才正確。 我們把它交給客戶。

結果,它將如下所示:

OpenResty:將 NGINX 轉變為成熟的應用服務器

但回到現實世界。 在生產環境中,沒有人部署 Hello World。 我們的應用程序通常會訪問數據庫或其他地方,並且大多數時間都會等待響應。

OpenResty:將 NGINX 轉變為成熟的應用服務器

只是坐著等待。 這不太好。 當100.000萬用戶來的時候,我們就很難了。 因此,我們以一個簡單的應用程序為例。 我們會尋找圖片,例如貓。 只是我們不只是搜索,我們會擴展關鍵詞,如果用戶搜索“小貓”,我們會找到貓、絨毛等等。 首先我們需要獲取後端的請求數據。 它看起來像這樣:

OpenResty:將 NGINX 轉變為成熟的應用服務器

兩行允許您獲取 GET 參數,沒有任何復雜性。 然後,例如,我們使用常規 SQL 查詢從包含關鍵字和擴展名的表的數據庫中獲取此信息。 一切都很簡單。 它看起來像這樣:

OpenResty:將 NGINX 轉變為成熟的應用服務器

我們連接圖書館 resty.mysql,我們已經在套件中了。 我們不需要安裝任何東西,一切都準備好了。 指定如何連接並進行 SQL 查詢:

OpenResty:將 NGINX 轉變為成熟的應用服務器

這有點可怕,但它確實有效。 這裡10是極限。 我們拿出10條記錄,我們很懶,我們不想展示更多。 在 SQL 中,我忘記了限制。

然後我們找到所有查詢的圖像。 我們收集一堆請求並填寫一個名為的 Lua 表 reqs,並做 ngx.location.capture_multi.

OpenResty:將 NGINX 轉變為成熟的應用服務器

所有這些請求同時進行,並將答案返回給我們。 運行時間等於最慢的響應時間。 如果我們都在 50 毫秒內回复,並且發送了一百個請求,那麼我們將在 50 毫秒內收到響應。

由於我們很懶,不想編寫 HTTP 處理和緩存,所以我們會讓 NGINX 為我們做所有事情。 正如您所看到的,有一個請求 url/fetch, 他在這裡:

OpenResty:將 NGINX 轉變為成熟的應用服務器

我們做簡單 proxy_pass,指定緩存位置、如何緩存,一切都對我們有用。

但這還不夠,我們還需要把數據交給用戶。 最簡單的想法是用兩行輕鬆地將所有內容序列化為 JSON。 我們給出 Content-Type,我們給出 JSON。

但有一個困難:用戶不想讀取 JSON。 我們需要吸引前端開發人員。 有時我們一開始並不想這樣做。 是的,SEO 專家會說,如果我們正在尋找圖片,那麼他們不在乎。 如果我們給他們一些內容,他們會說我們的搜索引擎不會索引任何內容。

該怎麼辦? 當然,我們會給用戶 HTML。 使用句柄生成並不常見,因此我們想使用模板。 有一個庫可以解決這個問題 lua-resty-template.

OpenResty:將 NGINX 轉變為成熟的應用服務器

您一定見過“OPM”這三個可怕的字母。 OpenResty 帶有自己的包管理器,通過它您可以安裝一堆不同的模塊,特別是, lua-resty-template。 它是一個類似於 Django 模板的簡單模板引擎。 您可以在那裡編寫代碼並進行變量替換。

結果,一切都會看起來像這樣:

OpenResty:將 NGINX 轉變為成熟的應用服務器

我們獲取數據並用兩行再次渲染模板。 用戶很高興,得到了貓。 由於我們擴大了要求,他還收到了一隻小海狗。 你永遠不知道,也許他正在尋找它,但他無法正確地表達他的要求。

一切都很酷,但我們正在開發中,我們還不想向用戶展示。 我們來做一下授權吧。 為此,我們來看看 NGINX 如何根據 OpenResty 處理請求:

  • 第一階段 - 訪問,當用戶剛來的時候,我們通過標頭、IP 地址、其他數據來查看他。 如果我們不喜歡你可以立即把它砍掉。 這可以用於授權,或者如果我們收到很多請求,我們可以在這個階段輕鬆地砍掉它們。
  • 改寫。 重寫一些請求數據。
  • 內容。 我們向用戶提供內容。
  • 標頭過濾器。 更改響應標頭。 如果我們使用 proxy_pass,我們可以在將其提供給用戶之前重寫一些標頭。
  • 身體過濾器。 我們可以改變身體。
  • 登錄 - 日誌記錄。 無需額外層即可在elasticsearch中寫入日誌。

我們的授權將如下所示:

OpenResty:將 NGINX 轉變為成熟的應用服務器

我們將其添加到其中 location,我們之前描述過,並將以下代碼放在那裡:

OpenResty:將 NGINX 轉變為成熟的應用服務器

我們查看是否有 cookie 令牌。 如果沒有,那麼我們就進行授權。 用戶很狡猾,可能會猜測需要設置 cookie 令牌。 因此,我們也將其放入Redis中:

OpenResty:將 NGINX 轉變為成熟的應用服務器

使用 Redis 的代碼非常簡單,與其他語言沒有什麼不同。 同時,所有的輸入/輸出,那裡有什麼,這裡有什麼,都不是阻塞的。 如果您編寫同步代碼,那麼它就會異步工作。 就像 gevent 一樣,只是做得好。

OpenResty:將 NGINX 轉變為成熟的應用服務器

讓我們自己進行授權:

OpenResty:將 NGINX 轉變為成熟的應用服務器

我們說我們需要讀取請求正文。 我們收到 POST 參數,檢查登錄名和密碼是否正確。 如果不正確,那麼我們會進行授權。 如果它們是正確的,那麼我們將令牌寫入 Redis:

OpenResty:將 NGINX 轉變為成熟的應用服務器

不要忘記設置 cookie,這也是通過兩行完成的:

OpenResty:將 NGINX 轉變為成熟的應用服務器

這個例子很簡單,是推測性的。 當然,我們不會提供向人們展示貓的服務。 但誰認識我們呢。 那麼讓我們看一下在生產中可以做什麼。

  • 極簡後端。 有時我們需要向後端提供相當多的數據:在某個地方我們需要替換日期,在某個地方我們需要顯示某種列表,比如現在有多少用戶在網站上,擰上計數器或統計數據。 這麼小的東西。 一些最小的部件可以很容易地製作。 這將是快速、簡單且出色的。
  • 數據預處理。 有時我們想在頁面中嵌入廣告,並通過 API 請求獲取這些廣告。 這在這裡很容易做到。 我們不加載已經在努力工作的後端。 您可以在這裡提貨和收集。 我們可以塑造一些 JS,或者相反,在將某些東西提供給用戶之前取消粘連、預處理。
  • 微服務的外觀。 這也是一個非常好的案例,我實現了。 在此之前,我曾在 Tenzor 工作,這是一家電子報告公司,為該國大約一半的法人實體提供報告。 我們做了一個服務,很多事情都是使用相同的機製完成的:路由、授權等等。
    OpenResty 可以用作微服務的粘合劑,提供對所有內容的單一訪問和單一接口。 由於微服務可以這樣編寫:這裡有 Node.js,這裡有 PHP,這裡有 Python,這裡有一些 Erlang 的東西,我們知道我們不想到處重寫相同的代碼。 因此,OpenResty可以插在前面。

  • 統計和分析。 通常NGINX在入口處,所有請求都經過它。 就是在這個地方,採集起來非常方便。 你可以立即計算出一些東西並將其扔到某個地方,例如相同的Elasticsearch、Logstash,或者只是將其寫入日誌然後將其發送到某個地方。
  • 多用戶系統。 比如網絡遊戲也很好做。 今天,Alexander Gladysh 將在開普敦告訴您如何使用 OpenResty 快速製作多人遊戲原型。
  • 請求過濾(WAF)。 現在製作各種Web應用程序防火牆很流行,有很多服務提供它們。 借助 OpenResty,您可以為自己打造一個 Web 應用程序防火牆,它將根據您的要求簡單輕鬆地過濾請求。 如果您有 Python,那麼您就會明白 PHP 絕對不會注入給您,當然,除非您從控制台的任何地方生成它。 你知道你有 MySQL 和 Python。 也許,他們可以在這裡嘗試進行某種目錄遍歷並向數據庫中註入一些內容。 因此,您可以在前面快速且廉價地過濾掉愚蠢的請求。
  • 社區。 由於 OpenResty 是基於 NGINX 的,所以它有一個好處——這就是 NGINX 社區。 它非常大,NGINX 社區已經解答了您一開始遇到的很多問題。

    Lua開發者。 昨天我和參加 HighLoad++ 培訓日的人交談,聽說只有 Tarantool 是用 Lua 編寫的。 事實並非如此,很多東西都是用Lua寫的。 示例:OpenResty、Prosody XMPP 服務器、Love2D 遊戲引擎、魔獸爭霸和其他地方編寫的 Lua 腳本。 Lua 開發者很多,他們有一個龐大且反應靈敏的社區。 我所有的 Lua 問題都在幾個小時內得到了解答。 當你寫信給郵件列表時,幾分鐘之內就已經有一堆答案,它們描述了什麼、如何、什麼是什麼。 這很棒。 不幸的是,這樣善良真誠的社區並不是到處都有。
    OpenResty 有 GitHub,如果出現問題,您可以在其中提出問題。 Google 網上論壇上有一個郵件列表,您可以在其中討論一般問題,也有一個中文郵件列表 - 您永遠不知道,也許您不會說英語,但您懂中文。

結果

  • 我希望我能夠傳達 OpenResty 是一個非常方便的 Web 框架。
  • 它的入門門檻較低,由於代碼與我們編寫的類似,因此語言相當簡單和簡約。
  • 它提供了沒有回調的異步 I/O,我們不會像有時在 NodeJS 中編寫的那樣有麵條。
  • 它的部署很簡單,因為我們只需要 NGINX 以及正確的模塊和我們的代碼,一切都會立即生效。
  • 大型且反應靈敏的社區。

我沒有詳細講路由是怎麼做的,原來是一個很長的故事。

謝謝你的關注!


Vladimir Protasov - OpenResty:將 NGINX 轉變為成熟的應用程序服務器

來源: www.habr.com

添加評論