美文网首页
并发抢票

并发抢票

作者: 彳亍口巴 | 来源:发表于2022-12-25 14:44 被阅读0次
    package main
    
    import (
        "fmt"
        "net/http"
    
        "github.com/gin-gonic/gin"
    )
    
    // 高并发抢票服务
    
    func main() {
        //startServer()
        fmt.Println("启动项目")
        r := RoutersInit()
        err := r.Run(":9090")
        if err != nil {
            panic(err)
        }
    }
    
    func startServer() {
        r := gin.Default()
        r.GET("/ping", func(c *gin.Context) {
            c.JSON(http.StatusOK, gin.H{
                "message": "pong",
            })
        })
    
        r.Run(":9090")
    }
    
    func RoutersInit() *gin.Engine {
    
        r := gin.New()
    
        c := r.Group("/index")
        {
    
            p := c.Group("/post")
            {
                p.GET("/", Buy)
    
            }
        }
    
        return r
    }
    
    
    package main
    
    import (
        "github.com/garyburd/redigo/redis"
    )
    
    var constLuaScript = `
            local ticket_key = KEYS[1]
            local ticket_total_key = ARGV[1]
            local ticket_sold_key = ARGV[2]
            local ticket_total_nums = tonumber(redis.call('HGET', ticket_key, ticket_total_key))
            local ticket_sold_nums = tonumber(redis.call('HGET', ticket_key, ticket_sold_key))
            -- 查看是否还有余票,增加订单数量,返回结果值
           if(ticket_total_nums >= ticket_sold_nums) then
                return redis.call('HINCRBY', ticket_key, ticket_sold_key, 1)
            end
            return 0
    `
    
    var (
        redisPool *redis.Pool
    )
    
    func init() {
        redisPool = NewPool()
    }
    
    func NewPool() *redis.Pool {
        return &redis.Pool{
            MaxIdle:   10000,
            MaxActive: 12000, // max number of connections
            Dial: func() (redis.Conn, error) {
                //c, err := redis.Dial("tcp", "127.0.0.1:6379", redis.DialPassword("123456"))
                c, err := redis.Dial("tcp", "127.0.0.1:6379")
                if err != nil {
                    panic(err.Error())
                }
                return c, err
            },
        }
    }
    
    // RemoteDeductionStock 远端统一扣库存
    func RemoteDeductionStock(conn redis.Conn) bool {
        lua := redis.NewScript(1, constLuaScript)
        result, err := redis.Int(lua.Do(conn, "a", "total", "order"))
        if err != nil {
            return false
        }
        return result != 0
    }
    
    
    package main
    
    import (
        "os"
        "strings"
    
        "github.com/gin-gonic/gin"
    )
    
    var done = make(chan struct{}, 1) // 控制并发,一次只能有一个请求通过
    var localTicket = &LocalTicket{}
    
    type LocalTicket struct {
        TotalTicket int32
        OrderCount  int32
    }
    
    func init() {
        localTicket = &LocalTicket{
            TotalTicket: 150,
            OrderCount:  0,
        }
        done <- struct{}{}
    }
    
    func Buy(c *gin.Context) {
        <-done
        localTicket.OrderCount++
        if localTicket.OrderCount > localTicket.TotalTicket {
            writeLog("本地已售罄", "./stat.log")
            c.String(200, "本地已售罄")
            return
        }
        if RemoteDeductionStock(redisPool.Get()) {
            writeLog("购买成功", "./stat.log")
            c.String(200, "购买成功")
            return
        }
        writeLog("全部售罄", "./stat.log")
        c.String(200, "全部售罄")
        return
    }
    
    func writeLog(msg string, logPath string) {
        fd, _ := os.OpenFile(logPath, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0644)
        defer fd.Close()
        content := strings.Join([]string{msg, "\r\n"}, "")
        buf := []byte(content)
        fd.Write(buf)
    }
    
    
    

    并发买票

    1、初始化 redis中设置总票数和0订单数
    2、本地初始化 根据评估,本地初始化票数,只能是本地有票的情况下才去redis中访问,两个地方同时有票才可以买票成功,否则失败
    3、买票时本地票已卖完的话,直接返回失败,不需要访问redis,减少访问
    4、为了保证票没有超卖,本地买票的解决方法是通过channel控制,缓存长度为1的channel,请求尝试往里面写入数据,如果可能写入,说明可以拿到锁,否则就阻塞,缺点是会一直阻塞,可通过上下文超时时间自动取消
    redis防止超卖是用LUA脚本解决,串行读写,先判断有票,再Hincrby增加即可

    订单

    上面描述的是并发去买票的经过,保证了买票的顺序进行,并且不会超卖,但是抢到票后,还需要创建订单,付款和发货

    相关文章

      网友评论

          本文标题:并发抢票

          本文链接:https://www.haomeiwen.com/subject/aklgqdtx.html