基于redis的秒杀

目录

并发请求秒杀正常,没有出现超卖情况

img_19.png


img_20.png

package main

import (
    "errors"
    "fmt"
    "github.com/gin-gonic/gin"
    "github.com/go-redis/redis"
    "math/rand"
    "net/http"
    "time"
    "visual/common"
)

const (
    KEY     = "SecKill"
    UserKey = "SecKill_User"
)

var (
    GoodTotal int
)

var StartTime string = "2022-01-19 04:37:00"
var SecKillStart int = 0

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

    _, err := client.Ping().Result()

    if err != nil {
        fmt.Println("Redis NewClient Error")
    }

    return client
}

func HttpClient() {
    router := gin.Default()
    router.GET("sec/kill", SecKill)
    router.GET("sec/test", Y)

    err := router.Run(":9999")
    if err != nil {
        fmt.Println("Router start failed")
    }
}

func GetUserId() int {
    return rand.Int()
}

func Y(c *gin.Context) {
    Redis().Set("tttt", 222, 0)
    c.AbortWithStatusJSON(http.StatusOK, gin.H{"Code": "201", "message": "YYYY"})
}

func SecKill(c *gin.Context) {
    l,_ := time.LoadLocation("Asia/Shanghai")
    startTime, _ := time.ParseInLocation(common.DateFormat, StartTime, l)

    //到了秒杀时间,并且有货
    if SecKillStart != 1 && time.Now().After(startTime) {
        SecKillStart = 1
    }

    //已购数量
    num, err := Redis().Get(KEY).Int()

    if err != nil && err != redis.Nil {
        c.AbortWithStatusJSON(http.StatusOK, gin.H{"Code": "201", "message": "秒杀失败"})
        return
    }

    if num >= GoodTotal {
        c.AbortWithStatusJSON(http.StatusOK, gin.H{"Code": "200", "message": "秒杀失败-抢光了1"})
        return
    }

    userId := GetUserId()

    //监视key,如果key被抢占改动,则事务打断
    Txf := func(tx *redis.Tx) error {
        num, err := tx.Get(KEY).Int()
        if err != nil && err != redis.Nil {
            return err
        }

        if num >= GoodTotal {
            return errors.New("秒杀失败-抢光了2")
        }

        num++

        _, err = tx.Pipelined(func(p redis.Pipeliner) error {
            p.Set(KEY, num, 0)
            p.RPush(UserKey, userId)
            return nil
        })

        return err
    }

    if SecKillStart > 0 {
        err = Redis().Watch(Txf, KEY)
        if err != nil {
            fmt.Println("Redis 监控失败")
            c.AbortWithStatusJSON(http.StatusOK, gin.H{"Code": "203", "message": "秒杀失败2"})
            return
        }
    } else {
        c.AbortWithStatusJSON(http.StatusOK, gin.H{"Code": "200", "message": "秒杀失败尚未开始"})
        return
    }

    c.AbortWithStatusJSON(http.StatusOK, gin.H{"Code": "200", "message": "秒杀成功"})
    return

}

func main() {

    GoodTotal = 2
    HttpClient()

}