nftablesのlimit rateのような単位時間内でのアクセス数制限(流量制御)を行うクラスの実装です。
- 流量制御の単位は単なる文字列で指定するので、IPアドレス単位、IPアドレス+ポート番号単位、ユーザID単位など、好きな単位での流量制御を行えます。
- インスタンスごとに最大アクセス数を別々に指定できます。A機能は最大で5分に1回、B機能は最大で1分に1回……といった指定も容易に行なえます。
- コンストラクタで流量を指定しない場合、デフォルトの流量(最大10回/分)になります。
- ここではインスタンスを
static
変数に保持しています。これはリクエストの処理終了後もRateLimitterのインスタンスを保持する必要があるためです。@Scope("request")
の場合、static
は必須です。デフォルトのスコープ(@Scope("singleton")
)の場合はstatic
がなくても機能します。
@RestController
public class HelloController {
private static RateLimitter limitter = new RateLimitter();
@Autowired
HttpServletRequest request;
@GetMapping("/")
public String index() {
if (!limitter.allowAccess(request.getRemoteAddr(), System.currentTimeMillis())) {
throw new ResponseStatusException(HttpStatus.TOO_MANY_REQUESTS);
}
// DO BUSINESS LOGIC
}
}
- CGNなどを考慮して、リクエスト送信元IPアドレス+ポート番号単位の流量制御を行う場合の例です。 流量制御のキーは単なる文字列なので、キーにポート番号を含めれば実現できます。
@RestController
public class HelloController {
private static RateLimitter limitter = new RateLimitter();
@Autowired
HttpServletRequest request;
@GetMapping("/")
public String index() {
if (!limitter.allowAccess(request.getRemoteAddr() + ":" + request.getRemotePort(), System.currentTimeMillis())) {
throw new ResponseStatusException(HttpStatus.TOO_MANY_REQUESTS);
}
// DO BUSINESS LOGIC
}
}
- コンストラクタで流量を指定できます。
- TODO: RateLimitterをSpringのBeanとして定義して、流量制御の単位を設定ファイル(
ApplicationContext.xml
)から指定する例を追加したい。
@RestController
public class HelloController {
private static RateLimitter limitter = new RateLimitter(60*1000, 1);
@Autowired
HttpServletRequest request;
@GetMapping("/")
public String index() {
if (!limitter.allowAccess(request.getRemoteAddr(), System.currentTimeMillis())) {
throw new ResponseStatusException(HttpStatus.TOO_MANY_REQUESTS);
}
// DO BUSINESS LOGIC
}
}
@RestController
public class GoodbyeController {
private static RateLimitter limitter = new RateLimitter(60*1000, 3);
@Autowired
HttpServletRequest request;
@GetMapping("/")
public String index() {
if (!limitter.allowAccess(request.getRemoteAddr(), System.currentTimeMillis())) {
throw new ResponseStatusException(HttpStatus.TOO_MANY_REQUESTS);
}
// DO BUSINESS LOGIC
}
}