Spring Cloud Zuulストリーム制限コンポーネントratelimit構成例およびカスタムkey生成ポリシー
com.marcosbarbero.cloud
spring-cloud-zuul-ratelimit
1.7.1.RELEASE
ratelimitプロジェクトのソースアドレス:https://github.com/marcosbarbero/spring-cloud-zuul-ratelimit
これは私が使用しているspring-cloud-zul-ratelimitバージョンです.使用する前にGithubで公式に与えられた構成例を参考にしましたが、あまり詳しくはありません.複数のURLに対して異なるストリーム制限構成を具体的に示していません.ネット上でも紹介されていません.そこで、構成クラスのソースコードを初歩的に見て、構成クラスのフォーマットに従って異なるURLに対して異なるストリーム制限構成を行いました.次は私の構成を貼り付けます.
zuul:
routes:
service_a:
path: /v1/service-a/**
serviceId: service_a
ratelimit:
key-prefix: service_a
enabled: true
behind-proxy: true
repository: IN_MEMORY
policy-list:
service_a:
- limit: 2
refresh-interval: 10
type:
- type: url
matcher: /t1
- type: url
matcher: /t2
#- type: user
# matcher: system_admin
#- type: origin
# matcher: localhost
- limit: 5
refresh-interval: 20
type:
- type: url
matcher: /t3
#- type: user
# matcher: system_admin
#- type: origin
# matcher: localhost
policy-listは実際にはmap>であり、stringはservice_に対応している.aこのカスタムサービス名は、複数のpolicyに対応しています
@Valid
@NotNull
private Map> policyList = Maps.newHashMap();
policyクラスには、リフレッシュサイクル、対応するサイクル内で許容される総要求回数、総要求時間、および制限カテゴリ(url制限、user制限、origin制限)、および各制限カテゴリに対応するマッチングプレフィックスmatcherを含むリストが含まれています.
@Data
@NoArgsConstructor
public static class Policy {
@NotNull
private Long refreshInterval = MINUTES.toSeconds(1L);
private Long limit;
private Long quota;
@Valid
@NotNull
private List type = Lists.newArrayList();
@Data
@NoArgsConstructor
@AllArgsConstructor
public static class MatchType {
@Valid
@NotNull
private Type type;
private String matcher;
}
public enum Type {
/**
* Rate limit policy considering the user's origin.
*/
ORIGIN,
/**
* Rate limit policy considering the authenticated user.
*/
USER,
/**
* Rate limit policy considering the request path to the downstream service.
*/
URL
}
}
以上の構成クラスから、異なるサービスの下で異なるURLに対して異なる制限を行うことができます.もちろん、構成ファイルを書くことが前提です.
なお、以上は私がピットを踏んだ経験であり、実際には以上のプロファイルの書き込みは正しいが、/t 1/t 2接頭辞のパスにアクセスする際、制限はされず、/t 3接頭辞のパスのみが制限され、制限タイプは20秒以内に5回しかアクセスが許可されていない.つまり、私が上記の/t 1/t 2に対する制限は発効していない
その後、ソースコードをさらに深く見なければならず、URL=/t 1のkey生成ポリシーはkey-prefix(service_a)+:+であるkeyを生成するクラスがあることが分かった. RouteId(service_a)+:+RoutePath(/t 1)は、現在これらに問題はありません.問題は、このkey()メソッドがループ呼び出しされていることです.
@RequiredArgsConstructor
public class DefaultRateLimitKeyGenerator implements RateLimitKeyGenerator {
private final RateLimitProperties properties;
private final RateLimitUtils rateLimitUtils;
@Override
public String key(final HttpServletRequest request, final Route route, final Policy policy) {
final List types = policy.getType().stream().map(MatchType::getType).collect(Collectors.toList());
final StringJoiner joiner = new StringJoiner(":");
joiner.add(properties.getKeyPrefix());
if (route != null) {
joiner.add(route.getId());
}
if (!types.isEmpty()) {
if (types.contains(Type.URL) && route != null) {
joiner.add(route.getPath());
}
if (types.contains(Type.ORIGIN)) {
joiner.add(rateLimitUtils.getRemoteAddress(request));
}
if (types.contains(Type.USER)) {
joiner.add(rateLimitUtils.getUser(request));
}
}
return joiner.toString();
}
}
ループ呼び出し部のコードは,要求されたURLに基づいて一致するPolicyを探し,URLに基づいて生成されたkeyとそれに一致するPolicyをマッピングしてpolicyMapに入れる.
protected List policy(Route route, HttpServletRequest request) {
Map policyMap = properties.getDefaultPolicyList().stream()
.collect(Collectors.toMap(policy -> rateLimitKeyGenerator.key(request, route, policy), Function.identity()));
if (route != null) {
properties.getPolicies(route.getId()).forEach(policy ->
policyMap.put(rateLimitKeyGenerator.key(request, route, policy), policy));
}
return policyMap.values().stream()
.filter(policy -> applyPolicy(request, route, policy))
.collect(Collectors.toList());
}
私の上のプロファイルに対して、このkeyメソッドが2回繰り返し呼び出されたのは、プロファイルに2つのPolicyを定義したからです.1つは10秒で2回(/t 1/t 1)、1つは20秒で5回アクセスを制限したからです(/t 3)
1回目の呼び出しと2回目の呼び出しで生成されたKEYは同じであるため、policyMapには/t 3に対するPolicyが1つしかない
shouldFilter()メソッドを呼び出すと対応するPolicyが見つからないため、/t 1をフィルタリングしない
@Override
public boolean shouldFilter() {
HttpServletRequest request = RequestContext.getCurrentContext().getRequest();
return properties.isEnabled() && !policy(route(request), request).isEmpty();
}
Route route(HttpServletRequest request) {
String requestURI = urlPathHelper.getPathWithinApplication(request);
return routeLocator.getMatchingRoute(requestURI);
}
その後RateLimitKeyGeneratorのkey()メソッドを再実現し,重複するkeyは生成されない.
@Component
public class KeyGenerator implements RateLimitKeyGenerator {
private final RateLimitProperties properties;
private final RateLimitUtils rateLimitUtils;
public KeyGenerator(RateLimitProperties properties,RateLimitUtils rateLimitUtils){
this.properties = properties;
this.rateLimitUtils = rateLimitUtils;
}
@Override
public String key(HttpServletRequest request, Route route, Policy policy) {
final List types = policy.getType().stream().map(MatchType::getType).collect(Collectors.toList());
// matchers
final List matchers = policy.getType().stream().map(MatchType::getMatcher).collect(Collectors.toList());
final StringJoiner joiner = new StringJoiner(":");
joiner.add(properties.getKeyPrefix());
if (route != null) {
joiner.add(route.getId());
}
if (!types.isEmpty()) {
if (types.contains(Type.URL) && route != null) {
joiner.add(route.getPath());
}
if (types.contains(Type.ORIGIN)) {
joiner.add(rateLimitUtils.getRemoteAddress(request));
}
if (types.contains(Type.USER)) {
joiner.add(rateLimitUtils.getUser(request));
}
}
// matchers
if (!matchers.isEmpty()) {
// matchers url, , path
// , url key
if (matchers.contains(route.getPath()) && route != null) {
joiner.add(route.getPath());
}
if (matchers.contains(rateLimitUtils.getRemoteAddress(request))) {
joiner.add(rateLimitUtils.getRemoteAddress(request));
}
if (matchers.contains(rateLimitUtils.getUser(request))) {
joiner.add(rateLimitUtils.getUser(request));
}
}
return joiner.toString();
}
}
以上、ピットを踏んだ経験ですが、正しくないところがあれば、ご指摘ください.
注意:新しいバージョンのソースコードにはkeyの生成ポリシーが書き換えられています.この問題は修復されたようです.興味があれば、自分で検証することができます.