时间:2022-05-19来源:www.pcxitongcheng.com作者:电脑系统城
lua是一夸小巧,灵活且高效的脚本语言,用标准C语言编写并以源代码形式开发,在很多业务场景下配合适当的设计,可以带来意想不到的效果;
举个常见的例子,现在几乎很多公司都会用到nginx作为代理服务器,假如现在有这么个需求,需要做黑名单过滤,或者在网关这一层做流控,这该怎么做呢?
这里列举了3种实现的思路,至于实现方案,可能还有更多,但是我们想想,在nginx中编写逻辑块貌似不是很多人擅长的;在代码层面做不是不可以,而是这样一来,在涉及到高并发的业务高峰期,这必然会对后端服务造成较大的压力,那么还有没有其他更好的处理办法呢?
这就是要说的lua,即nginx作为网关仍然作为代理服务器,由于nginx可以集成lua,于是使用lua进行配合,来完成上面的业务实现的设计;
OpenRestry
简单来说,直接安装并使用OpenRestry,就可以达到同时使用Nginx与Lua的效果,同时基于OpenRestry,还可以在内部操作其他中间件,比如mysql,redis,kafka等,这样就使得业务架构在设计上具备了更大的灵活性;
1 | wget https: //openresty .org /download/openresty-1 .15.8.2. tar .gz |
1 | tar -zxf openresty-1.15.8.2. tar .gz |
这一步有点类似于nginx的源码安装,进行相关的环境变量的配置,这里直接使用默认的就好;
1 | . /configure |
进入nginx目录,可以看到里面的目录和nginx自身安装完毕后的配置几乎一样
进入conf,找到nginx.conf配置文件,添加如下内容:
1 2 3 4 |
location /lua { default_type 'text/html' ; content_by_lua 'ngx.say(" <h1>hello,openRestry lua</h1>")' ; } |
进入nginx的sbin目录下启动nginx
启动完成后,浏览器访问下服务器即可,可以看到nginx本身服务已启动
然后访问上面配置的lua地址,可以看到也能够正常的访问到,说明openrestry的模块已经安装完毕
使用Lua编写Nginx脚本的基本构建块是指令,指令用于指定何时运行用户Lua代码以及如何使用结果,下面针对一些常用的指令做简单的说明
1、init_by_lua*
该指令在每次Nginx重新加载配置时执行,用来完成一些耗时操作模块加载,或初始化一些全局配置
2、init_worker_by_lua*
该指令用于启动一些定时任务,如心跳检查、定时拉取服务器配置等
3、set_by_lua*
该指令只要用来给变量赋值,这个指令一次只能返回一个值,并将结果 值给Nginx中指定变量
4、rewrite_by_lua*
用于执行内部URL重写或者外部重定向,典型的如伪静态化URL重 写,本阶段在rewrite处理阶段的最后默认执行(和nginx自身的rewrite功能有类似的地方)
5、access_by_lua*
该指令用于访问控制,例如,只允许内网IP访问
6、content_by_lua*
该指令是使用最多的指令,大部分任务是在这个阶段完成的,其他过程往往为这个阶段准备数据,正式处理往往都在本阶段执行
7、header_filter_by_lua*
用于设置应答消息的头部信息
8、body_filter_by_lua*
该指令对响应数据进行过滤,如截断、替换
9、log_by_lua*
该指令用于log请求处理阶段,用Lua代码处理日志,但并不替换原有 log处理
10、balancer_by_lua*
该指令主要作用是用来实现上游服务器的负载均衡器算法
11、ssl_certificate_by_*
该指令作用在Nginx和下游服务开始一个SSL握手操作时将允许本配置项的Lua代码
一个使用指令的需求
接下来针对上面提到的各种指令,来做一个简单的需求
nginx接收到请求后,根据参数中gender传入的值,如果gender传入的是1 则在页面上展示 “先生” , 如果gender传入的是0,则在页面上展示“女士”
注意:使用指令编写的基本步骤是,在nginx.conf模块中,自定义localtion块中编写lua的相关代码即可
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
location /getByGender { default_type 'text/html' ; set_by_lua $param " local uri_args = ngx.req.get_uri_args() local gender = uri_args[ 'gender' ] local name = uri_args[ 'name' ] if gender == '1' then return name.. ':先生' elseif gender== '0' then return name.. ':女士' else return name end "; charset utf-8; return 200 $param; } |
然后启动nginx做一下测试
1)访问服务,不携带任何参数
这时候无任何返回信息
2)访问服务,携带name参数
3)访问服务,携带name和gender参数
更多的指令可以参照此类方式编写,但是前提需要掌握一点lua的基本语法
Redis在系统中经常作为数据缓存、内存数据库使用,在各类互联网项目中扮演着非常重要的作用;
Lua-resty-redis库是OpenResty提供的一个操作Redis的接口库,可根据自己的业务情况做一些逻辑处理,适合做复杂的业务逻辑。所以下面将以Lua-resty-redis来进行说明。
1、提前安装好redis并启动服务
2、测试下redis客户端
lua-resty-redis提供了访问Redis的详细API,包括创建对接、连 接、操作、数据处理等。这些API基本上与Redis的操作是对应起来的
1、lua中导入redis依赖
1 | redis = require "resty.redis" |
2、new,创建一个Redis对象
1 | redis,err = redis:new() |
3、创建redis连接
ok,err=redis:connect(host,port[,options_table])
4、设置请求操作Redis的超时时间
1 | redis:set_timeout( time ) |
5、close,关闭连接
ok,err = redis:close()
补充说明:
在lua-resty-redis中,所有的Redis命令都有自己的方法;方法名字和命令名字相同,只是全部为小写;
具体实现效果展示
在nginx.conf模块下,添加如下的location内容
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
location /redis { default_type "text/html" ; content_by_lua_block { local redis = require "resty.redis" -- 引入 Redis local redisObj = redis:new() --创建Redis对象 redisObj:set_timeout(3000) --设置超时数据为3s local ok,err = redisObj:connect( "IP" ,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() } } |
重启nginx,进行测试,直接在浏览器访问一下如下地址,可以看到数据成功写入到redis
MySQL是一个使用广泛的关系型数据库。在ngx_lua中,MySQL有两种访问模式,分别是
安装,这个库现不在OpenResty中
lua-resty-mysql
lua-resty-mysql是OpenResty开发的模块,使用灵活、功能强大,适合复杂的业务场景,同时支持存储过程访问;
lua-resty-mysql实现数据库查询
1、准备好mysql服务
2、提前创建一张表
1 2 3 4 5 6 7 |
CREATE TABLE `users` ( `id` int (10) NOT NULL AUTO_INCREMENT, `username` varchar (255) DEFAULT NULL , `birthday` date DEFAULT NULL , `salary` double (10,2) DEFAULT NULL , PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; |
并提前准备几条数据
1 2 |
INSERT INTO `mydb`.`users` (`id`, `username`, `birthday`, `salary`) VALUES ( '1' , 'xiaowang' , '1991-03-15' , '9000.00' ); INSERT INTO `mydb`.`users` (`id`, `username`, `birthday`, `salary`) VALUES ( '2' , 'xiaoma' , '1992-07-15' , '8000.00' ); |
1、引入"resty.mysql"模块
1 | local mysql = require "resty.mysql" |
2、创建MySQL连接对象
遇到错误时,db为nil,err为错误描 述信息
1 | db ,err = mysql:new() |
3、创建连接对象
1 | ok,err=db:connect(options) |
options是一个参数的 Lua表结构,里面包含数据库连接的相关信息
4、设置子请求的超时时间(ms)
包括connect方法
1 | db:set_timeout(time) |
5、关闭当前MySQL连接并返回状态
如果成功,则返回1;如果出现任 何错误,则将返回nil和错误描述
1 | db:close() |
6、异步向远程MySQL发送一个查询
如果成功则返回成功发送的字节 数;如果错误,则返回nil和错误描述
1 | bytes,err=db:send_query(sql) |
7、从MySQL服务器返回结果中读取一行数据
1 | res, err, errcode, sqlstate = db:read_result() res, err, errcode, sqlstate = db:read_result(rows) |
返回结果类似下面这样
1 2 3 |
{ {id=1,username="TOM",birthday="1988-11- 11",salary=10000.0}, {id=2,username="JERRY",birthday="1989-11- 11",salary=20000.0} } |
如果是增删改,则返回类似如下数据
1 2 3 4 5 6 7 |
{ insert_id = 0, server_status=2, warning_count=1, affected_rows=2, message=nil } |
返回值说明:
具体操作案例
将下面的内容添加到server块,然后重启nginx
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
location /mysql { content_by_lua_block{ default_type "text/html" ; local mysql = require "resty.mysql" local db = mysql:new() local ok,err = db :connect{ host= "127.0.0.1" , port=3306, user= "root" , password= "123456" , database= "mydb" } db :set_timeout(3000) 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() } } |
可以看到,通过访问mysql这个路径,成功查询到数据库中ID为1的这条数据
使用cjson对查询结果进行格式化
从上面的返回结果来看,这种形式的返回数据在解析的时候其实并不是很友好,于是可以使用lua-cjson处理查询结果
使用步骤
步骤一:引入cjson
local cjson = require “cjson”
步骤二:调用cjson的encode方法进行类型转换
cjson.encode(res)
下面对上面程序模块做简单的改造
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
location /mysql-cjson { default_type "text/html" ; content_by_lua_block{ local cjson = require "cjson" local mysql = require "resty.mysql" local db = mysql:new() local ok,err = db :connect{ host= "127.0.0.1" , port=3306, user= "root" , password= "123456" , database= "mydb" } db :set_timeout(3000) 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() } } |
然后再次进行测试,这时候就以json的格式对数据进行了展现
增删改操作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
location /mysql-cjson { default_type "text/html" ; content_by_lua_block{ local cjson = require "cjson" local mysql = require "resty.mysql" local db = mysql:new() local ok,err = db :connect{ host= "127.0.0.1" , port=3306, user= "root" , password= "123456" , database= "mydb" } db :set_timeout(3000) -- 查询操作 db :send_query( "select * from users where id=1" ) -- 插入数据 -- local res,err,errcode,sqlstate = db :query( "insert into users(id,username,birthday,salary) values(3,'lifei','1995-10-17',3000)" ) -- 修改数据 -- local res,err,errcode,sqlstate = db :query( "update users set username='lisi' where id = 1" ) -- 删除数据 -- local res,err,errcode,sqlstate = db :query( "delete from users where id = 2" ) db :close() } } |
到此这篇关于nginx 集成lua操作mysql的过程解析的文章就介绍到这了
2024-07-07
myeclipse怎么导入tomcat教程2024-07-07
myeclipse如何启动tomcat2024-07-07
myeclipse如何绑定tomcat上线了一个小的预约程序,配置通过Nginx进行访问入口,默认的日志是没有请求时间的,因此需要配置一下,将每一次的请求的访问响应时间记录出来,备查与优化使用....
2023-03-17