#最初にプロセッサを登録します。
ループ リスニング ポートを開き、毎回ゴルーチンを作成します。接続は監視されています ;
次に、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 のいわゆる 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イベント リスニングおよびコールバック関数;
イベントが取得および処理されるのを待機するループ;
コールバック関数を呼び出してデータ ロジックを処理します。
データをクライアントに書き戻す;
fd を epoll に登録し、そして、コールバック関数 acceptTcpHandler を設定します。新しい接続がある場合、コールバック関数が呼び出されます。
無限ループを開始して epoll_wait を呼び出し、待機してイベントの処理を続行します。 aeMain 関数に戻り、aeProcessEvents 関数の呼び出しをループします。
ネットワーク イベントが発生すると、データ処理のためにコールバック関数 acceptTcpHandler が readQueryFromClient まで呼び出されます。クライアントのデータを解析し、対応する cmd 関数を見つけます。実行;
クライアント リクエストを受信した後、Redis インスタンスはクライアント コマンドを処理し、返されたデータを代わりにクライアントの出力バッファに書き込みます。すぐに戻る;
その後、aeMain 関数がループしてバッファ内のデータをクライアントに書き戻すたびに、beforeSleep 関数が呼び出されます;
上記のイベント全体 実際、ループ処理のコードステップは非常にわかりやすく書かれており、それに関する記事がインターネット上にたくさんあるので、詳細は説明しません。
#コマンド実行プロセスとライトバック クライアント#コマンド実行# インターネット上の多くの記事では言及されていないことについて説明し、Redis がコマンドをどのように実行するかを見てみましょう。次に、それをキャッシュに保存し、キャッシュからクライアントにデータを書き戻します。 ネットワーク イベントが発生すると、readQueryFromClient 関数が呼び出され、ここでコマンドが実際に実行されることも前のセクションで説明しました。server.commands 内のコマンドに従って対応するコマンドを検索します。 コマンド実行時のテーブル。関数を実行し、一連の検証の後、対応する関数を呼び出してコマンドを実行し、addReply を呼び出して返されたデータをクライアント出力バッファに書き込みます。
server.commands は、すべての Redis コマンドを、コマンド名に基づいてコマンド機能を取得するテーブルとして、populateCommandTable 関数に登録します。
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; }
以上がRedis リクエストの処理プロセスはどのようなものですか?の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。