• 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.  
      )
  • 相关阅读:
    Adodb.recorset中Field字段的属性及意义
    郁闷的新一篇
    最近身体越来越差了。。。
    成事在天,谋事在人
    我要带我老婆去看看机场...
    Javascript 时间比较
    同步异步传输
    使用WSPBuilder 生成wsp文件,部署,激活,使用
    动态添加SqlParameter
    C#几种排序方法
  • 原文地址:https://www.cnblogs.com/ExMan/p/14312289.html
Copyright © 2020-2023  润新知