ホームページ  >  記事  >  データベース  >  Redis リクエストの処理プロセスはどのようなものですか?

Redis リクエストの処理プロセスはどのようなものですか?

WBOY
WBOY転載
2023-06-01 20:49:471037ブラウズ

概要

  • #最初にプロセッサを登録します。

  • ループ リスニング ポートを開き、毎回ゴルーチンを作成します。接続は監視されています ;

  • 次に、Goroutine はループ内でリクエスト データを受信するのを待機し、リクエストされたアドレスに従ってプロセッサ ルーティング テーブル内の対応するプロセッサを照合し、処理を実行します。プロセッサへの処理リクエスト ;

これは次のようなコードで表現されます:

func (srv *Server) Serve(l net.Listener) error { 
    ...
    baseCtx := context.Background()  
    ctx := context.WithValue(baseCtx, ServerContextKey, srv)
    for {
        // 接收 listener 过来的网络连接
        rw, err := l.Accept()
        ... 
        tempDelay = 0
        c := srv.newConn(rw)
        c.setState(c.rwc, StateNew) 
        // 创建协程处理连接
        go c.serve(connCtx)
    }
}

Redis の場合は少し異なります。シングルスレッドであり、したがって、Redis は、Reactor パターンに基づいてイベント ドライバーを使用してイベントの同時処理を実装することを選択します。

Redis リクエストの処理プロセスはどのようなものですか?

Redis のいわゆる Reactor モードでは、epoll を通じて複数の fd を監視します。これらの fd が応答するたびに、epoll はコールバックのイベントの形式で通知されます。イベントには対応するイベント ハンドラーがあります。

例: accept は acceptTCPHandler イベント ハンドラーに対応し、読み取りと書き込みは readQueryFromClient イベント ハンドラーなどに対応し、イベントはイベント ループ ディスパッチを通じて処理するためにイベント プロセッサに割り当てられます。

したがって、上記の Reactor モードは epoll を通じて実装されます。epoll には、主に 3 つのメソッドがあります:

//创建一个epoll的句柄,size用来告诉内核这个监听的数目一共有多大
int epoll_create(int size);

/*
 * 可以理解为,增删改 fd 需要监听的事件
 * epfd 是 epoll_create() 创建的句柄。
 * op 表示 增删改
 * epoll_event 表示需要监听的事件,Redis 只用到了可读,可写,错误,挂断 四个状态
 */
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);

/*
 * 可以理解为查询符合条件的事件
 * epfd 是 epoll_create() 创建的句柄。
 * epoll_event 用来存放从内核得到事件的集合
 * maxevents 获取的最大事件数
 * timeout 等待超时时间
 */
int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout);

したがって、これらの 3 つのメソッドに基づいて簡単なメソッドを実装できます。サーバー:

// 创建监听
int listenfd = ::socket();

// 绑定ip和端口
int r = ::bind();  
// 创建 epoll 实例
int epollfd = epoll_create(xxx); 
// 添加epoll要监听的事件类型
int r = epoll_ctl(..., listenfd, ...);
 
struct epoll_event* alive_events =  static_cast<epoll_event*>(calloc(kMaxEvents, sizeof(epoll_event)));

while (true) {
    // 等待事件
    int num = epoll_wait(epollfd, alive_events, kMaxEvents, kEpollWaitTime);
	// 遍历事件,并进行事件处理
    for (int i = 0; i < num; ++i) {
        int fd = alive_events[i].data.fd;
        // 获取事件
        int events = alive_events[i].events;
		// 进行事件的分发
        if ( (events & EPOLLERR) || (events & EPOLLHUP) ) {
            ...
        } else  if (events & EPOLLRDHUP) {
            ...
        } 
        ...
    }   
}

呼び出しプロセス

#したがって、上記の紹介によると、Redis の場合、イベント ループはいくつかのステップにすぎないことがわかります:

  • Registerイベント リスニングおよびコールバック関数;

  • イベントが取得および処理されるのを待機するループ;

  • コールバック関数を呼び出してデータ ロジックを処理します。

  • データをクライアントに書き戻す;

Redis リクエストの処理プロセスはどのようなものですか?

  • fd を epoll に登録し、そして、コールバック関数 acceptTcpHandler を設定します。新しい接続がある場合、コールバック関数が呼び出されます。

  • 無限ループを開始して epoll_wait を呼び出し、待機してイベントの処理を続行します。 aeMain 関数に戻り、aeProcessEvents 関数の呼び出しをループします。

  • ネットワーク イベントが発生すると、データ処理のためにコールバック関数 acceptTcpHandler が readQueryFromClient まで呼び出されます。クライアントのデータを解析し、対応する cmd 関数を見つけます。実行;

  • クライアント リクエストを受信した後、Redis インスタンスはクライアント コマンドを処理し、返されたデータを代わりにクライアントの出力バッファに書き込みます。すぐに戻る;

  • その後、aeMain 関数がループしてバッファ内のデータをクライアントに書き戻すたびに、beforeSleep 関数が呼び出されます;

上記のイベント全体 実際、ループ処理のコードステップは非常にわかりやすく書かれており、それに関する記事がインターネット上にたくさんあるので、詳細は説明しません。

#コマンド実行プロセスとライトバック クライアント

#コマンド実行

# インターネット上の多くの記事では言及されていないことについて説明し、Redis がコマンドをどのように実行するかを見てみましょう。次に、それをキャッシュに保存し、キャッシュからクライアントにデータを書き戻します。

Redis リクエストの処理プロセスはどのようなものですか?

ネットワーク イベントが発生すると、readQueryFromClient 関数が呼び出され、ここでコマンドが実際に実行されることも前のセクションで説明しました。

  • readQueryFromClient は、リクエストされたコマンドを処理するために processInputBufferAndReplicate 関数を呼び出します;

  • processInputBufferAndReplicate 関数内processInputBuffer を呼び出し、クラスター モードの場合はコマンドを他のノードにコピーする必要があるかどうかを判断します。

  • processInputBuffer 関数は、要求されたコマンドをループし、要求されたプロトコルに従ってそれを呼び出します。 processInlineBuffer 関数では、redisObject オブジェクトを呼び出した後、processCommand が呼び出されてコマンドが実行されます。

  • processCommand は lookupCommand を使用して、

    server.commands 内のコマンドに従って対応するコマンドを検索します。 コマンド実行時のテーブル。関数を実行し、一連の検証の後、対応する関数を呼び出してコマンドを実行し、addReply を呼び出して返されたデータをクライアント出力バッファに書き込みます。

server.commands は、すべての Redis コマンドを、コマンド名に基づいてコマンド機能を取得するテーブルとして、populateCommandTable 関数に登録します。

たとえば、get コマンドを実行するには、getCommand 関数が呼び出されます。

void getCommand(client *c) {
    getGenericCommand(c);
}

int getGenericCommand(client *c) {
    robj *o;
	// 查找数据
    if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.nullbulk)) == NULL)
        return C_OK;
    ...
}

robj *lookupKeyReadOrReply(client *c, robj *key, robj *reply) {
    //到db中查找数据
    robj *o = lookupKeyRead(c->db, key);
    // 写入到缓存中
    if (!o) addReply(c,reply);
    return o;
}

getCommand 関数でデータを検索し、addReply を呼び出して、返されたデータをクライアント出力に書き込みます。バッファ。

データ書き戻しクライアント

#コマンドをバッファに書き込んだ後、データをバッファから取り出してクライアントに返す必要があります。クライアントにデータを書き戻すプロセスは、実際にはサーバーのイベント ループ内で完了します。

Redis リクエストの処理プロセスはどのようなものですか?

  • #まず、Redis は main 関数で aeSetBeforeSleepProc 関数を呼び出し、ライトバック パッケージの関数 beforeSleep をイベントループに登録します。

    #その後、Redis がイベント ループの aeMain 関数を呼び出すと、beforesleep が設定されているかどうかが判断され、設定されている場合はそれが呼び出されます;
  • beforesleep 関数go を呼び出して handleClientsWithPendingWrites 関数を呼び出します。この関数は writeToClient を呼び出して、バッファからクライアントにデータを書き戻します。

以上がRedis リクエストの処理プロセスはどのようなものですか?の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事はyisu.comで複製されています。侵害がある場合は、admin@php.cn までご連絡ください。