golang+redis 實現簡單秒殺功能


使用 golang + redis 實現一個簡單的秒殺功能

安裝 redigo
go get github.com/gomodule/redigo/redis
 

 

1. 創建 redis 連接池 (我這里是在 global 包下新建的一個 redis.go 文件,只有一行代碼如下)
 
var RedisPool *redis.Pool
 
緊接着,在 main.go 文件里初始化 redis 連接池
func init() {
   global.RedisPool = &redis.Pool{
      MaxIdle:     10,                // 最初的連接數量
      MaxActive:   0,                 // 最大連接數量 0表示不限制
      IdleTimeout: 300 * time.Second, // 連接關閉時間單位秒 (超時不使用自動關閉)
      Dial: func() (redis.Conn, error) {
         return redis.Dial("tcp", "127.0.0.1:6379")
      },
   }
}

  

2. 實現模擬秒殺功能

var lock sync.Mutex
var listName = "seckill"

func main() {
   createQueue()
}

  

// 模擬搶購
func createQueue() {
   var wg sync.WaitGroup
   rdb := global.RedisPool.Get()
   defer rdb.Close()

   // 設定20人搶購(用於隨機搶購)
   userIDs := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20}
   count := len(userIDs)
   var ch = make(chan int, count)

   f := func(id int, done func()) {
      defer func() {
         done()
      }()
      lock.Lock()
      defer lock.Unlock()

      // 判斷人數是否少於10
      lLen, _ := redis.Int64(rdb.Do("LLEN", listName))
      if lLen < 10 {
          // 寫入隊列,字符串使用 @ 符合連接用戶id和當前時間
         rdb.Do("RPUSH", listName, fmt.Sprintf("%d@%v", id, time.Now()))
         fmt.Println(id, "搶購成功")
      } else {
         fmt.Println("搶購活動已結束")
      }

      ch <- id
   }

   wg.Add(count)
   for _, v := range userIDs {
      go f(v, wg.Done)
   }

   for i := 0; i < count; i++ {
      <-ch
   }

   close(ch)
   wg.Wait()
}

  

運行以上代碼,結果:

20 搶購成功
10 搶購成功
1 搶購成功
2 搶購成功
3 搶購成功
4 搶購成功
5 搶購成功
6 搶購成功
7 搶購成功
8 搶購成功
搶購活動已結束
搶購活動已結束
搶購活動已結束
搶購活動已結束
搶購活動已結束
搶購活動已結束
搶購活動已結束
搶購活動已結束
搶購活動已結束
搶購活動已結束

 

查看隊列數據:

127.0.0.1:6379> lrange seckill 0 100
1) "20@2021-10-12 09:49:08.162533478 +0800 CST m=+0.001920843"
2) "10@2021-10-12 09:49:08.163083661 +0800 CST m=+0.002471032"
3) "1@2021-10-12 09:49:08.163379093 +0800 CST m=+0.002766464"
4) "2@2021-10-12 09:49:08.163807431 +0800 CST m=+0.003194802"
5) "3@2021-10-12 09:49:08.16409304 +0800 CST m=+0.003480411"
6) "4@2021-10-12 09:49:08.164380002 +0800 CST m=+0.003767371"
7) "5@2021-10-12 09:49:08.16480322 +0800 CST m=+0.004190580"
8) "6@2021-10-12 09:49:08.165220687 +0800 CST m=+0.004608047"
9) "7@2021-10-12 09:49:08.165518322 +0800 CST m=+0.004905684"
10) "8@2021-10-12 09:49:08.166351454 +0800 CST m=+0.005738847"

 

3. 保存到數據庫 
新建一個 main.go 文件,創建 redis 鏈接池和初始化 mysql
func init() {
   global.RedisPool = &redis.Pool{
      MaxIdle:     10,                // 最初的連接數量
      MaxActive:   0,                 // 最大連接數量 0表示不限制
      IdleTimeout: 300 * time.Second, // 連接關閉時間單位秒 (超時不使用自動關閉)
      Dial: func() (redis.Conn, error) {
         return redis.Dial("tcp", "127.0.0.1:6379")
      },
   }

   mysql.Init("root:123456@tcp(127.0.0.1:3306)/test001?charset=utf8&loc=Asia%2FShanghai")
}

  

讀取隊列數據,保存到數據表
代碼實現了,監控隊列是否有數據,如果有數據則馬上保存到數據表,如果讀取插入數據失敗,則把移出並獲取列表的元素重新寫入到隊列里

 

var listName = "seckill"

func main() {
   rdb := global.RedisPool.Get()
   defer rdb.Close()

   for {
      total, err := redis.Int64(rdb.Do("LLEN", listName))
      if err != nil {
         fmt.Println(err)
         return
      }
      if total == 0 {
         time.Sleep(1 * time.Second)
         fmt.Println("total:", total)
         continue
      }

      // LPOP:在隊列最左側取出一個值(移除並返回列表的第一個元素)
      str, err := redis.String(rdb.Do("LPOP", listName))
      if err != nil {
         fmt.Println(err)
         return
      }
      // 分隔字符串
      l := strings.Split(str, "@")

      // 保存到數據庫中
      query := "insert into seckill_list (user_id, time_stamp) values(?, ?)"
      _, err = mysql.Insert(query, l[0], l[1])
      if err != nil {
         fmt.Println("mysql.Insert err:", err)

         // 數據庫插入失敗時回滾機制
         rdb.Do("RPUSH", listName, str)
      } else {
         fmt.Println(fmt.Sprintf("id:%v保存成功", l[0]))
      }
   }
}

 

運行結果(如果有數據就馬上讀取並保存到數據表):

id:20保存成功
id:10保存成功
id:1保存成功
id:2保存成功
id:3保存成功
id:4保存成功
id:5保存成功
id:6保存成功
id:7保存成功
id:8保存成功
total: 0
total: 0
total: 0
total: 0
id:1保存成功
id:20保存成功
id:2保存成功
id:3保存成功
id:4保存成功
id:5保存成功
id:6保存成功
id:7保存成功
id:8保存成功
id:9保存成功
total: 0
total: 0
total: 0
total: 0

 

保存到數據表的結果:

 

 

 

 

以上是本次簡單秒殺功能的實現,如果有其他方式歡迎留言或給出建議,謝謝(#^.^#)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM