零、文章目录
Nginx16-Lua扩展案例
1、ngx_lua案例
(1)需求
- 请求地址:http://192.168.119.161/getByGender?name=张三&gender=1
- Nginx接收到请求后,根据gender传入的值
- 如果gender传入的是1,则在页面上展示张三先生
- 如果gender传入的是0,则在页面上展示张三女士
- 如果未传或者传入的不是1和2,则在页面上展示张三。
(2)配置代码实现
- 在配置文件中/usr/local/openresty/nginx/conf/nginx.conf添加如下配置
location /getByGender {
default_type 'text/html';
set_by_lua $name "
local uri_args = ngx.req.get_uri_args()
gender = uri_args['gender']
name = uri_args['name']
if gender=='1' then
return name..'先生'
elseif gender=='0' then
return name..'女士'
else
return name
end
";
header_filter_by_lua "
ngx.header.aaa='bbb'
";
return 200 $name;
}
(3)测试验证
- 重启Nginx
/usr/local/openresty/nginx/sbin/nginx -s reload
- 访问地址:http://192.168.119.161/getByGender?name=张三&gender=1
2、ngx_lua操作Redis
(1)概述
-
Redis在系统中经常作为数据缓存、内存数据库使用,在大型系统中扮演着非常重要的作用。在Nginx核心系统中,Redis是常备组件。
-
Nginx支持3种方法访问Redis,分别是HttpRedis模块、HttpRedis2Module、lua-resty-redis库。
- HttpRedis模块提供的指令少,功能单一,适合做简单缓存,
- HttpRedis2Module模块比HttpRedis模块操作更灵活,功能更强大。
- Lua-resty-redis库是OpenResty提供的一个操作Redis的接口库,可根据自己的业务情况做一些逻辑处理,适合做复杂的业务逻辑。所以本次课程将主要以Lua-resty-redis来进行讲解。
-
lua-resty-redis提供了访问Redis的详细API,包括创建对接、连接、操作、数据处理等。这些API基本上与Redis的操作一一对应。
(2)准备Redis环境
-
环境配置:Centos7.9 192.168.119.163: 6379 Docker安装Redis
-
Docker相关知识请参考Docker详解
-
Docker命令如下
docker run -d
:docker run
是 Docker 的命令,用于创建一个新的容器。-d
参数告诉 Docker 在后台运行容器。--name redis-container
:--name
参数用于为容器指定一个名称,这里我们将其命名为redis-container
。-v /path/to/your/data:/data
:-v
参数用于挂载卷,将宿主机的目录挂载到容器内部。/path/to/your/data
是宿主机上的目录,用于持久化 Redis 数据。:/data
表示将宿主机的目录挂载到容器的/data
目录。-v /path/to/your/redis.conf:/usr/local/etc/redis/redis.conf
:/path/to/your/redis.conf
是宿主机上的 Redis 配置文件路径。:/usr/local/etc/redis/redis.conf
表示将宿主机的配置文件挂载到容器的/usr/local/etc/redis/redis.conf
。-p 6379:6379
:-p
参数用于端口映射。6379:6379
表示将容器的 6379 端口映射到宿主机的 6379 端口,这样你就可以通过宿主机的端口访问 Redis 服务了。redis
:指定要使用的 Docker 镜像名称,这里是redis
。redis-server /usr/local/etc/redis/redis.conf
:在容器内运行redis-server
命令,并指定使用挂载的配置文件。
docker run -d \
--name redis-container \
-v /path/to/your/data:/data \
-v /path/to/your/redis.conf:/usr/local/etc/redis/redis.conf \
-p 6379:6379 \
redis redis-server /usr/local/etc/redis/redis.conf
- Redis运行成功进行测试,使用客户端进行连接,客户端百度网盘地址:https://pan.baidu.com/s/1tBdTkKWl241agJgfCUEYGA?pwd=1234
(3)配置代码实现
- 在配置文件中/usr/local/openresty/nginx/conf/nginx.conf添加如下配置
location /redis {
default_type "text/html";
content_by_lua_block{
local redis = require "resty.redis" -- 引入Redis
local redisObj = redis:new() --创建Redis对象
redisObj:set_timeout(1000) --设置超时数据为1s
local ok,err = redisObj:connect("192.168.119.163",6379) --设置redis连接信息
if not ok then --判断是否连接成功
ngx.say("failed to connection redis",err)
return
end
ok,err = redisObj:set("username","TOM")--存入数据
if not ok then --判断是否存入成功
ngx.say("failed to set username",err)
return
end
local res,err = redisObj:get("username") --从redis中获取数据
ngx.say(res) --将数据写会消息体中
redisObj:close()
}
}
(4)配置代码说明
-
(1)redis = require “resty.redis”
-
(2)new
- 语法: redis,err = redis:new(),创建一个Redis对象。
-
(3)connect
-
语法:ok,err=redis:connect(host,port[,options_table]),设置连接Redis的连接信息。
-
ok:连接成功返回 1,连接失败返回nil
-
err:返回对应的错误信息
-
-
(4)set_timeout
- 语法: redis:set_timeout(time) ,设置请求操作Redis的超时时间。
-
(5)close
- 语法: ok,err = redis:close(),关闭当前连接,成功返回1,失败返回nil和错误信息。
-
(6)redis命令对应的方法
- 在lua-resty-redis中,所有的Redis命令都有自己的方法,方法名字和命令名字相同,只是全部为小写。
(5)测试验证
- 重启Nginx
/usr/local/openresty/nginx/sbin/nginx -s reload
- 访问地址:http://192.168.119.161/redis
3、ngx_lua操作Mysql
(1)概述
-
MySQL是一个使用广泛的关系型数据库。MYSQL相关知识请参考MYSQL详解。
-
在ngx_lua中,MySQL有两种访问模式
-
用ngx_lua模块和lua-resty-mysql模块:这两个模块是安装OpenResty时默认安装的。
-
使用drizzle_nginx_module(HttpDrizzleModule)模块:需要单独安装,这个库现不在OpenResty中。
-
-
lua-resty-mysql是OpenResty开发的模块,使用灵活、功能强大,适合复杂的业务场景,同时支持存储过程的访问。我们以lua-resty-mysql为例进行讲解。
(2)准备MySQL环境
- 环境配置:Centos7.9 192.168.119.163: 3306 Docker安装MySQL。账号:root 密码:123456
- Docker相关知识请参考Docker详解
- Docker命令如下
docker run
:Docker 的命令,用于创建并启动一个新的容器。-d
:后台运行容器。使用-d
参数可以让容器在后台运行,而不是在当前终端占用前台。--name mysql
:为新创建的容器指定一个名称,这里名称为mysql
。如果不指定,Docker 会默认生成一个名称。-p 3306:3306
:端口映射参数,格式为<宿主机端口>:<容器端口>
。这里将容器的 3306 端口映射到宿主机的 3306 端口,使得宿主机可以通过 3306 端口访问 MySQL 服务。-e MYSQL_ROOT_PASSWORD=123456
:设置环境变量。这里设置的环境变量是MYSQL_ROOT_PASSWORD
,其值为123456
,即 MySQL 的 root 用户的密码。这是 Docker 容器启动时需要的配置信息之一。-v /path/to/mysql/data:/var/lib/mysql
:挂载数据卷参数,格式为<宿主机目录>:<容器目录>
。这里将宿主机的/path/to/mysql/data
目录挂载到容器的/var/lib/mysql
目录。这样,MySQL 的数据文件就会被持久化存储在宿主机的指定目录中,而不是容器内部,从而实现数据的持久化。mysql:latest
:指定要使用的 Docker 镜像。这里使用的是mysql
镜像的最新版本(latest
标签)。
docker run -d \
--name mysql-container \
-e MYSQL_ROOT_PASSWORD=123456 \
-v /path/to/mysql/data:/var/lib/mysql \
-p 3306:3306 \
mysql:latest
- Mysql运行成功进行测试,使用Navicat进行连接,Navicat百度网盘下载连接(科学安装方法请自行百度):https://pan.baidu.com/s/1xg5dJcaqv6cTw_opWJG9AQ?pwd=8888
- 创建数据库,创建表,插入数据
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);
(3)配置代码实现
- 在配置文件中/usr/local/openresty/nginx/conf/nginx.conf添加如下配置
location /mysql {
content_by_lua_block{
local mysql = require "resty.mysql"
local db = mysql:new()
local ok,err = db:connect{
host="192.168.119.163",
port=3306,
user="root",
password="123456",
database="nginx_db"
}
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()
}
}
(4)配置代码说明
-
(1)引入"resty.mysql"模块:local mysql = require “resty.mysql”
-
(2)new:
-
创建一个MySQL连接对象,遇到错误时,db为nil,err为错误描述信息
-
语法: db,err = mysql:new()
-
-
(3)connect
-
尝试连接到一个MySQL服务器
-
语法:ok,err=db:connect(options),options是一个参数的Lua表结构,里面包含数据库连接的相关信息。
-
host:服务器主机名或IP地址
-
port:服务器监听端口,默认为3306
-
user:登录的用户名
-
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:MySQL的错误码,比如1064
- sqlstate:返回由5个字符组成的标准SQL错误码,比如42000
- res, err, errcode, sqlstate = db:read_result(rows)
- rows:指定返回结果集的最大值,默认为4
- res, err, errcode, sqlstate = db:read_result()
/// 如果是查询,则返回一个容纳多行的数组。每行是一个数据列的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
}
(5)处理查询结果
- 通过上述的案例学习,read_result()得到的结果res都是table类型,要想在页面上展示,就必须知道table的具体数据结构才能进行遍历获取。处理起来比较麻烦。
- 接下来我们介绍一种简单方式cjson,使用它就可以将table类型的数据转换成json字符串,把json字符串展示在页面上即可。
# 步骤一:引入cjson
local cjson = require "cjson"
# 步骤二:调用cjson的encode方法进行类型转换
cjson.encode(res)
- 在配置文件中/usr/local/openresty/nginx/conf/nginx.conf添加如下配置
location /mysql1 {
content_by_lua_block{
local mysql = require "resty.mysql"
local cjson = require "cjson"
local db = mysql:new()
local ok,err = db:connect{
host="192.168.119.163",
port=3306,
user="root",
password="123456",
database="nginx_db"
}
db:set_timeout(1000)
--db:send_query("select * from users where id = 2")
db:send_query("select * from users")
local res,err,errcode,sqlstate = db:read_result()
ngx.say(cjson.encode(res))
for i,v in ipairs(res) do
ngx.say(v.id..","..v.username..","..v.birthday..","..v.salary)
end
db:close()
}
}
(4)实现数据增删改
- 本方法是send_query和read_result组合的快捷方法。
res, err, errcode, sqlstate = db:query(sql[,rows])
- 在配置文件中/usr/local/openresty/nginx/conf/nginx.conf添加如下配置
location /mysql2 {
content_by_lua_block{
local mysql = require "resty.mysql"
local db = mysql:new()
local ok,err = db:connect{
host="192.168.119.163",
port=3306,
user="root",
password="123456",
database="nginx_db",
max_packet_size=1024,
compact_arrays=false
}
db:set_timeout(1000)
local res,err,errcode,sqlstate = db:query("select * from users")
--local res,err,errcode,sqlstate = db:query("insert into users(id,username,birthday,salary) values(null,'zhangsan','2020-11-11',32222.0)")
--local res,err,errcode,sqlstate = db:query("update users set username='lisi' where id = 6")
--local res,err,errcode,sqlstate = db:query("delete from users where id = 6")
db:close()
}
}
4、综合案例-Redis缓存预热
(1)需求
- 使用ngx_lua模块完成Redis缓存预热
- 浏览器输入如下地址:http://191.168.119.163/mysqltoredis?username=TOM
- 从表中查询出符合条件的记录,此时获取的结果为table类型
- 使用cjson将table数据转换成json字符串
- 将查询的结果数据存入Redis中
(2)功能实现
- 在配置文件中/usr/local/openresty/nginx/conf/nginx.conf添加如下配置
init_by_lua_block{
redis = require "resty.redis"
mysql = require "resty.mysql"
cjson = require "cjson"
}
location /mysqltoredis {
default_type "text/html";
content_by_lua_block{
--获取请求的参数username
local param = ngx.req.get_uri_args()["username"]
--建立mysql数据库的连接
local db = mysql:new()
local ok,err = db:connect{
host="192.168.119.163",
port=3306,
user="root",
password="123456",
database="nginx_db"
}
if not ok then
ngx.say("failed connect to mysql:",err)
return
end
--设置连接超时时间
db:set_timeout(1000)
--查询数据
local sql = "";
if not param then
sql="select * from users"
else
sql="select * from users where username=".."'"..param.."'"
end
local res,err,errcode,sqlstate=db:query(sql)
if not res then
ngx.say("failed to query from mysql:",err)
return
end
--连接redis
local rd = redis:new()
ok,err = rd:connect("192.168.119.163",6379)
if not ok then
ngx.say("failed to connect to redis:",err)
return
end
rd:set_timeout(1000)
--循环遍历数据
for i,v in ipairs(res) do
rd:set("user_"..v.username,cjson.encode(v))
end
ngx.say("success")
rd:close()
db:close()
}
}