golang使用redis的lua脚本实现简单的超卖

609 阅读2分钟

本文已参与[新人创作礼]活动,一起开启掘金创作之路。

在golang中调用Redis的lua脚本

使用的redisgo,下面是简单的使用

  • Dial

import (
   "encoding/json"
   "fmt"
   "github.com/garyburd/redigo/redis"
   "github.com/spaolacci/murmur3"
   "testing"
)

/**
 * @Description: redis数据库连接
 */
var (
   RD = redis.Pool{
      Dial: func() (conn redis.Conn, err error) {
         conn, err = redis.Dial("tcp", "127.0.0.1:6379", redis.DialPassword("123456"))
         return
      }}
   Conn =RD.Get()
)
  • Do or Send

//Do让redis客户端执行命令
Conn.Do('GET',key)
//Send写入到客户端输出缓冲区,再调用Flush执行缓冲区的命令
Conn.Send('SET',key,val)
Conn.Send('GET',key)
Conn.Flush()
  • NewScript加载脚本

lua := redis.NewScript(2, string(script)) 
lua.Do(c,key1,key2)

开始运行一个超卖Demo吧

package main

import (
   "fmt"
   "github.com/garyburd/redigo/redis"
   "io/ioutil"
   "math/rand"
   "time"
)

/**
 * @Description: 生产者负责生产消息
 */
func producer(c redis.Conn, product string) {
   defer c.Close()
   for {
      c.Do("INCR", product)
      fmt.Println(product + "+1")
      time.Sleep(time.Second)
   }
}

/**
 * @Description: 消费者负责消费消息
 */
func consumer(c redis.Conn, user string, product string) {
   defer c.Close()
   script, err := ioutil.ReadFile("./consumer.lua")
   if err != nil {
      fmt.Println("Script read error", err)
      return
   }
   lua := redis.NewScript(2, string(script))
   for {
      res, err := lua.Do(c, user, product)
      if err != nil {
         fmt.Println(err)
         return
      }
      if r, _ := res.(int64); r == 1 {
         fmt.Println(user + " buy " + product)
      } else if r == 0 {
         fmt.Println("Unluckly " + user + "," + product + " is sold out!")
      } else {
         fmt.Print(user + "/")
      }
      time.Sleep(time.Millisecond * time.Duration(rand.Intn(10)*100))
   }
}

func main() {
   pool := &redis.Pool{
      // 最大空闲链接
      MaxIdle: 10,
      // 最大激活链接
      MaxActive: 10,
      // 最大空闲链接等待时间
      IdleTimeout: 10 * time.Second,
      Dial: func() (redis.Conn, error) {
         c, err := redis.Dial("tcp", "127.0.0.1:6379",redis.DialPassword("123456"))
         if err != nil {
            fmt.Println("Connect to redis error", err)
         }
         return c, nil
      },
   }
   //每次开始先清空一下redis
   c := pool.Get()
   c.Do("FLUSHALL")
   c.Close()

   go producer(pool.Get(), "apple")
   go producer(pool.Get(), "pineapple")
   go consumer(pool.Get(), "001", "apple")
   go consumer(pool.Get(), "002", "apple")
   go consumer(pool.Get(), "002", "pineapple")
   time.Sleep(time.Second * 10)
}

运行结果如下:

apple+1
pineapple+1
001 buy apple
Unluckly 002,apple is sold out!
002 buy pineapple
001/002/002/002/apple+1
pineapple+1
001 buy apple
001/002 buy pineapple
002/002/002/001/002/002/pineapple+1
apple+1
002/002 buy apple
Unluckly 001,apple is sold out!
002/002/apple+1
pineapple+1
002/001 buy apple
Unluckly 002,apple is sold out!
002 buy pineapple
pineapple+1
apple+1
001/002/002/001 buy apple
001/Unluckly 002,apple is sold out!
Unluckly 002,apple is sold out!
apple+1
pineapple+1
001/002 buy pineapple
002/002/001 buy apple
pineapple+1
apple+1
002 buy pineapple
002/002/001/002/002/001 buy apple
002/pineapple+1
apple+1
001/002 buy pineapple
002/002/001 buy apple
apple+1
pineapple+1
002/001/001/002 buy apple
002/002/apple+1
pineapple+1
001 buy apple
002/002/Unluckly 002,apple is sold out!

检查一下redis

没有超卖,看起来很Nice

image.png