Per-username Session Quota
このプラグインは、ユーザー名ごとのセッション数のクォータを強制します。
- セッションカウンターはユーザー名ごとに管理され、クラスター全体で同期されます。
- 設定されたクォータに達すると、認証は
quota_exceededで拒否されます。 - 既存の
clientidでの再接続は追加のクォータを消費しません。 - ユーザー名ごとのクォータオーバーライドにより、カスタム制限、無制限セッション、または接続ブロックが可能です。
注意
client_attrs_init 設定で client_attrs.tns を設定し、ユーザー名をネームスペースとして使用できる場合は、ネームスペースベースの制限でこの種のセッション制限を強制できます。 ネームスペースの割り当てが異なるスキームに従う場合のみ、このプラグインを使用してください。
設定
| フィールド | デフォルト | バリデーション | 説明 |
|---|---|---|---|
max_sessions_per_username | 100 | 正の整数(>= 1)でなければなりません。1未満または非数値は拒否されます。 | ユーザー名ごとのデフォルト最大同時セッション数。個別のユーザー名はオーバーライドAPIでこの値を上書き可能です。 |
snapshot_min_age_ms | 300000 | 120000 から 900000 の範囲内でなければなりません。範囲外の値はクランプされます。 | スナップショットが再構築される前の最小経過時間(ミリ秒)。大規模クラスターで頻繁な再構築を防止します。 |
snapshot_request_timeout_ms | 5000 | 正の整数に変換可能な文字列値も受け入れます。 | リストAPIのスナップショット要求処理のタイムアウト時間(ミリ秒)。 |
プラグイン設定は標準のプラグイン設定APIで更新します:
PUT /api/v5/plugins/<name-vsn>/config
ランタイムAPI
プラグインはプラグインAPIゲートウェイを通じてランタイムAPIを公開します。
ベースパス:/api/v5/plugin_api/emqx_username_quota
セッション照会
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
このエンドポイントは、毎回ライブのセッションデータをスキャンするのではなく、事前に構築されたスナップショットから結果を返します。
スナップショットは、ユーザー名ごとのセッション数の時点コピーで、効率的なカーソルベースのページネーションのためにセッション数でソートされています。スナップショットは非同期でバックグラウンドにて構築されキャッシュされます。現在のスナップショットが snapshot_min_age_ms より古い場合にのみ新規構築がトリガーされます。
最初のリクエストが到着しスナップショットがまだ存在しない場合、サーバーは進行中の構築完了を待ちます。待機時間はリクエストのデッドラインから1秒を引いた時間までです。構築が間に合えば通常の 200 レスポンスを返します。間に合わなければ部分データ付きで 503 を返します。
クエリパラメータ:
limit:正の整数、最大100(デフォルト100)used_gte:カーソルがない場合は必須。これは最小セッション数のフィルターです。この値以上のセッション数を持つユーザー名のみが含まれます。正の整数>= 1でなければなりません。cursor:前回のリスト呼び出しで返された不透明なカーソル。省略すると最初のページが返されます。
パラメータルール:
used_gteのみ(カーソルなし):OK(最初のページ)cursorのみ(used_gteなし):OK(used_gteはカーソルに埋め込まれている)used_gteとcursor両方:400BAD_REQUEST。フィルターはカーソルに固定されているため。used_gteもcursorもなし:400BAD_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 のようなリストAPI向けにプラグインがスナップショットをどのように構築し提供するかを説明します。
スナップショット所有ノードのルーティング
スナップショットはコアノードで構築されます。GET /quota/usernames と GET /metrics は、ソートされた稼働中のコアノードリストの最初のノードとして選ばれたスナップショット所有コアノードにルーティングされます。
ブルー/グリーンスナップショット
2つのスナップショットバッファ(ブルーとグリーン)を維持します。1つが読み取りリクエストに応答している間、もう1つは次のスナップショット構築に使用されます。構築が完了すると役割を入れ替えます。これにより、再構築中も古いスナップショットが利用可能なためデータのギャップが発生しません。
バックグラウンドスナップショット構築
スナップショットの再構築はバックグラウンドプロセスで実行され、サーバーのブロックを避けるためにイールドベースのスロットリングが行われます。構築中もリストAPIは応答可能な状態を維持します。
運用上の考慮事項と制限
このセクションでは、プラグインを本番環境で運用する際に考慮すべきランタイム動作と制限について説明します。
接続集中時のクォータ超過
クォータの判定は認証時に行われ、セッションカウンターの確定はセッションライフサイクルフックで行われます。高い同時接続集中(特にクラスター環境)では、あるユーザー名の同時セッション数が一時的に max_sessions_per_username を超える短い同期ウィンドウが発生します。
実務上の意味:
- このプラグインはクラスター全体でのクォータ強制を提供し、バースト負荷下で最終的整合性を保証します。
- 極端な接続集中時には、瞬間的な厳密な接続クォータ強制は保証されません。
プラグイン起動時のブートストラップ動作
プラグインを稼働中のクラスターにインストールすると、既存のクライアントセッションはフック登録前に確立されています。起動時にプラグインはローカルチャネルをすべて巡回し、各セッションを登録してクォータ状態をブートストラップします。
コアノードにDB書き込みの嵐が発生しないように(特にレプリカントノードに多数の既存接続がある場合)、ブートストラップループはスロットリングされます:
- セッションは100件ずつバッチ登録されます。
- 各バッチ後、最後に書き込まれたレコードがローカルテーブルにレプリケートされるのを待ちます。10msごとにポーリングします。
- 10秒以内にレプリケーションが完了しない場合、エラーがログに記録され、
errorレベルのログとともにブートストラップは中止されます。 タイムアウト前に登録されたセッションは保持され、残りは再接続時のフック登録で自然に拾われます。
リストAPIの 503 応答の取り扱い
サーバーがビジー状態かスナップショットを構築中の場合、リストAPIは 503 を返します。
503 のレスポンスボディには、進行中のスナップショットテーブルから部分的に読み取った最初のページの data 配列が含まれます。これにより呼び出し元は空レスポンスではなくベストエフォートのデータを即座に受け取れます。meta.partial: true フラグはデータが不完全であることを示します。構築開始直後の場合は部分ページが空の場合もあります。
APIクライアントへのガイダンス:
dataを確認し、利用可能な部分結果を取得してください。- バウンデッドバックオフでリトライしてください。
ダウンロード
各EMQXリリースのtarball:
| EMQXバージョン | プラグインバージョン | パッケージ |
|---|---|---|
| 6.3.0 | 1.2.3 | emqx_username_quota-1.2.3.tar.gz (sha256) |
| 6.3.1 | 1.2.3 | emqx_username_quota-1.2.3.tar.gz (sha256) |