nginx模块篇(四)

文章目录

  • 四、Nginx的扩展模块
    • 4.1. Lua
      • 4.1.1 概念
      • 4.1.2 特性
      • 4.1.3 应用场景
      • 4.1.4 Lua的安装
      • 4.1.5 Lua的语法
        • 4.1.5.1 第一个Lua程序
        • 4.1.5.2 Lua的注释
        • 4.1.5.3 标识符
        • 4.1.5.4 关键字
        • 4.1.5.5 运算符
        • 4.1.5.6 全局变量&局部变量
        • 4.1.5.7 Lua数据类型
          • nil
          • boolean
          • number
          • string
          • table
          • function
          • thread
          • userdata
        • 4.1.5.8 Lua控制结构
          • if then elseif else
          • while循环
          • repeat循环
          • for循环
    • 4.2. ngx_lua模块概念
    • 4.3. ngx_lua模块环境准备
      • 4.3.1. 方式一:lua-nginx-module
      • 4.3.2. 方式二:OpenRestry
        • 概述
        • 安装
    • 4.4. ngx_lua的使用
      • init_by_lua*
      • init_worker_by_lua*
      • set_by_lua*
      • rewrite_by_lua*
      • access_by_lua*
      • content_by_lua*
      • header_filter_by_lua*
      • body_filter_by_lua*
      • log_by_lua*
      • balancer_by_lua*
      • ssl_certificate_by_*
      • 需求:
    • 4.5. ngx_lua操作Redis
      • lua-resty-redis环境准备
    • 4.6. ngx_lua操作Mysql
      • 4.6.1 lua-resty-mysql
        • 使用lua-resty-mysql实现数据库的查询
        • 使用lua-cjson处理查询结果
        • lua-resty-mysql实现数据库的增删改
      • 4.6.2 综合小案例

在这里插入图片描述

四、Nginx的扩展模块

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

4.1. Lua

4.1.1 概念

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

4.1.2 特性

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

(1)轻量级

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

(2)可扩展

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

(3)支持面向过程编程和函数式编程

4.1.3 应用场景

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

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

4.1.4 Lua的安装

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

Lua的官网地址为:https://www.lua.org

在这里插入图片描述

  1. 点击download可以找到对应版本的下载地址,我们本次课程采用的是lua-5.3.5,其对应的资源链接地址为https://www.lua.org/ftp/lua-5.4.1.tar.gz,也可以使用wget命令直接下载:
wget https://www.lua.org/ftp/lua-5.4.1.tar.gz
  1. 编译安装
cd lua-5.4.1
make linux test
make install

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

在这里插入图片描述

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

yum install -y readline-devel

验证是否安装成功

lua -v

4.1.5 Lua的语法

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

4.1.5.1 第一个Lua程序

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

交互式之HELLOWORLD

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

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

在这里插入图片描述

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

在这里插入图片描述

脚本式之HELLOWORLD

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

方式一:

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

hello.lua

print("Hello World!!")

在这里插入图片描述

方式二:

将hello.lua做如下修改

#!/usr/local/bin/lua
print("Hello World!!!")

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

chmod 755 hello.lua

然后执行该文件

./hello.lua

在这里插入图片描述

补充一点,如果想在交互式中运行脚本式的hello.lua中的内容,我们可以使用一个dofile函数,如:

dofile("lua_demo/hello.lua")

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

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

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

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

4.1.5.2 Lua的注释

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

单行注释的语法为:

--注释内容

多行注释的语法为:

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

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

---[[注释内容注释内容
--]]
4.1.5.3 标识符

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

A0

4.1.5.4 关键字

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

andbreakdoelse
elseifendfalsefor
functionifinlocal
nilnotorrepeat
returnthentrueuntil
whilegoto

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

4.1.5.5 运算符

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

算术运算符:

+   加法
-	减法
*	乘法
/	除法
%	取余
^	乘幂
-	负号

例如:

10+20	-->30
20-10	-->10
10*20	-->200
20/10	-->2
3%2		-->1
10^2	-->100
-10		-->-10

关系运算符

==	等于
~=	不等于
>	大于
<	小于
>=	大于等于
<=	小于等于

例如:

10==10		-->true
10~=10		-->false
20>10		-->true
20<10		-->false
20>=10		-->true
20<=10		-->false

逻辑运算符

and	逻辑与	 A and B     &&   
or	逻辑或	 A or B     ||
not	逻辑非  取反,如果为true,则返回false  !

逻辑运算符可以作为if的判断条件,返回的结果如下:

A = true
B = trueA and B	-->true
A or  B -->true
not A 	-->falseA = true
B = falseA and B	-->false
A or  B -->true
not A 	-->falseA = false
B = trueA and B	-->false
A or  B -->true
not A 	-->true

其他运算符

..	连接两个字符串
#	一元预算法,返回字符串或表的长度

例如:

> "HELLO ".."WORLD"		-->HELLO WORLD
> #"HELLO"			-->5
4.1.5.6 全局变量&局部变量

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

在这里插入图片描述

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

在这里插入图片描述

4.1.5.7 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
nil

nil是一种只有一个nil值的类型,它的作用可以用来与其他所有值进行区分,也可以当想要移除一个变量时,只需要将该变量名赋值为nil,垃圾回收就会会释放该变量所占用的内存。

boolean

boolean类型具有两个值,true和false。boolean类型一般被用来做条件判断的真与假。在Lua语言中,只会将false和nil视为假,其他的都视为真,特别是在条件检测中0和空字符串都会认为是真,这个和我们熟悉的大多数语言不太一样。

number

在Lua5.3版本开始,Lua语言为数值格式提供了两种选择:integer(整型)和float(双精度浮点型)[和其他语言不太一样,float不代表单精度类型]。

数值常量的表示方式:

>4			-->4
>0.4		-->0.4
>4.75e-3	-->0.00475
>4.75e3		-->4750

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

>type(3)	-->number
>type(3.3)	-->number

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

string

Lua语言中的字符串即可以表示单个字符,也可以表示一整本书籍。在Lua语言中,操作100K或者1M个字母组成的字符串的程序很常见。

可以使用单引号或双引号来声明字符串

>a = "hello"
>b = 'world'
>print(a)	-->hello
>print(b) 	-->world

如果声明的字符串比较长或者有多行,则可以使用如下方式进行声明

html = [[
<html>
<head>
<title>Lua-string</title>
</head>
<body>
<a href="http://www.lua.org">Lua</a>
</body>
</html>
]]
table

​ table是Lua语言中最主要和强大的数据结构。使用表, Lua 语言可以以一种简单、统一且高效的方式表示数组、集合、记录和其他很多数据结构。 Lua语言中的表本质上是一种辅助数组。这种数组比Java中的数组更加灵活,可以使用数值做索引,也可以使用字符串或其他任意类型的值作索引(除nil外)。

创建表的最简单方式:

> a = {}

创建数组:

​ 我们都知道数组就是相同数据类型的元素按照一定顺序排列的集合,那么使用table如何创建一个数组呢?

>arr = {"TOM","JERRY","ROSE"}

​ 要想获取数组中的值,我们可以通过如下内容来获取:

print(arr[0])		nil
print(arr[1])		TOM
print(arr[2])		JERRY
print(arr[3])		ROSE

​ 从上面的结果可以看出来,数组的下标默认是从1开始的。所以上述创建数组,也可以通过如下方式来创建

>arr = {}
>arr[1] = "TOM"
>arr[2] = "JERRY"
>arr[3] = "ROSE"

上面我们说过了,表的索引即可以是数字,也可以是字符串等其他的内容,所以我们也可以将索引更改为字符串来创建

>arr = {}
>arr["X"] = 10
>arr["Y"] = 20
>arr["Z"] = 30

当然,如果想要获取这些数组中的值,可以使用下面的方式

方式一
>print(arr["X"])
>print(arr["Y"])
>print(arr["Z"])
方式二
>print(arr.X)
>print(arr.Y)
>print(arr.Z)

当前table的灵活不进于此,还有更灵活的声明方式

>arr = {"TOM",X=10,"JERRY",Y=20,"ROSE",Z=30}

如何获取上面的值?

TOM :  arr[1]
10  :  arr["X"] | arr.X
JERRY: arr[2]
20  :  arr["Y"] | arr.Y
ROESE?
function

在 Lua语言中,函数( Function )是对语句和表达式进行抽象的主要方式。

定义函数的语法为:

function functionName(params)end

函数被调用的时候,传入的参数个数与定义函数时使用的参数个数不一致的时候,Lua 语言会通过 抛弃多余参数和将不足的参数设为 nil 的方式来调整参数的个数。

function  f(a,b)
print(a,b)
endf()		--> nil  nil
f(2)	--> 2 nil
f(2,6)	--> 2 6
f(2.6.8)	--> 2 6 (8被丢弃)

可变长参数函数

function add(...)
a,b,c=...
print(a)
print(b)
print(c)
endadd(1,2,3)  --> 1 2 3

函数返回值可以有多个,这点和Java不太一样

function f(a,b)
return a,b
endx,y=f(11,22)	--> x=11,y=22	
thread

thread翻译过来是线程的意思,在Lua中,thread用来表示执行的独立线路,用来执行协同程序。

userdata

userdata是一种用户自定义数据,用于表示一种由应用程序或C/C++语言库所创建的类型。

4.1.5.8 Lua控制结构

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

if then elseif 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 语句,可以使用 elseif。 它类似于在 else 后面紧跟一个if。根据传入的年龄返回不同的结果,如

age<=18 青少年,
age>18 , age <=45 青年
age>45 , age<=60 中年人
age>60 老年人function show(age)
if age<=18 thenreturn "青少年"
elseif age>18 and age<=45 thenreturn "青年"
elseif age>45 and age<=60 thenreturn "中年人"
elseif age>60 thenreturn "老年人"
end
end
while循环

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

语法:

while 条件 do循环体
end

例子:实现数组的循环

function testWhile()local i = 1while i<=10 doprint(i)i=i+1end
end
repeat循环

顾名思义, repeat-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中的foreach语句。

语法

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

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

例如:

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

上述实例输出的结果为

1	TOM
2	JERRY
3	ROWS
4	LUCY

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

arr = {"TOME","JERRY","ROWS",x="JACK","LUCY"}

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

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

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

上述实例就输出的结果为

1	TOM
2	JERRY
3	ROWS
4	LUCY
x	JACK

4.2. ngx_lua模块概念

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

4.3. ngx_lua模块环境准备

4.3.1. 方式一:lua-nginx-module

  1. LuaJIT是采用C语言编写的Lua代表的解释器。

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

在官网上找到对应的下载地址:http://luajit.org/download/LuaJIT-2.0.5.tar.gz

在centos上使用wget来下载: 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

在这里插入图片描述

  1. 下载lua-nginx-module

下载地址:https://github.com/openresty/lua-nginx-module/archive/v0.10.16rc4.tar.gz

在centos上使用wget来下载: wget https://github.com/openresty/lua-nginx-module/archive/v0.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的目录执行如下命令:

./configure --prefix=/usr/local/nginx --add-module=../lua-nginx-module
make && make install

注意事项:

(1)如果启动Nginx出现如下错误:

在这里插入图片描述

解决方案:

设置软链接,使用如下命令

 ln -s /usr/local/lib/libluajit-5.1.so.2 /lib64/libluajit-5.1.so.2

(2)如果启动Nginx出现以下错误信息

在这里插入图片描述

分析原因:因为lua-nginx-module是来自openrestry,错误中提示的resty.core是openrestry的核心模块,对其下的很多函数进行了优化等工作。以前的版本默认不会把该模块编译进去,所以需要使用的话,我们得手动安装,或者禁用就可以。但是最新的lua-nginx-module模块已经强制性安装了该模块,所以此处因为缺少resty模块导致的报错信息。

解决方案有两个:一种是下载对应的模块,另一种则是禁用掉restry模块,禁用的方式为:

http{lua_load_resty_core off;
}
  1. 测试

在nginx.conf下配置如下内容:

location /lua{default_type 'text/html';content_by_lua 'ngx.say("<h1>HELLO,LUA</h1>")';
}

配置成功后,启动nginx,通过浏览器进行访问,如果获取到如下结果,则证明安装成功。

在这里插入图片描述

4.3.2. 方式二:OpenRestry

概述

​ 前面我们提到过,OpenResty是由淘宝工程师开发的,所以其官方网站(http://openresty.org/)我们读起来是非常的方便。OpenResty是一个基于Nginx与 Lua 的高性能 Web 平台,其内部集成了大量精良的 Lua 库、第三方模块以及大多数的依赖项。用于方便地搭建能够处理超高并发、扩展性极高的动态 Web 应用、Web 服务和动态网关。所以本身OpenResty内部就已经集成了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("<h1>HELLO,OpenRestry</h1>")';
}
(9)在sbin目录下启动nginx
(10)通过浏览器访问测试

在这里插入图片描述

在这里插入图片描述

4.4. 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_file

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代码。

需求:

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

实现代码

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' thenreturn name..'先生'elseif gender=='0' thenreturn name..'女士'elsereturn nameend";header_filter_by_lua "ngx.header.aaa='bbb'";return 200 $name;
}

4.5. ngx_lua操作Redis

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环境

连接地址
host= 192.168.200.111
port=6379

在这里插入图片描述

步骤二:准备对应的API

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

步骤三:效果实现

location / {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.200.1",6379) --设置redis连接信息if not ok then --判断是否连接成功ngx.say("failed to connection redis",err)returnendok,err = redisObj:set("username","TOM")--存入数据if not ok then --判断是否存入成功ngx.say("failed to set username",err)returnendlocal res,err = redisObj:get("username") --从redis中获取数据ngx.say(res)	--将数据写会消息体中redisObj:close()}
}

步骤四:运行测试效果

在这里插入图片描述

在这里插入图片描述

4.6. ngx_lua操作Mysql

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

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

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

4.6.1 lua-resty-mysql

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

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

步骤一:

准备MYSQL

host: 192.168.200.111
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.200.111:3306/nginx_db
username=root
password=123456

步骤二:API学习

(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:服务器监听端口,默认为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 /{content_by_lua_block{local mysql = require "resty.mysql"local db = mysql:new()local ok,err = db:connect{host="192.168.200.111",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()}}

问题:

1.如何获取返回数据的内容
2.如何实现查询多条数据
3.如何实现数据库的增删改操作
使用lua-cjson处理查询结果

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

步骤一:引入cjson

local cjson = require "cjson"

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

cjson.encode(res) 

步骤三:使用

location /{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.200.111",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) dongx.say(v.id..","..v.username..","..v.birthday..","..v.salary)enddb:close()}}
lua-resty-mysql实现数据库的增删改

优化send_query和read_result

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

语法:

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

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

location /{content_by_lua_block{local mysql = require "resty.mysql"local db = mysql:new()local ok,err = db:connect{host="192.168.200.1",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.6.2 综合小案例

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

分析:

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

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

http://191.168.200.133?username=TOM

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

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

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

init_by_lua_block{redis = require "resty.redis"mysql = require "resty.mysql"cjson = require "cjson"
}
location /{default_type "text/html";content_by_lua_block{--获取请求的参数usernamelocal param = ngx.req.get_uri_args()["username"]--建立mysql数据库的连接local db = mysql:new()local ok,err = db:connect{host="192.168.200.111",port=3306,user="root",password="123456",database="nginx_db"}if not ok thenngx.say("failed connect to mysql:",err)returnend--设置连接超时时间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.200.111",6379)if not ok thenngx.say("failed to connect to redis:",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()}}

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

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

串口助手的qt实现思路

要求实现如下功能&#xff1a; 获取串口号&#xff1a; foreach (const QSerialPortInfo &serialPortInfo, QSerialPortInfo::availablePorts()) {qDebug() << "Port: " << serialPortInfo.portName(); // e.g. "COM1"qDebug() <<…

绿色数据中心:实现可持续发展和具备盈利能力的全闪存解决方案

数据中心成为了当今数字世界的支柱&#xff0c;负责存储、处理和分发驱动几乎所有数字服务产生&#xff08;从网上银行到即时消息&#xff09;的数据。这使得数字中心逐渐成为了现代商业基础设施的关键组成部分。 但是&#xff0c;随之而来的是&#xff0c;数据中心也已经成为…

基于asp.net固定资产管理系统设计与实现

博主介绍&#xff1a;专注于Java vue .net php phython 小程序 等诸多技术领域和毕业项目实战、企业信息化系统建设&#xff0c;从业十五余年开发设计教学工作 ☆☆☆ 精彩专栏推荐订阅☆☆☆☆☆不然下次找不到哟 我的博客空间发布了1000毕设题目 方便大家学习使用 感兴趣的…

如何着手创建企业数据目录?(三)权限管理及版本控制

前文导读&#xff1a; 《如何着手创建企业数据目录&#xff1f;&#xff08;一&#xff09;数据目录的设定》 《如何着手创建企业数据目录&#xff1f;&#xff08;二&#xff09;数据的命名与维护》 前面聊过了数据目录的设定、数据命名规则和维护规则&#xff0c;今天我们继续…

34.打字机效果 水平滚动贴合

打字机效果 创建打字机效果动画。 定义两个动画,typing 用于字符动画,blink 用于光标动画。使用 ::after 伪元素在容器元素中添加光标。使用 JavaScript 为内部元素设置文本,并设置包含字符数的 --characters 变量。这个变量用于文本动画。使用 white-space: nowrap 和 overflo…

【华为杯】2024数学建模研赛题目

2024数学建模研赛题目已经发布 各个赛题题目如下&#xff1a; A题 B题 C题 D题 E题 F题 赛题完整版在文末&#xff0c;点击下方名片。

离散型制造业MES系统主要功能介绍

一、离散型制造业的特点 离散型制造业是指生产过程中涉及多个独立工序或步骤&#xff0c;且这些工序之间相对独立、缺乏连续性的企业。其特点主要包括&#xff1a; 产品种类多&#xff0c;开发频繁&#xff1a; 离散型制造业通常需要进行多品种产品开发&#xff0c;产品种类繁…

OpenCV特征检测(2)边缘检测函数Canny()的使用

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 使用 Canny 算法 48在图像中查找边缘。 该函数使用 Canny 算法在输入图像中查找边缘&#xff0c;并在输出地图 edges 中标记它们。在 threshold1…

微服务架构---Ribbon\Feign

Ribbon(负载均衡) Ribbon概述 在 SpringCloud 中&#xff0c; Nacos⼀般配合Ribbon进行使用&#xff0c;Ribbon提供了客户端负载均衡的功能&#xff0c;Ribbon利用从Nacos中读取到的服务信息&#xff0c;在调用服务节点提供的服务时&#xff0c;会合理的进行负载。 Ribbon作…

Java内部类一口气讲完!( •̀ ω •́ )✧

Java 内部类 Java面向对象的设计 - Java 内部类 什么是内部类&#xff1f; 作为包的成员的类被称为顶级类。 一个类可以在另一个类中声明。这种类型的类称为内部类。 如果在另一个类中声明的类被显式或隐式声明为static&#xff0c;它被称为嵌套类&#xff0c;而不是内部类…

Apache Flink 流批融合技术介绍

摘要&#xff1a;本文整理自阿里云高级研发工程师、Apache Flink Contributor 周云峰老师在 Apache Asia CommunityOverCode 2024中的分享。内容主要分为以下三个部分&#xff1a; 从流批一体到流批融合流批融合的技术解决方案社区进展及未来展望 一、从流批一体到流批融合 1&…

音视频开发之旅(95)-基于多模态的画质评测算法Q-Align

目录 1.背景与问题 2.人工MOS评测的过程 3.评分等级与评分的转换 4.构建对话式指令数据集 5.Q-ALIGN模型结构 6.实验结果 7.源码分析 8.资料 一、背景和问题 多模态模型&#xff08;LMMs&#xff09;在视觉和语言方面展现出非常强大的能力&#xff0c;它们能够很好地理…

【数据结构】假设二叉树采用二叉链表存储,编写一棵二又树中序遍历的非递归算法。

编程题&#xff1a; 假设二叉树采用二叉链表存储&#xff0c;编写一棵二又树中序遍历的非递归算法。 分析&#xff1a; 算法描述&#xff1a; 非递归中序遍历二叉树的算法使用栈来辅助实现。首先&#xff0c;从根节点开始&#xff0c;沿着左子树不断向下&#xff0c; 将每个节…

DataFrame生成excel后为什么多了一行数字

问题描述 python查询数据生成excel文件&#xff0c;生成的excel多了第一行数字索引&#xff0c;1,2,3,4,5...... 代码&#xff1a; df pd.DataFrame(data)df.to_excel(filename, sheet_name用户信息表, indexFalse) 解决&#xff1a; 原理也很简单&#xff0c;就是设置个参…

MCS-51汇编

伪指令&#xff1a; EQU: Equal&#xff0c;定义常量 COUNT EQU 10H ; 定义一个符号名COUNT&#xff0c;其值为10H DELAY EQU 500 ; 定义一个符号名DELAY&#xff0c;其值为500 数据传送&#xff1a; MOV: MOVE&#xff0c;传送数据 MOVC: 算术运算&#xff1a; 跳转…

开源 AI 智能名片 S2B2C 商城小程序与正能量融入对社群归属感的影响

摘要&#xff1a;本文探讨了开源 AI 智能名片 S2B2C 商城小程序在社群运营中的作用&#xff0c;以及融入正能量对提高社群归属感的关键意义。通过分析正能量的精神感染力和对社群氛围的积极影响&#xff0c;阐述了在开源 AI 智能名片 S2B2C 商城小程序的各类活动中融入正能量的…

数据结构之线性表——LeetCode:707. 设计链表,206. 反转链表,92. 反转链表 II

707. 设计链表 题目描述 707. 设计链表 你可以选择使用单链表或者双链表&#xff0c;设计并实现自己的链表。 单链表中的节点应该具备两个属性&#xff1a;val 和 next 。val 是当前节点的值&#xff0c;next 是指向下一个节点的指针/引用。 如果是双向链表&#xff0c;则…

【经验技巧】IBIS AMI模型眼图仿真问题探讨

最近&#xff0c;有同事问我&#xff1a;“拿到供应商的IBIS AMI模型&#xff0c;怎么判断是否可以进行应力&#xff08;统计&#xff09;眼图的仿真呀&#xff1f;如果不能进行&#xff0c;又怎么判断结果是瞬态仿真呢&#xff1f;” 不得不说&#xff0c;这的确是一个不错的话…

2024秋面向对象程序设计pta-实验二

6-1 设计一个矩形类Rectangle class Rectangle{ double width1; double height 1; public Rectangle(){} public Rectangle(double width, double height){ this.widthwidth; this.heightheight;} public double getArea(){ return width*height;} public double getPerimete…

en造数据结构与算法C# 用Unity实现简单的群组行为算法 之 对齐

en造数据结构与算法C# 用Unity实现简单的群组行为算法 之 聚集-CSDN博客 en造数据结构与算法C# 用Unity实现简单的群组行为算法 之 聚集-CSDN博客 演示 思路 1.检测 自然是沿用前两节的检测范围 2.对齐朝向 对齐朝向就是邻居鸟的forward加起来再除总数得到平均数 3.对齐…