溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

如何通過 Lua 擴(kuò)展 Nginx

發(fā)布時(shí)間:2021-07-10 10:31:01 來(lái)源:億速云 閱讀:167 作者:chen 欄目:大數(shù)據(jù)

這篇文章主要講解了“如何通過 Lua 擴(kuò)展 Nginx”,文中的講解內(nèi)容簡(jiǎn)單清晰,易于學(xué)習(xí)與理解,下面請(qǐng)大家跟著小編的思路慢慢深入,一起來(lái)研究和學(xué)習(xí)“如何通過 Lua 擴(kuò)展 Nginx”吧!

1. ngx_lua 模塊


  • Nginx 模塊需要用 C 開發(fā),而且必須符合一系列復(fù)雜的規(guī)則,最重要的用 C 開發(fā)模塊必須要熟悉 Nginx 的源代碼,使得開發(fā)者對(duì)其望而生畏。

  • ngx_lua 模塊通過將 lua 解釋器集成進(jìn) Nginx,可以采用 lua 腳本實(shí)現(xiàn)業(yè)務(wù)邏輯。

  • 該模塊具有以下特性:

    • 高并發(fā)、非阻塞地處理各種請(qǐng)求。

    • Lua 內(nèi)建協(xié)程,這樣就可以很好地將異步回調(diào)轉(zhuǎn)換成順序調(diào)用的形式。

    • 每個(gè)協(xié)程都有一個(gè)獨(dú)立的全局環(huán)境(變量空間),繼承于全局共享的、只讀的“comman data”。

  • 得益于 Lua 協(xié)程的支持,ngx_lua 在處理 10000 個(gè)并發(fā)請(qǐng)求時(shí)只需要很少的內(nèi)存。根據(jù)測(cè)試,ngx_lua 處理每個(gè)請(qǐng)求只需要 2KB 的內(nèi)存,如果使用 LuaJIT 則會(huì)更少。

  • ngx_lua 非常適合用于實(shí)現(xiàn)可擴(kuò)展的、高并發(fā)的服務(wù)。

2. 協(xié)程(Coroutine)


1. 協(xié)程類似一種多線程,與多線程的區(qū)別
  • 協(xié)程并非 os 線程,所以創(chuàng)建、切換開銷比線程相對(duì)較小。

  • 協(xié)程與線程一樣有自己的棧、局部變量等,但是協(xié)程的棧是在用戶進(jìn)程空間模擬的,所以創(chuàng)建、開銷很小。

  • 多線程程序是多個(gè)線程并發(fā)執(zhí)行,也就是說(shuō)在一瞬間有多個(gè)控制流在執(zhí)行。而協(xié)程強(qiáng)調(diào)的是一種多個(gè)協(xié)程間協(xié)作的關(guān)系,只有當(dāng)一個(gè)協(xié)程主動(dòng)放棄執(zhí)行權(quán),另一個(gè)協(xié)程才能取得執(zhí)行權(quán),所以在某一瞬間,多個(gè)協(xié)程間只有一個(gè)在運(yùn)行。

  • 由于多個(gè)協(xié)程是只有一個(gè)在運(yùn)行,所以對(duì)于臨界區(qū)的訪問不需要加鎖,而多線程的情況則必須加鎖。

  • 多線程程序由于有多個(gè)控制流,所以程序的行為不可控,而多個(gè)協(xié)程的執(zhí)行是由開發(fā)者定義的所以是可控的。

Nginx 的每個(gè) Worker 進(jìn)程都是在 epoll 或 kqueue 這樣的事件模型之上,封裝成協(xié)程,每個(gè)請(qǐng)求都有一個(gè)協(xié)程進(jìn)行處理。這正好與 Lua 內(nèi)建協(xié)程的模型是一致的,所以即使 ngx_lua 需要執(zhí)行 lua,相對(duì) C 有一定的開銷,但依然能保證高并發(fā)能力。

3. Nginx 進(jìn)程模型


  • Nginx 采用多進(jìn)程模型,單Master-多Worker,Master 進(jìn)程主要用來(lái)管理 Worker 進(jìn)程。

  • Worker 進(jìn)程采用單線程、非阻塞地事件模型(Event Loop,事件循環(huán))來(lái)實(shí)現(xiàn)端口的監(jiān)聽及客戶端請(qǐng)求的處理和響應(yīng),同時(shí) Worker 還要處理來(lái)自 Master 的信號(hào)。Worker 進(jìn)程個(gè)數(shù)一般設(shè)置為機(jī)器 CPU 核數(shù)。

1. Master 進(jìn)程具體包括如下 4 個(gè)主要功能
  • 接受來(lái)自外界的信號(hào)。

  • 向各 Worker 進(jìn)程發(fā)送信號(hào)。

  • 監(jiān)控 Worker 進(jìn)程的運(yùn)行狀態(tài)。

  • 當(dāng) Worker 進(jìn)程退出后(異常情況下),會(huì)自動(dòng)重新啟動(dòng)新的 Worker 進(jìn)程。

2. 進(jìn)程模型

如何通過 Lua 擴(kuò)展 Nginx

4. HTTP 請(qǐng)求處理


階段說(shuō)明
post-read讀取請(qǐng)求內(nèi)容階段,nginx 讀取并解析完請(qǐng)求頭之后就立即開始運(yùn)行
server-rewriteserver 請(qǐng)求地址重寫階段
find-config配置查找階段,用來(lái)完成當(dāng)前請(qǐng)求與 location 配置塊之間的配置工作
rewritelocation 請(qǐng)求地址重寫階段,當(dāng) ngx_rewrite 指令用于 location 中,就是在這個(gè)階段運(yùn)行的
post-rewrite請(qǐng)求地址重寫階段,當(dāng) nginx 完成 rewrite 階段所要求的內(nèi)部跳轉(zhuǎn)動(dòng)作,如果 rewrite 階段有這個(gè)要求的話
preaccess訪問權(quán)限檢查準(zhǔn)備階段,ngx_limit_req 和 ngx_limit_zone 在這個(gè)階段運(yùn)行,ngx_limit_req 可以控制請(qǐng)求的訪問頻率,ngx_limit_zone 可以控制訪問的并發(fā)度。
access權(quán)限檢查階段,ngx_access 在這個(gè)階段運(yùn)行,配置指令多是執(zhí)行訪問控制相關(guān)的任務(wù),入檢查用戶的訪問權(quán)限,檢查用戶的來(lái)源 IP 是否合法
post-access訪問權(quán)限檢查提交階段
try-files配置 try_files 處理階段
content內(nèi)容產(chǎn)生階段,是所有請(qǐng)求處理階段中最為重要的階段,因?yàn)檫@個(gè)階段的指令通常是用來(lái)生成 HTTP 響應(yīng)內(nèi)容的
log日志模塊處理階段

5. ngx_lua 指令


  • ngx_lua 屬于 nginx 的一部分,它的執(zhí)行指令都包含在 nginx 的 11 個(gè)步驟之中了,相應(yīng)的處理階段可以做插入式處理,即可插拔式架構(gòu),不過 ngx_lua 并不是所有階段都會(huì)運(yùn)行的;另外指令可以在 http、server、server if、location、location if 幾個(gè)范圍進(jìn)行配置。

指令所處處理階段使用范圍解釋
init_by_lua<br/>init_by_lua_fileloading-confighttpnginx Master 進(jìn)程加載配置時(shí)執(zhí)行;<br/>通常用于初始化全局配置/預(yù)加載 Lua 模塊
init_worker_by_lua<br/>init_worker_by_lua_filestarting-workerhttp每個(gè) Nginx Worker 進(jìn)程啟動(dòng)時(shí)調(diào)用的計(jì)時(shí)器,如果 Master 進(jìn)程不允許<br/>則只會(huì)在 init_by_\lua 之后調(diào)用;通常用于定時(shí)拉取配置/數(shù)據(jù),<br/>或者后端服務(wù)的健康檢查。
set_by_lua<br/>set_by_lua_filerewriteserver,server if,location,location if設(shè)置 nginx 變量,可以實(shí)現(xiàn)復(fù)雜的賦值邏輯;此處是阻塞的,Lua 代碼要做到非常快
write_by_lua<br/>rewrite_by_lua_filerewrite tailhttp,server,location,location ifrewrite 階段處理,可以實(shí)現(xiàn)復(fù)雜的轉(zhuǎn)發(fā)/重定向邏輯。
access_by_lua<br/>access_by_lua_fileaccess tailhttp,server,location,location if請(qǐng)求訪問階段處理,用于訪問控制
content_by_lua<br/>content_by_lua_filecontentlocation,location if內(nèi)容處理器,接受請(qǐng)求處理并輸出響應(yīng)
header_filter_by_lua<br/>header_filter_by_lua_fileoutput-header-firsthttp,server,location,location if設(shè)置 header 和 cookie
body_filter_by_lua<br/>body_filter_by_lua_fileoutput-body-filterhttp,server,location,location if對(duì)響應(yīng)數(shù)據(jù)進(jìn)行過濾,比如截?cái)唷⑻鎿Q
log_by_lua<br/>log_by_lua_fileloghttp,server,location,location iflog 階段處理,比如記錄訪問量/統(tǒng)計(jì)平均響應(yīng)時(shí)間

6. OpenResty


1. 概念
  • OpenResty 是一個(gè)基于 Nginx 與 Lua 的高性能 Web 平臺(tái),其內(nèi)部集成了大量精良的 Lua 庫(kù)、第三方模塊以及大多數(shù)的依賴項(xiàng)。用于方便地搭建能夠處理超高并發(fā)、擴(kuò)展性極高的動(dòng)態(tài) Web 應(yīng)用、Web 服務(wù)和動(dòng)態(tài)網(wǎng)關(guān)。

2. 工作原理
  • OpenResty 通過匯聚各種設(shè)計(jì)精良的 Nginx 模塊(主要由 OpenResty 團(tuán)隊(duì)自主開發(fā)),從而將 Nginx 有效地變成一個(gè)強(qiáng)大的通用 Web 應(yīng)用平臺(tái)。這樣,Web 開發(fā)人員和系統(tǒng)工程師可以使用 Lua 腳本語(yǔ)言調(diào)用 Nginx 支持的各種 C 以及 Lua 模塊,快速構(gòu)造出足以勝任 10k 乃至 1000k 以上單機(jī)并發(fā)連接的高性能 Web 應(yīng)用系統(tǒng)。

3. 目標(biāo)
  • OpenResty 的目標(biāo)是讓你的 Web 服務(wù)直接跑在 Nginx 服務(wù)內(nèi)部,充分利用 Nginx 的非阻塞 I/O 模型,不僅僅對(duì) HTTP 客戶端請(qǐng)求,甚至于對(duì)遠(yuǎn)程后端諸如 MySQL、PostgreSQL、Memcached 以及 Redis 等都進(jìn)行一致的高性能響應(yīng)。

7. ngx_lua 實(shí)例


  • content_by_lua:內(nèi)容處理器,接收請(qǐng)求處理并輸出響應(yīng)。

  • 該指令工作在 Nginx 處理流程的 content 階段,即內(nèi)容產(chǎn)生階段,是所有請(qǐng)求處理階段中最為重要的階段,因?yàn)檫@個(gè)階段的指令通常是用來(lái)生成 HTTP 響應(yīng)內(nèi)容的。

  • 測(cè)試

    輸出:
    $ curl http://127.0.0.1/
    $ Hello,world


1. 簡(jiǎn)單示例
user www-data;
worker_processes auto;
pid /run/nginx.pid;

error_log logs/error.log;

events {
    worker_connections 1024;
}

http {
    server {
        listen 8080;
        location / {
            default_type text/html;
            content_by_lua '
                ngx.say("<p>Hello, world!</p>")
            ';
        }
    }

}
user www-data;
worker_processes auto;
pid /run/nginx.pid;

error_log logs/error.log;

events {
    worker_connections 1024;
}

http {
    server {
        listen 8080;
        location / {
            default_type text/html;
            content_by_lua_block {
                ngx.say('Hello, world!')
            }
        }
    }

}
啟動(dòng)
/usr/local/openresty/nginx/sbin/nginx -p `pwd` -c conf/nginx_openresty_01.conf

測(cè)試
curl -i http://127.0.0.1/
2. 休眠示例
user www-data;
worker_processes auto;
pid /run/nginx.pid;

error_log logs/error.log;

events {
    worker_connections 1024;
}

http {
    server {
        listen 8080;
        location /lua_1 {
            default_type text/html;
            content_by_lua_block {
                ngx.say('Hello, world! @ Time 1!')
                ngx.sleep(3)
                ngx.say('Hello, world! @ Time 2!')
            }
        }
    }

}
啟動(dòng)
/usr/local/openresty/nginx/sbin/nginx -p `pwd` -c conf/nginx_openresty_02.conf

測(cè)試
curl -i http://127.0.0.1/lua_1
curl -i http://127.0.0.1/lua_1
3. 帶參數(shù)示例
user www-data;
worker_processes auto;
pid /run/nginx.pid;

error_log logs/error.log;

events {
    worker_connections 1024;
}

http {
    server {
        listen 8080;
        location /lua_1 {
            default_type text/html;
            content_by_lua_block {
                ngx.say(ngx.var.arg_a)
            }
        }
    }

}
啟動(dòng)
/usr/local/openresty/nginx/sbin/nginx -p `pwd` -c conf/nginx_openresty_03.conf

測(cè)試
curl -i http://127.0.0.1?a=nginx_lua

8. OpenResty 連接 redis


配置文件
user www-data;
worker_processes auto;
pid /run/nginx.pid;

error_log logs/error.log;

events {
    worker_connections 1024;
}

http {
    server {
        listen 8080;
        location ~/redis_lua/(\d+)$ {
            default_type text/html;
            charset utf-8;
            lua_code_cache on;
            content_by_lua_file '/home/zp/openresty/lua/redis.lua';
        }
    }

}
lua 腳本
local json = require("cjson")
local redis = require("resty.redis")
local red = redis:new()
red:set_timeout(1000)
local ip = "127.0.0.1"
local port = 6379
local ok, err = red:connect(ip, port)
if not ok then
    ngx.say('connect to redis error: ', err)
    return ngx.exit(500)
end

local id = ngx.var[1]
local value = "calue-"..id

red:set(id, value)

local resp, err = red:get(id)

if not resp then
    ngx.say('get from redis error: ', err)
    return ngx.exit(500)
end

red:close()

ngx.say(json.encode({content=resp}))
測(cè)試
啟動(dòng)
/usr/local/openresty/nginx/sbin/nginx -p `pwd` -c conf/nginx_openresty_04.conf

測(cè)試
curl -i http://127.0.0.1/redis_lua/1
curl -i http://127.0.0.1/redis_lua/2
curl -i http://127.0.0.1/redis_lua/3

感謝各位的閱讀,以上就是“如何通過 Lua 擴(kuò)展 Nginx”的內(nèi)容了,經(jīng)過本文的學(xué)習(xí)后,相信大家對(duì)如何通過 Lua 擴(kuò)展 Nginx這一問題有了更深刻的體會(huì),具體使用情況還需要大家實(shí)踐驗(yàn)證。這里是億速云,小編將為大家推送更多相關(guān)知識(shí)點(diǎn)的文章,歡迎關(guān)注!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI