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、範囲:120000–900000)— スナップショットを再構築可能になるまでの最小経過時間(ミリ秒)。範囲外の値はクランプされます。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: スナップショットのメタデータ:nodegeneration(インクリメンタルスナップショットID)taken_at_ms(スナップショット取得時刻(ミリ秒))
エラー応答:
400 BAD_REQUEST:used_gteがない、またはused_gteとcursorが同時に指定された場合400 INVALID_CURSOR: カーソルが利用不可のノードを参照しているか不正な形式503 SERVICE_UNAVAILABLE: スナップショットが再構築中- ボディに
snapshot_build_in_progress: true、data、metaを含む data: 進行中のスナップショットから部分的に読み取った最初のページ(構築開始直後の場合は空の場合あり)meta.count: 部分的なエントリ数、meta.partial: true- バウンデッドバックオフで同じリクエストを再試行してください
- ボディに
DELETE /quota/snapshot
即時にスナップショットの再構築を強制します。非同期で再構築を開始後、{"status": "ok"} を含む 200 を返します。スナップショットはバックグラウンドで再構築されます。
GET /quota/usernames/:username
単一ユーザー名の詳細を返します。レスポンスフィールドは username、used、limit、clientids です。
ユーザー名にアクティブなセッションがない場合は 404 NOT_FOUND を返します。
GET /metrics
プラグインのPrometheusテキスト形式メトリクスを返します。レプリカントノードでは、リクエストはスナップショット所有のコアノードに転送されます。
現在エクスポートされているメトリクス:
emqx_username_count— アクティブなスナップショット内のユーザー名総数
POST /kick/:username
指定ユーザー名のすべてのセッションをキックします。キックしたセッション数を {"kicked": N} の形式で返します。
ユーザー名にアクティブなセッションがない場合は 404 NOT_FOUND を返します。
POST /quota/overrides
ユーザー名ごとのクォータオーバーライドを設定します。ボディはJSON配列です:
[
{"username": "user1", "quota": 1000},
{"username": "vip", "quota": "nolimit"},
{"username": "blocked", "quota": 0}
]オーバーライドの意味:
quota の値 | 意味 |
|---|---|
| 正の整数 | このユーザー名のカスタムセッション制限 |
"nolimit" | 無制限セッション(クォータ無効化) |
0 | 接続禁止 — 新規接続をすべて拒否 |
オーバーライドはディスクに永続化され、クラスター全体でレプリケートされます。ユーザー名にオーバーライドがない場合は、グローバル設定の max_sessions_per_username が使用されます。
DELETE /quota/overrides
ユーザー名でオーバーライドを削除します。ボディはユーザー名文字列のJSON配列です:
["user1", "blocked"]GET /quota/overrides
すべてのオーバーライドを一覧表示します。{"data": [{"username": "...", "quota": ...}, ...]} の形式で返します。
アーキテクチャ
スナップショット所有者ルーティング
スナップショットはコアノード上で構築されます。GET /quota/usernames と GET /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.0 | 1.2.0 | emqx_username_quota-1.2.0.tar.gz |
| 6.2.1 | 1.2.1 | emqx_username_quota-1.2.1.tar.gz |