前回の記事では、redisマスターノードとスレーブノード間のデータ同期レプリケーションテクノロジーを紹介しました。フルコピーと中断のないコマンド伝播により、マスターデータとスレーブデータの同期バックアップの効果を得ることができます。マスターノードが停止したら、1つを選択できます。動作中のスレーブが新しいマスターノードになり、他のスレーブに同期させます。
これはredisフェイルオーバーを処理する方法ですが、結局のところ、手動で障害に対処するため、redis障害時間ノードは予測できないため、本番環境での実用性はありません。フェイルオーバーを自動的に処理するには、自動監視コンポーネントが必要です。
Redisセンチネルモード(Sentinel)は、Redis間の障害のあるノードの転送を自動的に監視および処理する「もの」です。Sentinelは実際にはRedisサーバープログラムですが、特別なモードで実行され、データストレージを提供しません。通常のredisノードの監視と管理専用のサービス。
1. Sentinelとは何ですか?
Sentinelは実際にはredisサーバープログラムです。また、serverCron関数を定期的に実行しますが、他のプログラムでは使用されず、通常のredisノードのモニタリングモジュールとフェイルオーバーモジュールを使用します。
Sentinelが初期化されると、元のコマンドテーブルがクリアされ、独自の固有のコマンドが書き込まれます。そのため、通常のredisノードでサポートされているデータ読み取りおよび書き込みコマンドは、これらのコマンドをまったく初期化しないため、Sentinelでは見つかりません。アクチュエータ。
Sentinelは、監視しているマスター上で定期的にinfoコマンドを実行して、最新のマスター/スレーブ関係を取得します。また、pingハートビート検出コマンドをすべてのredisノードに定期的に送信します。マスターが応答できないことが検出されると、他のノードに送信されますSentinelはメッセージを送信し、主観的にマスターがダウンしていると主観的に信じます。オフラインマスターの数が値に達したことにSentinelクラスターが同意すると、全員が同意してマスターをオフラインにします。
オフラインになる前に必要なことは、オフライン操作を実行するSentinelクラスタの1つを見つけることです。このステップはリーダー選出と呼ばれます。選出後、適切なマスターがマスターのすべてのスレーブノードから選択され、他のスレーブは新しいマスターを再同期します。
実際、上記ではSentinelとは何か、本質的に何が行われたかを簡単に紹介しましたが、ソースコードと組み合わせて実装の詳細について詳しく説明します。ここでは、Sentinelモニタリングを構成して開始する方法について説明します。(本番環境では4つ以上構成することをお勧めします)
最初のステップは、通常のredisサーバーノードを起動することです。
このステップでは何も言うことはありません。デフォルトのポート6379から始めます。
2番目のステップは、3つの異なるスレーブノードを起動することです。
3番目のステップは、センチネル構成ファイルを書き込むことです。
Sentinelは実際には特別なモードで実行されているredisサーバーであるため、ポートを実行する必要があると説明しました。次に、コマンドセンチネルモニター mymasterを使用して、現在のセンチネルと目的のオフラインパラメーターによって監視されるようにマスターノードのredis を構成します。センチネルダウンミリ秒後にパラメーターを構成します。マスターの最大応答時間は、この時間の後に主観的にオフラインになると判断されます。 。
センチネル並列同期構成は、マスタースレーブが完全同期フェーズを実行し、スレーブノードがデータの読み込み時にサービスを提供しないことがわかっているため、マスタースレーブ切り替え後にほとんどのデータを同期できるスレーブノードの数を制限するために使用されます。このパラメーターが大きい場合、それからもちろん、マスター/スレーブ切り替えを完了する時間が短いほど、多数のスレーブノードが読み取りサービスを提供できなくなり、逆もまた同様です。
Sentinelフェイルオーバータイムアウトは、フェイルオーバーの最大待機時間を設定します。
4番目のステップは、Sentinelを起動することです。
コマンドredis-sentinel [config]を使用して、3つのセンチネルを開始します。
この場合、実際には、簡単なセンチネルクラスター構成が完了しているので、手動でマスターをシャットダウンして、センチネル全体がフェールオーバーするかどうかを確認します。
結果から、センチネルは自動的に元のスレーブノード7003を新しいマスターとして設定します。具体的なプロセスについては詳しく説明しません。ソースコードと併せて詳しく説明します。ここでは、センチネルの実際のアプリケーションを一般的に理解する必要があります。 。
2. Sentinelの仕組み
コマンドredis-sentinelを使用してセンチネルを開始すると、
int main(int argc, char **argv) {
。。。。。
server.sentinel_mode = checkForSentinelMode(argc,argv);
。。。。。
if (server.sentinel_mode) {
initSentinelConfig();
initSentinel();
}
。。。。。
}
checkForSentinelMode関数は、コマンドとパラメータに従って、センチネルモードで開始されているかどうかをチェックして判断します。開始されている場合は1を返し、逆の場合も同様です。センチネルによって開始された場合、センチネルの初期化が実行されます。
void initSentinelConfig(void) {
server.port = REDIS_SENTINEL_PORT; //26379
}
initSentinelConfigは実際には現在の番兵実行ポートを初期化するためのもので、デフォルトは26379です。
void initSentinel(void) {
unsigned int j;
//清空普通redis-server下可用的命令表
dictEmpty(server.commands,NULL);
//加载sentinel需要的命令
for (j = 0; j < sizeof(sentinelcmds)/sizeof(sentinelcmds[0]); j++) {
int retval;
struct redisCommand *cmd = sentinelcmds+j;
retval = dictAdd(server.commands, sdsnew(cmd->name), cmd);
serverAssert(retval == DICT_OK);
}
sentinel.current_epoch = 0;
//根据配置文件,初始化自己需要监控的master(一个sentinel是可能监控多个 master的)
sentinel.masters = dictCreate(&instancesDictType,NULL);
sentinel.tilt = 0;
sentinel.tilt_start_time = 0;
sentinel.previous_time = mstime();
sentinel.running_scripts = 0;
sentinel.scripts_queue = listCreate();
sentinel.announce_ip = NULL;
sentinel.announce_port = 0;
sentinel.simfailure_flags = SENTINEL_SIMFAILURE_NONE;
sentinel.deny_scripts_reconfig = SENTINEL_DEFAULT_DENY_SCRIPTS_RECONFIG;
memset(sentinel.myid,0,sizeof(sentinel.myid));
}
initSentinelの主な機能は、通常モードでredisコマンドテーブルをクリアし、センチネルに固有のコマンドをロードし、監視するマスターコレクションを初期化することです。
この時点で、センチネルの初期化が完了しても、残りの自動監視はタイミング関数serverCronにあるので、見てみましょう。
//间隔 100 毫秒执行一次 sentinelTimer
run_with_period(100) {
if (server.sentinel_mode) sentinelTimer();
}
つまり、センチネルが開始された後、sentinelTimer関数がserverCronで1回呼び出され、100ミリ秒の間隔でいくつかの重要なイベントを処理します(実際、sentinelTimerは実行間隔を変更します)。
void sentinelTimer(void) {
sentinelCheckTiltCondition();
sentinelHandleDictOfRedisInstances(sentinel.masters);
sentinelRunPendingScripts();
sentinelCollectTerminatedScripts();
sentinelKillTimedoutScripts();
server.hz = CONFIG_DEFAULT_HZ + rand() % CONFIG_DEFAULT_HZ;
}
sentinelTimerの本体は非常に短いですが、満足しすぎないでください。sentinelCheckTiltCondition関数には入りません。Redisはシステム時間に大きく依存しています。システムクロックエポックが繰り返し検出されると、現在のシステムが不安定であると判断され、TITLになります。これは、スリープ状態に似ており、フェイルオーバーしません。データを収集し、システムが安定するのを待ちます。
void sentinelHandleDictOfRedisInstances(dict *instances) {
dictIterator *di;
dictEntry *de;
sentinelRedisInstance *switch_to_promoted = NULL;
di = dictGetIterator(instances);
//递归遍历监控的所有 master,执行监控操作
while((de = dictNext(di)) != NULL) {
sentinelRedisInstance *ri = dictGetVal(de);
//这是监控的核心逻辑,下文细说
sentinelHandleRedisInstance(ri);
if (ri->flags & SRI_MASTER) {
//不论是 slave 还是其他 sentinel,都视作一个redisInstance
sentinelHandleDictOfRedisInstances(ri->slaves);
sentinelHandleDictOfRedisInstances(ri->sentinels);
if (ri->failover_state == SENTINEL_FAILOVER_STATE_UPDATE_CONFIG) {
switch_to_promoted = ri;
}
}
}
if (switch_to_promoted)
sentinelFailoverSwitchToPromotedSlave(switch_to_promoted);
dictReleaseIterator(di);
}
sentinelHandleRedisInstanceは主に、監視とフェイルオーバーの2つの部分で構成されています。
void sentinelHandleRedisInstance(sentinelRedisInstance *ri) {
sentinelReconnectInstance(ri);
sentinelSendPeriodicCommands(ri);
if (sentinel.tilt) {
if (mstime()-sentinel.tilt_start_time < SENTINEL_TILT_PERIOD) return;
sentinel.tilt = 0;
sentinelEvent(LL_WARNING,"-tilt",NULL,"#tilt mode exited");
}
sentinelCheckSubjectivelyDown(ri);
if (ri->flags & (SRI_MASTER|SRI_SLAVE)) {
/* Nothing so far. */
}
if (ri->flags & SRI_MASTER) {
sentinelCheckObjectivelyDown(ri);
if (sentinelStartFailoverIfNeeded(ri))
sentinelAskMasterStateToOtherSentinels(ri,SENTINEL_ASK_FORCED);
sentinelFailoverStateMachine(ri);
sentinelAskMasterStateToOtherSentinels(ri,SENTINEL_NO_FLAGS);
}
}
sentinelReconnectInstance関数は2つのことを実行します。これは現在センチネルインスタンスであるため、最初に、現在トラバースされているインスタンスとの接続を確立し、それがマスター、スレーブ、センチネルのいずれであっても、接続が正常に確立された後にpingコマンドを送信します。第2に、現在のトラバーサルがマスターまたはスレーブの場合は、そのsentinel_helloチャネルにサブスクライブし、このチャネルにメッセージの更新があると、チャネルのサブスクライブしているすべてのクライアントをブロードキャストします。(このチャネルにサブスクライブする主な役割は、他のセンチネルを発見し、監視対象ノードでのビューを他のセンチネルと交換するために引き続き使用されます)
sentinelSendPeriodicCommands関数は、マスターとスレーブの関係を理解するために、デフォルトで10秒ごとにマスターとスレーブに情報コマンドを送信します。このインスタンスが主観的にオフラインの場合、情報コマンドの送信頻度が加速され、マスターとスレーブの関係ができるだけ早くわかるようになります。変更により、すべてのタイプのインスタンスに毎秒pingも送信されます。
上記は実際には、sentinelHandleRedisInstanceの監視ノードの一部です。以下では、フェイルオーバーの方法を引き続き説明します。
void sentinelHandleRedisInstance(sentinelRedisInstance *ri) {
sentinelReconnectInstance(ri);
sentinelSendPeriodicCommands(ri);
//判断是否需要进入 tilt 模式
if (sentinel.tilt) {
if (mstime()-sentinel.tilt_start_time < SENTINEL_TILT_PERIOD) return;
sentinel.tilt = 0;
sentinelEvent(LL_WARNING,"-tilt",NULL,"#tilt mode exited");
}
//判断是否需要主观下线该节点
sentinelCheckSubjectivelyDown(ri);
if (ri->flags & (SRI_MASTER|SRI_SLAVE)) {
/* Nothing so far. */
}
if (ri->flags & SRI_MASTER) {
//判断是否需要客户下线该节点
sentinelCheckObjectivelyDown(ri);
//如果确定该节点客观下线,进行领导者选举
if (sentinelStartFailoverIfNeeded(ri))
sentinelAskMasterStateToOtherSentinels(ri,SENTINEL_ASK_FORCED);
//故障转移
sentinelFailoverStateMachine(ri);
sentinelAskMasterStateToOtherSentinels(ri,SENTINEL_NO_FLAGS);
}
}
sentinelCheckSubjectivelyDownは、現在のノードを主観的にオフラインにする必要があるかどうかを検出し、判断条件はノードに独自の構成があることです。現在のインスタンスが構成された時間を超えて独自のpingに応答しない場合、オフラインであると判断され、主観的なオフラインフラグが設定されます。
sentinelCheckObjectivelyDownオフラインの目的の現在の条件に達したかどうかを検出します。検出ロジックはこれです。すべての兄弟センチネル構造を反復処理して、現在のノードが主観的にオフラインになっているかどうかを確認します。数をカウントします。クォーラムに達した場合、マスターは客観的にオフラインです、フラグを設定し、チャネルを通じて他の兄弟の歩哨に通知します。
sentinelStartFailoverIfNeededは、現在センチネルフェイルオーバーがあるかどうかを判別します(マスターのフラグビット。センチネルフェイルオーバーがある場合は、このフラグビットが設定されます)。存在する場合は、参加せず、何もしません。
sentinelAskMasterStateToOtherSentinelsは、マスターをリーダーとしてフェイルオーバーすることに同意するように要求するメッセージを他のセンチネルに送信します。具体的には、まず最初に、トラバーサルを実行するために私の側のすべての兄弟センチネルに関する情報を取得し、リーダーになることに同意するように依頼するコマンドis-master-down-by-addrを送信し、コールバック関数sentinelReceiveIsMasterDownReplyを設定します返信を処理します。
センチネルが他の人からリーダー投票を受け取り、他の人に投票していない場合は同意し、そうでない場合は無視されます。
番兵は十分な票を受け取ると、自分自身をリーダーであると見なし、マスターをフェイルオーバーとしてマークし、真のフェイルオーバー操作を実行します。
void sentinelFailoverStateMachine(sentinelRedisInstance *ri) {
serverAssert(ri->flags & SRI_MASTER);
if (!(ri->flags & SRI_FAILOVER_IN_PROGRESS)) return;
switch(ri->failover_state) {
//故障转移开始
case SENTINEL_FAILOVER_STATE_WAIT_START:
sentinelFailoverWaitStart(ri);
break;
//选择一个要晋升的从节点
case SENTINEL_FAILOVER_STATE_SELECT_SLAVE:
sentinelFailoverSelectSlave(ri);
break;
//发送slaveof no one命令,使从节点变为主节点
case SENTINEL_FAILOVER_STATE_SEND_SLAVEOF_NOONE:
sentinelFailoverSendSlaveOfNoOne(ri);
break;
//等待被选择的从节点晋升为主节点,如果超时则重新选择晋升的从节点
case SENTINEL_FAILOVER_STATE_WAIT_PROMOTION:
sentinelFailoverWaitPromotion(ri);
break;
//给所有的从节点发送slaveof命令,同步新的主节点
case SENTINEL_FAILOVER_STATE_RECONF_SLAVES:
sentinelFailoverReconfNextSlave(ri);
break;
}
}
sentinelFailoverStateMachineフェイルオーバーは5つのステップで構成されます。これらのステップは、定期的な処理を実行するために5つのsentinelTimerに分割されます。新しいマスターの選挙が完了すると、新しいマスターが登場したことを知らせるために他の兄弟センチネルにブロードキャストし、受信すると、元のマスターの主観的なオフラインをキャンセルし、新しいマスターの監視を再開します。
これまでのところ、Sentinelの紹介とソースコード分析は終了しました。これは、基本的に特別なモードで実行されているredis-serverです。マスターノードとスレーブノードに常にpingを送信することで、障害の可能性があることを検出した後、まとめて投票を行って決定し、マスターの客観的ダウンラインを実行する人を選出してください。
次の記事では、redisのより強力なクラスターについて説明します。