文章目录

  • 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..'"}')

然后把他们三个文件放进同一个文件夹中:

redis proxy连接池配置 openresty redis 连接池_数据


配置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