首页 >> 大全

五、lua的语法

2023-08-25 大全 21 作者:考证青年

Nginx的扩展模块 六、模块概念 的使用 七、操作Redis 八、操作Mysql 综合小案例

Nginx是可扩展的,可用于处理各种使用场景。本节中,我们将探讨使用Lua扩展Nginx的功能。 Lua 一、概念

Lua是一种轻量、小巧的脚本语言,用标准C语言编写并以源代码形式开发。设计的目的是为了嵌入到其他应用程序中,从而为应用程序提供灵活的扩展和定制功能。

二、特性

跟其他语言进行比较,Lua有其自身的特点:

Lua用标准C语言编写并以源代码形式开发,编译后仅仅一百余千字节,可以很方便的嵌入到其他程序中。

Lua提供非常丰富易于使用的扩展接口和机制,由宿主语言(通常是C或C++)提供功能,Lua可以使用它们,就像内置的功能一样。

三、应用场景

Lua在不同的系统中得到大量应用,场景的应用场景如下:

游戏开发、独立应用脚本、web应用脚本、扩展和数据库插件、系统安全上。

四、lua的安装

在linux上安装Lua非常简单,只需要下载源码包并在终端解压、编译即可使用。

Lua的官网地址为:

点击连接,可以直接进行下载。也可以使用wget 命令直接下载:

wget https://www.lua.org/ftp/lua-5.4.4.tar.gz

编译安装

tar -zxvf lua-5.4.4.tar.gz 解压
cd lua-5.4.4
make linux test
make install

如果在执行make linux test失败,报如下错误:

说明当前系统缺少-dev依赖包,需要通过命令来进行安装

yum install -y readline-devel

验证是否安装成功

lua -v

五、lua的语法

Lua和C/C++语法非常相似,整体上比较清晰,简洁。条件语句、循环语句、函数调用都与C/C++基本一致。如果对C/C++不太熟悉的同学来说,也没关系,因为天下语言是一家,基本上理解起来都不会太困难。我们一点点来讲。

第一个Lua程序

大家需要知道的是,Lua有两种交互方式,分别是:交互式和脚本式,这两者的区别,下面我们分别来讲解下:

交互式之

交互式是指可以在命令行输入程序,然后回车就可以看到运行的效果。

Lua交互式编程模式可以通过命令lua -i 或lua来启用:

在命令行中输入如下命令,并按回车,会有输出在控制台:

脚本式之

脚本式是将代码保存到一个以lua为扩展名的文件中并执行的方式。

方式一:

我们需要一个文件名为 hello.lua,在文件中添加要执行的代码,然后通过命令 lua hello.lua来执行,会在控制台输出对应的结果。

方式二:将hello.lua做如下修改

第一行用来指定Lua解释器所在位置为 /usr/local/bin/lua,加上#号标记解释器会忽略它。一般情况下#!就是用来指定用哪个程序来运行本文件。但是hello.lua并不是一个可执行文件,需要通过chmod来设置可执行权限,最简单的方式为:

chmod 755 hello.lua

未修改前

修改之后

注意:在Lua语言中,连续语句之间的分隔符并不是必须的,也就是说后面不需要加分号,当然加上也不会报错,

在Lua语言中,表达式之间的换行也起不到任何作用。如以下四个写法,其实都是等效的

写法一
a=1
b=a+2
写法二
a=1;
b=a+2;
写法三
a=1; b=a+2;
写法四
a=1 b=a+2

不建议使用第四种方式,可读性太差。

Lua的注释

关于Lua的注释要分两种,第一种是单行注释,第二种是多行注释。

单行注释的语法为:

--注释内容

多行注释的语法为:

--[[注释内容注释内容
--]]

如果想取消多行注释,只需要在第一个–之前在加一个-即可,如:

---[[注释内容注释内容
--]]

标识符

换句话说标识符就是我们的变量名,Lua定义变量名以一个字母 A 到 Z 或 a 到 z 或下划线 _ 开头后加上0个或多个字母,下划线,数字(0到9)。这块建议大家最好不要使用下划线加大写字母的标识符,因为Lua的保留字也是这样定义的,容易发生冲突。注意Lua是区分大小写字母的。

关键字

下列是Lua的关键字,大家在定义常量、变量或其他用户自定义标识符都要避免使用以下这些关键字:

end

false

for

if

in

local

nil

not

or

then

true

until

while

goto

一般约定,以下划线开头连接一串大写字母的名字(比如 )被保留用于 Lua 内部全局变量。这个也是上面我们不建议这么定义标识符的原因。

运算符

Lua中支持的运算符有算术运算符、关系运算符、逻辑运算符、其他运算符。

全局遍历&局部变量

在Lua语言中,全局变量无须声明即可使用。在默认情况下,变量总是认为是全局的,如果未提前赋值,默认为nil:

要想声明一个局部变量,需要使用local来声明

Lua数据类型

Lua有8个数据类型

nil(空,无效值)
boolean(布尔,true/false)
number(数值)
string(字符串)
function(函数)
table(表)
thread(线程)
userdata(用户数据)

可以使用type函数测试给定变量或者的类型:

print(type(nil))				-->nil
print(type(true))               --> boolean
print(type(1.1*1.1))             --> number
print(type("Hello world"))      --> string
print(type(io.stdin))			-->userdata
print(type(print))              --> function
print(type(type))               -->function
print(type{})					-->table
print(type(type(X)))            --> string

数值常量的表示方式:

不管是整型还是双精度浮点型,使用type()函数来取其类型,都会返回的是

所以它们之间是可以相互转换的,同时,具有相同算术值的整型值和浮点型值在Lua语言中是相等的

Lua控制结构

Lua 语言提供了一组精简且常用的控制结构,包括用于条件执行if 以及用于循环的 while、 和 for。 所有的控制结构语法上都有一个显式的终结符: end 用于终结 if、 for 及 while 结构, until 用于终结 结构。

if then else

if语句先测试其条件,并根据条件是否满足执行相应的 then 部分或 else 部分。 else 部分 是可选的。

function testif(a)if a>0 thenprint("a是正数")end
endfunction testif(a)if a>0 thenprint("a是正数")elseprint("a是负数")end
end

如果要编写嵌套的 if 语句,可以使用 。 它类似于在 else 后面紧跟一个if。根据传入的年龄返回不同的结果,如

while循环

顾名思义,当条件为真时 while 循环会重复执行其循环体。 Lua 语言先测试 while 语句 的条件,若条件为假则循环结束;否则, Lua 会执行循环体并不断地重复这个过程。

语法:

while 条件 do循环体
end

例子:实现数组的循环

循环

顾明思义, -until语句会重复执行其循环体直到条件为真时结束。由于条件测试在循环体之后执行,所以循环体至少会执行一次。

repeat循环体until 条件

function testRepeat()local i = 10repeatprint(i)i=i-1until i < 1
end

for循环

数值型for循环

语法

for param=exp1,exp2,exp3 do循环体
end

param的值从exp1变化到exp2之前的每次循环会执行 循环体,并在每次循环结束后将步长(step)exp3增加到param上。exp3可选,如果不设置默认为1

for i = 1,100,10 do
print(i)
end

泛型for循环

泛型for循环通过一个迭代器函数来遍历所有值,类似于java中的语句。

语法

for i,v in ipairs(x) do循环体
end

i是数组索引值,v是对应索引的数组元素值,是Lua提供的一个迭代器函数,用来迭代数组,x是要遍历的数组。

例如:

arr = {"TOME","JERRY","ROWS","LUCY"}
for i,v in ipairs(arr) doprint(i,v)
end

但是如果将arr的值进行修改为

同样的代码在执行的时候,就只能看到和之前一样的结果,而其中的x为JACK就无法遍历出来,缺失了数据,如果解决呢?

我们可以将迭代器函数变成pairs,如

for i,v in pairs(arr) doprint(i,v)
end

六、模块概念

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

模块环境准备

概述

前面我们提到过,是由淘宝工程师开发的,所以其官方网站()我们读起来是非常的方便。是一个基于Nginx与 Lua 的高性能 Web 平台,其内部集成了大量精良的 Lua 库、第三方模块以及大多数的依赖项。用于方便地搭建能够处理超高并发、扩展性极高的动态 Web 应用、Web 服务和动态网关。所以本身内部就已经集成了Nginx和Lua,所以我们使用起来会更加方便。

安装

(1) 下载OpenResty:https://openresty.org/download/openresty-1.15.8.2.tar.gz
(2)使用wget下载: wget https://openresty.org/download/openresty-1.15.8.2.tar.gz
(3)解压缩: tar -zxf openresty-1.15.8.2.tar.gz
(4)进入OpenResty目录: cd openresty-1.15.8.2
(5) 执行命令:./configure
(6) 执行命令:make && make install
(7)进入OpenResty的目录,找到nginx:cd /usr/local/openresty/nginx/
(8)在conf目录下的nginx.conf添加如下内容
location /lua{default_type 'text/html';content_by_lua 'ngx.say("

HELLO,OpenRestry

"
)'; } (9)在sbin目录下启动nginx (10)通过浏览器访问测试

的使用

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

先来解释下*的作用

*:无 , 即 xxx_by_lua ,指令后面跟的是 lua指令
*:_file,即 xxx_by_lua_file 指令后面跟的是 lua文件
*:_block,即 xxx_by_lua_block 在0.9.17版后替换init_by_lua_file

*

该指令在每次Nginx重新加载配置时执行,可以用来完成一些耗时模块的加载,或者初始化一些全局配置。

*

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

*

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

*

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

*

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

*

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

*

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

*

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

*

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

*

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

*

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

需求

http://192.168.38.131?name=张三&gender=1
Nginx接收到请求后,根据gender传入的值,如果gender传入的是1,则在页面上展示
张三先生,如果gender传入的是0,则在页面上展示张三女士,如果未传或者传入的不是12则在页面上展示张三。

nginx做如下配置,并在浏览器中访问张三发现了中文乱码

设置字符集,解决上面的乱码问题。修改nginx.conf如下

七、操作Redis

Redis在系统中经常作为数据缓存、内存数据库使用,在大型系统中扮演着非常重要的作用。在Nginx核心系统中,Redis是常备组件。Nginx支持3种方法访问Redis,分别是模块、、lua-resty-redis库。这三种方式中模块提供的指令少,功能单一,适合做简单缓存,模块比模块操作更灵活,功能更强大。而Lua-resty-redis库是提供的一个操作Redis的接口库,可根据自己的业务情况做一些逻辑处理,适合做复杂的业务逻辑。所以本次课程将主要以Lua-resty-redis来进行讲解。

lua-resty-redis环境准备

步骤一:准备一个Redis环境

连接地址
host = 192.168.38.131
port = 6379
password = 123456

查看是否连接成功

步骤二:准备对应的API

lua-resty-redis提供了访问Redis的详细API,包括创建对接、连接、操作、数据处理等。这些API基本上与Redis的操作一一对应。
(1)redis = require "resty.redis"2new语法: redis,err = redis:new(),创建一个Redis对象。
(3)connect语法:ok,err=redis:connect(host,port[,options_table]),设置连接Redis的连接信息。ok:连接成功返回 1,连接失败返回nilerr:返回对应的错误信息
(4)set_timeout语法: redis:set_timeout(time) ,设置请求操作Redis的超时时间。
(5)close语法: ok,err = redis:close(),关闭当前连接,成功返回1,失败返回nil和错误信息
(6)redis命令对应的方法在lua-resty-redis中,所有的Redis命令都有自己的方法,方法名字和命令名字相同,只是全部为小写。

location /testRedis{default_type "text/html";content_by_lua_block{local redis = require "resty.redis" -- 引入redislocal redisObj = redis:new()  -- 创建redis对象redisObj:set_timeout(1000) -- 设置超时数据为1slocal ok,err = redisObj:connect("192.168.38.131",6379) -- 设置redis连接信息if not ok then -- 判断是否连接成功ngx.say("faild to connection redis,",err) -- 输出到浏览器return -- 结束程序 不在往后走endlocal ok,err=redisObj:auth("123456") -- redis加了密码需要这一步操作if not ok thenngx.say("failed to authenticate:",err);returnendok, err = redisObj:set("username","TOM") -- 存入数据if not ok then -- 判断是否存入成功ngxi.say("failed to set username",err);returnendlocal res,err=redisObj:get("username") -- 从redis中获取数据ngx.say(res) -- 将数据写到消息体中redisObj:close() -- 关闭连接}}

八、操作Mysql

MySQL是一个使用广泛的关系型数据库。在中,MySQL有两种访问模式,分别是使

(1)用模块和lua-resty-mysql模块:这两个模块是安装时默认安装的。

(2)使用()模块:需要单独安装,这个库现不在中。

lua-resty-mysql

lua-resty-mysql是开发的模块,使用灵活、功能强大,适合复杂的业务场景,同时支持存储过程的访问。

使用lua-resty-mysql实现数据库的查询

步骤一准备MySQL

host: 192.168.38.131
port: 3306
username:root
password:123456

创建一个数据库表及表中的数据。

create database nginx_db;use nginx_db;create table users(id int primary key auto_increment,username varchar(30),birthday date,salary double
);insert into users(id,username,birthday,salary) values(null,"TOM","1988-11-11",10000.0);
insert into users(id,username,birthday,salary) values(null,"JERRY","1989-11-11",20000.0);
insert into users(id,username,birthday,salary) values(null,"ROWS","1990-11-11",30000.0);
insert into users(id,username,birthday,salary) values(null,"LUCY","1991-11-11",40000.0);
insert into users(id,username,birthday,salary) values(null,"JACK","1992-11-11",50000.0);

数据库连接四要素:

driverClass=com.mysql.jdbc.Driver
url=jdbc:mysql://192.168.38.131:3306/nginx
username=root
password=123456

步骤二:API学习

1)引入"resty.mysql"模块local mysql = require "resty.mysql"2new创建一个MySQL连接对象,遇到错误时,db为nil,err为错误描述信息语法: db,err = mysql:new()3)connect尝试连接到一个MySQL服务器语法:ok,err=db:connect(options),options是一个参数的Lua表结构,里面包含数据库连接的相关信息host:服务器主机名或IP地址port:服务器监听端口,默认为3306user:登录的用户名password:登录密码database:使用的数据库名
(4)set_timeout设置请求的超时时间(ms),包括connect方法语法:db:set_timeout(time)5)close关闭当前MySQL连接并返回状态。如果成功,则返回1;如果出现任何错误,则将返回nil和错误描述。语法:db:close()6)send_query异步向远程MySQL发送一个查询。如果成功则返回成功发送的字节数;如果错误,则返回nil和错误描述语法:bytes,err=db:send_query(sql)7)read_result从MySQL服务器返回结果中读取一行数据。res返回一个描述OK包或结果集包的Lua,语法:res, err, errcode, sqlstate = db:read_result() res, err, errcode, sqlstate = db:read_result(rows) :rows指定返回结果集的最大值,默认为4如果是查询,则返回一个容纳多行的数组。每行是一个数据列的key-value对,如{{id=1,username="TOM",birthday="1988-11-11",salary=10000.0},{id=2,username="JERRY",birthday="1989-11-11",salary=20000.0}}如果是增删改,则返回类上如下数据{insert_id = 0,server_status=2,warning_count=1,affected_rows=2,message=nil}返回值:res:操作的结果集err:错误信息errcode:MySQL的错误码,比如1064sqlstate:返回由5个字符组成的标准SQL错误码,比如42000

步骤三:效果实现

location /testMysql{default_type 'text/html';content_by_lua_block{local mysql = require "resty.mysql"local db = mysql:new()local ok,error = db:connect{host = "192.168.38.131",port = 3306,user ="root",password="123456",database="nginx"}db:set_timeout(1000)db:send_query("select * from users where id=1")local res,err,errcode,sqlstate = db:read_result()ngx.say(res[1].id..","..res[1].username..","..res[1].birthday..","..res[1].salary)db:close()}}

问题

1.如何获取返回数据的内容
2.如何实现查询多条数据
3.如何实现数据库的增删改操作

使用lua-cjson处理查询结果

通过上述的案例学习,()得到的结果res都是table类型,要想在页面上展示,就必须知道table的具体数据结构才能进行遍历获取。处理起来比较麻烦,接下来我们介绍一种简单方式cjson,使用它就可以将table类型的数据转换成json字符串,把json字符串展示在页面上即可。具体如何使用?

步骤一:引入cjson

local cjson = require "cjson"

步骤二:调用cjson的方法进行类型转换

步骤二:调用cjson的encode方法进行类型转换

步骤三:使用

 location /testMysql{default_type 'text/html';# 这里面是lua代码 content_by_lua_block{local mysql = require "resty.mysql"local cjson = require "cjson"local db = mysql:new()local ok,error = db:connect{host = "192.168.38.131",port = 3306,user ="root",password="123456",database="nginx"}db:set_timeout(1000)db:send_query("select * from users where id=1")local res,err,errcode,sqlstate = db:read_result()-- ngx.say(res[1].id..","..res[1].username..","..res[1].birthday..","..res[1].salary)ngx.say(cjson.encode(res))for i,v in ipairs(res) dongx.say(v.id..","..v.username..","..v.birthday..","..v.salary)end db:close()}}

lua-resty-mysql实现数据库的增删改

优化和

本方法是和组合的快捷方法。

语法:

res, err, errcode, sqlstate = db:query(sql[,rows])

有了该API,上面的代码我们就可以进行对应的优化,如下:

增、删、改

location /testMysql{default_type 'text/html';# 这里面是lua代码 content_by_lua_block{-- 引入mysqllocal mysql = require "resty.mysql"-- 引入cjsonlocal cjson = require "cjson"-- 创建MySQL对象local db = mysql:new()-- 创建连接local ok,error = db:connect{host = "192.168.38.131",port = 3306,user ="root",password="123456",database="nginx"}-- 设置连接超时时间db:set_timeout(1000)-- 使用query优化send_query、read_result-- local sql = "select * from users"-- local sql = "insert into users (id,username,birthday,salary) values(6,'lss','1998-07-28',20000)"local sql = "update users set username='lzz' where id =6"local sql="delete from users where id =6"local res,err,errcode,sqlstate = db:query(sql)ngx.say(cjson.encode(res))}}

增加

删除

修改

综合小案例

使用模块完成Redis缓存预热。

缓存预热 1. 定义

缓存预热就是系统上线后,提前将相关的缓存数据直接加载到缓存系统。避免在用户请求的时候,先查询数据库,然后再将数据缓存的问题!用户直接查询事先被预热的缓存数据。

2.解决方案 直接写个缓存页面刷新,上线时手工操作下。数据量不大,可以在项目启动的时候自动进行加载。定时刷新缓存。

分析:

(1)先得有一张表(users)

(2)浏览器输入如下地址

http://191.168.38.131?username=TOM

(3)从表中查询出符合条件的记录,此时获取的结果为table类型

(4)使用cjson将table数据转换成json字符串

(5)将查询的结果数据存入Redis中

http {include       mime.types;default_type  application/octet-stream;sendfile        on;keepalive_timeout  65;#该指令在每次nginx重新加载配置时执行 #可以用来完成一些耗时模块的加载或者#初始化一些全局                 init_by_lua_block{redis = require "resty.redis"mysql = require "resty.mysql"cjson = require "cjson"}server {listen       80;server_name  localhost;#charset koi8-r;#access_log  logs/host.access.log  main;location /{default_type "text/html";content_by_lua_block{-- 获取请求的参数username-- ngx.req.get_uri_args()-- 获取所有请求参数 返回数据-- 类型为tablelocal param = ngx.req.get_uri_args()["username"]-- 建立mysql数据库的连接local db = mysql:new()local ok,err = db:connect{host = "192.168.38.131",port = 3306,user = "root",password = "123456",database = 'nginx'}if not ok thenngx.say("failed connect to mysql,",err)return -- 结束程序,不再往下走end-- 设置连接超时时间db:set_timeout(1000)-- 查询数据local sql = ""if not param thensql = "select * from users"elsesql = "select * from users where username= ".."'"..param.."'"endlocal res,err,errcode,sqlstate =db:query(sql)if not res thenngx.say("failed to query from mysql,",err)returnend-- 连接redislocal rd = redis:new()ok,err = rd:connect("192.168.38.131", 6379)if not ok thenngx.say("failed to connect to redis,",err)returnend-- redis加了密码要加这行ok,err = rd:auth("123456")if not ok thenngx.say("failed to authenticate",err);returnendrd:set_timeout(1000)-- 循环遍历数据for i,v in ipairs(res) dord:set("user_"..v.username,cjson.encode(v))endngx.say("success")rd:close()db:close()}}

通过浏览器访问来更新redis中的字段的值。

关于我们

最火推荐

小编推荐

联系我们


版权声明:本站内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 88@qq.com 举报,一经查实,本站将立刻删除。备案号:桂ICP备2021009421号
Powered By Z-BlogPHP.
复制成功
微信号:
我知道了