モジュールのタスクは、Nginx WebサーバーをCometサーバーとして機能させることです。
このモジュールを使用するのに十分な資料があります: 公式プロジェクトページ 、 Basic HTTP Push Relay Protocolの説明、 およびNginx&Comet:Low Latency Server Pushなどの多くの記事が適しています。 ただし、多くのマニュアルでは、すべてのクライアントが単一のパブリックチャネルを使用するモジュールの基本構成のみを考慮しています。 このモジュールは非常に便利ですが、開発者に柔軟なチャネル管理と保護を提供しません。
この記事では、チャネルを制御するための可能な方法を示す小さな例を作成します。
挑戦する
何が必要ですか?
- 新しいチャンネルを作成する
- 既存のチャンネルを閉じる
- チャンネル存在チェック
- チャネルへのデータ送信
- すべてのチャネルにデータを送信する
その結果、一意のチャネルが各ユーザーに割り当てられます(たとえば、承認に合格した後)。
Nginxプッシュモジュール-セキュア
Nginxプッシュモジュールは、セキュリティを設定するためのnginx configのいくつかのディレクティブを提供します。 私が適用したものだけを考慮してください:
- push_authorized_channels_only [on | オフ]
on-クライアントは、明示的な作成(POSTまたはPUTリクエストをパブリッシャーポイントに送信)した後にのみ特定のチャンネルをリッスンできるようにします。 そうでない場合、閉じたチャネルをリッスンしようとすると、応答403がクライアントに返されます。
off-クライアントは閉じたチャンネルのリッスンを開始できます。
- push_max_channel_subscribers [number]
同時チャネルリスナーの最大数。
実装
それでは、モジュールに名前を付けましょう-チャンネル。 Rubyで開発します(Railsの小さな挿入も行われます)。
チャネルを管理するには( 基本HTTPプッシュリレープロトコルを参照)、HTTPクライアントが必要です。 私はパトロンが好きです。
オープンチャネルの配列は、opened_channels配列に格納されます。 チャネルIDは、generate_channel_idメソッドを使用して生成されます。
チャネルの作成 (オープンメソッド)は、PUTリクエストを公開ポイントに送信することで行われます(私たちにとっては、ただの/公開です)。 新しいチャネルが正常に作成されると(ステータス200)、生成されたIDがopend_channels配列に追加されて返されます。
チャネルを閉じる(閉じるメソッド)には、発行するDELETEリクエストを送信します。
チャネルの存在の確認 (既存のメソッド?)発行するGETリクエストを送信することにより行われます。 サーバーが200を返した場合、チャネルは開いていますが、そうでない場合、アレイからチャネルを削除します。
チャネルへのデータの送信 (プッシュメソッド)は、データとコンテンツタイプで発行するPOSTリクエストを送信することによって行われます。 開いているチャネルにのみデータを送信します。
すべてのHTTPリクエストにはチャンネルパラメータが含まれている必要があります(このチャンネルがあります)。 当然、公開ポイントは保護する必要があります。
モジュールコード:
module Channel @http_client = Patron::Session.new @http_client.base_url = "http://localhost/publish" @@opened_channels = [] mattr_accessor :opened_channels class << self def open id = generate_channel_id resp = @http_client.put(build_request_for_channel(id), "") if resp.status == 200 opened_channels << id id else false end end def close(id) resp = @http_client.delete(build_request_for_channel(id)) resp.status end def exist?(id) resp = @http_client.get(build_request_for_channel id) if resp.status == 200 true else opened_channels.delete id false end end def push(id, data, content_type) if exist? id puts "pushing to channel with id=#{id}..." resp = @http_client.post(build_request_for_channel(id), data, {"Content-Type" => content_type}) resp.status end end def push_to_all_channels(data, content_type="application/json") opened_channels.each { |c| push(c, data, content_type) } end private def generate_channel_id UUIDTools::UUID.timestamp_create.to_s end def build_request_for_channel(id) "/?channel=#{id}" end end end
リクエストに応じてチャンネルを開く:
def subscribe if channel_id = Channel::open render text: channel_id else render nothing: true, status: 500 end end
データ送信の例:
user = current_user channel_id = user.channel_id msg = user.messages.last data = msg.to_json(only: [:created_at, :text]) status = Channel::push(channel_id, msg, "application/json")