美文网首页
【Go - 超实用,3行代码实现个自增器】

【Go - 超实用,3行代码实现个自增器】

作者: wn777 | 来源:发表于2024-09-14 13:24 被阅读0次

    场景

    自增器的作用是生成一个唯一的递增序列号。这在一些需要生成自增id的场景十分有用,比如自增的订单号,任务号,序列号。

    要点

    • 全局统一:在整个服务体系下,多个服务或者进程,都统一调用这个自增器,来获取自增ID。
    • 严格自增:避免竞争,写冲突造成写覆盖等,导致不严格自增

    实现

    根据上面要点,需要跨服务进程可以访问,且保障严格自增。综上考虑, 依赖MonogoDB来实现这个自增器,以下是代码实现,

    代码

    mongodb-conn.go

    package main
    
    import (
        "context"
        "os"
        "sync"
    
        mongo "go.mongodb.org/mongo-driver/mongo"
        "go.mongodb.org/mongo-driver/mongo/options"
    )
    
    var (
        singletonMongoClient *mongo.Client
        once                 sync.Once
    )
    
    var MONGODB_URI = os.Getenv("MONGODB_URI")
    var MONGODB_DATABASE = os.Getenv("MONGODB_DATABASE")
    
    func getSingletonMongoClient() *mongo.Client {
    
        once.Do(func() {
            // 创建连接到 MongoDB 的客户端
            uri := MONGODB_URI
            client, err := mongo.Connect(context.TODO(), options.Client().
                ApplyURI(uri))
    
            if err != nil {
                panic(err)
            }
            singletonMongoClient = client
        })
        return singletonMongoClient
    }
    func GetMongoConn() *mongo.Database {
        return getSingletonMongoClient().Database(MONGODB_DATABASE)
    }
    
    

    main.go

    package main
    
    import (
        "context"
    
        bson "go.mongodb.org/mongo-driver/bson"
        "go.mongodb.org/mongo-driver/mongo/options"
    )
    
    type UniqSeq struct {
        Key string `bson:"key"`
        Seq int64  `bson:"seq"`
    }
    
    func GetNextSeq(key string) (int64, error) {
        conn := GetMongoConn()
        var collectionName = "uniqseqz"
        collection := conn.Collection(collectionName)
        filter := bson.M{"key": key}
        update := bson.M{"$inc": bson.M{"seq": 1}}
        opts := options.FindOneAndUpdate().SetUpsert(true).SetReturnDocument(options.After)
    
        var result UniqSeq
        err := collection.FindOneAndUpdate(context.Background(), filter, update, opts).Decode(&result)
    
        if err != nil {
            return 0, err
        }
        return result.Seq, nil
    }
    
    func main() {
        // Test
        key := "T"
        seq, err := GetNextSeq(key)
        if err != nil {
            panic(err)
        }
        println(seq)
    }
    
    

    这里的核心就在下面三行

    // 这里是借助了MongoDB $inc 和 upsert特性
    // 先按key过滤,找出对应的document,然后upsert,没有就插入一条,有就加一。
    // 以此来完成了自增
    filter := bson.M{"key": key}
    update := bson.M{"$inc": bson.M{"seq": 1}}
    opts := options.FindOneAndUpdate().SetUpsert(true).SetReturnDocument(options.After)
    

    关于竞争,

    // 这个操作调用的时候,MongoDB Server端会加锁执行,保障操作的原子性,
    // 对于调用方可以看以下一行是原子性的,不用担心写冲突或者写覆盖。
    var result UniqSeq
        err := collection.FindOneAndUpdate(context.Background(), filter, update, opts).Decode(&result)
    

    运行

    最后可以带入运行一下,

    MONGODB_URI="${YOUR_MONGODB_URL}" MONGODB_DATABASE="${YOUR_MONGODB_DATABASE}" go run mongodb-conn.go main.go
    

    相关文章

      网友评论

          本文标题:【Go - 超实用,3行代码实现个自增器】

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