文章目录
- 1、redis连接池
- 2、编写测试脚本
前言:
openresty 、lua 、redis 的 安装这里就不再赘述了,请自行百度。
使用到了OpenResty,很核心的目的是为了解决高并发的问题,为了不让数据库成为高并发的瓶颈,那么操作redis就变的不可避免了,下面介绍OpenResty操作redis的过程
1、redis连接池
创建 redis_factory.lua
local redis_factory = function(h)
local h = h
h.redis = require('resty.redis')
h.cosocket_pool = {max_idel = 10000, size = 10000}
h.commands = {
"append", "auth", "bgrewriteaof",
"bgsave", "bitcount", "bitop",
"blpop", "brpop",
"brpoplpush", "client", "config",
"dbsize",
"debug", "decr", "decrby",
"del", "discard", "dump",
"echo",
"eval", "exec", "exists",
"expire", "expireat", "flushall",
"flushdb", "get", "getbit",
"getrange", "getset", "hdel",
"hexists", "hget", "hgetall",
"hincrby", "hincrbyfloat", "hkeys",
"hlen",
"hmget", "hmset", "hscan",
"hset",
"hsetnx", "hvals", "incr",
"incrby", "incrbyfloat", "info",
"keys",
"lastsave", "lindex", "linsert",
"llen", "lpop", "lpush",
"lpushx", "lrange", "lrem",
"lset", "ltrim", "mget",
"migrate",
"monitor", "move", "mset",
"msetnx", "multi", "object",
"persist", "pexpire", "pexpireat",
"ping", "psetex", "psubscribe",
"pttl",
"publish", "punsubscribe", "pubsub",
"quit",
"randomkey", "rename", "renamenx",
"restore",
"rpop", "rpoplpush", "rpush",
"rpushx", "sadd", "save",
"scan", "scard", "script",
"sdiff", "sdiffstore",
"select", "set", "setbit",
"setex", "setnx", "setrange",
"shutdown", "sinter", "sinterstore",
"sismember", "slaveof", "slowlog",
"smembers", "smove", "sort",
"spop", "srandmember", "srem",
"sscan",
"strlen", "subscribe", "sunion",
"sunionstore", "sync", "time",
"ttl",
"type", "unsubscribe", "unwatch",
"watch", "zadd", "zcard",
"zcount", "zincrby", "zinterstore",
"zrange", "zrangebyscore", "zrank",
"zrem", "zremrangebyrank", "zremrangebyscore",
"zrevrange", "zrevrangebyscore", "zrevrank",
"zscan",
"zscore", "zunionstore", "evalsha",
-- resty redis private command
"set_keepalive", "init_pipeline", "commit_pipeline",
"array_to_hash", "add_commands", "get_reused_times",
}
-- connect
-- @param table connect_info, e.g { host="127.0.0.1", port=6379, pass="", timeout=1000, database=0}
-- @return boolean result
-- @return userdata redis_instance
h.connect = function(connect_info)
local redis_instance = h.redis:new()
redis_instance:set_timeout(connect_info.timeout)
if not redis_instance:connect(connect_info.host, connect_info.port) then
return false, nil
end
if connect_info.pass ~= '' then
redis_instance:auth(connect_info.pass)
end
redis_instance:select(connect_info.database)
return true, redis_instance
end
-- spawn_client
-- @param table h, include config info
-- @param string name, redis config name
-- @return table redis_client
h.spawn_client = function(h, name)
local self = {}
self.name = ""
self.redis_instance = nil
self.connect = nil
self.connect_info = {
host = "", port = 0, pass = "",
timeout = 0, database = 0
}
-- construct
self.construct = function(_, h, name)
-- set info
self.name = name
self.connect = h.connect
self.connect_info = h[name]
-- gen redis proxy client
for _, v in pairs(h.commands) do
self[v] = function(self, ...)
-- instance test and reconnect
if (type(self.redis_instance) == 'userdata: NULL' or type(self.redis_instance) == 'nil') then
local ok
ok, self.redis_instance = self.connect(self.connect_info)
if not ok then return false end
end
-- get data
local vas = { ... }
return self.redis_instance[v](self.redis_instance, ...)
end
end
return true
end
-- do construct
self:construct(h, name)
return self
end
local self = {}
self.pool = {} -- redis client name pool
-- construct
-- you can put your own construct code here.
self.construct = function()
return
end
-- spawn
-- @param string name, redis database serial name
-- @return boolean result
-- @return userdata redis
self.spawn = function(_, name)
if self.pool[name] == nil then
ngx.ctx[name] = h.spawn_client(h, name)
self.pool[name] = true
return true, ngx.ctx[name]
else
return true, ngx.ctx[name]
end
end
-- destruct
-- @return boolean allok, set_keepalive result
self.destruct = function()
local allok = true
for name, _ in pairs(self.pool) do
local ok, msg = ngx.ctx[name].redis_instance:set_keepalive(
h.cosocket_pool.max_idel, h.cosocket_pool.size
)
if not ok then allok = false end
end
return allok
end
-- do construct
self.construct()
return self
end
return redis_factory
创建 config_constant.lua
config = {}
config.redisConfig = {
redis_a = { -- your connection name
--ip
host = '192。168.56.10',
--端口
port = 6379,
--密码【如果没密码,可以不设置】
-- pass = '123456789',
--超时时间,如果是测试环境debug的话,这个值可以给长一点;如果是正式环境,可以设置为200
timeout = 120000,
--redis的库
database = 0,
},
}
return config
2、编写测试脚本
前端传递需要查询的key,将key的值取出来并返回给前端
创建 redistest.lua
--平台公共的配置文件常量
local config = require "luajscript.config_constant"
--redis连接池工厂
local redis_factory = require('luajscript.redis_factory')(config.redisConfig) -- import config when construct
--获取redis的连接实例
local ok, redis_a = redis_factory:spawn('redis_a')
--用于接收前端数据的对象
local args=nil
--获取前端的请求方式 并获取传递的参数
local request_method = ngx.var.request_method
--判断是get请求还是post请求并分别拿出相应的数据
if"GET" == request_method then
args = ngx.req.get_uri_args()
elseif "POST" == request_method then
ngx.req.read_body()
args = ngx.req.get_post_args()
--兼容请求使用post请求,但是传参以get方式传造成的无法获取到数据的bug
if (args == nil or args.data == null) then
args = ngx.req.get_uri_args()
end
end
--获取前端传递的key
local key = args.key
--在redis中获取key对应的值
local va = redis_a:get(key)
--响应前端
ngx.say('{"key":"'..key..'","va":"'..va..'"}')
然后把他们三个文件放进同一个文件夹中:
配置OpenResty下Nginx的Lua文件关联
在nginx.con中的80配置下添加如下配置
location /redis {
# root html;
# index index.html index.htm;
default_type text/html;
#content_by_lua_block {
#ngx.say("hello world");
#}
content_by_lua_file /mydata/luascript/lua-redis/testRedis.lua;
}
seckill.lua
-- 连接池
--平台公共的配置文件常量
local config = require "luajscript.config_constant"
--redis连接池工厂
local redis_factory = require('luajscript.redis_factory')(config.redisConfig) -- import config when construct
--获取redis的连接实例
local ok, redis_a = redis_factory:spawn('redis_a')
--用于接收前端数据的对象
local args=nil
--获取前端的请求方式 并获取传递的参数
local request_method = ngx.var.request_method
--判断是get请求还是post请求并分别拿出相应的数据
if"GET" == request_method then
args = ngx.req.get_uri_args()
elseif "POST" == request_method then
ngx.req.read_body()
args = ngx.req.get_post_args()
--兼容请求使用post请求,但是传参以get方式传造成的无法获取到数据的bug
if (args == nil or args.data == null) then
args = ngx.req.get_uri_args()
end
end
-- 秒杀证书处理
-- 获取URL信息
--获取前端传递的 method , 根据这个执行某个函数
local method = args.method
--获取前端传递的 randomId
local randomId = args.randomId
-- 下单时进行令牌验证
function getStockToken(randomIds)
-- 拼接 库存 信号量的KEY
local stockCountId = "seckill:stock:" .. randomIds;
ngx.say("<br>")
ngx.say("stockCountId : ", stockCountId)
ngx.say("<br>")
-- 根据信号量来看看是否有库存,如果有库存,则分配令牌,并在redis 记录下来 该 用户ID+令牌 防止 本用户多次秒杀
-- 在redis中获取key对应的值
local stockCount = redis_a:get(stockCountId)
--响应前端
ngx.say("<br>")
ngx.say("stockCount : ", stockCount)
ngx.say("<br>")
return;
end
-- 根据参数来选择执行
--返回值说明
--1 排队成功
--2 排队商品没有找到
--3 人数超过限制
--4 库存不足
--5 排队过了
--6 秒杀过了
-- -2 Lua 方法不存在
if method == 'getStockToken' then
ngx.say("<br>")
ngx.say("randomId : ", randomId)
ngx.say("<br>")
getStockToken(randomId)
elseif method == 'checkStockToken' then
return checkStockToken(randomId)
elseif method == 'deleteStockTokenNumber' then
return deleteStockTokenNumber(randomId)
else
return -2; -- Lua方法不存在
end
测试:
http://192.168.56.10:8189/seckill?method=getStockToken&randomId=18d29960d01045248d42277045bbc528 结果:
randomId : 18d29960d01045248d42277045bbc528
stockCountId : seckill:stock:18d29960d01045248d42277045bbc528
stockCount : 5