Redisのソースコード解析()サーバとクライアントとの対話プロセス

C / SモデルはRedisの
スタンドアロンデータベースのための基盤となるネットワーク、または要求に基づいて、Redisの、クライアント・サーバ・コンピュータで唯一のマシン、すなわち、上のインタラクティブなネットワーク要求

入力端子Redisのは、サーブするとき、それはRedisのサーバを起動し、内部データを初期化するために開始し、Redisの目的のために含めました

設定ファイルは、内部パラメータを初期化読み込み
、デフォルトのデータベース(デフォルト16)を作成するために、
コールバック関数(接続要求受信クライアント)にバインドされたソケット聞いて作成するために、
イベント駆動型サイクルを、クライアントの要求に応答を開始
...
とき入力端子redis-クライアントはすべての上を開始し、CLI

ここでは単に、Redisのコマンドおおよその対話プロセスを推測することができます

クライアントからは、要求がサーバーに接続するために
、サーバーがクライアントの要求を受け、接続の確立に成功し、サーバとクライアントのファイル記述子コールバックのバインディング聴き始める
クライアント入力コマンドは、サーバー側のリスニングファイルが読み込み可能になっ原因サーバは、読み出しコマンド起動
サーバはコマンドを解析し、対応するコマンドハンドラインボーク
クライアントにサーバ処理結果バック
クライアントのファイル記述子が読み取り可能となり、フィードバック情報を読み取り、出力端子
上には、共通のC / Sでありますモデル、接続モード処理を使用してRedisの原子炉は、原子炉のパターンは、多くの場合、この方法は、リスニングクライアントioの多重化機能を使用してと言われています。しかし、Redisのは、シングルスレッドの下で反応器である、あなたは、原子炉+(もスレッドごとに一つのループとして知られ、使用される設計モデルmuduoネットワークライブラリ)同時実行性を向上させるために設計された、高度に同時サーバモデルに共通のメソッドスレッドプールを使用することができます

ここでは、ソースサーバーとクライアントとの対話プロセスの観点から、(クリティカルセクションの唯一の傍受を)体験

サーバーとクライアントの相互作用のプロセス
クライアント接続用のサーバーリッスン
サーバーが起動し、server.c /メイン関数の最初の実行は、前述したように、多くの作業を初期化するための主な機能は、そのうちの一つは、リスニングソケットを作成することですあり

//server.c
メインINT(INTのARGC、CHAR * ARGV){
...
/
サーバの初期化、* /作成されたソケットリスニング
initServerを();
...
}
。1
2
3。
4。
5。
6。
7。
機能initServer同様に大きな初期化などリスニングソケットを作成するために、どの部分であります

//server.c
/ *コールサーバの起動、サーバの初期化/
無効initServer(無効){
...
/
リスニング作成ソケット/
/
ipfd_countリスニングソケットの数は、* /である
ため(J = 0; J <サーバー。 ipfd_count; J ++){
IF(aeCreateFileEvent(server.el、server.ipfd [J]、AE_READABLE、
acceptTcpHandler、NULL)== AE_ERR)
{
serverPanic(
"回復不能なエラーがファイルイベントを作成server.ipfd。");
}
}
...
}
。1
2
3
4
5
6
7
8
9
10
11
12は、
13である
14
15
16
関数aeCreateFileEventによって実装リスニングソケット関数を作成します。

//ae.c
/ *

  • ファイルイベントを作成します(イベント駆動型)

  • イベントループ:サイクルイベント・サーバーのドライブアレイ

  • FD:ファイルディスクリプタ

  • マスク:イベントを監視する必要性を

  • PROC:コールバック関数

  • clientData:コールバック関数に渡されたパラメータ
    * /
    int型aeCreateFileEvent(aeEventLoop *イベントループ、FDはint、int型マスク、
    aeFileProc procを*は、無効clientDataを)
    {
    /
    ファイルディスクリプタは、サーバ、エラー* /より大きい所定のサイズ作成する場合は
    IFを(FD> = eventLoop->のsetSize){
    errnoにERANGE =であり;
    リターンAE_ERR;
    }

    / *ドライブサイクルFDイベントのイベントでサーバーを返します* /
    aeFileEvent * =鉄&eventLoop->イベント[FD]。

    / *ファイルディスクリプタと、それはioの機能を多重化するイベントをリッスンする必要が追加されます/
    (aeApiAddEvent(イベントループ、FD、マスク)== -1)IF
    AE_ERRを返す;
    /は、
    イベントリスナーのドライブに保存されます/
    Fe系>マスク| =マスク;
    /
    セットコールバック/
    IF(マスク&AE_READABLE)Fe系> rfileProc = PROC;
    IF(マスク&AE_WRITABLE)Fe系> wfileProc = procの;
    /
    パラメータを設定* /
    Fe系> = clientData clientData ;
    IF(FD> eventLoop-> maxFd)
    eventLoop-> maxFd = FD;
    リターンAE_OK;
    }
    。1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12は、
    13である
    14
    15
    16
    17。
    18である
    。19
    20
    21であり、
    22は
    23であり、
    24
    25
    である26
    27
    28
    29
    30
    31が
    32
    33である
    34である
    35
    Ioが全てのイベントが格納されるアクティブに戻った多重化するとき、すべてのイベントの関数aeEventLoopイベント駆動型サイクルとストレージで聞いていますaeEventLoopを処理するため、およびイベント駆動型ループlibevent同じ効果

aeFileEventは、イベントのパッケージ、内部保存リスニングファイル、同じモニタイベントとコールバック関数、およびlibeventのイベント(イベント)アクションです

クライアントが接続要求を受信
するコールバック関数がaeCreateFileEventを通過したことに加えを、あなたはそれが主な機能だと思うことができ受け入れる関数を呼び出すことであるIO多重に登録されたクライアントに関連付けられたファイルディスクリプタを、確立するために、クライアントの接続要求を受け取ります。これは次のように定義されています

//networking.c
/ *コールバックサーバのリスニングソケット、クライアントの接続要求を受信するための* /
無効acceptTcpHandler(aeEventLoop EL *、int型FD、privdata void *型、int型マスク){
int型のCPORT、CFD、最大= MAX_ACCEPTS_PER_CALL;
CHAR CIP [NET_IP_STR_LEN]。

while(max--) {
    /* 调用accept接收客户端连接请求,返回与客户端关联的文件描述符 */
    cfd = anetTcpAccept(server.neterr, fd, cip, sizeof(cip), &cport);
    ...
    /* 根据文件描述符创建客户端实例(client对象),用于与客户端交互 */
    acceptCommonHandler(cfd,0,cip);
}

}
。1
2
3
4
5
6
7
8
9
10
11
12である
13である
14
15
は、クライアント要求を受信するたびに、サーバは、クライアント(クライアントタイプ)のインスタンスを作成し、クライアント・ファイル・ディスクリプタ、クライアントサーバとクライアントによりますすべてのインタラクティブコマンドブリッジ、クライアント入力バッファは、さらに、処理中にクライアントに読み込まれます

クライアントのインスタンスを作成すると、
次のように定義されているクライアントインスタンス関数を作成するには

//networking.c
/ *

  • クライアントのファイルディスクリプタに基づいてクライアントのインスタンスを作成します
  • FD:クライアントの接続要求を受信すると、返されたファイルディスクリプタ
  • IP:クライアントアドレス<IP、ポート>
    * /
    静的な無効acceptCommonHandler(int型FD、フラグINT、CHAR * IP){
    クライアント; C
    /
    クライアントを作成するファイルディスクリプタのクライアントインスタンス/
    IF((C = createClient(FDを))== NULL){
    のServerLog(LL_WARNING、
    "!新しい新しいクライアントイベントをFDを登録するためのエラー:%S(%FD = D)"、
    はstrerror(errnoに)、FD);
    閉じる(FD); /
    オンは、すでに閉鎖することができます、 *エラーだけ/無視
    リターン;
    }
    ...
    }
    。1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12である
    13である
    14
    15
    16
    17
    18 IS
    関数呼び出しcreateClient、実行が実際にクライアントのインスタンスの操作を作成します

//networking.c
/ *ファイルディスクリプタは、クライアント* /との接続を作成するには
クライアント(FD int型)createClient {
/
アプリケーションクライアントのメモリサイズ* /
クライアント; C = zmalloc(はsizeof(クライアント))
IF(FD =を! -1){
/
IOノンブロッキング配置/
anetNonBlock(NULL、FD)を、
anetEnableTcpNoDelay(NULL、FD);
/
キープアライブオプション/
IF(server.tcpkeepalive)
anetKeepAlive(NULL、FD、server.tcpkeepalive);
/
あります接続イベントリスナー読み取り可能なイベントを生成するために、コールバック関数* readQueryFromClient /
IF(aeCreateFileEvent(server.el、FD、AE_READABLE、
readQueryFromClient、C)== AE_ERR)
{
使用閉じる(FD);
zfree©;
戻りNULL;
}
}
...
}
。1
2
3
4
5
6
7
8
9
10
11
12である
13である
14
15
16
17
18である
。19
20
21であり、
22は
23にされている
接続はしないので、イベントに関連付けられたファイル記述子を使用して作成し、上記と同様、しかしコールバックはreadQueryFromClientなる場合クライアントの受信接続要求が、クライアントは入力コマンドを受け取るように構成されています。

クライアントによって入力されたコマンドの処理
クライアントがコマンドを入力するときに、対応するコールバック関数readQueryFromClientを実行し、メイン関数呼び出しは、関数は、クライアントのファイルディスクリプタコマンドからの入力を読み取り読んで

//networking.c
/ *コールバック関数場合クライアント可読* /
ボイドreadQueryFromClient(aeEventLoop EL *、INT FD、privdataボイド*、INTマスク){
クライアントC =(クライアント)privdata;
INT NREAD、readlen。

readlen = PROTO_IOBUF_LEN;
/* 为缓冲区申请空间,用于保存客户端命令 */
c->querybuf = sdsMakeRoomFor(c->querybuf, readlen);
/* 从客户端读取命令,保存在c->querybuf中 */
nread = read(fd, c->querybuf+qblen, readlen);
...
/* 处理客户端命令 */
processInputBuffer(c);

。1
2
3
4
5
6
7
8
9
10
11
12である
13である
14
の異なる動作を実行するクライアントの機能に基づいてprocessInputBufferオプション、機能、コマンドキーワードを解析するために、クライアント機能を最終的な呼処理コマンドをprocessCommand、キーワードは合法か否かが判定されます。正当な場合には、パラメータの数は、合法性を決定するために

//server.c
/ *パースクライアントコマンド、最初のコマンドキーワードの合法性を決定した後、パラメータの数を決定することは合法である* /
int型processCommand(クライアントC){
...
/
コマンドは、かどうかを判断するために、辞書からコマンド名が存在して下さいコマンド/
/
機能に対応する* /処理コマンドを含むCMDのコマンド、保存
C-> C-CMD => lastcmd = lookupCommand(C->のargv [0] - > PTR);
...
IF(...)
{
...
}

{
//コマンドハンドラを呼び出し
コール(C、CMD_CALL_FULLを);
...
}
}
。1
2
3
4
5
6
7
8
9
10
11
12である
13である
14
15
16
17
18である
19。
あなたが主な機能は、もしあれば、一緒にCMD変数の割り当てと対応するハンドラコマンドで返すようになり、下のキーワードから辞書があるかどうかを見つけるために、パースコマンドのキーワードにある見ることができ、cmdの変数は構造体redisCommandはわずか*タイプであり、あなたは有用性を見ることができた後、

次のように定義されたコール機能

//server.c
/コマンドハンドラの呼び出し* /
ボイドコール(クライアントC、フラグINT){
...
/
呼出コマンドコールバック* /
C-> CMD-> PROC©;
...
}
。1
2
3。
4。
5。
6。
7。
8。
コマンド構造
各Redisの内部が良いコマンドとそれに対応するハンドラのパッケージを持って、この構造は、構造体redisCommand、変数のより重要なメンバーの2であります

//server.h
構造体redisCommand {
CHAR *名; //コマンドキーワード
redisCommandProc * PROC; //ハンドラ
...
};
1
2
3。
4。
5。
6。
ここで推測したとき、関数呼び出しlookupCommand processCommand機能、意志コマンド構造があれば、適切なキーワードを見つけるcmdを変数に返すことがあります。Redisのはのコマンドであれば、私たちは今、それぞれのパッケージ内部を見ることができます

redisCommand redisCommandTable構造体[] = {
0、NULL、1,1,1,0,0}、 "RFの" { "GET"、getCommand、2、
{ "SET"、setCommand、-3、 "WM"、0 、NULL、1,1,1,0,0}、
{ "SETNX"、setnxCommand ,. 3、 "WMF"、0、NULL、1,1,1,0,0}、
{ "SETEX"、setexCommand ,. 4 、 "WM"、0、NULL、1,1,1,0,0}、
{ "psetex"、psetexCommand ,. 4、 "WM"、0、NULL、1,1,1,0,0}、
...
} ;
1
2
3
4
5
6
7
8
オリジナルRedisの各コマンド構造の構造体redisCommand良いために設計されているが、あまりにも遅いように、アレイ内の「スーパー」から1ずつを見つけるダイレクトコマンドのキーワードがないかどうかを確認します。Redisの各コマンドの内部にあり、辞書に記録されたその対応するキーワード構造体redisCommand構造、辞書検索効率はO(1)である(基本となるハッシュテーブルにより実装される)ので、それは、パフォーマンスのボトルネック引き起こさないよう

適切なコマンド構造、また、取得したコマンドハンドラを取得した後、setコマンドの目的のために、getCommandの面で順序を取得することですおそらく、コマンドキーワードの後setCommand、ある、プラスのコマンドは、対応するコマンドハンドラです

彼らは直接、適切なハンドラを呼び出すときに、処理が完了した上記C-> cmd-> PROC©関数呼び出しだから、クライアントへのフィードバックを送って、相互作用を完了

概要
サーバーの起動は、クライアントのファイルディスクリプタ(ソケット)を聴いたときに、クライアントがコマンドを入力すると、サーバーやネットワーククライアントとの相互作用が実際に要求に基づいて、クライアント要求に対するサーバーのリッスン、クライアントが接続を要求し、接続が確立され、 、サーバは、入力されたクライアント次に構文解析、実行クライアントの機能を処理し、構造バックを取得したファイル記述子を読み取り、クライアントが対話を完了すると、端末設定に表示されます
------------ ----
免責事項:この記事はCSDNブロガー「プログラムスラグスラグは小さな裏庭」CC 4.0 BY-SAの著作権契約書に従って、元の記事のある、元のソースのリンクと、この文を添付してください、再現。
オリジナルリンクします。https://blog.csdn.net/sinat_35261315/article/details/78966882

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

おすすめ

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