我正在创建一种以MongoDB作为数据存储的后台作业队列系统。在催生工作者来处理作业之前,如何“监听” MongoDB集合的插入内容?我是否需要每隔几秒钟轮询一次,以查看自上次以来是否有任何更改,或者我的脚本有什么方法可以等待插入发生?这是我正在从事的一个PHP项目,但可以使用Ruby或与语言无关的方式随意回答。
我正在创建一种以MongoDB作为数据存储的后台作业队列系统。在催生工作者来处理作业之前,如何“监听” MongoDB集合的插入内容?我是否需要每隔几秒钟轮询一次,以查看自上次以来是否有任何更改,或者我的脚本有什么方法可以等待插入发生?这是我正在从事的一个PHP项目,但可以使用Ruby或与语言无关的方式随意回答。
Answers:
您在想什么听起来很像触发器。MongoDB没有对触发器的任何支持,但是有些人使用一些技巧“自行滚动”。这里的关键是操作日志。
在副本集中运行MongoDB时,所有MongoDB操作都会记录到操作日志(称为oplog)中。操作日志基本上只是对数据所做修改的运行清单。副本集功能通过侦听此操作日志上的更改,然后在本地应用更改来实现。
这听起来很熟悉吗?
我无法在此处详细说明整个过程,它是几页的文档,但是您可以使用所需的工具。
首先是关于oplog的一些文章- 简要说明
- 集合的布局local
(包含oplog)
您还将需要利用可尾游标。这些将为您提供一种侦听更改而不是轮询更改的方法。请注意,复制使用可尾游标,因此这是受支持的功能。
--replSet
选项启动服务器,它将创建/填充该服务器oplog
。即使没有中学。这绝对是“监听”数据库中更改的唯一方法。
MongoDB具有所谓的功能capped collections
,tailable cursors
它允许MongoDB将数据推送到侦听器。
A capped collection
本质上是一个固定大小的集合,仅允许插入。这是创建一个的样子:
db.createCollection("messages", { capped: true, size: 100000000 })
红宝石
coll = db.collection('my_collection')
cursor = Mongo::Cursor.new(coll, :tailable => true)
loop do
if doc = cursor.next_document
puts doc
else
sleep 1
end
end
的PHP
$mongo = new Mongo();
$db = $mongo->selectDB('my_db')
$coll = $db->selectCollection('my_collection');
$cursor = $coll->find()->tailable(true);
while (true) {
if ($cursor->hasNext()) {
$doc = $cursor->getNext();
print_r($doc);
} else {
sleep(1);
}
}
Python(罗伯特·斯图尔特(Robert Stewart))
from pymongo import Connection
import time
db = Connection().my_db
coll = db.my_collection
cursor = coll.find(tailable=True)
while cursor.alive:
try:
doc = cursor.next()
print doc
except StopIteration:
time.sleep(1)
Perl(按Max排序)
use 5.010;
use strict;
use warnings;
use MongoDB;
my $db = MongoDB::Connection->new;
my $coll = $db->my_db->my_collection;
my $cursor = $coll->find->tailable(1);
for (;;)
{
if (defined(my $doc = $cursor->next))
{
say $doc;
}
else
{
sleep 1;
}
}
从MongoDB 3.6开始,将有一个名为Change Streams的新通知API,可用于此目的。有关示例,请参见此博客文章。例子:
cursor = client.my_db.my_collection.changes([
{'$match': {
'operationType': {'$in': ['insert', 'replace']}
}},
{'$match': {
'newDocument.n': {'$gte': 1}
}}
])
# Loops forever.
for change in cursor:
print(change['newDocument'])
*编辑:我写了一篇有关如何执行此操作的文章https://medium.com/riow/mongodb-data-collection-change-85b63d96ff76
它是mongodb 3.6中 的新功能https://docs.mongodb.com/manual/release-notes/3.6/ 2018/01/10
$ mongod --version
db version v3.6.2
为了使用changeStreams,数据库必须是复制集
有关复制集的更多信息:https : //docs.mongodb.com/manual/replication/
默认情况下,您的数据库将为“ 独立 ”。
如何将独立版本转换为副本集:https : //docs.mongodb.com/manual/tutorial/convert-standalone-to-replica-set/
以下示例是有关如何使用它的实际应用程序。
* 专门针对Node。
/* file.js */
'use strict'
module.exports = function (
app,
io,
User // Collection Name
) {
// SET WATCH ON COLLECTION
const changeStream = User.watch();
// Socket Connection
io.on('connection', function (socket) {
console.log('Connection!');
// USERS - Change
changeStream.on('change', function(change) {
console.log('COLLECTION CHANGED');
User.find({}, (err, data) => {
if (err) throw err;
if (data) {
// RESEND ALL USERS
socket.emit('users', data);
}
});
});
});
};
/* END - file.js */
有用的链接:
https : //docs.mongodb.com/manual/tutorial/convert-standalone-to-replica-set
https://docs.mongodb.com/manual/tutorial/change-streams-example
https://docs.mongodb.com/v3.6/tutorial/change-streams-example
http://plusnconsulting.com/post/MongoDB-Change-Streams
MongoDB 3.6版现在包括变更流,该变更流本质上是OpLog之上的API,允许类似触发/通知的用例。
这是指向Java示例的链接:http : //mongodb.github.io/mongo-java-driver/3.6/driver/tutorials/change-streams/
NodeJS示例可能类似于:
var MongoClient = require('mongodb').MongoClient;
MongoClient.connect("mongodb://localhost:22000/MyStore?readConcern=majority")
.then(function(client){
let db = client.db('MyStore')
let change_streams = db.collection('products').watch()
change_streams.on('change', function(change){
console.log(JSON.stringify(change));
});
});
另外,您可以使用标准的Mongo FindAndUpdate方法,并在回调中运行回调时触发EventEmitter事件(在Node中)。
侦听此事件的应用程序或体系结构的任何其他部分都将收到更新通知,并向其中发送任何相关数据。这是从Mongo获得通知的一种非常简单的方法。
这些答案中的许多只会给您新的记录,而不会给您更新和/或效率极低
唯一可靠,高效的方法是在本地db:oplog.rs集合上创建一个可尾的游标,以获取对MongoDB的所有更改并按照您的意愿进行操作。(MongoDB甚至在内部或多或少都这样做以支持复制!)
关于操作日志包含的内容的说明:https ://www.compose.com/articles/the-mongodb-oplog-and-node-js/
一个Node.js库的示例,该库提供了关于可以通过oplog完成的操作的API:https : //github.com/cayasso/mongo-oplog
有一组很棒的服务,称为MongoDB Stitch。查看针迹功能/触发器。请注意,这是基于云的付费服务(AWS)。在您的情况下,您可以在插入代码上调用用javascript编写的自定义函数。
有一个有效的Java示例,可以在这里找到。
MongoClient mongoClient = new MongoClient();
DBCollection coll = mongoClient.getDatabase("local").getCollection("oplog.rs");
DBCursor cur = coll.find().sort(BasicDBObjectBuilder.start("$natural", 1).get())
.addOption(Bytes.QUERYOPTION_TAILABLE | Bytes.QUERYOPTION_AWAITDATA);
System.out.println("== open cursor ==");
Runnable task = () -> {
System.out.println("\tWaiting for events");
while (cur.hasNext()) {
DBObject obj = cur.next();
System.out.println( obj );
}
};
new Thread(task).start();
关键是此处给出的QUERY OPTIONS。
如果您不需要每次都加载所有数据,也可以更改查找查询。
BasicDBObject query= new BasicDBObject();
query.put("ts", new BasicDBObject("$gt", new BsonTimestamp(1471952088, 1))); //timestamp is within some range
query.put("op", "i"); //Only insert operation
DBCursor cur = coll.find(query).sort(BasicDBObjectBuilder.start("$natural", 1).get())
.addOption(Bytes.QUERYOPTION_TAILABLE | Bytes.QUERYOPTION_AWAITDATA);