Redis起動プロセス

9344 ワード

redisの起動プロセスについて話します.
まず起動関数を見つけなければならない.Cプログラムはmain関数から始まることを知っているので、「夢」が始まる場所server.c->mainを見つけた.ここでは主に起動過程の主な部分を話しているので、一つ一つは触れません.

概ねプロセスの開始


initServerConfig関数


コード全体で最も重要な構造体はstruct redisServer serverであり、グローバル変数として現れる.この関数は主にそのメンバーに付与操作を行い、これらのメンバーは基本的にredisを通過することができる.confファイルで構成します.

ほとんどのメンバーに初期値を付与


例:
serverフィールド
意味
runid
ノード識別占有40 B
port
起動ポートのデフォルトは6379です
tcp_backlog
デフォルト511 B
aof_fsync
デフォルトaofは1秒あたりディスクをブラシしますが、aofはデフォルトで閉じます.
aof_filename
デフォルトaofファイル名はappendonlyです.aof
rdb_filename
デフォルトrdbファイル名はdump.rdb
cluster_node_timeout
デフォルト15 s、デフォルトclusterモードオフ

デフォルトrdbトリガ条件

appendServerSaveParams(60 * 60,1);  /* save after 1 hour and 1 change */
appendServerSaveParams(300,100);    /* save after 5 minutes and 100 changes */
appendServerSaveParams(60,10000);   /* save after 1 minute and 10000 changes */

Replication related


backlogに関する設定が含まれます.

Double constants initialization


浮動小数点データの精度設定.

client output buffer limit


全部で3種類あり、以下の通りです.
clientBufferLimitsConfig clientBufferLimitsDefaults[CLIENT_TYPE_OBUF_COUNT] = {
    {0, 0, 0},                         /* normal */
    {1024*1024*256, 1024*1024*64, 60}, /* slave */
    {1024*1024*32, 1024*1024*8, 60}    /* pubsub */
};

redisコマンドテーブル


初期化redisコマンドテーブルはserver.commandsに配置され、これは主にpopulateCommandTable関数で完了する.
注意:redis.confプロファイルではrename-commandを使用してCommandの名前を変更できます(通常はセキュリティ上の考慮のためにいくつかのコマンドを無効にします).したがって、コマンドテーブルにはserver.commandsserver.orig_commandsの2つが保存されます.
また、頻繁にクエリーされるコマンドも個別に提出され、以下の変数にそれぞれ配置されます.
struct redisCommand *delCommand, *multiCommand, *lpushCommand, *lpopCommand,
                    *rpopCommand, *sremCommand, *execCommand;

Slow log


デフォルトは10 msです.

sentinelモード


このモードをオンにするには、次のようにします.
int checkForSentinelMode(int argc, char **argv) {
    int j;

    if (strstr(argv[0],"redis-sentinel") != NULL) return 1;
    for (j = 1; j < argc; j++)
        if (!strcmp(argv[j],"--sentinel")) return 1;
    return 0;
}

コマンドラインパラメータ--sentinelを使用するか、バイナリファイルredis-sentinelを直接使用します.
このモードをオンにすると、対応する初期を行い、オンにしないでスキップします.
if (server.sentinel_mode) {
    initSentinelConfig(); // sentinel      26379
    initSentinel(); // sentinel      
}

コマンドラインパラメータ解析とプロファイルの読み込み


主に、プロファイルの絶対パスserver.configfile = getAbsolutePath(configfile)が取得される.
プロファイルの読み込みには専用の関数があります
void loadServerConfig(char *filename, char *options){}

プロファイルをロードすると、以前のserverのデフォルト構成の一部が上書きされます.実際には、redis-serverが起動すると、いくつかの構成はconfig getコマンドで表示することも、config setコマンドで変更することもでき、修正後のconfig rewriteブラシディスクを表示することもできます.

initServer関数

initServerConfig関数とは異なり、redis-server実行中のメンバーを主に初期化します.

しんごうしょり


信号処理をredisで復習します.
//   SIGHUP SIGPIPE  
signal(SIGHUP, SIG_IGN);
signal(SIGPIPE, SIG_IGN);
void setupSignalHandlers(void) {
    struct sigaction act;
    sigemptyset(&act.sa_mask);
    act.sa_flags = 0;
    act.sa_handler = sigShutdownHandler;
    sigaction(SIGTERM, &act, NULL);
    sigaction(SIGINT, &act, NULL);
    return;
}

主にプログラム終了の善後作業である.

システムログ

if (server.syslog_enabled) {
    openlog(server.syslog_ident, LOG_PID | LOG_NDELAY | LOG_NOWAIT,
            server.syslog_facility);
}

前提はシステムのrsyslogを使用したことです.

createSharedObjects関数


この関数は、頻繁に使用される文字列を保存し、リリースを申請するときのCPU時間、メモリの破片などを減らすことを目的としています.
例えばshared.ok = createObject(OBJ_STRING,sdsnew("+OK\r
"))
です.
さらに、ここでは、0〜999の大きな共有デジタルオブジェクトも初期化される.したがってvalueを設定するときにこれらの数字を使用すると、メモリの使用を減らすことができます.
#define OBJ_SHARED_INTEGERS 10000

for (j = 0; j < OBJ_SHARED_INTEGERS; j++) { // 10000    
    shared.integers[j] = createObject(OBJ_STRING,(void*)(long)j);
    shared.integers[j]->encoding = OBJ_ENCODING_INT;
}
struct sharedObjectsStruct sharedもグローバル変数です.

adjustOpenFilesLimit関数


この関数は、プロファイルで構成されている最大client数に基づいて、開くことができる最大ファイル数を増やします.

eventLoopの作成

 server.el = aeCreateEventLoop(server.maxclients+CONFIG_FDSET_INCR)

ここでio多重化はepollを用いると仮定し,これも最も多く用いられる.

データベース・オブジェクトの初期化

server.db = zmalloc(sizeof(redisDb)*server.dbnum);

データベース・オブジェクトはstruct redisDbで、16個あります.

ポートのリスニング

if (server.port != 0 &&
    listenToPort(server.port,server.ipfd,&server.ipfd_count) == C_ERR)
    exit(1);
server.portを傍受し、返されたfdをserver.ipfdに格納し、エラーが発生したら返す.

システムcronタイマの作成

if(aeCreateTimeEvent(server.el, 1, serverCron, NULL, NULL) == AE_ERR) {
    serverPanic("Can't create the serverCron time event.");
    exit(1);
}

登録タイミング時間は、コールバック関数serverCronをバインドします.この関数では、実行サイクルが1000/server.hz msであることがわかります.したがって、1秒当たりserver.hzが実行されます(この値はユーザーが設定できます).
では、なぜこの周波数なのでしょうか.redisではイベント処理について以前のブログで書いたことがありますが、Redisのイベントを参考にして、ここでも簡単に振り返ることができます.
タイムイベント処理関数ae.c->processTimeEventsでは、タイムイベントのコールバック戻り値に基づいて、このときの1サイクルイベントが1回のイベントであるか、すなわち、
{
    int retval;

    id = te->id;
    retval = te->timeProc(eventLoop, id, te->clientData);
    processed++;
    if (retval != AE_NOMORE) {
        aeAddMillisecondsToNow(retval,&te->when_sec,&te->when_ms);
    } else {
        te->id = AE_DELETED_EVENT_ID;
    }
}

ユーザーリクエストのリスニング/受信

for (j = 0; j < server.ipfd_count; j++) {
    if (aeCreateFileEvent(server.el, server.ipfd[j], AE_READABLE, //       
                          acceptTcpHandler,NULL) == AE_ERR)
    {
        serverPanic(
            "Unrecoverable error creating server.ipfd file event.");
    }
}

ユーザ要求を受信し(ユーザ接続はここから入る)、読み取り可能なイベントを傍受し、コールバック関数acceptTcpHandlerを登録する.

初期化


cluster modeがオンの場合、対応する初期化が行われます.
if (server.cluster_enabled) clusterInit();

その他の環境初期化

replicationScriptCacheInit();
scriptingInit(1);
slowlogInit();
latencyMonitorInit();
bioInit();

プロセス名の設定


この関数は実用的で、psが良好なフォーマットのプロセス名を見るのに便利です.一緒に復習しましょう.
void redisSetProcTitle(char *title) {
#ifdef USE_SETPROCTITLE
    char *server_mode = "";
    if (server.cluster_enabled) server_mode = " [cluster]";
    else if (server.sentinel_mode) server_mode = " [sentinel]";

    setproctitle("%s %s:%d%s",
        title,
        server.bindaddr_count ? server.bindaddr[0] : "*",
        server.port,
        server_mode);
#else
    UNUSED(title);
#endif
}

永続化データのロード


sentinelモードで起動しない場合は、loadDataFromDiskの処理関数で永続化されたデータがロードされます.
aofがオンの場合はaofファイルをロードし、そうでない場合はrdbファイルをロードします.
loadAppendOnlyFile
この関数はaofファイルを記述するために使用され、主な流れは偽クライアントを作成し、aofファイルからコマンドを解析し、serverにもう一度実行させることです.
if (buf[0] != '*') goto fmterr;   //         
if (buf[1] == '\0') goto readerr; //         
argc = atoi(buf+1);
if (argc < 1) goto fmterr;

argv = zmalloc(sizeof(robj*)*argc); // argc   robj   
fakeClient->argc = argc;
fakeClient->argv = argv;

for (j = 0; j < argc; j++) {
    if (fgets(buf,sizeof(buf),fp) == NULL) { //      128B
        fakeClient->argc = j; /* Free up to j-1. */
        freeFakeClientArgv(fakeClient);
        goto readerr;
    }
    if (buf[0] != '$') goto fmterr;
    len = strtol(buf+1,NULL,10); //      
    argsds = sdsnewlen(NULL,len);
    if (len && fread(argsds,len,1,fp) == 0) {
        sdsfree(argsds);
        fakeClient->argc = j; /* Free up to j-1. */
        freeFakeClientArgv(fakeClient);
        goto readerr;
    }
    argv[j] = createObject(OBJ_STRING,argsds);
    if (fread(buf,2,1,fp) == 0) { // \r
fakeClient->argc = j+1; /* Free up to j. */ freeFakeClientArgv(fakeClient); goto readerr; /* discard CRLF */ } } cmd = lookupCommand(argv[0]->ptr); if (!cmd) { serverLog(LL_WARNING,"Unknown command '%s' reading the append only file", (char*)argv[0]->ptr); exit(1); } // fakeClient cmd->proc(fakeClient);

以上の関数がaofファイル解析プロセスです.
redisプロトコルデータを添付し、関数の分析を容易にします.
*3
$3
SET
$2
xx
$2
yy
*3

注意:aofファイルのロード中にaofが一時的に閉じられます.
rdbLoad
この関数はrdbファイルをロードするために使用されます.aofロードとは異なり、rdbファイルを解析してメモリに直接入れます.

イベントループ初期化

//            beforeSleep()   
aeSetBeforeSleepProc(server.el,beforeSleep);
//       
aeMain(server.el);
//      ,      
aeDeleteEventLoop(server.el);

小結


フローチャートを描いて、以上のフローをよく体現することができます.