ホームページ  >  記事  >  データベース  >  Redis のイベント駆動モデルとは何ですか?

Redis のイベント駆動モデルとは何ですか?

WBOY
WBOY転載
2023-06-04 10:20:07674ブラウズ

なぜ Redis は基本的なソケット プログラミング モデルを使用しないのですか?

ソケット モデルを使用してネットワーク通信を実装する場合、ソケットの作成、ポートのリッスン、接続の処理、リクエストの読み取りと書き込みなど、複数の手順を実行する必要があります。これらのステップの主要な操作は、ソケット モデルの欠陥を分析するのに役立ちます。

まず、サーバーとクライアントが通信できるようにする必要がある場合、次の 3 つの手順に従って、サーバー側でクライアント接続をリッスンするリスニング ソケット (リスニング ソケット) を作成できます。

  • #ソケット関数を呼び出してソケットを作成します。通常の状況では、このソケットをアクティブ ソケットと呼びます。

  • バインド関数を呼び出して、アクティブ ソケットを現在のサーバーの IP およびリスニング ポートにバインドします。

  • listen 関数を呼び出して、アクティブなソケットを listen ソケットに変換し、クライアント接続の listen を開始します。

上記の 3 つの手順を完了すると、サーバーはクライアントの接続リクエストを受信できるようになります。クライアントの接続要求をタイムリーに受信するために、accept 関数を呼び出してクライアントの接続要求を受信するループ処理を実行できます。

ここで注意する必要があるのは、accept 関数はブロック関数であるということです。つまり、この時点でクライアント接続要求がない場合、サーバー側の実行プロセスは常にブロックされます。受け入れ機能。クライアント接続要求が到着すると、accept はブロックしなくなり、接続要求を処理し、クライアントとの接続を確立し、接続されたソケットを返します。

最後に、サーバーは、recv 関数または send 関数を呼び出して返された接続ソケット上で読み取りおよび書き込みリクエストを受信して​​処理するか、データをクライアントに送信できます。

コード:

listenSocket = socket(); //调用socket系统调用创建一个主动套接字
bind(listenSocket); //绑定地址和端口
listen(listenSocket); //将默认的主动套接字转换为服务器使用的被动套接字,也就是监听套接字
while(1) { //循环监听是否有客户端连接请求到来
connSocket = accept(listenSocket);//接受客户端连接
recv(connSocket);//从客户端读取数据,只能同时处理一个客户端
send(connSocket);//给客户端返回数据,只能同时处理一个客户端
}

ただし、上記のコードから、サーバーとクライアント間の通信は実現できますが、プログラムが accept 関数を呼び出すたびに、クライアント接続を処理します。したがって、複数のクライアント要求を同時に処理したい場合は、マルチスレッドを使用して、accept 関数を通じて確立された複数のクライアント接続上の要求を処理する必要があります。

このメソッドを使用した後、accept 関数が接続されたソケットを返した後にスレッドを作成し、接続されたソケットを担当する作成されたスレッドに接続されたソケットを渡す必要があります。言葉の上で。同時に、サーバー側の実行プロセスは再び accept 関数を呼び出し、次のクライアント接続を待ちます。

マルチスレッド:

listenSocket = socket(); //调用socket系统调用创建一个主动套接字
bind(listenSocket); //绑定地址和端口
listen(listenSocket); //将默认的主动套接字转换为服务器使用的被动套接字,也就是监听套接字
while(1) { //循环监听是否有客户端连接请求到来
connSocket = accept(listenSocket);//接受客户端连接
pthread_create(processData, connSocket);//创建新线程对已连接套接字进行处理
}

processData(connSocket){
recv(connSocket);//从客户端读取数据,只能同时处理一个客户端
send(connSocket);//给客户端返回数据,只能同时处理一个客户端
}

この方法ではサーバーの同時処理能力を向上させることができますが、Redis の主要な実行プロセスは 1 つのスレッドで実行されるため、マルチスレッドを使用して向上させることはできません。同時処理機能。したがって、この方法は Redis では機能しません。

Redis による同時クライアントの処理能力の向上に役立つ他の方法はありますか?これには、オペレーティング システムが提供する IO 多重化機能を使用する必要があります。基本的なソケット プログラミング モデルでは、accept 関数はリッスン ソケット上でクライアント接続のみをリッスンでき、recv 関数は接続されたソケット上でクライアントによって送信されたリクエストを待機することしかできません。

Linux オペレーティング システムは実用的なアプリケーションで広く使用されているため、このレッスンでは主に Linux 上の IO 多重化メカニズムを学習します。 Select、poll、epoll は、Linux が提供する IO 多重化メカニズムの 3 つの主要な形式です。次に、これら 3 つの仕組みのそれぞれの実装アイデアと使用方法を学びます。次に、Redis がネットワーク通信の実装に epoll メカニズムの使用を選択することが多い理由を見てみましょう。

選択およびポーリング メカニズムは IO 多重化を実装します

まず、選択メカニズムのプログラミング モデルを理解しましょう。

しかし、詳細を学ぶ前に、IO 多重化メカニズムについて習得する必要があるキーポイントを知る必要があります。これは、さまざまなメカニズム間の接続と違いをすぐに把握するのに役立ちます。実際、IO 多重化メカニズムを学ぶときは、次の質問に答えられる必要があります。まず、多重化メカニズムはソケット上のどのイベントをリッスンしますか?次に、多重化メカニズムはいくつのソケットをリッスンできるでしょうか?第三に、ソケットの準備ができたとき、多重化メカニズムはどのようにして準備ができたソケットを見つけるのでしょうか?

選択メカニズム

選択メカニズムの重要な機能は選択関数です。 select 関数の場合、そのパラメーターには、監視対象ファイル記述子の数 __nfds、監視対象記述子の 3 つのコレクション readfds、writefds、Exceptfds、および監視中のブロック待機のタイムアウト タイムアウトが含まれます。 select function プロトタイプ:

int select(int __nfds, fd_set *__readfds, fd_set *__writefds, fd_set *__exceptfds, struct timeval *__timeout)

ここで注意する必要があるのは、Linux には各ソケットのファイル記述子 (負ではない整数) があり、ソケットを一意に識別するために使用されるということです。 Linux では、多重化メカニズムの関数の引数としてファイル記述子を使用するのが一般的です。この関数は、ファイル記述子を通じて対応するソケットを見つけて、監視、読み取り、書き込みなどの操作を実装します。

select 関数の 3 つのパラメーターは、監視する必要があるファイル記述子のセットを指定します。これは、実際には監視する必要があるソケットのセットを表します。では、なぜ 3 セットあるのでしょうか?

关于刚才提到的第一个问题,即多路复用机制监听的套接字事件有哪些。select 函数使用三个集合,表示监听的三类事件,分别是读数据事件,写数据事件,异常事件。

我们进一步可以看到,参数 readfds、writefds 和 exceptfds 的类型是 fd_set 结构体,它主要定义部分如下所示。其中,fd_mask类型是 long int 类型的别名,__FD_SETSIZE 和 __NFDBITS 这两个宏定义的大小默认为 1024 和 32。

所以,fd_set 结构体的定义,其实就是一个 long int 类型的数组,该数组中一共有 32 个元素(1024/32=32),每个元素是 32 位(long int 类型的大小),而每一位可以用来表示一个文件描述符的状态。了解了 fd_set 结构体的定义,我们就可以回答刚才提出的第二个问题了。每个描述符集合都可以被 select 函数监听 1024 个描述符。

如何使用 select 机制来实现网络通信

首先,我们在调用 select 函数前,可以先创建好传递给 select 函数的描述符集合,然后再创建监听套接字。而为了让创建的监听套接字能被 select 函数监控,我们需要把这个套接字的描述符加入到创建好的描述符集合中。

接下来,我们可以使用 select 函数并传入已创建的描述符集合作为参数。程序在调用 select 函数后,会发生阻塞。一旦 select 函数检测到有就绪的描述符,会立即终止阻塞并返回已就绪的文件描述符数。

那么此时,我们就可以在描述符集合中查找哪些描述符就绪了。然后,我们对已就绪描述符对应的套接字进行处理。比如,如果是 readfds 集合中有描述符就绪,这就表明这些就绪描述符对应的套接字上,有读事件发生,此时,我们就在该套接字上读取数据。

而因为 select 函数一次可以监听 1024 个文件描述符的状态,所以 select 函数在返回时,也可能会一次返回多个就绪的文件描述符。我们可以使用循环处理流程,对每个就绪描述符对应的套接字依次进行读写或异常处理操作。

select函数有两个不足

  • 首先,select 函数对单个进程能监听的文件描述符数量是有限制的,它能监听的文件描述符个数由 __FD_SETSIZE 决定,默认值是 1024。

  • 其次,当 select 函数返回后,我们需要遍历描述符集合,才能找到具体是哪些描述符就绪了。这个遍历过程会产生一定开销,从而降低程序的性能。

poll机制

poll 机制的主要函数是 poll 函数,我们先来看下它的原型定义,如下所示:

int poll(struct pollfd *__fds, nfds_t __nfds, int __timeout)

其中,参数 *__fds 是 pollfd 结构体数组,参数 __nfds 表示的是 *__fds 数组的元素个数,而 __timeout 表示 poll 函数阻塞的超时时间。

pollfd 结构体里包含了要监听的描述符,以及该描述符上要监听的事件类型。从 pollfd 结构体的定义中,我们可以看出来这一点,具体如下所示。pollfd 结构体中包含了三个成员变量 fd、events 和 revents,分别表示要监听的文件描述符、要监听的事件类型和实际发生的事件类型。

pollfd 结构体中要监听和实际发生的事件类型,是通过以下三个宏定义来表示的,分别是 POLLRDNORM、POLLWRNORM 和 POLLERR,它们分别表示可读、可写和错误事件。

了解了 poll 函数的参数后,我们来看下如何使用 poll 函数完成网络通信。这个流程主要可以分成三步:

  • 第一步,创建 pollfd 数组和监听套接字,并进行绑定;

  • 第二步,将监听套接字加入 pollfd 数组,并设置其监听读事件,也就是客户端的连接请求;

  • 第三步,循环调用 poll 函数,检测 pollfd 数组中是否有就绪的文件描述符。

而在第三步的循环过程中,其处理逻辑又分成了两种情况:

  • 如果是连接套接字就绪,这表明是有客户端连接,我们可以调用 accept 接受连接,并创建已连接套接字,并将其加入 pollfd 数组,并监听读事件;

  • 如果是已连接套接字就绪,这表明客户端有读写请求,我们可以调用 recv/send 函数处理读写请求。

其实,和 select 函数相比,poll 函数的改进之处主要就在于,它允许一次监听超过 1024 个文件描述符。但是当调用了 poll 函数后,我们仍然需要遍历每个文件描述符,检测该描述符是否就绪,然后再进行处理。

epoll メカニズム

まず、epoll メカニズムは epoll_event 構造体を使用して、監視対象のファイル記述子と監視対象のイベント タイプを記録します。これは、ポーリング メカニズムで使用される pollfd 構造体に似ています。 。

したがって、epoll_event 構造体の場合、epoll_data_t 共用体変数と整数型の events 変数が含まれます。ファイル記述子を記録する epoll_data_t 共用体にはメンバー変数 fd があり、イベント変数は、epoll_data_t 変数内のファイル記述子が関係するイベント タイプを表すさまざまなマクロ定義値を受け取ります。イベントの種類には次の種類があります。

  • EPOLLIN: ファイル記述子に対応するソケットに読み取るデータがあることを示す読み取りイベント。

  • EPOLLOUT: ファイル記述子に対応するソケットに書き込むデータがあることを示す書き込みイベント。

  • EPOLLERR: ファイル記述子がソケットに対して間違っていることを示すエラー イベント。

select 関数またはpoll関数を使用する場合、ファイル記述子セットまたはpollfd配列を作成した後、監視する必要があるファイル記述子を配列に追加できます。

ただし、epoll メカニズムの場合は、最初に epoll_create 関数を呼び出して epoll インスタンスを作成する必要があります。この epoll インスタンスは、監視対象のファイル記述子と準備完了ファイル記述子を記録する 2 つの構造を内部的に保持しており、準備完了ファイル記述子については、処理のためにユーザー プログラムに返されます。

したがって、epoll メカニズムを使用する場合、選択とポーリングを使用する場合のように、どのファイル記述子が準備ができているかを走査してクエリする必要はありません。したがって、epoll は選択してポーリングするよりも効率的です。

epoll インスタンスを作成した後、epoll_ctl 関数を使用して監視対象のファイル記述子にリスニング イベント タイプを追加し、epoll_wait 関数を使用して準備ができたファイル記述子を取得する必要があります。

これで、epoll 関数の使用方法がわかりました。実際、epoll は監視対象の記述子の数をカスタマイズし、準備完了の記述子を直接返すことができるため、Redis がネットワーク通信フレームワークを設計および実装する際には、epoll メカニズムの epoll_create、epoll_ctl、epoll_wait などの関数に基づいています。書き込みイベントはカプセル化され、ネットワーク通信用のイベント駆動フレームワークを実装するために開発されているため、Redis はシングル スレッドで実行されますが、同時実行性の高いクライアント アクセスを効率的に処理できます。

Reactor モデルの動作メカニズム

Reactor モデルは、ネットワーク サーバーが同時実行性の高いネットワーク IO リクエストを処理するために使用するプログラミング モデルです。モデルの機能:

  • 3 種類の処理イベント (接続イベント、書き込みイベント、読み取りイベント)、

  • 3 つの主要な役割 (リアクター、アクセプター、およびハンドラー)。

Reactor モデルは、クライアントとサーバー間の対話プロセスを扱います。これらの 3 種類のイベントは、クライアントとサーバー間の対話中にサーバー側でトリガーされるさまざまな種類のリクエストに対応します。保留中のイベント:

  • クライアントがサーバーと対話したい場合、クライアントはサーバーに接続要求を送信して、リンクに相当する接続を確立します。イベント

  • 接続が確立されると、クライアントはデータを読み取るために読み取りリクエストをサーバーに送信します。サーバーが読み取りリクエストを処理するときは、サーバー側の書き込みイベントに対応するデータをクライアントに書き戻す必要があります。

  • クライアントが読み取りまたは書き込みリクエストを送信するかどうかは関係ありません。サーバー、サーバー リクエストの内容はクライアントから読み取る必要があるため、ここでは読み取りまたは書き込みリクエストがサーバー側の読み取りイベントに対応します

3 つの主要な役割:

  • まず、接続イベントは接続の受信を担当するアクセプターによって処理されます。アクセプターは接続を受信した後、後続の読み取りおよび書き込みイベントを処理するためのハンドラーを作成します。ネットワーク接続;

  • 2 番目に、読み取りおよび書き込みイベントはハンドラーによって処理されます;

  • 最後に、同時実行性の高いシナリオでは、接続イベントと読み取りイベントと書き込みイベントは同時に発生するため、イベントの監視と配布に特化したロールが必要です。これがリアクターのロールです。接続リクエストがある場合、リアクターは生成された接続イベントを処理のためにアクセプターに渡します。読み取りまたは書き込みリクエストがある場合、リアクターは読み取りおよび書き込みイベントを処理のためにハンドラーに渡します。

では、これら 3 つの役割がイベントの監視、転送、処理に関して相互作用することがわかったので、プログラミング時にこれら 3 つをどのように実装できるでしょうか?相互作用についてはどうすればよいでしょうか?これはイベントの運転と切り離せないものです。

Reactor モデルを実装する場合、記述する必要があるコード制御ロジック全体は、イベント駆動型フレームワークと呼ばれます。イベント駆動型フレームワークは、イベントの初期化と、イベントのキャプチャ、オフロード、および処理のメイン ループの 2 つの部分で構成されます。要するに。

イベントの初期化は、サーバー プログラムの起動時に実行され、その主な機能は、監視する必要があるイベントの種類と、この種類のイベントに対応するハンドラーを作成することです。サーバーが初期化を完了すると、それに応じてイベントの初期化も完了し、サーバー プログラムはイベントのキャプチャ、配信、および処理のメイン ループに入る必要があります。

while ループをメイン ループとして使用します。次に、このメイン ループで、発生したイベントをキャプチャし、イベント タイプを決定し、イベント タイプに基づいて、初期化中に作成されたイベント ハンドラーを呼び出して実際にイベントを処理する必要があります。

たとえば、接続イベントが発生すると、サーバー プログラムはアクセプター処理関数を呼び出してクライアントとの接続を作成する必要があります。読み取りイベントが発生すると、読み取りまたは書き込みリクエストがサーバーに送信されたことを示し、サーバー プログラムは特定のリクエスト処理関数を呼び出して、クライアント接続からリクエストの内容を読み取り、読み取りイベントの処理を完了します。

Reactor モデルの基本的な動作メカニズム: クライアントからのさまざまな種類のリクエストにより、サーバー側での接続、読み取り、書き込みという 3 種類のイベントがトリガーされます。これら 3 つの監視、配信、および処理イベントはリアクター、アクセプター、ハンドラーによって実行され、3 種類のロールによって完成され、これら 3 種類のロールによってイベント駆動フレームワークによるインタラクションとイベント処理が実装されます。

以上がRedis のイベント駆動モデルとは何ですか?の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

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