【Redis】redis紹介-起動プロセス

6286 ワード

reidisのシリーズ記事では、これが2編目です.以前の記事では、redisの使用について説明しましたが、主にredisで提供される特性がpub/subモードをどのように使用するかを説明し、pythonに基づいて簡単なコード例と紹介をしました.この文章では,redisの起動過程について整理を与え,redisの起動過程に対してマクロ的な概念を持っている.
redisは本質的に単一プロセスのサービスであり、1つのプロセスを通じて対外的に要求を提供する.しかし、redisシステム全体に複数のプロセスが存在し、一部のコア操作はプロセスによって実行される.例えば、コアのクライアントコマンドを受け入れて対応する操作を実行することは、個別のプロセスが実行している.いくつかのlogの書き込みはプロセスなどです.この文章はこのマルチプロセスの実行方式を詳しく展開しないで、主にredisの中で起動する過程の中ですべてどんな操作を実行したかを整理します.
redisの起動入口はredisです.cではredis.cにはmain関数が見えます.これがredisの起動の入り口です.すべてのコード解析はここから展開されます.redisは起動中にいくつかの操作にまとめることができ、プロファイルを初期化し、redisがサポートするコマンドを初期化し、サーバを初期化することができます.コアオペレーションは,socket傍受,各種傍受イベントの確立など,初期化サーバに含まれる.
構成の初期化
redis起動コマンドを入力すると、redisはまず言語タイプを設定し、初期化後hashで使用するシードを設定し、initServerConfig初期化を呼び出してredisServerを構成し、すべての属性をデフォルト値に初期化します.redisのような複雑なサーバにとって、サポート可能な構成も多い.したがって、構成の初期化については、redisは実装で単独で取り出し、1つの方法で呼び出し、構成をすべて初期化し、デフォルト値に初期化し、具体的な使用では、具体的な値を変更します.
イニシャルコマンド
初期化構成では、redisでサポートされているコマンドも初期化されます.reidisのコマンドの初期化を初期化構成に置くのは、redisのコマンドが構成中に名前変更操作を行うことができるため、これも初期化構成の一部であるため、両者は1つの方法で完了した.redisでcでは、巨大な配列redisCommandTableが定義され、配列の要素は構造体redisCommandである.この構造体はredisのコマンドの属性をカプセル化している.
struct redisCommand {
    char *name;
    redisCommandProc *proc;
    int arity;
    char *sflags; /* Flags as string representation, one char per flag. */
    int flags;    /* The actual flags, obtained from the 'sflags' field. */
    /* Use a function to determine keys arguments in a command line.
     * Used for Redis Cluster redirect. */
    redisGetKeysProc *getkeys_proc;
    /* What keys should be loaded in background when calling this command? */
    int firstkey; /* The first argument that's a key (0 = no keys) */
    int lastkey;  /* The last argument that's a key */
    int keystep;  /* The step between first and last key */
    long long microseconds, calls;
};

大きな配列redisCommandTableでは、各コマンドにそのコマンドの関連プロパティを指定します.構成を読み込み、大きな配列を参照します.
redisCommandTableは、各コマンドに対して対応する修正操作を実行します.最後のコマンドの初期化が完了すると、すべてのコマンドがdict形式で保存されます.
redisServerの構造体のcommondsポインタオブジェクト.次はmainメソッドに戻り、判定します
起動したら
sentinelオプション、では
まずsentinelを初期化して起動します.そして、起動コマンドのコマンドを処理することにより、与えられたすべてのコマンドラインを文字配列optionsに一時保存し、以下の処理ではこれらをすべてプロファイルに追加します.現在のコマンド処理でも次のプロファイル処理でも、reidsではsdsによって実現されます.
はredisによって実現される文字列をカプセル化するデータ構造である.setが1つのvalueが文字列であるたびに、redisはその文字列に2倍のメモリサイズを割り当てます.このポリシーを使用すると、文字列を再割り当てするときにメモリの申請と解放を回避できます.コマンドラインのパラメータ処理を開始すると、プロファイルの読み取りが開始され、上のoptionsのパラメータをプロファイルに一緒に保存します.redisにおけるプロファイルの処理はconfigである.cでは、すべてのプロファイルのパラメータがredisServer構造体によって保存されます.
サーバの初期化
上記のいくつかの操作は、redisの準備作業を開始し、構成、環境などを準備してから、本当のredisを開始することができると理解できます.初期化サーバは主にinitServer()を呼び出すことによって行われ、ここではredisを起動するコア操作のみがリストされ、initServerが実行する操作は主に以下の通りである.
1:redis自身が実装した軽量レベルのイベントライブラリaeであるイベントリスニングライブラリeventLoopを作成する.
2:
ListenToPortでは、プロファイルから必要なリスニングのipとポート番号に基づいて、socketの作成、リスニングを開始します.
ポート番号、リンクリクエストを受け入れる準備ができています.プロファイルに特定のリスニングするipが指定されていない場合、redisはデフォルトですべてのソースへのアクセスを受け入れます.
ここで作成するsoketは、次に具体的なファイルfdイベントを作成するときに、ここでsocketを使用します.
if (server.bindaddr_count == 0) server.bindaddr[0] = NULL;
    for (j = 0; j < server.bindaddr_count || j == 0; j++) {
         if (server.bindaddr[j] == NULL) {
            /* Bind * for both IPv6 and IPv4, we enter here only if
             * server.bindaddr_count == 0. */
            fds[*count] = anetTcp6Server(server.neterr,port,NULL,
                server.tcp_backlog);
            if (fds[*count] != ANET_ERR) {
                anetNonBlock(NULL,fds[*count]);
                (*count)++;
            }
            fds[*count] = anetTcpServer(server.neterr,port,NULL,
                server.tcp_backlog);
            if (fds[*count] != ANET_ERR) {
                anetNonBlock(NULL,fds[*count]);
                (*count)++;
            }
        }else
            do bind every ip gived in conf
      }
 }

3:プロファイルに設定されているデータベースの数に基づいて、データベースの作成を開始します.
/* Create the Redis databases, and initialize other internal state. */
    for (j = 0; j < server.dbnum; j++) {
        server.db[j].dict = dictCreate(&dbDictType,NULL);
        server.db[j].expires = dictCreate(&keyptrDictType,NULL);
        server.db[j].blocking_keys = dictCreate(&keylistDictType,NULL);
        server.db[j].ready_keys = dictCreate(&setDictType,NULL);
        server.db[j].watched_keys = dictCreate(&keylistDictType,NULL);
        server.db[j].id = j;
        server.db[j].avg_ttl = 0;
 }

redisは、複数のデータベースが同時に存在することをサポートし、プログラムで異なるデータベースを使用することができます.selectコマンドを使用して、プログラムでどのデータベースを使用するかを選択します.
4:aofとrdb関連フラグを初期化し、この2つの操作は主にredisのデータのバックアップとリカバリのための操作である.
5:サーバの計画タスクイベントを作成し、イベントの実行方法はserverCronである.
serverCronでは主に以下のいくつかの動作を行い、タイミングの長さに応じて以下の動作を順次実行します.ClientsCron、databaseCron、AOF、rdbSaveBackgroundの実行
6:ファイルイベントを作成し、上のステップ2にバインドして成功したsocke idを作成します.イベント発生時の実行方法はacceptTcpHandlerで、
このメソッドでは対応するメソッドcreateClientが呼び出され、createClientメソッドではファイルイベントが作成され、イベントトリガの実行方法はreadQueryFromClientである.
 /* Create an event handler for accepting new connections in TCP and Unix
     * domain sockets. */
    for (j = 0; j < server.ipfd_count; j++) {
        if (aeCreateFileEvent(server.el, server.ipfd[j], AE_READABLE,
            acceptTcpHandler,NULL) == AE_ERR)
            {
                redisPanic(
                    "Unrecoverable error creating server.ipfd file event.");
            }
    }

redisのネットワーク通信はanetを用いて実現され,下位層のsocketの確立,傍受をカプセル化し定義した.ipv 4のメソッドではanetTcpServerがカプセル化され、下位層でもsocket、bind、listenなどのメソッドが順次呼び出され、acceptではイベントの作成によって処理されます.
時間ポーリングイベント、ファイルfdイベントを含むすべてのイベントは、redisによっていくつかの精巧なイベントライブラリaeにカプセル化される.ライブラリの起動はredisです.cのmain関数の末尾にaeMainを呼び出して起動します.
7:現在の機械アーキテクチャを検査する;
現在のアーキテクチャが32ビットであり、最大使用可能メモリが構成されていないことを確認する場合、redisは、現在の最大使用可能メモリが3 GBであることを構成に強制的に宣言します.これは,32ビットにおいてポインタが最大で示す範囲が4 GBであるためである.64ビットの場合、このオプションはありません.
Redisを起動するときにそのIPからのリクエストのみを傍受するように構成することができ、設定しなければすべてのIPのリクエストを受け入れることができます.
InitServerの実行が完了すると、redisは基本的に起動に成功します.次に、1つのマシンで複数のプロセスを起動して管理するのを容易にするために、プロセスの名前を設定します.sentinelを起動して異なる操作を行うかどうかを確認します.次に、イベントリスニングライブラリを起動し、上のすべてのtimeイベントとfileイベントのリスニングを開始します.
上記のいくつかのコア操作の実行が完了すると、redisは本当に起動し、要求の到来を待つ.