Nginx15-Lua扩展模块

零、文章目录

Nginx15-Lua扩展模块

1、ngx_lua模块概念

  • 淘宝开发的ngx_lua模块通过将lua解释器集成进Nginx,可以采用lua脚本实现业务逻辑,由于lua的紧凑、快速以及内建协程,所以在保证高并发服务能力的同时极大地降低了业务逻辑实现成本。

2、ngx_lua模块环境准备

(1)方式一:安装lua-nginx-module模块
  • LuaJIT是采用C语言编写的Lua解释器。

  • 官网地址为:http://luajit.org/

  • 官网下载地址:http://luajit.org/download/LuaJIT-2.0.5.tar.gz

  • 百度网盘下载:https://pan.baidu.com/s/14-assOENbR2xRIBBslTqQQ?pwd=1234

# 创建目录
mkdir /opt/luajit
cd /opt/luajit

# 在centos上下载
wget http://luajit.org/download/LuaJIT-2.0.5.tar.gz

# 解压
tar -zxf LuaJIT-2.0.5.tar.gz

# 进入解压的目录
cd LuaJIT-2.0.5

# 执行编译和安装
make && make install
  • 下载lua-nginx-module:https://github.com/openresty/lua-nginx-module/archive/v0.10.16rc4.tar.gz
  • 百度网盘地址:https://pan.baidu.com/s/1LqmHIb1qHHoJLk6fiBteDA?pwd=1234
# 创建目录
mkdir /opt/lua-nginx-module
cd /opt/lua-nginx-module

# 在centos上下载
wget https://github.com/openresty/lua-nginx-module/archive/v0.10.16rc4.tar.gz -O lua-nginx-module-0.10.16rc4.tar.gz

# 解压
tar -zxf lua-nginx-module-0.10.16rc4.tar.gz

# 更改目录名
mv lua-nginx-module-0.10.16rc4 lua-nginx-module

# 导入环境变量,告诉Nginx去哪里找luajit
export LUAJIT_LIB=/usr/local/lib
export LUAJIT_INC=/usr/local/include/luajit-2.0

# 进入Nginx的目录执行如下命令
cd /opt/nginx/core/nginx-1.26.2/
./configure --prefix=/usr/local/nginx --add-module=/opt/lua-nginx-module/lua-nginx-module
make && make install
  • 验证是否安装成功,配置文件中添加如下配置信息
location /lua{
    default_type 'text/html';
    content_by_lua 'ngx.say("<h1>HELLO,LUA</h1>")';
}
  • 重启Nginx,访问地址http://192.168.119.161/lua
nginx -s reload

image-20241018153355321

(2)方式二:直接使用OpenRestry
  • OpenResty是由淘宝工程师开发的,所以其官方网站(http://openresty.org/)我们读起来是非常的方便。
  • OpenResty是一个基于Nginx与 Lua 的高性能 Web 平台,其内部集成了大量精良的 Lua 库、第三方模块以及大多数的依赖项。用于方便地搭建能够处理超高并发、扩展性极高的动态 Web 应用、Web 服务和动态网关。所以本身OpenResty内部就已经集成了Nginx和Lua,所以我们使用起来会更加方便。
  • OpenResty在Linux上安装步骤如下
# 创建目录
mkdir /opt/openresty
cd /opt/openresty

# 下载
# 下载OpenResty:https://openresty.org/download/openresty-1.15.8.2.tar.gz
# 使用wget下载:wget https://openresty.org/download/openresty-1.15.8.2.tar.gz
# 百度网盘地址:https://pan.baidu.com/s/18Dg8JarnaZrdfYZjWJOg9A?pwd=1234 

# 解压缩
tar -zxf openresty-1.15.8.2.tar.gz

#进入OpenResty目录
cd openresty-1.15.8.2

# 编译安装 
./configure
make && make install
  • 修改配置文件/usr/local/openresty/nginx/conf/nginx.conf,添加如下内容
location /lua{
    default_type 'text/html';
    content_by_lua 'ngx.say("<h1>HELLO,OpenRestry</h1>")';
}
  • 启动Nginx
/usr/local/openresty/nginx/sbin/nginx
  • 通过浏览器访问测试http://192.168.119.161/lua

image-20241018183144108

3、ngx_lua指令

(1)指令执行顺序图
  • 使用Lua编写Nginx脚本的基本构建块是指令。指令用于指定何时运行用户Lua代码以及如何使用结果。下图显示了执行指令的顺序。

image-20241018183536662

(2)*的作用
  • *:无 , 即 xxx_by_lua ,指令后面跟的是 lua指令
  • *:_file,即 xxx_by_lua_file 指令后面跟的是 lua文件
  • *:_block,即 xxx_by_lua_block 在0.9.17版后替换init_by_lua_file
(3)执行明细说明
  • *init_by_lua:**该指令在每次Nginx重新加载配置时执行,可以用来完成一些耗时模块的加载,或者初始化一些全局配置。

  • *init_worker_by_lua:**该指令用于启动一些定时任务,如心跳检查、定时拉取服务器配置等。

  • *set_by_lua:**该指令只要用来做变量赋值,这个指令一次只能返回一个值,并将结果赋值给Nginx中指定的变量。

  • *rewrite_by_lua:**该指令用于执行内部URL重写或者外部重定向,典型的如伪静态化URL重写,本阶段在rewrite处理阶段的最后默认执行。

  • *access_by_lua:**该指令用于访问控制。例如,如果只允许内网IP访问。

  • *content_by_lua:**该指令是应用最多的指令,大部分任务是在这个阶段完成的,其他的过程往往为这个阶段准备数据,正式处理基本都在本阶段。

  • *header_filter_by_lua:**该指令用于设置应答消息的头部信息。

  • *body_filter_by_lua:**该指令是对响应数据进行过滤,如截断、替换。

  • *log_by_lua:**该指令用于在log请求处理阶段,用Lua代码处理日志,但并不替换原有log处理。

  • *balancer_by_lua:**该指令主要的作用是用来实现上游服务器的负载均衡器算法

  • **ssl_certificate_by_*:**该指令作用在Nginx和下游服务开始一个SSL握手操作时将允许本配置项的Lua代码。

4、常用API

(1)ngx.say
  • 返回结果给客户端。
location / {
    default_type 'text/plain';
    content_by_lua_block {
        ngx.say("Hello World")
    }
}
(2)ngx.print
  • 将输入参数合并发送给 HTTP 客户端 (作为 HTTP 响应体)。如果此时还没有发送响应头信息,本函数将先发送 HTTP 响应头,再输出响应体。
  • 语法:ok, err = ngx.print(...)
    • 其中 ok 存储着输出的内容,如果输出失败,err 存储失败的原因。
    • 本函数为异步调用,将立即返回,不会等待所有数据被写入系统发送缓冲区。要以同步模式运行,请在调用 ngx.print 之后调用 ngx.flush。
local table = {
     "hello, ",
     {"world: ", true, " or ", false,
         {": ", nil}}
 }
ok, err = ngx.print(table)

# 输出
hello, world: true or false: nil
(3)ngx.flush
  • 向客户端刷新响应输出。
  • 语法:ok, err = ngx.flush(wait)
    • ngx.flush 接受一个布尔型可选参数 wait (默认值 false)。当通过默认参数(false)调用时,本函数发起一个异步调用。当把 wait 参数设置为 true 时,本函数将以同步模式执行。
    • 异步调用下,直接将数据返回,不等待输出数据被写入系统发送缓冲区。
    • 同步模式下,本函数不会立即返回,一直到所有输出数据被写入系统输出缓冲区,或到达发送超时 send_timeout 时间。
  • 这个要和上方的 ngx.print 进行配合使用,开启同步模式,可以优化返回客户端多条数据的速度。
local table = {
     "hello, ",
     {"world: ", true, " or ", false,
         {": ", nil}}
 }
ok, err = ngx.print(table)
ngx.flush(true) 		-- 开启同步模式
(4)ngx.arg
  • 获取定义的变量。
  • 语法:ngx.arg[n]
 location /foo {
     set $a 32;
     set $b 56;
     sum = ngx.arg[1] + ngx.arg[2]  # 等价于 $a + $b
     echo $sum;# 88
 }
(5)ngx.var
  • 读写 Nginx 变量值。

  • 语法:ngx.var.xxx

location /foo {
    set $my_var '';  # 创建 $my_var 变量
    content_by_lua '
        ngx.var.my_var = 123;  # 使用 $my_var 变量
    ';
}
(6)ngx.log
  • 输出到日志中。
  • 语法:ngx.log(ngx.level,...)
  • ngx.level有:
    • ngx.STDERR
    • ngx.EMERG
    • ngx.ALERT
    • ngx.CRIT
    • ngx.ERR
    • ngx.WARN
    • ngx.NOTICE
    • ngx.INFO
    • ngx.DEBUG
  • print:
    • 将参数值以 ngx.NOTICE 日志级别写入 Nginx 的 error.log 文件。
    • 语法:print(...)
ngx.log(ngx.NOTICE, "Hello")

# 等价于

print("Hello")
(7)ngx.ctx
  • 一个 location 模块里的全局环境变量,存储基于请求的 Lua 环境数据。
  • 语法:ngx.ctx.xxx
location /sub {
    content_by_lua '
        ngx.say("sub pre: ", ngx.ctx.blah)
        ngx.ctx.blah = 32
        ngx.say("sub post: ", ngx.ctx.blah)
    ';
}

location /main {
    content_by_lua '
        ngx.ctx.blah = 73
        ngx.say("main pre: ", ngx.ctx.blah)
        local res = ngx.location.capture("/sub")
        ngx.print(res.body)
        ngx.say("main post: ", ngx.ctx.blah)
    ';
}
(8)ngx.exit
  • 退出某个阶段,如处理请求阶段、重定向阶段等。

  • 语法:ngx.exit(status)。

    • status 参数可以是 ngx.OK,ngx.ERROR 等等或者 HTTP 状态常量。
  • HTTP 状态常量

    • ngx.HTTP_OK(等于 200)
    • ngx.HTTP_CREATED(等于 201)
    • ngx.HTTP_SPECIAL_RESPONSE(等于 300)
    • ngx.HTTP_MOVED_PERMANENTLY(等于 301)
    • ngx.HTTP_MOVED_TEMPORARILY(等于 302)
    • ngx.HTTP_SEE_OTHER(等于 303)
    • ngx.HTTP_NOT_MODIFIED(等于 304)
    • ngx.HTTP_BAD_REQUEST (等于 400)
    • ngx.HTTP_UNAUTHORIZED (等于 401)
    • ngx.HTTP_FORBIDDEN(等于 403)
    • ngx.HTTP_NOT_FOUND(等于 404)
    • ngx.HTTP_NOT_ALLOWED(等于 405)
    • ngx.HTTP_GONE(等于 410)
    • ngx.HTTP_INTERNAL_SERVER_ERROR(等于 500)
    • ngx.HTTP_METHOD_NOT_IMPLEMENTED (等于 501)
    • ngx.HTTP_SERVICE_UNAVAILABLE(等于 503)
    • ngx.HTTP_GATEWAY_TIMEOUT(等于 504) (v0.3.1rc38 版本加入)
ngx.status = ngx.HTTP_GONE
ngx.say("This is our own content")

# 退出整个请求而不是当前处理阶段
ngx.exit(ngx.HTTP_OK)
ngx.exit(501)
(9)ngx.sleep
  • 无阻塞地休眠特定秒。时间可以精确到 0.001 秒 (毫秒)。
  • 语法:ngx.sleep(seconds)。
ngx.sleep(1000)

5、请求API

(1)ngx.req.get_uri_args
  • 返回一个 Lua table,包含当前请求的所有 URL 查询参数。
  • 语法:args = ngx.req.get_uri_args([max_args])
location = /test {
     content_by_lua '
         local args = ngx.req.get_uri_args()
         for key, val in pairs(args) do
             if type(val) == "table" then
                 ngx.say(key, ": ", table.concat(val, ", "))
             else
                 ngx.say(key, ": ", val)
             end
         end
     ';
 }
  • 访问 GET /test?foo=bar&bar=baz&bar=blah 将输出:
 foo: bar
 bar: baz, blah
  • 多次出现同一个参数 key 时,将生成一个 Lua table,按顺序保存其所有 value。
(2)ngx.req.set_uri_args
  • 用 args 参数重写当前请求的 URI 请求参数。
  • 语法:ngx.req.set_uri_args(args)。
ngx.req.set_uri_args("a=3&b=hello%20world")

# 在第二种情况下,本方法将根据 URI 转义规则转义参数的 key 和 value。
ngx.req.set_uri_args({ a = 3, b = "hello world" })

# 在第三种情况下,请求参数字符串为 a=3&b=5&b=6。
ngx.req.set_uri_args({ a = 3, b = {5, 6} })
(3)ngx.header.HEADER
  • 修改、添加、或清除当前请求待发送的 HEADER 响应头信息。
  • 语法:ngx.header.HEADER = VALUE。
  • HEADER 响应头信息不是自定义的,是请求头带有的。
# 与 ngx.header["Content-Type"] = 'text/plain' 相同
 ngx.header.content_type = 'text/plain';

 ngx.header["X-My-Header"] = 'blah blah'
(4)ngx.req.get_method
  • 获取当前请求的 HTTP 请求方法名称。结果为类似 “GET” 和 “POST” 的字符串。
  • 语法:ngx.req.get_method。
(5)ngx.req.set_method
  • 用 method_id 参数的值改写当前请求的 HTTP 请求方法。
  • 当前仅支持 HTTP 请求方法 中定义的数值常量。
    • ngx.HTTP_GET
    • ngx.HTTP_HEAD
    • ngx.HTTP_PUT
    • ngx.HTTP_POST
    • ngx.HTTP_DELETE
    • ngx.HTTP_OPTIONS (v0.5.0rc24 版本加入)
    • ngx.HTTP_MKCOL(v0.8.2 版本加入)
    • ngx.HTTP_COPY(v0.8.2 版本加入)
    • ngx.HTTP_MOVE(v0.8.2 版本加入)
    • ngx.HTTP_PROPFIND (v0.8.2 版本加入)
    • ngx.HTTP_PROPPATCH(v0.8.2 版本加入)
    • ngx.HTTP_LOCK(v0.8.2 版本加入)
    • ngx.HTTP_UNLOCK (v0.8.2 版本加入)
    • ngx.HTTP_PATCH (v0.8.2 版本加入)
    • ngx.HTTP_TRACE (v0.8.2 版本加入)
  • 语法:ngx.req.set_method(method_id)。
ngx.req.set_method(method_id)

ngx.req.set_method(ngx.HTTP_GET)
(6)ngx.req.read_body
  • 同步读取客户端请求体,不阻塞 Nginx 事件循环。
  • 语法:ngx.req.read_body()。
(7)ngx.req.get_post_args
  • 返回一个 Lua table,包含当前请求的所有 POST 查询参数。
  • 语法:args, err = ngx.req.get_post_args(max_args?)
  • 注意:使用 ngx.req.get_post_args 获取参数前,必须使用 ngx.req.read_body 读取请求体。
 location = /test {
     content_by_lua '
         ngx.req.read_body()
         local args, err = ngx.req.get_post_args()
         if not args then
             ngx.say("failed to get post args: ", err)
             return
         end
         for key, val in pairs(args) do
             if type(val) == "table" then
                 ngx.say(key, ": ", table.concat(val, ", "))
             else
                 ngx.say(key, ": ", val)
             end
         end
     ';
 }
  • 测试请求
 # Post request with the body 'foo=bar&bar=baz&bar=blah'
 $ curl --data 'foo=bar&bar=baz&bar=blah' localhost/test
  • 输出结果:多次出现同一个参数 key 时,将生成一个 Lua table,按顺序保存其所有 value。
 foo: bar
 bar: baz, blah
(8)ngx.redirect
  • 发出一个 HTTP 301 或 302 重定向到 uri。
  • 可选项 status 参数指定 301 或 302 哪个被使用。 默认使用 302。
  • 语法:ngx.redirect(uri, [status])。
return ngx.redirect("/foo")

# 等价于
return ngx.redirect("/foo", 301)

# 等价于
return ngx.redirect("/foo", ngx.HTTP_MOVED_TEMPORARILY)

# 重定向到任意外部 URL 也是支持的,例如:
return ngx.redirect("http://www.baidu.com")
(9)ngx.exec
  • 使用 uri、args 参数执行一个内部跳转。内部指的是 Nginx 的某个 location 模块。
  • 语法:ngx.exec(uri, [args])
  • 注意:ngx.exec 方法与ngx.redirect是完全不同的,前者是个纯粹的内部跳转并且没有引入任何额外 HTTP 信号。此方法的调用终止当前请求的处理。
  • 访问 GET /foo/file.php?a=hello,将返回"hello“,而不是”goodbye“
location /foo {
     content_by_lua '
         ngx.exec("/bar", "a=goodbye");
     ';
 }

 location /bar {
     content_by_lua '
         local args = ngx.req.get_uri_args()
         for key, val in pairs(args) do
             if key == "a" then
                 ngx.say(val)
             end
         end
     ';
 }
(10)ngx.location.capture
  • 向 uri 发起一个同步非阻塞 Nginx 子请求。 它会请求 Nginx 的其他 location 模块,location 模块可以是其他文件目录的配置文件中,或任何其他 Nginx 模块。

  • 语法:res=ngx.location.capture(uri,[options])。

    • res 是返回的结果,它是一个「对象」,将包含四个元素的 Lua 表 (res.status, res.header, res.body, 和 res.truncated)。

      • res.status (状态) 保存子请求的响应状态码。

      • res.header (头) 用一个标准 Lua 表储子请求响应的所有头信息。如果是“多值”响应头,这些值将使用 Lua (数组) 表顺序存储。

    • options 选项

      • method:指定子请求的请求方法, 只接受类似 ngx.HTTP_POST 的常量,method 选项默认值是 ngx.HTTP_GET。
      • body:指定子请求的请求体 (仅接受字符串值)
      • args:指定子请求的 URI 请求参数 (可以是字符串或者 Lua 表)
      • ctx:指定一个 Lua 表作为子请求的 ngx.ctx 表,可以是当前请求的 ngx.ctx 表
      • vars:用一个 Lua 表设置子请求中的 Nginx 变量值
      • copy_all_vars:设置是否复制所有当前请求的 Nginx 变量值到子请求中,修改子请求的 nginx 变量值不影响当前 (父) 请求
      • share_all_vars:设置是否共享所有当前 (父) 请求的 Nginx 变量值到子请求中,修改子请求的 nginx 变量值将影响当前 (父) 请求
      • always_forward_body:当设置为 true 时,如果没有设置 body 选项,当前 (父) 请求的请求体将被转发给子请求
  • ngx.exec vs ngx.location.capture

    • ngx.exec:只会访问同一个配置文件的 location 模块。
    • ngx.location.capture:不仅如此,还可以访问其他配置文件的 location 模块。
 res = ngx.location.capture(
     '/foo/bar',
     { method = ngx.HTTP_POST, args = { a = 1,b = 3}, body = 'Hello,World' }
 )

# 等价于
res = ngx.location.capture('/foo/bar?a=1&b=3')
(11)ngx.req.set_uri
  • 语法:ngx.req.set_uri(uri, jump?)

    • uri:重写当前请求的 uri;

    • jump:表明是否进行 locations 的重新匹配。

      • jump 为 true 时:调用 ngx.req.set_uri 后,Nginx 将会根据修改后的 uri,重新匹配新的 locations;等价于 rewrite…last

      • jump 为 false时:将不会进行 locations 的重新匹配,而仅仅是修改了当前请求的 URI 而已。jump 的默认值为 false。等价于 rewrite…break

ngx.req.set_uri("/foo", true)  === rewrite ^ /foo last;

ngx.req.set_uri("/foo", false)  ===  rewrite ^ /foo break;

6、指令API

(1)init_by_lua
  • 该指令在每次 Nginx 重新加载配置时执行,可以用来完成一些耗时模块的加载,或者初始化一些全局配置。
  • 这是一个公共模块,把所有都用到的代码放到这个模块里,避免重复使用相同的代码。
  • 比如每个模块都需要 MySQL 和 Redis,则在这个公共模块进行引用。
init_by_lua_block{
    mysql = require "resty.mysql"
	redis = require "resty.redis"
}
# 下方直接使用 MySQL 和 Redis 的 API
  • 如果不喜欢直接写 Lua 语法,把 Lua 语法 放到 Lua 文件里,使用 init_by_lua_file 引用 Lua 文件。
(2)init_worker_by_lua
  • 该指令用于启动一些定时任务,如心跳检查、定时拉取服务器配置等。
 init_worker_by_lua '
     local delay = 3  -- in seconds
     local new_timer = ngx.timer.at
     local log = ngx.log
     local ERR = ngx.ERR
     local check

     check = function(premature)
         if not premature then
             -- do the health check or other routine work
             local ok, err = new_timer(delay, check)
             if not ok then
                 log(ERR, "failed to create timer: ", err)
                 return
             end
         end
     end

     local ok, err = new_timer(delay, check)
     if not ok then
         log(ERR, "failed to create timer: ", err)
         return
     end
 ';
  • 如果不喜欢直接写 Lua 语法,把 Lua 语法 放到 Lua 文件里,使用 init_worker_by_lua_file 引用 Lua 文件。
(3)set_by_lua
  • 该指令只要用来做变量赋值,这个指令一次只能返回一个值,并将结果赋值给 Nginx 中指定的变量。
  • 语法:set_by_lua* <key> <value>,key要加上 $ 符号,value 是 Lua 语言的格式。
# 此时 key 为 $name 的 value 值是 URL 的参数 name 加上「先生」。
set_by_lua $name "
		local uri_args = ngx.req.get_uri_args()   -- 获取请求 ? 后的参数
		name = uri_args['name']   -- 获取 key 为 name 的参数
		return name..'先生'   -- 在 name 后面加上 先生,作为 $name 的 value 返回给客户端
	";
  • 如果不喜欢直接写 Lua 语法,把 Lua 语法 放到 Lua 文件里,使用 set_by_lua_file 引用 Lua 文件。
(4)rewrite_by_lua
  • 该指令用于执行内部 URL 重写或者外部重定向,典型的如伪静态化 URL 重写,本阶段在 Rewrite 处理阶段的最后默认执行。
location /foo {
    set $a 12; # 创建变量 $a
    set $b ""; # 创建变量 $b
    rewrite_by_lua '
         ngx.var.b = tonumber(ngx.var.a) + 1  # 此时 b = 13
         if tonumber(ngx.var.b) == 13 then
             return ngx.redirect("/bar");   # 重定向到 /bar
         end
     ';
    echo "res = $b";  # res = 13
}
  • 如果不喜欢直接写 Lua 语法,把 Lua 语法 放到 Lua 文件里,使用 rewrite_by_lua_file 引用 Lua 文件。
(5)access_by_lua
  • 该指令用于访问控制。例如,如果只允许内网 IP 访问。
  • 注意,在 access_by_lua 处理内部,当调用 ngx.exit(ngx.OK) 时,nginx 请求将继续下一阶段的内容处理。要在 access_by_lua 处理中终结当前请求,调用 ngx.exit ,成功的请求设定 status >= 200 (ngx.HTTP_OK) 并 status < 300 (ngx.HTTP_SPECIAL_RESPONSE),失败的请求设定ngx.exit(ngx.HTTP_INTERNAL_SERVER_ERROR) (或其他相关的)。
location / {
    access_by_lua '
        local res = ngx.location.capture("/auth")

        if res.status == ngx.HTTP_OK then
        return
        end

        if res.status == ngx.HTTP_FORBIDDEN then
        ngx.exit(res.status)
        end

        ngx.exit(ngx.HTTP_INTERNAL_SERVER_ERROR)
        ';

        # proxy_pass/fastcgi_pass/postgres_pass/...
}
  • 如果不喜欢直接写 Lua 语法,把 Lua 语法 放到 Lua 文件里,使用 access_by_lua_file 引用 Lua 文件。
(6)content_by_lua
  • 该指令是应用最多的指令,大部分任务是在这个阶段完成的,其他的过程往往为这个阶段准备数据,正式处理基本都在本阶段。
  • 这个指令就相当于 Java 的一个方法,所有的代码都需要一个方法体作为环境。
content_by_lua_block {
    set_by_lua $name "
        local uri_args = ngx.req.get_uri_args()   -- 获取请求 ? 后的参数
        name = uri_args['name']   -- 获取 key 为 name 的参数
        return name..'先生'   -- 在 name 后面加上 先生,作为 $name 的 value 返回给客户端
    ";
}
  • 如果不喜欢直接写 Lua 语法,把 Lua 语法 放到 Lua 文件里,使用 content_by_lua_file 引用 Lua 文件。
(7)header_filter_by_lua
  • 该指令用于设置应答消息的头部信息。
# 你会在请求头看到 name 为 frx
location / {
	 proxy_pass http://mybackend;
	 header_filter_by_lua 'ngx.header.username = "frx"';
}
  • 如果不喜欢直接写 Lua 语法,把 Lua 语法 放到 Lua 文件里,使用 header_filter_by_lua_file 引用 Lua 文件。
(8)body_filter_by_lua
  • 该指令是对响应数据进行过滤,如截断、替换。
# 在输出体转换所有的大写字母,我们可以这样用:
 location / {
     proxy_pass http://mybackend;
     body_filter_by_lua 'ngx.arg[1] = string.upper(ngx.arg[1])';  # 转大写
 }
  • 如果不喜欢直接写 Lua 语法,把 Lua 语法 放到 Lua 文件里,使用 body_filter_by_lua_file 引用 Lua 文件。
(9)log_by_lua
  • 该指令用于在 log 请求处理阶段,用 Lua 代码处理日志,但并不替换原有 log 处理。
server {
    location / {
        proxy_pass http://mybackend;

        log_by_lua '
            local log_dict = ngx.shared.log_dict
            local upstream_time = tonumber(ngx.var.upstream_response_time)

            local sum = log_dict:get("upstream_time-sum") or 0
            sum = sum + upstream_time
            log_dict:set("upstream_time-sum", sum)

            local newval, err = log_dict:incr("upstream_time-nb", 1)
            if not newval and err == "not found" then
            log_dict:add("upstream_time-nb", 0)
            log_dict:incr("upstream_time-nb", 1)
            end
            ';
    }
}
  • 如果不喜欢直接写 Lua 语法,把 Lua 语法 放到 Lua 文件里,使用 log_by_lua_file 引用 Lua 文件。
(10)balancer_by_lua
  • 该指令主要的作用是用来实现上游服务器的负载均衡器算法。
http {
    upstream myapp {
        server 192.168.1.1;
        server 192.168.1.2;
        server 192.168.1.3;

        balancer_by_lua '
            local idx = tonumber(ngx.shared.my_cache:get("balancer_idx") or "0")
            local servers = {"192.168.1.1:80", "192.168.1.2:80", "192.168.1.3:80"}
            local peer = servers[idx + 1]
            ngx.shared.my_cache:set("balancer_idx", (idx % 3) + 1)
            ngx.balancer.set_current_peer(peer)
        ';
    }

    server {
        listen 80;
        location / {
            proxy_pass http://myapp;
        }
    }
}
(11)ssl_certificate_by_lua
  • 该指令作用在 Nginx 和下游服务开始一个 SSL 握手操作时将允许本配置项的 Lua 代码。
server {
    listen 443 ssl;
    server_name example.com;

    # 定义 SSL 证书和私钥文件的路径
    ssl_certificate /path/to/cert.pem;
    ssl_certificate_key /path/to/key.pem;

    # 使用 ssl_certificate_by_lua 指令执行 Lua 代码
    ssl_certificate_by_lua '
        -- 获取 SNI 服务器名称
        local server_name = ngx.var.server_name

        -- 根据 SNI 服务器名称动态选择证书和私钥
        if server_name == "example.com" then
            ngx.ssl_certificate("/path/to/example_com_cert.pem")
            ngx.ssl_certificate_key("/path/to/example_com_key.pem")
        elseif server_name == "www.example.com" then
            ngx.ssl_certificate("/path/to/www_example_com_cert.pem")
            ngx.ssl_certificate_key("/path/to/www_example_com_key.pem")
        else
            ngx.ssl_certificate("/path/to/default_cert.pem")
            ngx.ssl_certificate_key("/path/to/default_key.pem")
        end
    ';
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/897143.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

ECharts饼图-饼图纹理,附视频讲解与代码下载

引言&#xff1a; 在数据可视化的世界里&#xff0c;ECharts凭借其丰富的图表类型和强大的配置能力&#xff0c;成为了众多开发者的首选。今天&#xff0c;我将带大家一起实现一个饼图图表&#xff0c;通过该图表我们可以直观地展示和分析数据。此外&#xff0c;我还将提供详…

信号(二)【信号的产生】

目录 1. 键盘组合键2. kill 命令3. 系统调用4. 异常5. 软件条件6. Term 和 Core 的区别 本篇文章介绍五种信号产生的方式&#xff0c;键盘组合键、kill 命令、系统调用、代码异常&#xff08;进程异常&#xff09;、软件条件来产生信号。 1. 键盘组合键 信号&#xff08;一&a…

商汤科技十周年公布新战略,将无缝集成算力、模型及应用

10月18日&#xff0c;恰逢商汤科技十周年庆典&#xff0c;“2024商汤十周年国际论坛&#xff1a;迈向AI 2.0共融新时代”在香港科学园成功举办。 据「TMT星球」了解&#xff0c;来自全球的行业领袖、政府代表、AI专家共聚于此&#xff0c;共同探讨AI行业的未来。 活动上&…

Linux隐藏权限介绍

隐藏权限概览 在Linux系统中&#xff0c;有时即便是以root用户身份&#xff0c;你也可能遇到无法修改特定文件的情况。这种限制往往源自chattr命令的应用&#xff0c;该命令用于为文件或目录设置“隐藏权限”&#xff0c;即底层属性&#xff0c;以增强系统安全性。值得注意的是…

Standard IO

为了提高可移植性&#xff0c;将通用IO接口经过再封装就形成了标准IO&#xff0c;标准IO不仅适用于Unix环境&#xff0c;也兼容非Unix环境&#xff0c;这也是为什么说我们应该尽可能的使用标准IO&#xff0c;通用IO通过文件描述符fd来与文件交互&#xff0c;为了以示区分&#…

极氪MIX:一台只有你想不到,没有它做不到的“家用神车”

了解极氪品牌的朋友应该都知道 极氪一直都在尝试打破目前汽车或者生活的一些现状 更愿意创造一些破界、超前的产品 比如说将家庭城市通勤、假日露营、自驾旅行、户外垂钓、朋友相聚等多场景融入一个空间的极氪MIX 这款车突破了SUV或MPV车型形态的固有限制 前悬仅 865mm&am…

【ArcGIS Pro实操第八期】绘制WRF三层嵌套区域

【ArcGIS Pro实操第八期】绘制WRF三层嵌套区域 数据准备ArcGIS Pro绘制WRF三层嵌套区域Map-绘制三层嵌套区域更改ArcMap地图的默认显示方向指定数据框范围 Map绘制研究区Layout-布局出图 参考 本博客基于ArcGIS Pro绘制WRF三层嵌套区域&#xff0c;具体实现图形参考下图&#x…

Centos安装Nginx 非Docker

客户的机器属于 Centos7 系列&#xff0c;由于其较为陈旧&#xff0c;2024开始众多镜像和软件源都已失效。此篇文章将详细记录在 Centos7 操作系统上从零开始安装 Nginx 的整个流程。 本文Nginx是安装在/usr/local/nginx下 详细步骤如下&#xff1a; 准备Nginx安装包&#x…

安防监控摄像头图传模组,1公里WiFi无线传输方案,监控新科技

在数字化浪潮汹涌的今天&#xff0c;安防监控领域也迎来了技术革新的春风。今天&#xff0c;我们就来聊聊这一领域的产品——摄像头图传模组&#xff0c;以及它如何借助飞睿智能1公里WiFi无线传输技术&#xff0c;为安防监控带来未有的便利与高效。 一、安防监控的新篇章 随着…

程序员适合玩的游戏:《人力资源机器》提升编程思维【Human Resource Machine】

程序员适合玩的游戏&#xff1a;《人力资源机器》提升编程思维【Human Resource Machine】 在当今这个技术日新月异的时代&#xff0c;编程已经成为一门不可或缺的技能。对于程序员来说&#xff0c;不仅需要扎实的专业知识&#xff0c;还需要不断锻炼逻辑思维和解决问题的能力…

用.NET开发跨平台应用程序采用 Avalonia 与MAUI如何选择

Avalonia是一个强大的框架&#xff0c;使开发人员能够使用.NET创建跨平台应用程序。它使用自己的渲染引擎绘制UI控件&#xff0c;确保在Windows、macOS、Linux、Android、iOS和WebAssembly等不同平台上具有一致的外观和行为。这意味着开发人员可以共享他们的UI代码&#xff0c;…

RNN、LSTM 与 Bi-LSTM

一. RNN 循环神经网络&#xff08;Recurrent Neural Network, RNN&#xff09;是深度学习领域一类具有内部自连接的神经网络能够学习复杂的矢量到矢量的映射。 最大特点&#xff1a;前面的序列数据可以用作后面的结果预测中。 一个简单的循环神经网络结构&#xff0c;其结构包…

如何写一个视频编码器演示篇

先前写过《视频编码原理简介》&#xff0c;有朋友问光代码和文字不太真切&#xff0c;能否补充几张图片&#xff0c;今天我们演示一下&#xff1a; 这是第一帧画面&#xff1a;P1&#xff08;我们的参考帧&#xff09; 这是第二帧画面&#xff1a;P2&#xff08;需要编码的帧&…

Golang | Leetcode Golang题解之第480题滑动窗口中位数

题目&#xff1a; 题解&#xff1a; type hp struct {sort.IntSlicesize int } func (h *hp) Push(v interface{}) { h.IntSlice append(h.IntSlice, v.(int)) } func (h *hp) Pop() interface{} { a : h.IntSlice; v : a[len(a)-1]; h.IntSlice a[:len(a)-1]; return v }…

SCCB协议与IIC协议不同

SCCB开始信号与结束信号都与IIC协议的大概一致&#xff0c;这里就不细讲了 开始、结束信号参考&#xff1a;【I2C】IIC读写时序_iic读时序-CSDN博客 SSCB写时序&#xff1a; 即&#xff1a;start phase_1 phase_2 phase_3 stop SCCB读时序&#xff1a; 即&#xff…

电脑视频剪辑大比拼,谁更胜一筹?

随着短视频的火爆&#xff0c;越来越多的人开始尝试自己动手制作视频&#xff0c;无论是记录生活点滴还是创作个性短片&#xff0c;一款好用的视频剪辑软件是必不可少的。今天&#xff0c;我们就从短视频运营的角度&#xff0c;来聊聊几款热门的电脑视频剪辑软件&#xff0c;看…

在做题中学习(66):两数相加

解法&#xff1a;模拟 思路&#xff1a;定义一个变量t&#xff0c;存储相加后的结果&#xff0c;个位赋给新节点&#xff0c;十位&#xff08;表示有进位&#xff09;留下&#xff0c;累加到下一次加法&#xff08;相当于上进位&#xff09;。while里即便cur1和cur2都为空了&a…

windows文件拷贝给wsl2的Ubuntu

参考&#xff1a; windows文件如何直接拖拽到wsl中_win 移到文件到wsl-CSDN博客 cp -r /mnt/盘名/目标文件 要复制到wsl中的位置e.g.cp -r /mnt/d/byt5 /home Linux文件复制、移动、删除等操作命令_linux移动命令-CSDN博客 Linux 文件、文件夹的复制、移动、删除 - Be-myse…

重生之“我打数据结构,真的假的?”--1.顺序表(无习题)

C语言中的顺序表详细总结 1. 概述 顺序表&#xff08;Sequential List&#xff09;是一种线性数据结构&#xff0c;用于存储具有相同数据类型的一组元素。顺序表采用一段连续的存储空间&#xff0c;使用数组来实现&#xff0c;能够高效地支持随机访问操作。在 C 语言中&#…

No.19 笔记 | WEB安全 - 任意文件操作详解 part 1

1. 任意文件上传漏洞基础 什么是文件上传功能? 在网站和应用中,我们经常会看到允许用户上传文件的功能,比如: 更换头像:让用户上传自己的照片作为头像发布图片:在社交媒体或论坛上传图片提交文档:在办公系统中上传Word、Excel等文档 这些都是常见的文件上传功能。 任意文…