简单秒杀活动场景

ByWhat'sUs

简单秒杀活动场景

采用Go+Redis+Lua脚本实现一个简单的抢购或秒杀场景

redis.go

package redis

import "github.com/go-redis/redis"

func InitRedis() *redis.Client {
    return redis.NewClient(&redis.Options{
	Addr:     "127.0.0.1:6379",
	Password: "",
	DB:       0,
    })
}

lua.go

package lua

import (
    "fmt"
    "github.com/go-redis/redis"
)

func RunLua(client *redis.Client, luaScript string) (string, error) {
    evalSha, err := client.ScriptLoad(luaScript).Result()
    if err != nil {
	fmt.Println(err)
    }

    return evalSha, nil
}

logic.go

package logic

import (
    "fmt"
    "github.com/go-redis/redis"
    "math/rand"
    "seckill/lua"
    "strconv"
    "time"
)

var luaScript = `
			local userId   = KEYS[1];
			local goodKey  = KEYS[2];
			local stock    = KEYS[3];
			local userExit = redis.call("sismember",goodKey,userId);
            if tonumber(userExit) == 1 then
                return 2;
            end 
            local num = redis.call("get",stock);
            if tonumber(num) <= 0 then
                return 3;
            else
                redis.call("decr",stock);
                redis.call("sadd",goodKey,userId);
            end 
            return 1;`

func SecKill(client *redis.Client) {
    errStock := client.SetNX("go_stock", 1000, 0).Err()
    if errStock != nil {
	panic(errStock)
    }

    rand.Seed(time.Now().UnixNano()) // 毫秒
    id := rand.Intn(990001) + 10000  // 10000-1000000的随机数
    // lua脚本
    evalSha, errLua := lua.RunLua(client, luaScript)
    if errLua != nil {
	panic(errLua)
    }
    userId := strconv.Itoa(id) // 整数转字符,如果直接int(id)会出现意想不到的结果
    res, err2 := client.EvalSha(evalSha, []string{userId, "go_user_ids", "go_stock"}).Result()
    if err2 != nil {
	panic(err2)
    }
    if res.(int64) == int64(1) {
	fmt.Println("秒杀成功")
    } else if res.(int64) == int64(2) {
	fmt.Println("请勿重复操作")
    } else {
	fmt.Println("暂无库存了")
    }
}

kill.go

package main

import (
    "github.com/gin-gonic/gin"
    "net/http"
    "seckill/logic"
    "seckill/redis"
)

func main() {
    redisDb := redis.InitRedis()
    // 1.创建路由
    r := gin.Default()
    // 2.绑定路由规则,执行的函数
    // gin.Context,封装了request和response
    r.GET("/kill", func(c *gin.Context) {
	logic.SecKill(redisDb)
	c.String(http.StatusOK, "秒杀成功")
    })

    // 3.监听端口,默认在8080
    // Run("里面不指定端口号默认为8080")
    r.Run(":8000")
}

About the author

What'sUs administrator

Leave a Reply

PHP Code Snippets Powered By : XYZScripts.com