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はカーソルに埋め込まれている)used_gteとcursor両方:400BAD_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とカーソルが同時に指定された場合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
指定ユーザー名の全セッションをキックします。キックしたセッション数 N を含む {"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つのスナップショットバッファ(ブルーとグリーン)を維持します。1つは読み取りリクエストに使用され、もう1つは次のスナップショット構築に使用されます。構築が完了すると役割を入れ替えます。これにより再構築中のデータギャップがなくなり、新しいスナップショットが準備できるまで古いスナップショットが利用可能です。
バックグラウンドスナップショット構築
スナップショットの再構築はバックグラウンドプロセスで実行され、サーバーのブロックを避けるためにイールドベースのスロットリングが行われます。リストAPIは構築中も応答可能です。
運用上の注意
バースト接続時のクォータオーバーシュート
クォータ判定は認証時に行われ、セッションカウンターはセッションライフサイクルフックで確定します。 高い同時接続バースト(特にクラスター環境)では、一時的に1ユーザー名の同時セッション数が max_sessions_per_username を超える同期ギャップが生じる場合があります。
実務的な意味:
- このプラグインはバースト負荷下で最終的整合性を持つクラスター全体のクォータ強制を提供します。
- 極端な接続集中時に厳密なパケット単位の入場ゲートではありません。
プラグイン起動時のブートストラップ
プラグインを稼働中のクラスターにインストールした場合、既存クライアントセッションはフック登録前に確立されています。 起動時にプラグインはローカルチャネルをすべて走査して各セッションを登録し、クォータ状態をブートストラップします。
コアノードへのDB書き込み負荷集中を避けるため(特にレプリカントノードに大量接続がある場合)、ブートストラップループはスロットリングされます:
- セッションは100件ずつバッチ登録されます。
- 各バッチ後、最後に書き込んだレコードがローカルテーブルにレプリケートされるのを待ちます。10msごとにポーリングします。
- 10秒以内にレプリケーションが完了しない場合はエラーログを出力し、
errorレベルログでブートストラップを中止します。 タイムアウト前に登録されたセッションは保持され、残りは再接続時のフック登録で自然に拾われます。
リスト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 |