go语言web开发系列之十七:用go-redis+lua实现顺序自增的唯一id发号器

一,安装go-redis

1,从命令行执行:

liuhongdi@ku:~$ go get -u github.com/go-redis/redis/v8

说明:安装命令及版本等信息可以从github站直接得到

说明:刘宏缔的go森林是一个专注golang的博客,
          地址:https://blog.csdn.net/weixin_43881017

说明:作者:刘宏缔 邮箱: 371125307@qq.com

二,演示项目的相关信息

1,地址:

https://github.com/liuhongdi/digv17

2,功能:演示通过go-redis库+lua实现一个顺序自增的id发号器

              通常用在文件存储的目录计算等方面

3,项目结构:如图:

三,go代码说明

1,global/redisDb.go

  1.  
    package global
  2.  
     
  3.  
    import (
  4.  
    "context"
  5.  
    "github.com/go-redis/redis/v8"
  6.  
    )
  7.  
     
  8.  
    var (
  9.  
    RedisDb *redis.Client
  10.  
    )
  11.  
     
  12.  
    //创建redis链接
  13.  
    func SetupRedisDb() (error) {
  14.  
    var ctx = context.Background()
  15.  
    RedisDb = redis.NewClient(&redis.Options{
  16.  
    Addr: RedisSetting.Addr,
  17.  
    Password: RedisSetting.Password, // no password set
  18.  
    DB: 0, // use default DB
  19.  
    })
  20.  
     
  21.  
    _, err := RedisDb.Ping(ctx).Result()
  22.  
    if err != nil {
  23.  
    return err
  24.  
    }
  25.  
    return nil
  26.  
     
  27.  
    }

2,controller/idController.go

  1.  
    package controller
  2.  
     
  3.  
    import (
  4.  
    "fmt"
  5.  
    "github.com/gin-gonic/gin"
  6.  
    "github.com/liuhongdi/digv17/pkg/result"
  7.  
    "github.com/liuhongdi/digv17/service"
  8.  
    )
  9.  
     
  10.  
    type IdController struct{}
  11.  
     
  12.  
    func NewIdController() IdController {
  13.  
    return IdController{}
  14.  
    }
  15.  
    //得到一个id
  16.  
    func (a *IdController) GetOne(c *gin.Context) {
  17.  
    resultRes := result.NewResult(c)
  18.  
    idType := "logId"
  19.  
    idOne,err := service.GetOneId(idType);
  20.  
    if err != nil {
  21.  
    resultRes.Error(404,"数据查询错误")
  22.  
    } else {
  23.  
    fmt.Println(idOne)
  24.  
    resultRes.Success(&idOne);
  25.  
    }
  26.  
    return
  27.  
    }

3,service/id.go

  1.  
    package service
  2.  
     
  3.  
    import (
  4.  
    "github.com/liuhongdi/digv17/cache"
  5.  
    )
  6.  
     
  7.  
    //得到一个id
  8.  
    func GetOneId(idType string) (int64, error) {
  9.  
    //get from cache
  10.  
    id,err := cache.GetOneId(idType);
  11.  
    return id,err
  12.  
    }

4,cache/id.go

  1.  
    package cache
  2.  
     
  3.  
    import (
  4.  
    "context"
  5.  
    "github.com/liuhongdi/digv17/global"
  6.  
    "strconv"
  7.  
    "github.com/go-redis/redis/v8"
  8.  
    )
  9.  
     
  10.  
    //得到cache的名字
  11.  
    func getIdCacheName(idType string) (string) {
  12.  
    return "id_"+idType
  13.  
    }
  14.  
     
  15.  
    //得到一个id
  16.  
    func GetOneId(idType string) (int64,error) {
  17.  
    key := getIdCacheName(idType);
  18.  
    luaId := redis.NewScript(`
  19.  
    local id_key = KEYS[1]
  20.  
    local current = redis.call('get',id_key)
  21.  
    if current == false then
  22.  
    redis.call('set',id_key,1)
  23.  
    return '1'
  24.  
    end
  25.  
    --redis.log(redis.LOG_NOTICE,' current:'..current..':')
  26.  
    local result = tonumber(current)+1
  27.  
    --redis.log(redis.LOG_NOTICE,' result:'..result..':')
  28.  
    redis.call('set',id_key,result)
  29.  
    return tostring(result)
  30.  
    `)
  31.  
    var ctx = context.Background()
  32.  
    n, err := luaId.Run(ctx, global.RedisDb, []string{key}, 2).Result()
  33.  
     
  34.  
    if (err != nil) {
  35.  
    return -1,err
  36.  
    } else {
  37.  
    var ret string = n.(string)
  38.  
    retint,err := strconv.ParseInt(ret, 10, 64)
  39.  
    if (err == nil) {
  40.  
    return retint,err
  41.  
    } else {
  42.  
    return -1,err
  43.  
    }
  44.  
    }
  45.  
    }

lua代码的说明:

id_key变量作为存储的kv对的key

如果变量不存在,设置id_key值为1并返回

如果变量存在,值加1后返回

注意转为字符串形式后返回,方便java代码接收

5,其他相关代码可以访问github查看

四,测试效果

1,从redis控制台把原key删除

  1.  
    liuhongdi@ku:~$ /usr/local/soft/redis6/bin/redis-cli
  2.  
    127.0.0.1:6379> keys *
  3.  
    1) "id_logId"
  4.  
    2) "article_2"
  5.  
    3) "article_3"
  6.  
    127.0.0.1:6379> del id_logId
  7.  
    (integer) 1
  8.  
    127.0.0.1:6379> get id_logId
  9.  
    (nil)
  10.  
    127.0.0.1:6379> exit

2,通过ab发起100个并发连接:

liuhongdi@ku:~$ ab -c 100 -n 100 http://127.0.0.1:8000/id/getone

     通过控制台查看效果:

  1.  
    1
  2.  
    2
  3.  
    3
  4.  
    4
  5.  
    7
  6.  
    8
  7.  
    9
  8.  
    10
  9.  
    11
  10.  
    12
  11.  
    13
  12.  
    14
  13.  
    15
  14.  
    19
  15.  
    20
  16.  
    21
  17.  
    22
  18.  
    23
  19.  
    5
  20.  
    61
  21.  
    6
  22.  
    16
  23.  
    ...
  24.  
    38
  25.  
    58
  26.  
    100

说明:可以看到并发时也没出现重复的id 

五,查看库的版本:

  1.  
    module github.com/liuhongdi/digv17
  2.  
     
  3.  
    go 1.15
  4.  
     
  5.  
    require (
  6.  
    github.com/gin-gonic/gin v1.6.3
  7.  
    github.com/go-playground/universal-translator v0.17.0
  8.  
    github.com/go-playground/validator/v10 v10.2.0
  9.  
    github.com/go-redis/redis/v8 v8.3.3
  10.  
    github.com/jinzhu/gorm v1.9.16 // indirect
  11.  
    github.com/magiconair/properties v1.8.4 // indirect
  12.  
    github.com/mitchellh/mapstructure v1.3.3 // indirect
  13.  
    github.com/pelletier/go-toml v1.8.1 // indirect
  14.  
    github.com/spf13/afero v1.4.1 // indirect
  15.  
    github.com/spf13/cast v1.3.1 // indirect
  16.  
    github.com/spf13/jwalterweatherman v1.1.0 // indirect
  17.  
    github.com/spf13/pflag v1.0.5 // indirect
  18.  
    github.com/spf13/viper v1.7.1
  19.  
    golang.org/x/sys v0.0.0-20201119102817-f84b799fce68 // indirect
  20.  
    golang.org/x/text v0.3.4 // indirect
  21.  
    gopkg.in/ini.v1 v1.62.0 // indirect
  22.  
    gopkg.in/yaml.v2 v2.3.0 // indirect
  23.  
    )
原文地址:https://www.cnblogs.com/ExMan/p/14312289.html