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の生成ポリシーが書き換えられています.この問題は修復されたようです.興味があれば、自分で検証することができます.