Skip to content

Per-username Session Quota

このプラグインはユーザー名ごとのセッションクォータを強制します。

  • セッションカウンターはユーザー名ごとに管理され、クラスター全体で同期されます。
  • 設定されたクォータに達した場合、認証は quota_exceeded エラーで拒否されます。
  • 既存の clientid での再接続は追加のクォータを消費しません。
  • ユーザー名ごとのクォータオーバーライドにより、カスタム制限、無制限セッション、接続ブロックが可能です。

NOTE

ユーザー名ごとのセッション数制限は、ユーザー名をネームスペースとして設定することで実現できます(client_attrs_init 設定の client_attrs.tns を設定)。 このプラグインはネームスペースに異なるスキームがある場合にのみ必要です。

設定

プラグインの設定項目:

  • max_sessions_per_username(デフォルト: 100)— 正の整数(>= 1)である必要があります。
  • snapshot_min_age_ms(デフォルト: 300000、範囲: 120000900000)— スナップショットを再構築可能になるまでの最小経過時間(ミリ秒)。範囲外の値はクランプされます。
  • snapshot_request_timeout_ms(デフォルト: 5000

設定の意味:

  • max_sessions_per_username: ユーザー名ごとのデフォルト最大同時セッション数。個別のユーザー名はオーバーライドAPIで上書き可能です。
  • snapshot_min_age_ms: スナップショットの再構築をトリガーするまでの最小経過時間(ミリ秒)。大規模クラスターでの頻繁な再構築を防ぎます。
  • snapshot_request_timeout_ms: リストAPIのスナップショット要求処理のタイムアウト時間。

バリデーション:

  • max_sessions_per_username は 1 以上である必要があります。1 未満または数値でない値は拒否されます。
  • 数値フィールドには、正の整数に変換可能な文字列も受け入れられます。

プラグイン設定は標準のプラグイン設定APIから更新できます:

PUT /api/v5/plugins/<name-vsn>/config

ランタイムAPI

プラグインはプラグインAPIゲートウェイを通じてランタイムAPIを公開します。

ベースパス: /api/v5/plugin_api/emqx_username_quota

スナップショット: GET /quota/usernames エンドポイントは、毎回ライブのセッションデータをスキャンするのではなく、事前に構築されたスナップショットから結果を返します。スナップショットはユーザー名ごとのセッション数の時点コピーで、カウント順にソートされており、カーソルベースのページネーションに最適化されています。スナップショットは非同期にバックグラウンドで構築されキャッシュされます。現在のスナップショットが snapshot_min_age_ms より古い場合にのみ新規構築がトリガーされます。レスポンスの各項目には、リアルタイムのカウントがスナップショット値と異なる場合に snapshot_used フィールドが含まれ、呼び出し元はキャッシュされた値と現在の値の両方を確認できます。

初回リクエスト待機: 最初のリクエストでスナップショットがまだ存在しない場合、サーバーは進行中の構築が完了するまで(リクエストのデッドラインから1秒を引いた時間まで)待機します。構築が間に合えば通常の 200 レスポンスを返し、間に合わなければ部分データ付きの 503 を返します(後述)。

セッションクエリ

  • GET /quota/usernames — アクティブなセッションを持つすべてのユーザー名を一覧表示
  • GET /quota/usernames/:username — 単一ユーザー名の詳細取得
  • GET /metrics — プラグインのメトリクスをPrometheusテキスト形式でエクスポート
  • POST /kick/:username — 指定ユーザー名のすべてのセッションをキック

スナップショット管理

  • DELETE /quota/snapshot — スナップショットの強制再構築

クォータオーバーライド

  • POST /quota/overrides — ユーザー名ごとのクォータオーバーライド設定
  • DELETE /quota/overrides — ユーザー名ごとのクォータオーバーライド削除
  • GET /quota/overrides — すべてのクォータオーバーライド一覧

GET /quota/usernames

クエリパラメータ:

  • limit: 正の整数、最大100(デフォルト100)
  • used_gte: 必須(カーソルなしの場合)— 最小セッション数フィルター。これ以上のセッション数を持つユーザー名のみ含まれます。1以上の正の整数である必要があります。
  • cursor: オプション。前回のリスト呼び出しで返された不透明なカーソル。指定しない場合は最初のページを返します。

パラメータルール:

  • used_gte のみ(cursorなし): OK(最初のページ)
  • cursor のみ(used_gteなし): OK(used_gteはカーソルに埋め込まれている)
  • 両方指定: 400 BAD_REQUEST — フィルターはカーソルに固定されているため
  • 両方なし: 400 BAD_REQUEST

動作:

  • 結果は常にセッション数、次にユーザー名でソートされます。
  • ページネーションはカーソルベースです。最初のページは cursor を省略します。
  • 各項目は username、リアルタイムの used、および有効なクォータ limit を含みます。
  • リアルタイムの used がスナップショットのカウントと異なる場合、snapshot_used が含まれます。

成功時のレスポンス構造:

  • data: ユーザー名クォータのエントリ
  • meta.limit: ページサイズ(ページネーション制限)
  • meta.count: このページのエントリ数
  • meta.total: スナップショット内の総エントリ数
  • meta.next_cursor: 次ページ用カーソル(存在する場合)
  • meta.snapshot: スナップショットのメタデータ:
    • node
    • generation(インクリメンタルスナップショットID)
    • taken_at_ms(スナップショット取得時刻(ミリ秒))

エラー応答:

  • 400 BAD_REQUEST: used_gte がない、または used_gtecursor が同時に指定された場合
  • 400 INVALID_CURSOR: カーソルが利用不可のノードを参照しているか不正な形式
  • 503 SERVICE_UNAVAILABLE: スナップショットが再構築中
    • ボディに snapshot_build_in_progress: truedatameta を含む
    • data: 進行中のスナップショットから部分的に読み取った最初のページ(構築開始直後の場合は空の場合あり)
    • meta.count: 部分的なエントリ数、meta.partial: true
    • バウンデッドバックオフで同じリクエストを再試行してください

DELETE /quota/snapshot

即時にスナップショットの再構築を強制します。非同期で再構築を開始後、{"status": "ok"} を含む 200 を返します。スナップショットはバックグラウンドで再構築されます。

GET /quota/usernames/:username

単一ユーザー名の詳細を返します。レスポンスフィールドは usernameusedlimitclientids です。

ユーザー名にアクティブなセッションがない場合は 404 NOT_FOUND を返します。

GET /metrics

プラグインのPrometheusテキスト形式メトリクスを返します。レプリカントノードでは、リクエストはスナップショット所有のコアノードに転送されます。

現在エクスポートされているメトリクス:

  • emqx_username_count — アクティブなスナップショット内のユーザー名総数

POST /kick/:username

指定ユーザー名のすべてのセッションをキックします。キックしたセッション数を {"kicked": N} の形式で返します。

ユーザー名にアクティブなセッションがない場合は 404 NOT_FOUND を返します。

POST /quota/overrides

ユーザー名ごとのクォータオーバーライドを設定します。ボディはJSON配列です:

json
[
  {"username": "user1", "quota": 1000},
  {"username": "vip", "quota": "nolimit"},
  {"username": "blocked", "quota": 0}
]

オーバーライドの意味:

quota の値意味
正の整数このユーザー名のカスタムセッション制限
"nolimit"無制限セッション(クォータ無効化)
0接続禁止 — 新規接続をすべて拒否

オーバーライドはディスクに永続化され、クラスター全体でレプリケートされます。ユーザー名にオーバーライドがない場合は、グローバル設定の max_sessions_per_username が使用されます。

DELETE /quota/overrides

ユーザー名でオーバーライドを削除します。ボディはユーザー名文字列のJSON配列です:

json
["user1", "blocked"]

GET /quota/overrides

すべてのオーバーライドを一覧表示します。{"data": [{"username": "...", "quota": ...}, ...]} の形式で返します。

アーキテクチャ

スナップショット所有者ルーティング

スナップショットはコアノード上で構築されます。GET /quota/usernamesGET /metrics は、稼働中のコアノードリストをソートした最初のノードであるスナップショット所有コアノードにルーティングされます。

ブルー/グリーンスナップショット

2つのスナップショットバッファ(ブルーとグリーン)を保持します。片方が読み取りリクエストに応答している間、もう片方で次のスナップショットを構築します。構築完了後に役割を入れ替えます。これにより再構築中のデータギャップがなくなり、新しいスナップショットが準備できるまで古いスナップショットが利用可能です。

バックグラウンドスナップショット構築

スナップショットの再構築はバックグラウンドプロセスで実行され、イールドベースのスロットリングによりサーバーのブロックを回避します。構築中もリストAPIは応答可能です。

運用上の注意

バースト接続時のクォータ超過

クォータの判定は認証時に行われ、セッションカウンターはセッションライフサイクルフックで確定します。 高い同時接続バースト(特にクラスター環境)では、あるユーザー名の観測される同時セッション数が一時的に max_sessions_per_username を超える短い同期ウィンドウが発生します。

実務上の意味:

  • このプラグインはバースト負荷下で最終的整合性を持つクラスター全体のクォータ強制を提供します。
  • 極端な接続集中時の厳密なパケット単位の入場ゲートではありません。

プラグイン起動時のブートストラップ

プラグインが稼働中のクラスターにインストールされた場合、既存のクライアントセッションはフック登録前に確立されています。 起動時にプラグインはローカルチャネルをすべて走査し、各セッションを登録してクォータ状態をブートストラップします。

コアノードへのDB書き込み負荷を避けるため(特にレプリカントノードに大量の既存接続がある場合)、ブートストラップループはスロットリングされます:

  • セッションは100件ずつバッチ登録されます。
  • 各バッチ後、最後に書き込んだレコードがローカルテーブルにレプリケートされるのを待機します。10msごとにポーリングします。
  • 10秒以内にレプリケーションが完了しない場合、エラーログを出してブートストラップを中断します。タイムアウト前に登録されたセッションは保持され、残りは再接続時のフック登録で自然に拾われます。

リストAPIからの 503 対応

サーバーがビジー状態かスナップショット構築中の場合、リストAPIは 503 を返します。

503 のレスポンスボディには、進行中のスナップショットテーブルから部分的に読み取った最初のページの data 配列が含まれます。これにより呼び出し元は空レスポンスではなく即時にベストエフォートのデータを得られます。meta.partial: true フラグはデータが不完全であることを示します。構築開始直後の場合は部分ページが空の場合もあります。

APIクライアントへのガイダンス:

  • data を確認し、即時利用可能な部分結果を取得してください。
  • バウンデッドバックオフで再試行してください。

ダウンロード

各EMQXリリース用のtarball:

EMQXバージョンプラグインバージョンパッケージ
6.2.01.2.0emqx_username_quota-1.2.0.tar.gz
6.2.11.2.1emqx_username_quota-1.2.1.tar.gz