MongoDBをデータストアとして使用して、一種のバックグラウンドジョブキューシステムを作成しています。ジョブを処理するワーカーを生成する前に、MongoDBコレクションへの挿入を「リッスン」するにはどうすればよいですか?前回からの変更があるかどうかを確認するために数秒ごとにポーリングする必要がありますか、それともスクリプトが挿入の発生を待機する方法はありますか?これは私が取り組んでいるPHPプロジェクトですが、Rubyまたは言語にとらわれずに自由に回答してください。
MongoDBをデータストアとして使用して、一種のバックグラウンドジョブキューシステムを作成しています。ジョブを処理するワーカーを生成する前に、MongoDBコレクションへの挿入を「リッスン」するにはどうすればよいですか?前回からの変更があるかどうかを確認するために数秒ごとにポーリングする必要がありますか、それともスクリプトが挿入の発生を待機する方法はありますか?これは私が取り組んでいるPHPプロジェクトですが、Rubyまたは言語にとらわれずに自由に回答してください。
回答:
あなたが考えていることは、トリガーのように聞こえます。MongoDBはトリガーをサポートしていませんが、一部のトリックを使用して「自分でロール」した人もいます。ここでの鍵はoplogです。
レプリカセットでMongoDBを実行すると、すべてのMongoDBアクションが操作ログ(oplogと呼ばれる)に記録されます。oplogは基本的に、データに対して行われた変更の単なる実行リストです。レプリカセットは、このoplogの変更をリッスンし、変更をローカルに適用することによって機能します。
これは聞き覚えがありますか?
ここではプロセス全体を詳しく説明することはできません。数ページのドキュメントですが、必要なツールを利用できます。
最初にoplogに関するいくつかの説明
- 簡単な説明 - コレクションのレイアウトlocal
(oplogを含む)
また、テールカーソルを活用することもできます。これらは、変更をポーリングする代わりに、変更をリッスンする方法を提供します。レプリケーションではテール可能なカーソルが使用されるため、これはサポートされている機能です。
--replSet
オプションを使用してサーバーを起動すると、サーバーが作成/設定されますoplog
。セカンダリなしでも。これは間違いなく、DBの変更を「聞く」唯一の方法です。
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;
}
}
Ruby / Node.jsチュートリアルでは、MongoDBのキャップされたコレクションへの挿入をリッスンするアプリケーションの作成について説明しています。
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/
次の例は、これをどのように使用するかについての実用的なアプリケーションです。
*特にノードの場合。
/* 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イベントを発生させることができます。
このイベントをリッスンするアプリケーションまたはアーキテクチャの他の部分には、更新が通知され、関連するデータもそこに送信されます。これは、Mongoからの通知を取得するための本当に簡単な方法です。
これらの回答の多くは、新しいレコードのみを提供し、更新は提供しないか、非常に非効率的です。
これを行うための唯一の信頼性の高い、パフォーマンスの高い方法は、ローカルdb:oplog.rsコレクションに調整可能なカーソルを作成して、MongoDBへのすべての変更を取得し、それを実行することです。(MongoDBは、レプリケーションをサポートするために、内部的に多かれ少なかれこれを実行します!)
oplogの内容の説明:https ://www.compose.com/articles/the-mongodb-oplog-and-node-js/
oplogで実行できることに関するAPIを提供するNode.jsライブラリの例:https : //github.com/cayasso/mongo-oplog
MongoDB Stitchと呼ばれる素晴らしいサービスのセットがあります。見ステッチ機能/トリガ。これはクラウドベースの有料サービス(AWS)であることに注意してください。あなたの場合、挿入時に、JavaScriptで記述されたカスタム関数を呼び出すことができます。
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);
実際には、出力を監視する代わりに、mongooseスキーマによって提供されるミドルウェアを使用して新しいものが挿入されたときに通知を受け取らない理由
新しいドキュメントを挿入するイベントをキャッチし、この挿入が完了した後に何かを行うことができます