百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术资源 > 正文

让我直接涨薪5K的Nginx/OpenResty详解,NginxLua操作Redis有多牛

off999 2025-02-16 22:27 12 浏览 0 评论

Nginx Lua操作Redis

本节介绍如何使用开源的lua-resty-redis模块在Lua脚本中连接和访问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模块lua-resty-redis之前需要在官方网站下载resty/redis.lua库文件,然后将该库文件加入项目工程所在的Lua外部库路径。lua-resty-redis官方已经申明,大部分的Redis操作命令都实现了同名的Lua API方法。

下面是一个简单的使用Lua模块lua-resty-redis操作Redis的实

例,代码如下:

local redis = require "resty.redis"
local config = require("luaScript.module.config.redis-config");
---启动调试
local mobdebug = require("luaScript.initial.mobdebug");
mobdebug.start();
--设置超时时长
local red = redis:new()
--设置超时时长,单位为ms
red:set_timeouts(config.timeout, config.timeout, config.timeout)
--连接服务器
local ok, err = red:connect(config.host_name, config.port)
if not ok then
 ngx.say("failed to connect: ", err)
 return
end
--设置值
ok, err = red:set("dog", "an animal")
if not ok then
 ngx.say("failed to set dog: ", err, "
") return else ngx.say("set dog: ok", "
") end --取值 local res, err = red:get("dog") --判空演示 if not res or res == ngx.null then ngx.say("failed to get dog: ", err, "
") 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("cat")red:get("horse") red:get("dog") local results, err = red:commit_pipeline() if not results then ngx.say("failed to commit the pipelined requests: ", err) return end for i, res in ipairs(results) do if type(res) == "table" then if res[1] == false then ngx.say("failed to run command ", i, ": ", res[2], "
") else --处理表容器 ngx.say("succeed to run command ", i, ": ", res[i], "
") end else --处理变量 ngx.say("succeed to run command ", i, ": ", res, "
") end end --简单的关闭连接 local ok, err = red:close() if not ok then ngx.say("failed to close: ", err) return else ngx.say("succeed to close 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 {
 content_by_lua_file luaScript/redis/RedisDemo.lua;
 }

修改nginx-redis-demo.conf文件后需要重启OpenRestry,然后可以使用浏览器访问其地址/redis_demo,具体的访问结果如图8-21所示。

RedisDemo.lua通过require导入了redis-config.lua配置文件,该文件定义了整个项目都需要使用的全局Redis配置信息,其代码如下:

--定义一个统一的redis配置模块
--统一的模块对象
local _Module = {
 --redis服务器的地址
 host_name = "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操作的基础类,主要的代码如下:

local redis = require "resty.redis"
local basic = require("luaScript.module.common.basic");
local config = require("luaScript.module.config.redis-config");
--连接池大小
local pool_size = config.pool_size;
--最大的空闲时间,单位:毫秒
local pool_max_idle_time = 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 sec,connect_timeout, send_timeout, read_timeout
 red:set_timeout(config.timeout, config.timeout, config.timeout);
 local 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
--缓存值
function _Module.setValue(self, key, value)
 ok, err = self.red:set(key, value)
 if not ok then
 basic.error("redis缓存设置失败")
 return false;
 end basic.log("set result ok")
 return true;
end
--值递增
function _Module.incrValue(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
--过期
function _Module.expire(self, key, seconds)
 ok, err = self.red:expire(key, seconds)
 if not ok then
 basic.error("redis设置过期失败 ")
 return false;
 end
 return true;
end
--获取值
function _Module.getValue(self, key)
 local resp, err = self.red:get(key)
 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
 local ok, err = self.red:set_keepalive(pool_max_idle_time, pool_size)
 if not ok then
 basic.error("redis set_keepalive执行失败 ")
 end
 basic.log("redis连接释放成功")
end
return _Module

此基础操作类的名称为RedisOperator,需要使用时通过require("
luaScript.redis.RedisOperator")导入即可。

在Lua中使用Redis连接池

在示例代码RedisDemo.lua脚本中,每一次客户端请求,luaresty-redis模块都会创建一个新的Redis连接。在生产环境中,每一次请求都开启一个服务器新连接会导致以下问题:

(1)连接资源被快速消耗。

(2)网络一旦抖动,会有大量TIME_WAIT连接产生,需要定期重启服务程序或机器。

(3)服务器工作不稳定,QPS忽高忽低。

(4)性能普遍上不去。

为什么会出现这些性能问题呢?因为每一次传输数据,我们需要完成创建连接、收发数据、拆除连接3个基本步骤,在低并发场景下每次请求完整走完这3步基本上不会有什么问题。然而,一旦挪到高并发应用场景,性能问题就出现了。

性能优化的第一件事情就是把短连接改成长连接,可以减少大量创建连接、拆除连接的时间。从性能上来说肯定要比短连接好很多,但还是有比较大的浪费。

性能优化的第二件事情就是使用连接池。通过一个连接池pool将所有长连接缓存和管理起来,谁需要使用,就从这里取走,干完活立马放回来。

实际上,大家在开发过程中用到的连接池是非常多的,比如HTTP连接池、数据库连接池、消息推送连接池等。实际上,几乎所有点到点之间的连接资源复用都需要通过连接池完成。在OpenResty中,lua-resty-redis模块管理了一个连接池,并且定义了set_keepalive方法完成连接的回收和复用。set_keepalive方法的语法如下:

ok, err = red:set_keepalive(max_idle_timeout, pool_size)

该方法将当前的Redis连接立即放入连接池。其中,max_idle_timeout参数指定连接在池中的最大空闲超时时长(以毫秒为单位);pool_size参数指定每个Nginx工作进程的连接池的最大连接数。如果入池成功,就返回1;如果入池出现错误,就返回nil,并返回错误描述字符串。

下面看一个连接回收的示例,具体的代码如下:

 location /pool_demo {
 content_by_lua_block {
 local redis = require "resty.redis"
 local config = require("luaScript.module.config.redis-config");
 --连接池大小
 local pool_size = config.pool_size;
 --最大的空闲时间,单位:毫秒
 local pool_max_idle_time = config.pool_max_idle_time;
 local red = redis:new()
 local ok, err = red:connect(config.host_name, config.port)
 if not ok then
 ngx.say("failed to connect: ", err)
 return
 else
 ngx.say("succeed to connect redis", "
") end red: auth(config.password) --red: set_keepalive(pool_max_idle_time, pool_size) --① 坑 ok, err = red:set("dog", "an animal") if not ok then --red: set_keepalive(pool_max_idle_time, pool_size) --② 坑 return end --③ 正确回收 red: set_keepalive(pool_max_idle_time, pool_size) ngx.say("succeed to collect redis connection", "
") } }

以上代码中,有3个需要注意的地方,具体介绍如下:

①坑:只有数据传输完毕、Redis连接使用完成之后,才能调用set_keepalive方法将连接放到池子里,set_keepalive方法会立即将red连接对象转换到closed状态,后面的Redis调用将出错。

②坑:如果设置错误,那么red连接对象不一定可用,不能把可用性存疑的连接放回池子里,如果另一个请求从连接池获取到一个不能用的连接,就会直接报错。

③正确回收:此处的set_keepalive方法调用是正确的。

以上代码处于nginx-redis-demo.conf文件中,修改后需要重启OpenRestry,然后可以使用浏览器访问其地址/pool_demo,具体的访问结果如图8-22所示。

set_keepalive方法完成连接回收之后,下一次通过red:

connect(...)获取连接时,connect方法在创建新连接前会在连接池中查找空闲连接,只有查找失败才会真正创建新连接。

总之,作为一个专业的服务端开发工程师,大家必须要对连接池有较深理解,其实不论是Redis连接池、HTTP连接池,还是数据库连接池,甚至是线程池,其原理都是差不多的。

本文给大家讲解的内容是Nginx/OpenResty详解,Nginx Lua编程,Nginx Lua操作Redis

  1. 下篇文章给大家讲解的是 Nginx/OpenResty详解,Nginx Lua编程, Nginx Lua编程实战案例;
  2. 觉得文章不错的朋友可以转发此文关注小编;
  3. 感谢大家的支持!

相关推荐

Python进阶教程:如何自定义异常(附电商库存管理案例)

在Python中,你可以使用异常(exceptions)来预期和处理那些干扰程序正常执行流程的错误。Python内置了许多常见的异常,例如ValueError、TypeError和KeyError,但...

Python 中使用try来处理异常的方法

六二,直方大,不习无不利。在学习python中会经常遇到各种异常事件;现归纳一下如何捕捉并处理这些异常;今天来给大家整理一下。一、异常的概念什么是异常?异常即是一个事件,该事件会在程序执行过程中发生,...

python入门-day3:异常处理(异常处理方法及流程python)

异常处理的内容,包括try、except、finally的用法,介绍常见异常类型,最后通过一个练习(处理用户输入的非法数字)帮你把知识点串起来。我会用简单易懂的语言,确保新手也能轻松掌握。Da...

16-Python的异常(python的异常类型及处理)

1-异常介绍1-2-什么是异常异常是程序运行时发生的错误或异常情况,它会中断正常的程序执行流程;Python使用异常处理机制来处理这些运行时错误。1-4-为什么要捕获异常异常会中断程序的执行;1-3-...

Python 异常处理详解(python中异常)

一、什么是异常?核心定义在程序运行过程中,当Python解释器检测到无法继续执行的操作时,会立即创建异常对象并中断当前流程。此时若不处理异常,程序将直接崩溃退出。典型场景与原理用户输入无效数据nu...

掌握这些 Python 异常处理技巧,代码稳如老狗!

在Python中,异常处理不仅可以帮助我们捕获和处理错误,还能让我们更清晰地了解错误发生的背景。1.异常层次结构Python内置了许多异常,我们在编程时可能会经常遇到,例如ZeroDivisi...

python异常处理机制最佳实践(python异常处理总结)

Python异常处理的最佳实践需要兼顾代码健壮性、可读性和性能。以下是经过工程验证的10个核心原则和技巧:一、精准捕获原则避免裸except禁止使用except:或exceptExce...

python之异常处理(python异常处理过程可以概括为三个步骤)

异常语法try:<代码块>except<异常类型>:<代码块>捕获通用异常try:<代码块>exceptExceptionas...

一天学一点,今天学习掌握Python:异常处理与文件操作全攻略

这一笔记记录了我学习python的异常和文件的操作,这也是针对Python异常和文件操作教程的进一步优化建议和注意事项:异常处理优化1.避免过度捕获异常o不要为了捕获异常而捕获异常,应根据实际需求...

新手学Python避坑,学习效率狂飙! 十二、Python 异常处理

异常处理系统分享在Python里,异常指的是程序运行期间出现的错误。当异常发生时,正常的程序流程会被中断,Python会抛出异常对象。为了防止程序因异常而崩溃,你可以使用异常处理机制捕获并处理这...

Python异常处理全攻略:从入门到精通,轻松应对代码“翻车”时刻

喜欢的条友记得关注、点赞、转发、收藏,你们的支持就是我最大的动力源泉。引言:异常处理——代码世界的“保险丝”在编程的世界里,错误和异常就像路上的“坑”,稍不留神就会让你的程序“翻车”。而异常处理,就是...

Python异常处理全面指南(python异常处理步骤)

Python异常处理完全指南异常处理是编写健壮程序的关键技术。Python提供了灵活的语法和丰富的内置异常类型,能够有效管理程序运行时的错误。以下是Python异常处理的全面指南:一、异常处理...

进阶突破python——异常处理机制(异常处理用什么语句python)

Python的异常处理机制是其健壮性设计的核心,通过清晰的语法结构和灵活的处理策略实现错误管理。以下从核心语法、异常对象、高级特性和最佳实践四个维度详解:一、核心语法结构Python使用try-...

Python基础编程——详细介绍Python的异常捕获示例代码

这篇文章主要为大家详细介绍了python的异常捕获,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下,希望能够给你带来帮助①捕捉一个异常捕捉一个异常以用0作为除数会得到Ze...

Python学习 -- 异常捕获技巧(python怎么捕获异常)

在编写Python代码时,异常处理是确保程序稳定性的关键。Python提供了灵活的异常捕获机制,包括try...except语句、try...except...else语句和try...except....

取消回复欢迎 发表评论: