如何通过 MongoDB 同步在两个不同服务器上运行的两个应用程序

2024-03-27

我正在 golang 中开发一个 Web 应用程序,并使用单个 MongoDB 实例作为数据存储。我有应该专门执行的代码。由于我的 Web 应用程序运行在两个不同的服务器上,因此我无法使用 golang 同步工具。

想法是通过锁定文档来使用 MongoDB,但我不知道这是否可能,如果可能,该怎么做?


预先注意:使用Redis https://redis.io/将是分布式锁定更好、更有效的选择。

但如果您仍然想使用 MongoDB 来实现此目的,请继续阅读。

以下解决方案的一些注释:

  • 即使您有多个 MongoDB 服务器(共享集群),下面的所有解决方案都是安全且有效的,因为下面的解决方案都不依赖于简单的读取;和所有写入(例如insert or update) 转到主实例。

  • 如果 goroutine 无法获取锁,它可能会决定休眠一会儿(例如 1 秒),然后重试获取锁。放弃之前应该有最大重试次数。


使用文档的存在作为锁

最简单的方法是依赖 MongoDB,不允许存在 2 个具有相同 ID 的文档(在同一个集合中)。

因此,要获取锁,只需将文档插入指定的集合(例如locks)与锁 ID。如果插入成功,则说明您成功获取了锁。如果插入失败,则说明您没有插入。要释放锁定,只需删除(移除)文档即可。

需要注意的一些事情:您必须释放锁,因为如果您不这样做,则尝试获取此锁的所有代码都将永远不会成功。因此释放锁应该使用延迟函数来完成(defer)。不幸的是,这不能确保在出现某些通信错误(网络故障)时发布。

为了保证锁释放,您可以创建一个指定的索引文件过期 https://docs.mongodb.com/manual/tutorial/expire-data/,因此,如果 Go 应用程序在持有锁时出现任何问题,一段时间后锁会自动删除。

Example:

锁定文档不会事先插入。但需要一个索引:

db.locks.createIndex({lockedAt: 1}, {expireAfterSeconds: 30})

获取锁:

sess := ... // Obtain a MongoDB session
c := sess.DB("").C("locks")

err := c.Insert(bson.M{
    "_id":      "l1",
    "lockedAt": time.Now(),
})
if err == nil {
    // LOCK OBTAINED! DO YOUR STUFF
}

释放锁:

err := c.RemoveId("l1")

Pros:最简单的解决方案。

Cons:您只能为所有锁指定相同的超时,以后很难更改它(必须删除并重新创建索引)。

请注意,最后一个陈述并不完全正确,因为您不会被迫将当前时间设置为lockedAt场地。例如。如果您设置的时间戳指向过去 5 秒,则锁定将在 25 秒后自动过期。如果将其设置为未来 5 秒,则锁定将在 35 秒后过期。

另请注意,如果一个 goroutine 获得了锁,并且没有任何问题,它需要将其保持超过 30 秒,则可以通过更新lockedAt锁定文档的字段。例如。 20秒后,如果goroutine没有遇到任何问题,但需要更多时间来完成持有锁的工作,它可能会更新lockedAt字段设置为当前时间,防止它被自动删除(从而为等待该锁的其他 goroutine 亮绿灯)。

使用预先创建的锁定文档和update()

另一种解决方案可能是拥有一个包含预先创建的锁定文档的集合。锁可以有一个 ID(_id),以及一个状态,告诉它是否被锁定(locked).

之前创建锁:

db.locks.insert({_id:"l1", locked:false})

要获取锁,请使用Collection.Update() https://godoc.org/github.com/globalsign/mgo#Collection.Update方法,其中在选择器中您必须按 ID 和锁定状态进行过滤,其中状态必须解锁。更新值应该是$set操作,将锁定状态设置为true.

err := c.Update(bson.M{
    "_id":    "l1",
    "locked": false,
}, bson.M{
    "$set": bson.M{"locked": true},
})
if err == nil {
    // LOCK OBTAINED! DO YOUR STUFF
}

这是如何运作的?如果多个 Go 实例(甚至同一个 Go 应用程序中的多个 goroutine)尝试获取锁,则只有一个会成功,因为其余的选择器将返回mgo.ErrNotFound,因为占主导地位的决定了locked字段到true.

一旦你完成了持有锁的工作,你必须释放锁:

err := c.UpdateId("l1", bson.M{
    "$set": bson.M{"locked": false},
})

为了保证锁的释放,可以在锁文档中包含锁定时的时间戳。当尝试获取锁时,选择器还应该接受已锁定但早于给定超时(例如 30 秒)的锁。在这种情况下,更新还应该设置锁定时间戳。

保证超时锁释放的示例:

锁定文档:

db.locks.insert({_id:"l1", locked:false})

获取锁:

err := c.Update(bson.M{
    "_id": "l1",
    "$or": []interface{}{
        bson.M{"locked": false},
        bson.M{"lockedAt": bson.M{"$lt": time.Now().Add(-30 * time.Second)}},
    },
}, bson.M{
    "$set": bson.M{
        "locked":   true,
        "lockedAt": time.Now(),
    },
})
if err == nil {
    // LOCK OBTAINED! DO YOUR STUFF
}

释放锁:

err := c.UpdateId("l1", bson.M{
    "$set": bson.M{ "locked": false},
})

Pros:您可以对不同的锁使用不同的超时,甚至可以对不同位置的相同锁使用不同的超时(尽管这是不好的做法)。

Cons:稍微复杂一点。

请注意,为了“延长锁的生命周期”,可以使用与上面描述的相同的技术,即,如果锁过期即将到来并且 goroutine 需要更多时间,它可能会更新lockedAt锁定文档的字段。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何通过 MongoDB 同步在两个不同服务器上运行的两个应用程序 的相关文章

  • MongoDB - 手册参考示例

    我正在读手册参考 http docs mongodb org manual reference database references document referencesMongoDB 数据库参考文档的一部分 但我不太理解 解析引用字段
  • 读取一个文本文件,替换其中的单词,输出到另一个文本文件

    所以我试图在 GO 中编写一个程序来获取一个充满代码的文本文件并将其转换为 GO 代码 然后将该文件保存到 GO 文件或文本文件中 我一直在试图弄清楚如何保存对文本文件所做的更改 但我可以看到更改的唯一方法是通过 println 语句 因为
  • 嵌套过滤器:$filter 数组,然后是 $filter 子数组

    本质上 我试图过滤掉已被 废弃 的子文档和子子文档 这是我的架构的精简版本 permitSchema id name feeClassifications new Schema id trashed name fees new Schema
  • foo.Name undefined(类型接口{}没有字段或方法名称)

    我使用本机 golang 包 container list 来管理堆栈中的 inotify 事件 当我访问堆栈的项目时 我的类型失败 我认为 import golang org x exp inotify container list lo
  • 需要对 mongodb 中的数组对象值求和

    如果该值存在 我正在尝试计算总价值 但查询并不能 100 工作 那么有人可以帮我解决这个问题吗 这是我的示例文档 我附上了两份文件 请提供这些文件并找出最佳解决方案 文件 1 id 1 message count 4 messages da
  • 带 cookie 身份验证的 Gorilla websocket

    这是我的设置 我正在构建一个带有用户登录的服务 使用 Negroni 和 Gorilla 登录后 用户会获得一个会话 cookie 服务器使用该会话 cookie 来授权受保护的端点 受保护的端点之一允许用户 客户端与服务器打开 Webso
  • FindAndUpdate 如何检查文档是否真的更新

    想象一下以下模型 var Office id 1 name My Office branches adddress Some street that avenue isPrincipal true adddress Another addr
  • 如何指定 GridFS 存储桶?

    这是我的 express js 代码 用于将文件上传和下载到 GridFS var fs require fs var gridStream require gridfs stream var mongoose require mongoo
  • 更新插入不适用于 updateOnebulkWrite v3.4

    我正在尝试批量写入一些更新 除了更新插入之外的所有内容都正常 我的代码完美地更新了所有项目 并且没有给出任何错误 这里的问题是批量插入updateOne没有更新插入 这是我的代码的未经测试和缩短的示例 因此您可能会发现一些语法错误 希望您明
  • 具有多个等待组的管道中通道范围内的死锁

    我正在练习通过同时将计算分为 100 组来计算阶乘的挑战 我解决了 WaitGroups 上的很多问题 但仍然处于calculateFactorial函数我在通道部分的范围上陷入了僵局 希望有人能指出这个问题 谢谢 package main
  • Cgo 生成的源无法在 MVC 上编译

    我有一个用 CGo 制作的共享库 它在 Linux 和 Android 上链接得很好 但是 当使用 Microsoft Visual Studio 2017 在 Windows 10 上进行编译时 出现以下错误 Microsoft R Pr
  • 使用 sidekiq 只执行众多重复作业之一?

    我有一个后台作业 在 MongoDB 上执行映射 归约作业 当用户向文档发送更多数据时 它会启动在文档上运行的后台作业 如果用户发送多个请求 它将启动同一文档的多个后台作业 但实际上只有一个需要运行 有没有办法可以防止多个重复实例 我正在考
  • RoboMongo:不显示所有文档

    当我打开集合时 它仅显示前 50 个文档 而不是全部文档 如何使 RoboMongo 显示集合中的所有文档 最好是自动 罗博蒙戈结果 https i stack imgur com K5fn8 png 2019 年 12 月 6 日更新 最
  • 在 Alpine 中找不到运行时/cgo

    In an alpine edge我安装的容器通过 RUN apk add no cache musl dev go 我试着跑go get github com golang protobuf protoc gen go then 这会导致
  • 引用 MongoDB Aggregation Pipeline 中的整个文档

    我可以使用 运算符引用 MongoDB 聚合管道中属性的各个值 但是 我如何访问 引用 整个文档 UPDATE 提供一个示例来解释场景 这是我正在尝试做的事情的一个例子 我有一系列推文 每条推文都有一个成员 集群 它指示特定推文属于哪个集群
  • Mongoose 使用 GeoJSON 点作为查询参数调用 geoNear 不起作用

    给定一个为包含 GeoJSON 位置的文档定义的模式 var BranchSchema new Schema location type type String required true enum Point LineString Pol
  • 使用 Morphia 配置 Spring Boot?

    我不想利用 Spring DATA MongoDB 支持 我想利用名为 Morphia 的 MongoDB ORM https github com mongodb morphia https github com mongodb morp
  • MongoDB - 解释特定的解释输出

    我使用的是 MongoDB 版本 2 4 8 test 2014 03 25 14 42 13 0 gt gt gt db users getIndexes v 1 key id 1 ns test users name id v 1 ke
  • 在 Go 中执行字节数组

    我正在尝试在 Go 程序中执行 shellcode 类似于使用其他语言执行此操作的方式 示例 1 C 程序中的 Shellcode https stackoverflow com questions 16626857 shellcode i
  • Golang 网络爬虫 NTLM 身份验证

    Golang 网络抓取工具需要从经过 NTLM 验证的网页中提取信息 有了有效的用户名和密码 网络抓取工具如何与服务器进行 NTLM 4 次握手 以获得对后面受保护网页的访问权限 url username password http www

随机推荐

  • 我可以向 JLabel 添加操作侦听器吗?

    我想用 JLabel 替换 JButton 并且希望我的代码在单击 JLabel 时执行某些操作 当我拥有 JButton 时 我使用操作侦听器来处理按钮上的点击 myButton addActionListener new clicksL
  • 如何在Robot Framework中将图像添加到html日志中?

    如何将图像添加到机器人框架的html日志中 我想在 Robot Framework 的 html 日志中添加一些图片 有人可以对此有一些想法吗 Keyword Log来自内置库有html参数可能可以满足您的需要 参见文档 http robo
  • DDD:通过身份引用聚合根内的实体

    我一直在寻找正确的参考方式entities位于一个聚合根 当我们只得到他们的身份来自 URL 参数 我问了一个上一个问题 https stackoverflow com questions 7196820 update an entity
  • C++ 方法调用中前导“::”的目的是什么[重复]

    这个问题在这里已经有答案了 我一直在使用 Boost 库 在 Boost Exception 中 我注意到如下代码 define BOOST THROW EXCEPTION x boost throw exception x 只是出于好奇
  • 避免控制台消息形式封装函数

    我正在使用一个包函数 coreenv 来自 seewave 它在控制台中创建一条 请稍候 消息 正如我反复所说的那样 该消息非常烦人 所以 我需要一种方法 从我的代码中 暂时禁止控制台消息 OR 访问功能代码并取消消息行 以下不是我的真实代
  • 为什么 JSON 比 XML 更轻量?

    我发现 JSON 和 XML 之间的区别 因为 两者都是为了系统之间的数据交换 但是JSON和XML之间有一个很大的区别 即JSON比XML更轻量级 但我无法找到 JSON 轻量级的真正原因 是什么让 JSON 变得轻量级 我发现的一个答案
  • 在 Mac OS X 上使用 pip 安装 pycrypto 时出现 Broken Pipe 错误

    我正在尝试通过 pip 在 OS X 上安装 pycrypto 版本 2 3 当编译器尝试编译 MD2 c 时 我收到 Broken pipeline 错误 使用 easy install 时我遇到了非常类似的错误 这是我收到的错误 bas
  • 在 Three.js 中针对“子场景”进行光线投射

    因此 我正在使用 Three js 示例中的 webgl interactive cubes html 并且我有一个相对简单的问题 是否可以测试光线与对象的子对象的相交 例如 如果我做类似的事情 for var i 0 i lt 2000
  • IPython 的历史向后搜索未按预期工作

    IPython 的history search backward功能是我最喜欢的功能之一 history search backward允许您键入命令的一部分 然后在阅读行历史记录中向后搜索以该命令的该部分开头的命令 默认情况下 我相信 这
  • Kafka分区中消息分布不均匀

    我有一个主题 有 10 个分区 1 个消费者组 有 4 个消费者 工作线程大小为 3 我可以看到分区中的消息分布不均匀 一个分区有太多数据 而另一个分区是空闲的 如何让我的生产者将负载均匀分配到所有分区 以便所有分区都得到正确利用 根据De
  • VBscript 正则表达式替换

    我不知道为什么这仅适用于找到的最后一个实例 而不是我所期望的所有实例 任何帮助表示赞赏 输入字符串 a href http www scirra com target blank http www scirra com a br br a
  • Windows 应用程序认证失败通用 Windows 应用程序 10

    我使用 Html CSS 和 JS 开发了一个 Windows 10 通用应用程序 为了允许内联脚本 我使用 ms appx web 上下文 并将 ms appx web login html 设置为清单中的起始页 我已在清单文件中添加了
  • 从 firebase 函数连接到 MongoDB Atlas

    我正在尝试从 firebase 函数连接到 mongodb atlas 例如 export default async gt try const url mongodb srv foo email protected cdn cgi l e
  • 解组到相同的结构但不同的 json 名称

    我正在尝试解组特定的 json 数据 执行一些数据转换 然后编组数据并发送它 但是我想用不同的 json 变量名称来编组它 我可以将数据编组到另一个 json 名称 例如使用 xyz 而不是 abc abc 1 to xyz 1 packa
  • 日历控件 - 以编程方式突出显示日期

    我正在摆弄日历控件 但似乎无法完成对日期进行着色的简单任务 如果用户输入 7 个日期 我想在日历上对这些日期进行阴影处理 以便用户知道它们已被选择 本质上我想做 Calendar HighlightDate 5 1 11 gt 想象的哈哈我
  • CUDA - 为什么基于扭曲的并行减少速度较慢?

    我有关于基于扭曲的并行减少的想法 因为根据定义 扭曲的所有线程都是同步的 因此 我们的想法是输入数据可以减少 64 倍 每个线程减少两个元素 而无需任何同步 与 Mark Harris 的原始实现相同 减少应用于块级 数据位于共享内存上 h
  • 在 C++ 中使用冒号(':')访问数组中的元素(在 Rcpp 中)

    我正在尝试运行以下代码 坦率地说 我对 C 知之甚少 但我想让以下函数运行 你能帮我运行这个愚蠢的例子吗 cppFunction NumericVector abc int x int x end NumericVector y Numer
  • 即使安装“Apache Cordova 工具”后,Ionic 模板也不会在 Visual Studio 2015 Community Edition 中加载

    我已经按照此安装了 Apache Cordova 工具 link http taco visualstudio com en us docs install vs tools apache cordova 微软给出的 然而 这样做之后我仍然
  • 在 Windows 中设置 cron 作业

    我每天都必须从 SFTP 服务器下载文件 我有一个从服务器检索文件的程序 但我正在考虑设置一个 cron 作业 或任何类似的作业 来自动执行该操作 我们是一家 Windows 商店 需要在 Windows 中设置 cron 作业 windo
  • 如何通过 MongoDB 同步在两个不同服务器上运行的两个应用程序

    我正在 golang 中开发一个 Web 应用程序 并使用单个 MongoDB 实例作为数据存储 我有应该专门执行的代码 由于我的 Web 应用程序运行在两个不同的服务器上 因此我无法使用 golang 同步工具 想法是通过锁定文档来使用