openresty nginx 让我直接涨薪5K的Nginx/OpenResty详解,NginxLua操作Redis有多牛
openresty nginx 让我直接涨薪5K的Nginx/OpenResty详解,NginxLua操作Redis有多牛
Nginx Lua操作Redis
本节将阐述如何在Lua脚本中运用lua-resty-redis模块来建立与Redis的连接并对其进行操作,该模块的官方网址可访问:
访问该网址:https://github.com/openresty/lua-resty-redis,即可获取相关信息。
实战案例操作前的准备工作:本部分内容将涉及源码工程中的nginxredis-demo.conf配置文件。在执行本节示例之前,必须对启动脚本openresty-start.bat(或openresty-start.sh)进行编辑,将其中PROJECT_CONF变量的设定更改为nginx-redis-demo.conf,随后对OpenRestry进行重启操作。
Redis的CRUD基本操作
在应用lua-resty-redis模块之前,需先从其官方网站下载resty/redis.lua库文件,并将此库文件纳入项目工程对应的外部Lua库路径之中。据lua-resty-redis官方所述,该模块已将大部分Redis操作命令映射为同名的Lua API函数。
以下是一个示例,展示了如何使用Lua模块lua-resty-redis来对Redis进行操作,过程相对简便。
例,代码如下:
在本地,我们通过require关键字引入了名为resty.redis的模块,并将其赋值给变量local redis。
在本地文件中,我们通过require函数调用了名为"luaScript.module.config.redis-config"的模块配置,并将其赋值给变量local config。
---启动调试
在本地环境中,我们通过调用"luaScript.initial.mobdebug"模块,成功引入了变量mobdebug。
mobdebug.start();
--设置超时时长
local red = redis:new()
--设置超时时长,单位为ms
red: 设置超时时间为配置文件中的timeout值,分别应用于三个不同的场景,具体为:config.timeout, config.timeout, config.timeout。
--连接服务器
本地连接操作成功与否分别为ok和err,red模块尝试连接到配置文件中指定的主机名和端口。
if not ok then
ngx输出("连接失败:", 错误信息)
return
end
--设置值
red库对"dog"键进行了设置,赋予其值为"an animal",操作结果为ok和err。
if not ok then
无法成功设置狗:", 错误信息为,, , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , ,
")
return
else
ngx.say("set dog: ok", "
")
end
--取值
在执行red对象的get方法,针对键值"dog"进行查询操作后,获取了局部变量res和err,其中res代表查询结果,err则表示可能出现的错误信息。
--判空演示
若非res不存在,亦或是res等于ngx的null值。
无法获取狗的信息,错误信息为:,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
")
return
else
ngx.say("get dog: ok", "
", res, "
")
end
--批量操作,减少网络IO次数
red:init_pipeline()
red:set("cat", "cat 1")
red:set("horse", "horse 1")
获取“猫”的指令,获取“马”的指令。
red:get("dog")
本地操作结果,以及可能出现的错误,均由red:commit_pipeline()函数执行并返回。
if not results then
ngx.say("提交管道请求时出现错误:", err)
return
end
遍历results数组,对每个元素i及其对应的值res进行操作。
if type(res) == "table" then
if res[1] == false then
无法执行命令,编号为:,错误信息为:。
")
else
--处理表容器
成功执行了命令,编号为:", i, ",结果为:", res[i], "。"
")
end
else
--处理变量
成功执行了命令,编号为:", i, ",结果为:", res, "。"
")
end
end
--简单的关闭连接
local ok, err = red:close()
if not ok then
ngx输出信息表示:“关闭失败,原因如下:”,随后跟随着错误信息。
return
else
成功关闭了redis连接。
end
以上Lua脚本处于工程目录下的
在luaScript/redis目录下的RedisDemo.lua文件里,实现了以下对Redis的操作:
(1)连接Redis服务器。
(2)根据key设置缓存值。
(3)根据key获取缓存值。
(4)批量Redis操作。
(5)简单地关闭Redis连接。
在nginx-redis-demo.conf配置文件内,需创建一个location配置段,以启用该脚本,具体配置代码表现如下:
简单操作演示 #redis CRUD简单操作演示
location /redis_demo {
执行脚本文件 luaScript/redis/RedisDemo.lua,内容通过 Lua 语言编写;
}
对nginx-redis-demo.conf文件进行修改后,必须重新启动OpenRestry服务。启动成功后,通过浏览器输入地址/redis_demo,即可进行访问,访问效果详见图8-21。
图8-21 Redis CRUD简单操作演示的访问结
RedisDemo.lua文件通过require函数引入了redis-config.lua配置脚本,该脚本中包含了项目所需的全局Redis配置数据,具体内容如下:
--定义一个统一的redis配置模块
--统一的模块对象
local _Module = {
--redis服务器的地址
主机名称设定为192.168.233.128。
--redis服务器的端口
port = "6379";
服务器的数据库 --redis服务器的数据库
db = "0";
--redis服务器的密码
password = '123456';
--连接超时时长
timeout = 20000;
--线程池的连接数量
pool_size = 100;
--最大的空闲时间,单位:毫秒
pool_max_idle_time = 10000;
}
return _Module
实战:封装一个操作Redis的基础类
在Lua脚本中操作Redis时,需要完成建立连接、处理数据以及释放连接等一系列基础任务。鉴于此,建议将这些基本操作封装在一个Redis操作的基础类中openresty nginx 让我直接涨薪5K的Nginx/OpenResty详解,NginxLua操作Redis有多牛,具体代码如下:,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
local redis = require "resty.redis"
局部变量 basic 被定义为从 "luaScript.module.common.basic" 文件中引入的模块。
local config = require("luaScript.module.config.redis-config");
--连接池大小
本地池的大小被设置为配置文件中定义的池大小。
--最大的空闲时间,单位:毫秒
本地的连接池最大空闲时间设置为配置文件中定义的pool_max_idle_time值。
--一个统一的模块对象
local _Module = {}
_Module.__index = _Module
--类的方法new
function _Module.new(self)
local object = { red = nil }
setmetatable(object, self)
return object
end
--获取redis连接
function _Module.open(self)
local red = redis:new()
将超时时间设定为2秒钟,包括连接超时、发送超时以及读取超时。
red设置超时时间为config.timeout,同时配置中的timeout值也被设置为config.timeout,并且这个值同样被应用在config.timeout上。
本地变量ok和err分别被赋值为red:connect函数调用返回的结果,其中该函数尝试连接到由config.host_name和config.port指定的主机名和端口。
if not ok then
basic.error("无法建立与redis服务器的连接,具体错误信息为:", err)
return false;
end
if config.password then
red:auth(config.password)
end
if config.db then
red:select(config.db)
end
basic.log("连接redis服务器成功")
self.red = red;
return true;
end
--缓存值
该函数名为“_Module.setValue”,其作用是为对象self设置属性key的值为value。
好的,错误码等于self.red对象调用set方法,传入key和value参数后返回的结果。
if not ok then
basic.error("redis缓存设置失败")
return false;
结束basic.log操作,确认结果设置成功。
return true;
end
--值递增
该模块的函数用于对指定键值进行自增操作,self参数代表当前模块实例,key参数则表示需要增加值的键。
ok, err = self.red:incr(key)
if not ok then
basic.error("redis缓存递增失败 ")
return false;
end
basic.log("incr ok")
return true;
end
--过期
该模块的expire方法,用于对指定键值进行过期设置,其参数包括自身对象self、键名key以及过期时间seconds。
执行操作后,返回值中包含成功标识与错误信息,即ok表示操作是否成功,err则记录可能出现的错误,self.red:expire(key, seconds)为执行的具体操作。
if not ok then
basic.error("redis设置过期失败 ")
return false;
end
return true;
end
--获取值
定义函数,名为_getValue,隶属于_Module模块,该函数接收两个参数:self和key。
self.red获取键值对时,返回了局部变量resp和错误信息err。
if not resp then
basic.error("redis缓存读取失败 ")
return nil;
end
return resp;
end
--省略封装的其他Redis操作方法
--将连接还给连接池
function _Module.close(self)
if not self.red then
return
end
self.red调用了set_keepalive方法,设定了连接池的最大空闲时间以及连接池的大小,操作返回了两个值,一个是ok表示操作是否成功,另一个是err表示可能出现的错误信息。
if not ok then
基本错误提示:“redis设置保持活跃操作未能成功完成。”
end
basic.log("redis连接释放成功")
end
return _Module
此操作模块的名称定义为RedisOperator,在使用过程中,需通过require函数进行调用。
只需导入“luaScript.redis.RedisOperator”模块,即可实现。
在Lua中使用Redis连接池
在RedisDemo.lua脚本示例中,每当客户端发起请求openresty nginx 让我直接涨薪5K的Nginx/OpenResty详解,NginxLua操作Redis有多牛,luaresty-redis模块便会建立一个全新的Redis连接。然而,在生产环境中,每有请求便启动一个新服务器连接,这一做法将引发以下问题:,
(1)连接资源被快速消耗。
网络一旦出现波动,便会引发众多TIME_WAIT状态的连接openresty nginx,这就要求我们定期对服务程序或设备进行重启处理。
(3)服务器工作不稳定,QPS忽高忽低。
(4)性能普遍上不去。
为何此类性能问题频发?究其原因,每次数据传输均需经历建立连接、数据交互与连接断开三个必要环节。在并发量较低的情况下,逐个完成这三个步骤通常不会有显著问题。但若切换至高并发应用环境,性能瓶颈便显现出来。
性能提升的首要任务是将短暂连接转变为持久连接,这样做能够显著降低频繁建立和断开连接所需的时间。在性能方面,这无疑优于短连接,尽管如此,仍存在一定的资源浪费。
性能提升的第二步举措是引入连接池机制。借助连接池这一工具,我们将所有持久的连接进行集中存储与管控,当有需求时,用户可从中提取所需连接,完成任务后即刻归还。
在开发过程中,我们广泛使用各种连接池,诸如HTTP连接池、数据库连接池以及消息推送连接池等。实际上,实现点对点连接资源的重复利用,几乎都依赖于连接池。以OpenResty为例,其中的lua-resty-redis模块负责管理一个连接池,并通过set_keepalive方法实现了连接的回收与复用。set_keepalive方法的语法如下:
执行red:set_keepalive操作,设定最大空闲超时时间为max_idle_timeout,连接池大小为pool_size,返回结果为ok和err。
此方法将现有Redis连接即刻纳入连接池管理。在配置中,max_idle_timeout参数设定了连接在连接池中允许的最大空闲时间,单位为毫秒;而pool_size参数则限定了每个Nginx进程所能维持的连接池中最大连接数。若连接成功加入池中,则函数返回1;若在加入过程中发生错误,则返回nil,并附带相应的错误描述信息。
下面看一个连接回收的示例,具体的代码如下:
location /pool_demo {
content_by_lua_block {
在本地,我们引入了名为redis的模块,该模块是通过require "resty.redis"实现的。
在本地,我们通过require函数调用了名为"luaScript.module.config.redis-config"的模块配置,并将其赋值给变量local config。
--连接池大小
局部变量pool_size的值被设置为配置文件中的pool_size。
--最大的空闲时间,单位:毫秒
本地池的最大空闲时间设置为配置文件中定义的pool_max_idle_time值。
local red = redis:new()
本地执行red:connect(config.host_name, config.port)操作后,返回了两个值,一个是ok,另一个是err。
if not ok then
ngx.say("failed to connect: ", err)
return
else
成功连接到redis后,ngx输出提示信息:,
")
end
red: auth(config.password)
设置连接池的存活时间与最大空闲连接数,执行操作时可能会遇到问题。
好的,执行结果为:ok表示成功,err为空,即red:set函数将字符串"dog"赋值为"an animal"。
if not ok then
设置连接池的存活时间与最大空闲连接数,此操作存在潜在风险。
return
end
--③ 正确回收
红色部分:设定连接池的最大空闲时间与池大小,启用保活机制。
成功获取了Redis连接,ngx.say("succeed to collect redis connection", ",");
")
}
}
以上代码中,有3个需要注意的地方,具体介绍如下:
只有在数据传输彻底完成,并且Redis的连接已经被成功使用之后,我们才能执行set_keepalive方法,将这一连接存入连接池中。此时,set_keepalive方法会立刻将Redis连接对象的状态调整为关闭,若在此之后继续进行Redis的调用操作openresty nginx,将会引发错误。
若配置不当,red所连接的对象可能无法正常使用;将不确定能否使用的连接放回池中并不可取;若其他请求从该连接池中取出了无效的连接,系统将直接出现错误提示。
此处对set_keepalive方法的调用是恰当无误的。
该代码位于nginx-redis-demo.conf文件内,对其进行修改后,必须重新启动OpenRestry。启动成功后,通过浏览器访问/pool_demo路径,即可看到如图8-22所示的访问效果。
图8-22 Redis连接池演示实例的执行结果
使用set_keepalive方法处理完连接回收事宜后,在后续操作中,每当通过red:
在调用connect函数以建立连接的过程中,该函数会首先在连接池中搜寻可用的空闲连接,只有在未能找到空闲连接的情况下openresty nginx,才会着手创建一个新的连接。
总的来说,作为一名资深的后端开发者,我们必须对连接池的概念有深入的认识。实际上,无论是Redis的连接池、HTTP的连接池,还是数据库的连接池,甚至是线程池,它们的运作机制都是大同小异的。
本文旨在为大家深入剖析Nginx与OpenResty的技术细节,以及Nginx Lua编程的相关知识。在下篇文章中,我们将继续探讨Nginx Lua编程,并分享一些实战案例。若您觉得这篇文章有价值,不妨将其转发并关注我们的公众号;您的支持是我们前进的动力!
- 随机文章
- 热门文章
- 热评文章