Redisのソース解析 - イベントAE

しばらくの鎖Redisのソースコード解析、イベント駆動型及び原子炉パターンの彼の理解の深さは十分ではありませんので、ソースルックスはより難しく、簡単なイベント駆動型モデルを実現するためにしたいと考え、幸い自分自身。かなり簡単に、このように、選択およびキューのpythonモジュールの使用は、何かを学ぶためにたくさんの練習を簡単なチャットサーバーを開発戻って、RedisのAEイベントのソースを見て。

イベントの概要
簡単なポイントを使用してハンドルイベントにRedisの原子炉のモデルは、Redisのイベントは、すべてのイベント・プールに加工され、その後、一つのイベントでそれらを1つずつ削除されます、イベントのコールバック関数は、それを処理するために、このようなモデルは、希望イベントの態度を処理している原子炉パターンと呼ばれるには、次のとおりです。「DOは私に来ていない、静かに数直線を取る、私はコールの番号に変わります。」

イベントは、Redisのファイルイベントと時間イベント2種類に分かれています。このような読み書き可能に読み取り、書き込み、イベント、イベント記述子としてリスナーの関心のI / Oイベントを、多重化ファイルイベントI / Oを使用しており、それがイベントに置かれていますイベントプール保留、Redisのは、イベントループの1ずつそれら1に対処します。

イベントの時刻が予め設定された時間を満たすために必要なときはいつでも、タイマを維持することで、イベントの時間が処理され、その後のRedisのイベントループで処理されるマーク。これら二つの事象の優先順位を処理するためのRedisは次のとおりです。ファイルには、イベントのタイムイベントよりも優先されます。

イベントデータ構造
上のアウトラインでは、我々はいくつかのキー・プール取得することができます:イベントプールとイベントループを保留し、ファイルイベント、タイムイベントを、Redisのは言葉が自分の構造を定義するいくつかのキーです。ここでは、見てみましょうするために:

/ *ファイルのイベント構造体* /
typedefは構造体aeFileEvent {
int型のマスク; //マークは、読み取りまたは書き込み
aeFileProc * rfileProcを; //読み込みイベントハンドラ
aeFileProc * wfileProc; //書き込みイベントハンドラ
void *型clientData; //イベント処理されるデータに含ま
} aeFileEvent;
明らかにイベント・ファイルの処理においては、マスク機能を読み書きすることが必要であると判断し、イベントを処理するようによれば、これはよく理解されています。

/ *タイムイベント構造* /
typedefは構造体{aeTimeEvent
ロングロングID、イベントの時間をマーキングするための//時間イベント識別子
長いwhen_sec; //タイムイベントトリガ時間秒
長いwhen_ms; //時間イベント・トリガ時間微分秒
aeTimeProc * timeProc;対応するための//イベントハンドラ
aeEventFinalizerProc * finalizerProc;最後のハンドラは、設定した場合、イベントはイベントの//ときに削除されて呼び出されます
void *型のclientDataを; //データ
構造体aeTimeEvent *次; //次回のイベント
} aeTimeEventに、
上記構成において、識別子は、イベントの時間を含む、各時間はサイクルタイムがトリガ時間にか否かが判断される、イベントIDとタイムトリガ時間、そのようなコールに対応するよう処理のためのハンドラは、事件の時間も前回イベントハンドラを定義します。また、タイムイベントへの次のポインタの下に、そのような秒数を実行するための時間間隔などの一部のイベントは、この時間次のポインタは便利です。

/ *トリガ構造までの時間* /
typedefは構造体{aeFiredEvent
INT FDに; //イベント記述子ファイル
INTマスク; //書き込みタグ
} aeFiredEvent、
各I / Oは、プロシージャ・コールを多重化した後、ファイルを返すが用意されていますイベント記述子、今回は構造ダウンの形で保存されます。Redisの処理されるべきプールのイベントの上にある1つの処理により、イベントの1に対処する準備ができてイベントループ、。

/ *イベントループ構造* /
typedefは構造体{aeEventLoop
INT maxFd; //現在の最大レジスタ記述子
int型のsetSize; //ニーズ記述子の数監視する
長い長いtimeEventNextIdを; //次回のイベントID、用一意発生時間イベント識別
lastTimeのtime_tは; //入射サイクルタイムのシステム時刻変更するか否かを検出するための
イベントaeFileEvent *を、使用する//イベント登録文書を
aeFiredEvent *が発射; //を処理する準備ができています時間
aeTimeEvent * timeEventHead; //初めてイベントイベント時間は、実際のリストであるため、
int型の停止; //一時停止の標識、ストップ示し
void *型apidataを; //基礎となるデータの特定のAPIを処理するため、ファイルディスクリプタのために、そのepoll_fdとepoll_eventは含め
//なし保留中のイベントと呼ばれる。aeBeforeSleepProc * beforesleep
} aeEventLoopは、

悪い構造、すべてのファイルイベントとイベントの時間の保存、およびイベントが準備されていて、時間は、以下の識別パラメータがあります。これらのイベントは、サイクリング、安全かつ効率的な運用を行います。

作成および削除イベント
ファイルイベントとイベントの時に私たちが知っている、Redisのイベント、上記にもマッピングされた構造を提供します。まず第一に、私たちはイベントを作成および削除する方法を見ています。

/ * * /ファイルイベントを作成します
int型aeCreateFileEvent(aeEventLoop *イベントループ、int型のfd、int型のマスク、
aeFileProc * procのボイドclientData);
/
削除ファイルイベント* /
無効aeDeleteFileEvent(aeEventLoop イベントループ、int型のfd、int型マスク);
/
ファイルに基づいて記述子GETファイルイベント* /
int型aeGetFileEvents(aeEventLoop イベントループ、int型のfd);
/
イベントの作成時刻* /
ロングロングaeCreateTimeEvent(aeEventLoop *イベントループ、ロングロングミリ秒、
aeTimeProc * PROC、void *型clientData、
aeEventFinalizerProc finalizerProc);
/
消去時間イベント* /
; int型aeDeleteTimeEvent(上記のid上記aeEventLoop *イベントループ、ロングロング)
、いくつかの初期化操作を、その構造であるこれらの機能は非常に単純です、私たちは主にコードを追跡し続け、いつでもイベントを作成するために見て、どの一目で。

ファイルイベントについて、二つの場所は、あなたがそれを作成する必要があります。

サーバは新しいクライアント接続を監視する必要があるときに初期化
server.cへ/ *傍受/ initServer機能/
/
サーバは、各イベントに応じてリスナーのファイル記述子を作成するために、複数のファイル記述子を有していてもよく、新しいクライアント接続をリッスン/
について(J = 0; J <server.ipfd_count; J ++){
IF(aeCreateFileEvent(server.el、server.ipfd [J]、AE_READABLE、
acceptTcpHandler、NULL)== AE_ERR)
{
ファイルserver.ipfd作成serverPanic(「回復不能なエラーイベント。 ");
}
}
(server.sofd> 0 && aeCreateFileEvent(server.el、server.sofd、AE_READABLE、IF
acceptUnixHandler、NULL)== AE_ERR)serverPanic("ファイルイベントを作成する回復不能なエラーserver.sofd「);
サーバーへのクライアント接続した後、クライアントは、読み取りと書き込みのイベントに耳を傾ける必要があります
/
netWorking.c / createClient機能に取らコード/
/
新しいユーザー接続のニーズがクライアントの構造を作成するときに、あなたは読み取りと書き込みの操作* /監視するために使用されるファイルイベント、作成する必要があります
IF(aeCreateFileEvent(server.el、FD、AE_READABLE、readQueryFromClient、C)== AE_ERR)
{
閉じます(FD);
zfree©;
戻りNULL;
}
フロントAOFと処理RDBの永続性とは、我々が言及したキー操作を満了したときに、イベントの時間については、Redisのサーバーは、それは、時限操作するように設計され、初期化時に作成されますこれらの動作時間イベントタイミングは、特定の時間にイベントのトリガ時間を設定することで、対応するプロセス。

/ * server.cに撮影/ initServer機能/
/
タイムイベントを作成し、タスク処理タイミング* /のための
IF(aeCreateTimeEvent(server.el ,. 1、serverCron、NULL、NULL)== AE_ERR){
serverPanic(「できるわけではありません);.」作成serverCronでタイムイベントを
終了(1);
}
I / Oマルチプレクサ
イベントの処理ファイルを作成する前に、あなたは私に、準備ができたファイルディスクリプタを取得するために多重化I / Oを呼び出す必要があります/ O多重化、次の4つの機能を使用することができます。

選択
evport(libevent)
ファイルディスクリプタ
たkqueue
異なる多重化システムは、さまざまな機能をサポートする、請求を選択して、MacOSの下で使用することができるファイルディスクリプタのLinuxの使用および添付libeventが使用evportなる可能性がある場合、kqueueのを選択します。I / O多重化のために、私はこの研究ノートを参照することができます理解していない:UNIXネットワークはファイブプログラミング:選択して、ポーリング機能を

異なるオペレーティングシステムに適用するためのRedisだけでなく、最大効率を達成するために、現在のI / O多重化機能に応じて、最も効率的なオペレーティングシステム環境を選択するために、マクロ・ライブラリーの選択として定義。

HAVE_EVPORTの#ifdef
の#include "ae_evport.c"
の#else
の#ifdef HAVE_EPOLL
の#include "ae_epoll.c"
の#else
の#ifdef HAVE_KQUEUE
の#include "ae_kqueue.c"
の#else
の#include "ae_select.c"
#endifの
#endifの
#endifの
も外部インタフェースが統一されていて、これらのI / O多重化機能の使用を統一するために、Redisのその特定のパッケージには、動作も非常に便利です。

/ *モニター・イベントへの必要性を追加* /
静的なint型aeApiAddEvent(aeEventLoop イベントループ、int型のfd、int型マスク)
/
I Oの多重化パラメータ初期化ライブラリ/ * /必要な
静的int型aeApiCreate(aeEventLoop イベントループ)
/
削除不要なリスナーをイベント* /
静的な無効aeApiDelEvent(aeEventLoop イベントループ、int型のfd、int型マスク)
/
空* /
静的な無効aeApiFree(aeEventLoop イベントループ)
/
現在使用ライブラリの名前を返す/
aeApiName int型の静的(ボイド)
/
ファイルを削除するには、用意されています記述子* /
静的なint型aeApiPoll(aeEventLoopイベントループは*、体timeval * TVPを構造体)
、我々はそのため、ニーズを読み書きするタイプの構造を定義する必要が店に耳を傾けなければ、例えば、それはファイルディスクリプタの種類を読み書きするために耳を傾けて、選択ファイル記述子のコレクション。

typedefは構造体aeApiState {
FD_SETのRFDS、wfds。//读写文件描述符集合
/ *我々はそれを再利用するのは安全ではありませんとして、FDのセットのコピーを持っている必要があります
)(選択した後、* FDのセットを。* /
FD_SETの_rfds、_wfds。
} aeApiState。

イベントループ構造体では、apidataフィールドは、データ型がaeApiStateで保持しています。追加、削除、およびその他の操作は、2つのファイル記述子が動作するように設定されている主aeApiPoll機能、話に何をクリアしないように、それはI / Oの機能を多重化根底には、準備ができて記述子が設定されている取得を呼び出します。

/ *を選択し、関数呼び出し、取得準備セットディスクリプタ* /
静的INT aeApiPoll(aeEventLoopイベントループ*、構造体体timeval * TVP){
aeApiState状態* = eventLoop-> apidata;
INT RETVAL、J、numEvents = 0。

memcpy(&state->_rfds,&state->rfds,sizeof(fd_set));
memcpy(&state->_wfds,&state->wfds,sizeof(fd_set));
// 调用select函数
retval = select(eventLoop->maxfd+1,
            &state->_rfds,&state->_wfds,NULL,tvp);
if (retval > 0) {
    for (j = 0; j <= eventLoop->maxfd; j++) {
        int mask = 0;
        // 找到该描述符对应的事件
      	aeFileEvent *fe = &eventLoop->events[j];
		// 根据mask判断读或写事件,并在需要监听的描述符中确定存在该描述符
        if (fe->mask == AE_NONE) continue;
        if (fe->mask & AE_READABLE && FD_ISSET(j,&state->_rfds))
            mask |= AE_READABLE;
        if (fe->mask & AE_WRITABLE && FD_ISSET(j,&state->_wfds))
            mask |= AE_WRITABLE;
        // 加入到已经准备好的事件中,等待处理
        eventLoop->fired[numevents].fd = j;
        eventLoop->fired[numevents].mask = mask;
        numevents++;
    }
}
// 返回已经准备好的事件个数
return numevents;

}
単に機能を多重化/、O Iの役割を言えば、その用意されている、この機能をポーリング記述子セット、モニタ記述子設定する必要が入力として与えられ、その後、ファイルの説明を見つけます文字セット。

イベントループ
呼び出しI / O多重化プログラム上で調製したイベントを取得しており、我々は、これらのイベントに対処する必要があります。server.cファイル、メイン関数のRedisにインポート機能では、aeMain()を呼び出し関数はイベントループを実行するために使用されます。

/ *メインイベントループ機能* /
無効aeMain(aeEventLoopイベントループ*){
eventLoop-> STOP = 0; //オープンイベントループ
中(eventLoop-> STOP!){
IF(eventLoop-> beforesleep = NULL!)
EventLoop-> beforesleep(イベントループ);
aeProcessEvents(イベントループ、AE_ALL_EVENTS); //イベントハンドラ
}
}

私たちは、それは常にすべてのタイプのイベントを処理し、ノンストップの実行されている、基本的にはイベントループのスタートを実行しているサーバーから、見ることができます。イベントハンドラの具体的な機能は以下の通り:

/ *イベントハンドラ* /
int型aeProcessEvents(aeEventLoopイベントループ*、フラグINT)
{
INT = 0加工、numEvents。

// 没有需要处理的事件就直接返回
if (!(flags & AE_TIME_EVENTS) && !(flags & AE_FILE_EVENTS)) return 0;

// 即使没有要处理的文件事件,只要我们想处理时间事件就需要调用select()函数
// 这是为了睡眠直到下一个时间事件准备好。
if (eventLoop->maxfd != -1 ||
    ((flags & AE_TIME_EVENTS) && !(flags & AE_DONT_WAIT))) {
    int j;
    aeTimeEvent *shortest = NULL;
    struct timeval tv, *tvp;
	// 获取最近的时间事件
    if (flags & AE_TIME_EVENTS && !(flags & AE_DONT_WAIT))
        shortest = aeSearchNearestTimer(eventLoop);
    if (shortest) {
        // 运行到这里说明时间事件存在,则根据最近可执行时间事件和现在的时间的时间差
        // 来决定文件事件的阻塞事件
        long now_sec, now_ms;

        aeGetTime(&now_sec, &now_ms);
        tvp = &tv;

        // 计算下一次时间事件准备好的时间
        long long ms =
            (shortest->when_sec - now_sec)*1000 +
            shortest->when_ms - now_ms;
        if (ms > 0) {
            tvp->tv_sec = ms/1000;
            tvp->tv_usec = (ms % 1000)*1000;
        } else {
            // 时间差小于0,代表可以处理了
            tvp->tv_sec = 0;
            tvp->tv_usec = 0;
        }
    } else {
        // 执行到这里,说明没有待处理的时间事件
        // 此时根据AE_DONT_WAIT参数来决定是否设置阻塞和阻塞的时间
        if (flags & AE_DONT_WAIT) {
            tv.tv_sec = tv.tv_usec = 0;
            tvp = &tv;
        } else {
            /* Otherwise we can block */
            tvp = NULL; /* wait forever */
        }
    }
	// 调用I/O复用函数获取已准备好的事件
    numevents = aeApiPoll(eventLoop, tvp);
    for (j = 0; j < numevents; j++) {
        // 从已就绪事件中获取事件
        aeFileEvent *fe = &eventLoop->events[eventLoop->fired[j].fd];
        int mask = eventLoop->fired[j].mask;
        int fd = eventLoop->fired[j].fd;
        int rfired = 0;

        // 如果为读事件则调用读事件处理函数
        if (fe->mask & mask & AE_READABLE) {
            rfired = 1;
            fe->rfileProc(eventLoop,fd,fe->clientData,mask);
        }
        // 如果为写事件则调用写事件处理函数
        if (fe->mask & mask & AE_WRITABLE) {
            if (!rfired || fe->wfileProc != fe->rfileProc)
                fe->wfileProc(eventLoop,fd,fe->clientData,mask);
        }
        processed++;
    }
}
// 处理时间事件,记住,此处说明Redis的文件事件优先于时间事件
if (flags & AE_TIME_EVENTS)
    processed += processTimeEvents(eventLoop);
// 返回处理的事件个数
return processed;

}
要するに、ステップイベントのプロセスです。

必要であれば、マークダウンし、それを実行する必要があるかどうか、イベントの最も早い時間を見つけて処理されるのを待っている
セット用意したファイルディスクリプタのイベントを取得するために
優先順位がイベントを読んで
書き込みイベントを処理し
、それは処理時間イベント時間イベントである場合は
、イベントハンドラ
フロントいくつかのセクションでは、イベント、イベントを処理するために、その後、持っているこれらのイベントのRedisのは、それに対処するためのイベントループを作成するために分析しましたか?

処理タイムイベント
イベント作成機能の時には、リアルタイムのイベントハンドラserverCronに渡して、関数は、ソースコードが長すぎるため、ここでは簡単に機能の実行を要約し、ソースコード解析で前に何度も言及されていますどのようなアクション。

このような時、メモリ使用量、データベースの使用状況およびその他などの統計情報の更新サーバの種類
にデータベースをクリーンアップする期限切れのキー
クライアントのクローズとクリーンアップの接続障害
AOFとRDB永続操作への試み
サーバーから右の場合は、プライマリサーバ、定期的な同期
クラスタモデル、クラスタ定期的に同期させ、接続のテスト場合は
ファイル処理イベント
イベントの登録書類の時に、指定したファイルハンドラがacceptTcpHandlerとreadQueryFromClientです。のは、かつてのを見てみましょう。

、初期化などのクライアント接続の取り扱い、および新規顧客構造を作成します。もちろん、かつての対応するイベントは、そのイベントハンドラをしなければならない新しいクライアント接続をリッスンします

ハンドラ/ *新しいクライアント接続イベントをリッスン* /
無効acceptTcpHandler(aeEventLoop * EL、int型FD、void *型privdata、int型のマスク){
int型のCPORT、CFD、最大= MAX_ACCEPTS_PER_CALL;
char型のCIP [NET_IP_STR_LEN];
UNUSED(EL);
未使用(マスク)、
未使用(privdata);
// TCP接続は、接続クライアントは受け入れ
ながら(MAX-){
; CFD = anetTcpAccept(server.neterr、FD、CIP、はsizeof(CIP)、&CPORT)
//接続エラー
場合を(== ANET_ERR CFD){
IF(= errnoにEWOULDBLOCK!)
のServerLog(LL_WARNING、
"クライアント接続を受け入れる:%S"​​、server.neterr);
リターン;
}
//ログ
SERVERLOG(LL_VERBOSE、「承認%S :%dを」、CIP、CPORT);
//は動作するゲスト初期化
acceptCommonHandler(CFD、0、CIPを);
}
}
機能の上にのみクライアントの接続を受け入れ、クライアントは、実際に操作が構造acceptCommonHandler機能によって完成されて作成します。ソースコードの機能が列挙され、そしてここで主にその主なタスクについて話されていません。

redisClient構造を作成し、確立し、接続情報格納サーバとクライアント
イベントに対して登録ソケットクライアント、対応するコールバック関数readQueryFromClient(これは以下で説明する)を読み出し
、後者はクライアント構造を作成した後クライアントを監視する必要が摩耗へのクライアントへのリードコマンドのイベントハンドラを読み込み、そして行動を取る、その後、クライアントに返します。

そのため、ファイルのイベントを処理するためには、次のとおりです。クライアントは、コマンドを解析し、適切な治療を実行し、クライアントに応答するコマンドを送るなど、顧客の読書イベントを監視し続け、その後、新たな顧客を作成するための接続など、新しいクライアント接続のために聞きます。

概要
このブログでは、これは、以前の分析と組み合わせて、我々は基本的にコマンドは、クライアント、サーバの後に来たし、それに応じて対処して、クライアントに応答し、この全体の大きなサイクルを理解することができ、イベントの作成、流通及び加工を解析し、 A。タイムイベントおよびファイルのイベントのために、私は少しを合計するには、以下のチャートを使用したいと思います。

時間------------------------------------------------- ------------------------------>
| <10msの------- ------> | < ------ 10msの-------> | <------ 10msの -------> | <------ 10msの-------> |
| fileEvent1 | fileEvent2 | |ブロックfileEvent3 | fileEvent4 |ブロック|
| | timeEvent1 | | timeEvent2 |
このチャートはそう、再びのRedisは単一で確認し、イベントの実行時間の時点で、イベントファイルはブロックされた状態にある、ということを教えてくれるスレッドは、全体のイベントループは、シリアルです。

ソースコード解析を書きながら、私はそう多くは気付かなかったか、間違った場所を理解し、見ていたので、私も私の学生は私が一緒にRedisのを探検し、学ぶためにRedisの接触を学ぶことを願って、コメント欄に記入してください!

公開された100元の記事 ウォン称賛12 ビュー10000 +

おすすめ

転載: blog.csdn.net/hmh13548571896/article/details/103622842