BoyChai's Blog - Lua https://blog.boychai.xyz/index.php/category/Lua/ [Nginx]ngx_lua模块 https://blog.boychai.xyz/index.php/archives/71/ 2024-04-25T00:51:00+00:00 概述淘宝开发的ngx_lua模块通过将lua解释器解释器集成Nginx,可以采用lua脚本实现业务逻辑,由于lua的紧凑、快速以及内建协程,所以在保证高并发服务能力的同时极大降低了业务逻辑实现成本。安装方式1(已弃用)lua-nginx-moduleLuaJIT是采用C语言编写的Lua代表的解释器。官网: http://luajit.org在官网找到对应下载地址: https://github.com/LuaJIT/LuaJIT/tags[root@work env]# wget https://github.com/LuaJIT/LuaJIT/archive/refs/tags/v2.0.5.tar.gz [root@work env]# tar xvf v2.0.5.tar.gz [root@work env]# cd LuaJIT-2.0.5/ [root@work LuaJIT-2.0.5]# make && make install make[1]: Leaving directory '/opt/env/LuaJIT-2.0.5/src' ==== Successfully built LuaJIT 2.0.5 ==== ==== Installing LuaJIT 2.0.5 to /usr/local ==== mkdir -p /usr/local/bin /usr/local/lib /usr/local/include/luajit-2.0 /usr/local/share/man/man1 /usr/local/lib/pkgconfig /usr/local/share/luajit-2.0.5/jit /usr/local/share/lua/5.1 /usr/local/lib/lua/5.1 cd src && install -m 0755 luajit /usr/local/bin/luajit-2.0.5 cd src && test -f libluajit.a && install -m 0644 libluajit.a /usr/local/lib/libluajit-5.1.a || : rm -f /usr/local/bin/luajit /usr/local/lib/libluajit-5.1.so.2.0.5 /usr/local/lib/libluajit-5.1.so /usr/local/lib/libluajit-5.1.so.2 cd src && test -f libluajit.so && \ install -m 0755 libluajit.so /usr/local/lib/libluajit-5.1.so.2.0.5 && \ ldconfig -n /usr/local/lib && \ ln -sf libluajit-5.1.so.2.0.5 /usr/local/lib/libluajit-5.1.so && \ ln -sf libluajit-5.1.so.2.0.5 /usr/local/lib/libluajit-5.1.so.2 || : cd etc && install -m 0644 luajit.1 /usr/local/share/man/man1 cd etc && sed -e "s|^prefix=.*|prefix=/usr/local|" -e "s|^multilib=.*|multilib=lib|" luajit.pc > luajit.pc.tmp && \ install -m 0644 luajit.pc.tmp /usr/local/lib/pkgconfig/luajit.pc && \ rm -f luajit.pc.tmp cd src && install -m 0644 lua.h lualib.h lauxlib.h luaconf.h lua.hpp luajit.h /usr/local/include/luajit-2.0 cd src/jit && install -m 0644 bc.lua v.lua dump.lua dis_x86.lua dis_x64.lua dis_arm.lua dis_ppc.lua dis_mips.lua dis_mipsel.lua bcsave.lua vmdef.lua /usr/local/share/luajit-2.0.5/jit ln -sf luajit-2.0.5 /usr/local/bin/luajit ==== Successfully installed LuaJIT 2.0.5 to /usr/local ====lua-nginx-modulenginx第三方模块lua-nginx-module官网: https://github.com/openresty/lua-nginx-module[root@work env]# wget https://github.com/openresty/lua-nginx-module/archive/refs/tags/v0.10.26.tar.gz [root@work env]# tar xvf v0.10.26.tar.gz [root@work env]# ln -s lua-nginx-module-0.10.26 lua-nginx-module环境变量设置[root@work ~]# tail -n2 /etc/profile export LUAJIT_LIB=/usr/local/lib export LUAJIT_INC=/usr/local/include/luajit-2.0 [root@work ~]# source /etc/profile扩展nginx模块打开nginx编译安装的位置 进行重新编译安装[root@work nginx-1.24.0]# ./configure --prefix=/usr/local/nginx --sbin-path=/usr/local/nginx/sbin/nginx --conf-path=/usr/local/nginx/conf/nginx.conf --error-log-path=/var/log/nginx/error.log --http-log-path=/var/log/nginx/access.log --pid-path=/var/run/nginx/nginx.pid --lock-path=/var/lock/nginx.lock --user=nginx --group=nginx --with-http_ssl_module --with-http_stub_status_module --with-http_gzip_static_module --http-client-body-temp-path=/var/tmp/nginx/client/ --http-proxy-temp-path=/var/tmp/nginx/proxy/ --http-fastcgi-temp-path=/var/tmp/nginx/fcgi/ --http-uwsgi-temp-path=/var/tmp/nginx/uwsgi --http-scgi-temp-path=/var/tmp/nginx/scgi --with-pcre --add-module=/opt/package/nginx/lua-nginx-module [root@work nginx-1.24.0]# make && make install扩展的重点是--with-pcre --add-module=/opt/package/nginx/lua-nginx-module这里就相当于重新安装了,之前安装的模块还需要再这里再添加一遍错误libluajit-5.1.so.2当在扩展号nginx模块后执行nginx相关命令出现以下错误[root@work ~]# nginx -V nginx: error while loading shared libraries: libluajit-5.1.so.2: cannot open shared object file: No such file or directory这个错误表明 Nginx 在启动时无法找到名为 libluajit-5.1.so.2 的共享库文件。这很可能是由于 Nginx 模块依赖 LuaJIT 库,但系统中缺少了该库所致。解决办法如下[root@work ~]# ln -s /usr/local/lib/libluajit-5.1.so.2 /lib64/liblua-5.1.so.2reason: module 'resty.core' not found[root@work conf]# nginx nginx: [alert] detected a LuaJIT version which is not OpenResty's; many optimizations will be disabled and performance will be compromised (see https://github.com/openresty/luajit2 for OpenResty's LuaJIT or, even better, consider using the OpenResty releases from https://openresty.org/en/download.html) nginx: [alert] failed to load the 'resty.core' module (https://github.com/openresty/lua-resty-core); ensure you are using an OpenResty release from https://openresty.org/en/download.html (reason: module 'resty.core' not found: no field package.preload['resty.core'] no file './resty/core.lua' no file '/usr/local/share/luajit-2.0.5/resty/core.lua' no file '/usr/local/share/lua/5.1/resty/core.lua' no file '/usr/local/share/lua/5.1/resty/core/init.lua' no file './resty/core.so' no file '/usr/local/lib/lua/5.1/resty/core.so' no file '/usr/local/lib/lua/5.1/loadall.so' no file './resty.so' no file '/usr/local/lib/lua/5.1/resty.so' no file '/usr/local/lib/lua/5.1/loadall.so') in /usr/local/nginx/conf/nginx.conf:117原因似乎是缺少lua-resty-core模块,这里手动编译安装一下项目地址: https://github.com/openresty/lua-resty-core[root@work nginx]# tar xvf v0.1.28.tar.gz tar xvf make install安装方式2概述直接使用OpenRestry,它是由淘宝工程师开发的,它是基于Nginx与Lua的高性能Web平台,其内部集成了大量精良的Lua库,第三方模块以及大多数的依赖项,用于方便搭建能够处理高并发、扩展性极高的动态Web应用、Web服务和动态网关。所以本身OpenResty内部就已经集成了Nginx和Lua,我们用起来会更加方便安装参考: https://openresty.org/cn/linux-packages.html配置:/usr/local/openrestry/nginx/conf关于OpenRestryOpenRestry,它是由淘宝工程师开发的,它是基于Nginx与Lua的高性能Web平台,其内部集成了大量精良的Lua库,第三方模块以及大多数的依赖项,用于方便搭建能够处理高并发、扩展性极高的动态Web应用、Web服务和动态网关。所以本身OpenResty内部就已经集成了Nginx和Lua,我们用起来会更加方便。PS:本文只讲ngx_lua的使用,其他的基本和nginx配置无区别。ngx_lua相关指令块使用Lua编写Nginx脚本的基本构建块是指令。指令用于指定何时运行用户Lua代码以及如何使用结果。下图显示了执行指令的顺序。先来解释一下*的作用*:无 , 即 xxx_by_lua ,指令后面跟的是 lua指令 *:_file,即 xxx_by_lua_file 指令后面跟的是 lua文件 *:_block,即 xxx_by_lua_block 在0.9.17版后替换init_by_lua_fileinit_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代码。案例1需求输出内容配置 location /lua { default_type 'text/html'; content_by_lua 'ngx.say("<h1>HELLO,OpenResty</h1>")'; }案例2需求http://xxx/?name=张三&gender=1 Nginx接收到请求后根据gender传入的值,如果是gender传入的是1,则展示张三先生,如果是0则展示张三女士,如果都不是则展示张三。配置 location /getByGender { default_type 'text/html'; set_by_lua $param " -- 获取请求URL上的参数对应的值 local uri_args = ngx.req.get_uri_args() local name = uri_args['name'] local gender = uri_args['gender'] -- 条件判断 if gender 1 先生 0 女士 if gender == '1' then return name..'先生' elseif gender == '0' then return name..'女士' else return name end "; # 解决中文乱码 charset utf-8; # 返回数据 return 200 $param; }ngx.req.get_uri_args()返回的是一个table类型案例3需求动态获取docker容器ip,做代理配置server{ listen 80; server_name code.boychai.xyz; client_max_body_size 4096M; set_by_lua $param ' local name = "gitea" local port = "3000" local command = string.format("echo -n `docker inspect --format=\'{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}\' %s`", name) local handle = io.popen(command) local result = handle:read("*a") handle:close() return "http://"..result..":"..port '; location / { if ( $param = 'http://:3000' ) { return 500 "Error in obtaining site IP"; } proxy_pass $param; proxy_set_header Host $proxy_host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } } [Lua]快速入门 https://blog.boychai.xyz/index.php/archives/69/ 2024-04-17T08:01:00+00:00 Lua概念Lua是一种轻量、小巧的脚本语言,用标准的C语言编写并以源代码形式开发。设计目的是为了嵌入其他的程序中,从而为应用程序提供灵活的扩展和定制功能。特性和他语言相比,Lua有其自身的特点:(1)轻量级lua用标准C语言编写并以源代码形式开发,编译后仅仅一百余千字节,可以很方便的嵌入道其他程序中。(2)可扩展lua提供非常丰富易于使用的扩展接口和机制,由宿主语言(通常是C或C++)提供功能,lua可以使用它们,就像内置的功能一样。(3)支持面向过程编程和函数式编程应用场景游戏开发、独立应用脚本、web应用脚本、扩展和数据库插件、系统安全上。安装官网: https://www.lua.org/[root@work env]# wget https://www.lua.org/ftp/lua-5.4.6.tar.gz [root@work env]# tar xvf lua-5.4.6.tar.gz [root@work lua-5.4.6]# make linux test [root@work lua-5.4.6]# make install cd src && mkdir -p /usr/local/bin /usr/local/include /usr/local/lib /usr/local/man/man1 /usr/local/share/lua/5.4 /usr/local/lib/lua/5.4 cd src && install -p -m 0755 lua luac /usr/local/bin cd src && install -p -m 0644 lua.h luaconf.h lualib.h lauxlib.h lua.hpp /usr/local/include cd src && install -p -m 0644 liblua.a /usr/local/lib cd doc && install -p -m 0644 lua.1 luac.1 /usr/local/man/man1 [root@work lua-5.4.6]# lua -v Lua 5.4.6 Copyright (C) 1994-2023 Lua.org, PUC-Rio语法他的语法和C/C++语法非常相似,整体上比较清晰,简洁。条件语句、循环语句、函数调用都与C/C++基本一致。交互式HelloWorld[root@work env]# lua Lua 5.4.6 Copyright (C) 1994-2023 Lua.org, PUC-Rio > print('hello world!!') hello world!! > 脚本式HelloWorld第一种方式[root@work ~]# mkdir lua_demo [root@work ~]# cd lua_demo/ [root@work lua_demo]# vim hello.lua [root@work lua_demo]# cat hello.lua print('hello world!!!') [root@work lua_demo]# lua hello.lua hello world!!!第二种方式[root@work lua_demo]# vim hello.lua [root@work lua_demo]# cat hello.lua #! /usr/local/bin/lua print('hello world!!!') [root@work lua_demo]# chmod +x hello.lua [root@work lua_demo]# ./hello.lua hello world!!!注释%% 单行注释 %% -- print("111") %% 多行注释 %% --[[ print("222") --]] %% 取消多行注释 %% ---[[ print("222") --]]测试[root@work lua_demo]# vim demo2.lua [root@work lua_demo]# cat demo2.lua -- print("111") --[[ print("222") --]] ---[[ print("333") --]] [root@work lua_demo]# lua demo2.lua 333标识符标识符就是变量名,Lua定义变量名以 一个字母A到Z或a到z或下划线_开头后加上0个或者多个字母,下划线,数字(0-9)。这块建议最好不要使用下划线加大写字母的标识符,因为Lua的保留字也是这样定义的,容易发生冲突。注意Lua是区分大小写字母的。关键字下面Lua的关键词,大家在定义常量、变量或其他用户定义标识符都要避免使用一下关键字andbreakdoelseelseifendfalseforfunctionifinlocalnilnotorrepeatreturnthentrueuntilwhilegoto 一般约定,一以下划线开头连接一串大写字母的名字(比如_VERSION)被保留用于Lua内部全局变量。这个也是上面我们不建议这么定义标识符的原因运算符Lua中支持的运算符有算数运算符、关系运算符、逻辑运算符、其他运算符。算数运算符+ 加 - 减 * 乘 / 除 % 取余 ^ 乘幂 - 负号关系运算符== 等于 ~= 不等于 > 大于 < 小于 >= 大于等于 <= 小于等于逻辑运算符and 与 同时true返回true or 或 一个true返回true not 非 取反其他运算符.. 连接两个字符串 # 一元预算法,返回字符串或表的长度例如[root@work lua_demo]# lua Lua 5.4.6 Copyright (C) 1994-2023 Lua.org, PUC-Rio > > print('HELLO '..'WORLD') HELLO WORLD > print(#'hello') 5全局变量&局部变量在Lua语言中,全局变量无须声明即可使用。在默认情况下,变量总是认为是全局的,如果未提前赋值,默认为nil。如果想要声明一个局部变量需要使用local来声明。[root@work lua_demo]# lua Lua 5.4.6 Copyright (C) 1994-2023 Lua.org, PUC-Rio > b=10 > print(b) 10 > local a = 100 > print(a) nil > local a = 100; print(a) 100 > 数据类型全部的类型Lua有8个数据类型nil(空,无效值) boolean(布尔,true/false) number(数值) string(字符串) function(函数) table(表) thread(线程) userdata(数据用户)可以使用type函数测试给定变量或者类型:[root@work lua_demo]# lua Lua 5.4.6 Copyright (C) 1994-2023 Lua.org, PUC-Rio > print(type(nil)) nil > print(type("aaa")) string > nilnil是一种只有一个nil值的类型,他的作用可以用来与其他所有值进行区分,也可以当想要移除一个变量时,只需要将该变量名赋值为nil,垃圾回收就会释放该变量所占用的内存。booleanboolean类型具有两个值,true和false。在Lua中,只会将false和nil视为假,其他都是真,特别是在条件检测中0和空字符串都会认为是真,这个和我们熟悉的大多语言不太一样。number在lua5.3开始,lua语言为数值格式提供了两种选择:integer(整型)和float(双精度浮点型)[和其他语言不太一样,floatu代表单精度类型],u不管是整形还是双精度浮点型,使用type()函数来取其类型,返回的都是number。还有就是他们之间是可以直接相互转换的。stringLua语言中的字符串可以标识单个字符,也可以标识一整本书籍。在Lua语言中,操作100k或者1M个字母组成的字符串的程序很常见。如果字符串数据很多可以这样写a = [[ <html> xxx xxxx xxx </html> ]]tabletable是lua语言中最主要和强大的数据结构。使用表,Lua语言可以以一种简单、统一且高效的方式标识数组、合集、记录和其他很多数据结构。Lua语言中的表本质上是一种辅助数组。这种数组比Java中的数组更加灵活,可以使用数值做索引,也可以使用字符串或其他任意类型的值做索引(nil除外)[root@work lua_demo]# lua Lua 5.4.6 Copyright (C) 1994-2023 Lua.org, PUC-Rio > a = {} > arr = {"TOM","JERRY","ROSE"} > print(arr[0]) nil > print(arr[1]) TOM > print(arr[2]) JERRY > print(arr[3]) ROSE > arr={} > arr["X"]=10 > arr["Y"]=20 > arr["Z"]=30 > print(arr["X"]) 10 > print(arr["Y"]) 20 > print(arr["Z"]) 30 > arr.X 10 > arr.Y 20 > arr.Y 20 > arr={"TOM",X=10,"JERRY",Y=20,"ROSE",Z=30} > arr[1] TOM > arr[2] JERRY > arr[3] ROSE > arr[4] nil > arr.X 10 > arr["X"] 10 > arr.Z 30 > function在Lua语言中,函数(Function)是对语句和表达式进行抽象的主要方式定义函数:function functionName(params) code end函数被调用的时候,传入的参数个数与定义函数时使用的参数个数不一致的时候,Lua会通过抛弃多余参数和将不足的参数设为nil的方式来调整数的个数。[root@work lua_demo]# lua Lua 5.4.6 Copyright (C) 1994-2023 Lua.org, PUC-Rio > function f(a,b) >> print(a,b) >> end > f() nil nil > f(2) 2 nil > f(2,6) 2 6 > f(2,6,8) 2 6可变参数[root@work lua_demo]# lua Lua 5.4.6 Copyright (C) 1994-2023 Lua.org, PUC-Rio > function add(...) >> local a,b,c=... >> print(a,b,c) >> end > add(1,2,3) 1 2 3 > add(1) 1 nil nil > add(1,2,3,4,5,6) 1 2 3 > 返回值[root@work lua_demo]# lua Lua 5.4.6 Copyright (C) 1994-2023 Lua.org, PUC-Rio > function add(a,b) >> return b,a >> end > x,y=add(100,200) > print(y) 100 > print(x) 200 > 控制结构Lua语言提供了一组精简且常用的控制结构,包括用于条件执行的if以及用户循环的while、repeat和for。所有的控制语法上都有一个显示的终结符:end用于中介if、for以及while结构,until用于中介repeat结构。if语句if语句先测试其条件,并根据条件是否满足执行响应的then部分或else部分。else部分是可选的。[root@work lua_demo]# lua Lua 5.4.6 Copyright (C) 1994-2023 Lua.org, PUC-Rio > function testif(a) >> if a>0 then >> print("a是正数") >> end >> end > testif(2) a是正数 > testif(1) a是正数 > testif(-1) > function testif(a) >> if a>0 then >> print("a是正数") >> else >> print("a是负数") >> end >> end > testif(1) a是正数 > testif(-1) a是负数 > 嵌套IF相关案例如下[root@work lua_demo]# lua Lua 5.4.6 Copyright (C) 1994-2023 Lua.org, PUC-Rio > function show(age) >> if age <= 18 then >> return "qingshaonian" >> elseif age>18 and age <=45 then >> return "qingnian" >> elseif age>45 and age <=60 then >> return "zhongnianren" >> else >> return "laonianren" >> end >> end > print(show(17)) qingshaonian > print(show(19)) qingnian > print(show(56)) zhongnianren > print(show(80)) laonianrenwhile循环语法如下while 条件 do 循环体 end案例[root@work lua_demo]# lua Lua 5.4.6 Copyright (C) 1994-2023 Lua.org, PUC-Rio > function testwhile() >> local i=1 >> while i<=10 do >> print(i) >> i=i+1 >> end >> end > testwhile() 1 2 3 4 5 6 7 8 9 10repeat循环repeat-until语句回重复执行其循环体直到条件为真时结束。由于条件测试在循环体之后执行,所以至少会循环执行一次。语法如下repeat 循环体 until 条件案例如下[root@work lua_demo]# lua Lua 5.4.6 Copyright (C) 1994-2023 Lua.org, PUC-Rio > function testRepeat() >> local i = 10 >> repeat >> print(i) >> i=i-1 >> until i < 1 >> end > testRepeat() 10 9 8 7 6 5 4 3 2 1for循环数值型语法如下for param=exp1,exp2,exp3 do 循环体 endparam的值从exp1变化到exp2之前的每次循环会执行循环体,并在每次循环结束的时候步长,和python的for差不多。案例如下[root@work lua_demo]# lua Lua 5.4.6 Copyright (C) 1994-2023 Lua.org, PUC-Rio > for i = 1,100,10 do >> print(i) >> end 1 11 21 31 41 51 61 71 81 91泛型泛型for循环是通过一个迭代器函数来遍历所有的值,类似于java中的foreach语句语法for i,v in ipairs(x) do 循环体 endi是数组索引,v是对应索引的数组元素值,ipairs是Lua提供的一个迭代器函数,用来迭代数组,x是要遍历的数组。只后pairs也是Lua提供的夜歌迭代函数,他和ipairs的区别是pairs可以迭代一些指定键的table。案例如下[root@work lua_demo]# lua Lua 5.4.6 Copyright (C) 1994-2023 Lua.org, PUC-Rio > arr = {"TOME","JERRY","ROWS","LUCY"} > for i,v in ipairs(arr) do >> print(i,v) >> end 1 TOME 2 JERRY 3 ROWS 4 LUCY [root@work lua_demo]# lua Lua 5.4.6 Copyright (C) 1994-2023 Lua.org, PUC-Rio > arr = {"TOM","JERRY","ROSES",x="JACK","LUCY"} > function testfor(arr) >> for i,v in pairs(arr) do >> print(i,v) >> end >> end > testfor(arr) 1 TOM 2 JERRY 3 ROSES 4 LUCY x JACK