良い提案だと思います。 実用的なコードを書く際にpub / subについてかなり混乱しています
+1、非同期libev pub / subで問題が発生しています
ng
フラグが付けられました。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include "hiredis/hiredis.h"
#include "hiredis/async.h"
#include "hiredis/adapters/libevent.h"
void onMessage(redisAsyncContext *c, void *reply, void *privdata) {
redisReply *r = reply;
if (reply == NULL) return;
if (r->type == REDIS_REPLY_ARRAY) {
for (int j = 0; j < r->elements; j++) {
printf("%u) %s\n", j, r->element[j]->str);
}
}
}
int main (int argc, char **argv) {
signal(SIGPIPE, SIG_IGN);
struct event_base *base = event_base_new();
redisAsyncContext *c = redisAsyncConnect("127.0.0.1", 6379);
if (c->err) {
printf("error: %s\n", c->errstr);
return 1;
}
redisLibeventAttach(c, base);
redisAsyncCommand(c, onMessage, NULL, "SUBSCRIBE testtopic");
event_base_dispatch(base);
return 0;
}
Ubuntu 14.04では、この例を正常にコンパイルするには、 libevent-dev
ライブラリをインストールする必要があります。 また、コンパイル中に-levent
フラグを渡す必要があります。
ドキュメントの問題は、何かをサブスクライブできる場所には書かれていないが、他のイベントをサブスクライブする以外に何もできないことです。
次のようなものを試してください
redisAsyncCommand(c, onMessage, NULL, "SUBSCRIBE testtopic");
redisAsyncCommand(c, onAnotherMessage, NULL, "SUBSCRIBE anothertopic");
redisAsyncCommand(c, onReply, NULL, "SET toto 5");
redisAsyncCommand(c, onReply, NULL, "PUBLISH testtopic \"hello\"");
redisAsyncCommand(c, onReply, NULL, "GET toto");
そして、最初の2つのコマンドのみが機能します。 もう1つはREDIS_OK
応答しますが、 onReply()
コールバックはNULL
応答を受け取ります。
物事を正しく行う唯一の方法は、2つのredisコンテキストを使用することです。1つはサブスクライブ(およびMONITOR)に使用し、もう1つは残りに使用します。 たぶん、この制限を反映するようにドキュメントを更新する必要がありますか?
各APIのドキュメントはありますか?
ソケットが最終的に閉じるときのイベントをどのようにキャプチャしますか?
ソケットが最終的に閉じるときのイベントをどのようにキャプチャしますか?
または、タイムアウトを迅速に検出して新しい接続をやり直すにはどうすればよいですか? たとえば、間にあるファイアウォールが接続を閉じ、後でパケットをドロップした場合です。
その接続でサブスクライブするだけでは、タイムアウトをチェックするために何かを送信することが理解できるので、それは不可能になります。 また、TCPキープアライブはASYNCには存在しないようであり、とにかく、たとえば、Dockerコンテナ内のLinuxでこれらを適切に構成するのは困難です(間隔、再試行など)。
通常、TCPソケットでは、ソケットの読み取り/書き込みタイムアウトが短くなる可能性があります。何かを送信した後、タイムアウトするかどうかを確認し、別のサーバーにすばやく再接続するか、何か他のことを試みます...
pingコマンドを非同期に送信し、読み取り/書き込みタイムアウトを構成して切断をトリガーできるものが他にある場合は、それが便利です。
@Gerporgl
私自身の実装では、redisAsyncSetDiconnectCallbackを利用しましたが、これがどのように機能するかのメカニズムについては調査していません。
https://github.com/nidhhoggr/twemproxy_sentinel/commit/602e07cfdbd57a307ff008e8e9d41909ac34b004
私の場合、切断イベントは、私がいつも「サイレント切断」と呼んでいたこのケースでは十分に速く発生しないようです(またはまったく発生しない場合もあります)。一般に、時間がかかります(5回以上待機しました)。数分でイベントを受信しませんでしたが、10〜15分後にようやく取得しました)
この件についてもう少し読んだ後、サブスクライブしている間もPINGsコマンドを実行できることに気付きました。この投稿では、公開されたメッセージであるかのように、独自のPING応答イベントをサブスクライブできることに言及しています。
https://github.com/redis/hiredis/issues/351
基本的に、現在うまく機能していると思われるのは、たとえば1秒間隔でPINGを送信することです。これ以上、ping応答を受信しない場合(または特定の許容しきい値を許可する場合)、接続が切断されていると見なし、クリーンアップを実行してから、再接続します。 それははるかに堅牢で迅速なようです。
libeventとhiredisを使用すると、通常のメッセージ配信を非同期で処理しながら、PINGのタイマーイベントを使用して完全な非同期サブスクライブの実装を行うことができます。
@Gerporgl
これはとても役に立ちました!
万が一、キースペースイベント通知に同じredisAsyncCommand /非同期コンテキストメカニズムを使用しようとしましたか?
私はその部分を機能させようとしています。 しかし、残念ながらメッセージは届きません。
これを試しましたか?
redis-cli config set notify-keyspace-events KEA
redisAsyncCommand(c, subscribeCallback, NULL, "PSUBSCRIBE __key*__:*");
@ joe-at-startupmedia
ありがとう! 私はすでにこれを試しました..そして私が戻ってきているのは最初のサブスクライブ応答だけのようです。
データの更新は、実際にはコールバックで受信されません。
サンプルコードは次のとおりです。
`
空所
onPubsubMessage(redisAsyncContext * c、void * reply、void * privdata)
{{
redisReply * r =(redisReply *)返信;
if(reply == NULL)return;
/* What if reply type is something else... */
if (r->type == REDIS_REPLY_ARRAY) {
for (int j = 0; j < r->elements; j++) {
if (r->element[j]->type == REDIS_REPLY_STRING) {
myPubsubFile.open(PUBSUB_FILE, fstream::in | fstream::out | fstream::app);
myPubsubFile << r->element[j]->str << endl;
myPubsubFile.close();
} else if (r->element[j]->type == REDIS_REPLY_INTEGER) {
cout << "Integer : "<< r->element[j]->integer << endl;
}
}
}
return;
}
空所
onKeyspaceMessage(redisAsyncContext * c、void * reply、void * privdata)
{{
redisReply * r =(redisReply *)返信;
if(reply == NULL)return;
cout << "Got keyspace event notification from REDIS.. " << endl;
/* What if reply type is something else... */
if (r->type == REDIS_REPLY_ARRAY) {
cout << "Type is an array.. " << endl;
cout << "Number of elements here: " << r->elements << endl;
for (int j = 0; j < r->elements; j++) {
cout << "\t\t Type for element : " << r->element[j]->type << endl;
if (r->element[j]->type == REDIS_REPLY_STRING) {
cout << "\t\t\t" << r->element[j]->str << endl;
} else if (r->element[j]->type == REDIS_REPLY_INTEGER) {
cout << "\t\t\t" << "Integer : "<< r->element[j]->integer << endl;
}
}
} else {
cout << "This is the type for response : " << r->type << endl;
}
return;
}
空所*
pubsubRecipient(void * arg)
{{
struct event_base * base = event_base_new();
redisAsyncContext *c = redisAsyncConnect("127.0.0.1", 6379);
if (c->err) {
printf("error: %s\n", c->errstr);
return NULL;
}
redisLibeventAttach(c, base);
redisAsyncCommand(c, onPubsubMessage , NULL, SUBSCRIBE_CHANNEL);
redisAsyncCommand(c, onKeyspaceMessage , NULL, SUBSCRIBE_KEYEVENT);
`
PS REDISでキースペース通知を有効にする構成部分は、別のプロセスにあります。 別のターミナルでredis-cliのインスタンスを実行すると、その構成の効果を確認できます。
ここで何かが足りないのに、キーイベントのサブスクリプション文字列が次の場合はご容赦ください。
"PSUBSCRIBE 'key*:*'"
そのパターンに合わせて次のように変更しないでください。
"PSUBSCRIBE __key*__:*"
ああ..このエディターで何か奇妙なことが起こっています...コードをコピーして貼り付けたときに、キーを囲むアンダースコアが表示されていました。
それにもかかわらず、問題は私の__キー* _ _パターンを囲む-> '' <-(一重引用符)であったように見えます。
redis-cliは問題なくそれらを受け入れます。 しかし、redisコマンドで送信すると、ここに問題があります。
古い問題を通過します。 例は何年も前にWikiに追加されました(@aluikenに感謝)
最も参考になるコメント