Je crée une sorte de système de file d'attente de tâches en arrière-plan avec MongoDB comme magasin de données. Comment puis-je "écouter" les insertions dans une collection MongoDB avant que des travailleurs ne se reproduisent pour traiter le travail? Dois-je interroger toutes les quelques secondes pour voir si des modifications ont été apportées depuis la dernière fois ou mon script peut-il attendre que des insertions aient lieu? Il s'agit d'un projet PHP sur lequel je travaille, mais n'hésitez pas à répondre en Ruby ou en langage agnostique.
MongoDB a ce qu'on appelle capped collections
et tailable cursors
qui permet à MongoDB d'envoyer des données aux écouteurs.
Un _capped collection
_ est essentiellement une collection de taille fixe qui autorise uniquement les insertions. Voici à quoi cela ressemblerait pour en créer un:
_db.createCollection("messages", { capped: true, size: 100000000 })
_
Ruby
_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 (par 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 (by 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;
}
}
_
n article sur les curseurs disponibles plus en détail.
exemples PHP, Ruby, Python et Perl d'utilisation de curseurs disponibles.
Ce que vous pensez ressemble beaucoup à des déclencheurs. MongoDB ne prend pas en charge les déclencheurs, mais certaines personnes ont "fait leur propre" en utilisant certaines astuces. La clé ici est le oplog.
Lorsque vous exécutez MongoDB dans un jeu de réplicas, toutes les actions de MongoDB sont consignées dans un journal des opérations (appelé oplog). L'oplog est fondamentalement juste une liste récapitulative des modifications apportées aux données. Les ensembles de répliques fonctionnent en écoutant les modifications apportées à cet oplog, puis en les appliquant localement.
Cela vous semble-t-il familier?
Je ne peux pas détailler l’ensemble du processus ici, c’est plusieurs pages de documentation, mais les outils dont vous avez besoin sont disponibles.
Quelques premiers écrits sur oplog - Brève description - Mise en page de la collection local
(qui contient oplog)
Vous voudrez également tirer parti de curseurs disponibles . Celles-ci vous fourniront un moyen d'écouter les modifications au lieu de les interroger. Notez que la réplication utilise des curseurs disponibles, il s'agit donc d'une fonctionnalité prise en charge.
Depuis MongoDB 3.6, il y aura une nouvelle API de notification appelée Change Streams que vous pourrez utiliser pour cela. Voir cet article de blog pour un exemple . Exemple de celui-ci:
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'])
* EDIT: j'ai écrit un article sur la façon de procéder https://medium.com/riow/mongodb-data-collection-change-85b63d96ff76
C'est nouveau dans mongodb 3.6 https://docs.mongodb.com/manual/release-notes/3.6/ 2018/01/10
$ mongod --version
db version v3.6.2
Afin d'utiliser changeStreams , la base de données doit être un jeu de réplication
Informations complémentaires sur les jeux de réplication: https://docs.mongodb.com/manual/replication/
Votre base de données sera un " autonome " par défaut.
Comment convertir un fichier autonome en jeu de réplicas: https://docs.mongodb.com/manual/tutorial/convert-standalone-to-replica-set/
L'exemple suivant est une application pratique pour vous en servir.
* Spécifiquement pour le nœud.
/* 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 */
Liens utiles:
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 version 3.6 inclut désormais les flux de modifications, qui sont essentiellement des API superposées à OpLog, permettant des cas d'utilisation analogues à des déclencheurs/notifications.
Voici un lien vers un exemple Java: http://mongodb.github.io/mongo-Java-driver/3.6/driver/tutorials/change-streams/
Un exemple de NodeJS pourrait ressembler à quelque chose comme:
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));
});
});
Vous pouvez également utiliser la méthode Mongo FindAndUpdate standard et déclencher un événement EventEmitter (dans le nœud) lors du rappel lors de l'exécution du rappel.
Toute autre partie de l'application ou de l'architecture écoutant cet événement sera avisée de la mise à jour et de toutes les données pertinentes qui y seront envoyées. C'est un moyen très simple d'obtenir des notifications de Mongo.
Plusieurs de ces réponses ne vous donneront que de nouveaux enregistrements et non des mises à jour et/ou sont extrêmement inefficaces
Le seul moyen fiable et performant de le faire est de créer un curseur disponible sur la collection locale db: oplog.rs pour obtenir TOUTES les modifications apportées à MongoDB et en faire ce que vous voulez. (MongoDB le fait même plus ou moins en interne pour supporter la réplication!)
Explication de ce que contient le journal des opérations: https://www.compose.com/articles/the-mongodb-oplog-and-node-js/
Exemple de bibliothèque Node.js fournissant une API autour de ce qui peut être fait avec le journal oplog: https://github.com/cayasso/mongo-oplog
Il existe un exemple de travail Java qui peut être trouvé ici .
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();
La clé est OPTIONS DE RECHERCHE donnée ici.
Vous pouvez également modifier la requête de recherche si vous n'avez pas besoin de charger toutes les données à chaque fois.
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);
En fait, au lieu de regarder la sortie, pourquoi ne pas être averti quand quelque chose de nouveau est inséré en utilisant du middleware fourni par schéma de mangouste
Vous pouvez intercepter l'événement d'insertion d'un nouveau document et faire quelque chose après cette insertion
Il existe un ensemble impressionnant de services appelés Point MongoDB . Regardez dans fonctions de point/déclencheurs . Notez qu’il s’agit d’un service AWS payant basé sur un nuage. Dans votre cas, sur une insertion, vous pouvez appeler une fonction personnalisée écrite en javascript.