• nginx+lua乐观锁实现秒杀


    package.path = '/class_goods/nginx/lua/5.1/lua-redis-cluster-master/?.lua;;/class_goods/nginx/lua/5.1/lua-resty-redis/lib/?.lua;;/class_goods/nginx/lua/5.1/lua-resty-limit-traffic-master/lib/?.lua;;'
    ngx.header.content_type="text/plain";
    --获取get或post参数--------------------

    local request_method = ngx.var.request_method
    local args = nil
    local param = nil
    --获取参数的值
    --获取秒杀下单的用户id
    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()
    end
    user_id = args["user_id"]
    --用户身份判断--省略
    --用户能否下单--省略

    --关闭redis的函数--------------------

    local function close_redis(redis_instance)
    -- if not redis_instance then
    -- return
    -- end
    -- local ok,err = redis_instance:close();
    -- if not ok then
    ngx.say("close redis error : ",err);
    -- end
    end

    --引入cjson类--------------------
    --local cjson = require "cjson"

    --连接redis--------------------
    --[[
    local function redis_cluster_init ()
    local redis_cluster = require "resty.redis_cluster"
    local cluster_id = "redis_cluster"
    local startup_nodes = {
    {"192.168.142.142", 6320},
    {"192.168.142.142", 6321},
    {"192.168.142.142", 6322},
    {"192.168.142.142", 6323},
    {"192.168.142.142", 6324},
    {"192.168.142.142", 6325}
    }
    local opt = {
    timeout = 1000,--执行超时时间
    keepalive_size = 50,--长连接数量
    keepalive_duration = 60000 --长连接保持时间
    }
    rc = redis_cluster:new(cluster_id, startup_nodes, opt)
    return rc
    end
    local redis_instance = redis_cluster_init();
    ]]

    local redis = require("resty.redis");
    --local redis = require "redis"
    -- 创建一个redis对象实例。在失败,返回nil和描述错误的字符串的情况下
    local redis_instance = redis:new();
    --设置后续操作的超时(以毫秒为单位)保护,包括connect方法
    redis_instance:set_timeout(1000)
    --建立连接
    local ip = '192.168.142.142'
    local port = 6323
    --尝试连接到redis服务器正在侦听的远程主机和端口
    local ok,err = redis_instance:connect(ip,port)
    if not ok then
    ngx.say("connect redis error : ",err)
    return close_redis(redis_instance);
    end

    -- local redis = require "redis"

    -- 加载nginx—lua限流模块
    local limit_req = require "resty.limit.req"
    -- 这里设置rate=50个请求/每秒,漏桶桶容量设置为1000个请求
    -- 因为模块中控制粒度为毫秒级别,所以可以做到毫秒级别的平滑处理
    local lim, err = limit_req.new("my_limit_req_store", 2, 10)
    if not lim then
    ngx.log(ngx.ERR, "failed to instantiate a resty.limit.req object: ", err)
    return ngx.exit(501)
    end

    local key = ngx.var.binary_remote_addr
    local delay, err = lim:incoming(key, true)

    ngx.say("计算出来的延迟时间是:")
    ngx.say(delay)

    --if ( delay <0 or delay==nil ) then
    --return ngx.exit(502)
    --end

    --先死这个值为-1, 就是先不限流, 先测试下面的乐观锁代码。
    --delay = -1

    -- 1000以外的就溢出,回绝掉,比如100000个人来抢购,那么100000-1000的请求直接nginx回绝
    if not delay then
    if err == "rejected" then
    return ngx.say("1000以外的就溢出")
    -- return ngx.exit(502)
    end
    ngx.log(ngx.ERR, "failed to limit req: ", err)
    return ngx.exit(502)
    end

    -- 计算出要等很久,比如要等10秒的, 也直接不要他等了。要买家直接回家吃饭去
    if ( delay >10) then
    ngx.say("抢购超时")
    return
    end

    --先到redis里面添加sku_num键(参与秒杀的该商品的数量)
    --并到redis里面添加watch_key键(用于做乐观锁之用)

    local resp, err = redis_instance:get("{miaosha}sku_num")
    local watch_key, err = redis_instance:get("{miaosha}watch_key")
    resp = tonumber(resp)
    ngx.say("sku_num:")
    ngx.say(resp)
    if (resp > 0) then
    --ngx.say("抢购成功")
    redis_instance:watch("{miaosha}watch_key");
    ngx.sleep(1)
    local ok, err = redis_instance:multi();
    local sku_num = tonumber(resp) - 1;
    redis_instance:set("{miaosha}sku_num",sku_num);
    redis_instance:set("{miaosha}watch_key",watch_key+1);
    ans,err = redis_instance:exec()

    if (tostring(ans) == "userdata: NULL") then
        ngx.say("抢购失败,慢一丁点")
        return
        
    else
        ngx.say("抢购成功")
        return
    end
    

    else
    ngx.say("抢购失败,手慢了")
    return
    end

    --下面这行代码是进入正式下单;
    ngx.exec('/create_order'); --注意这行代码前面不能执行ngx.say()

    --[[
    --每个用户限购1个,判断用户是否已经抢购过了的参考代码逻辑思路如下(具体过程略,前端缓存中也有这个类似的判断用于限制对后端的请求):

    建一张用于保存已经抢购成功了的用户的redis哈希表

    抢购前判断是否在该表中
    local res, err = redis_instance:hmget("myhash", "user_id")
    抢购成功则保存到该表
    local res, err = redis_instance:hmset("myhash", "user_id", "1")
    --]]

  • 相关阅读:
    EntityFramework+MySql 笔记2
    EntityFramework+MySql 笔记1
    软件详细设计文档(终)
    软件测试文档(终)
    软件测试计划文档(初)
    软件概要设计文档(终)
    软件需求规格说明文档(终)
    例会记录(六)
    例会记录(五)
    例会记录(四)
  • 原文地址:https://www.cnblogs.com/xivzhou/p/14984907.html
Copyright © 2020-2023  润新知