Spring Gatewayリクエスト転送メカニズムの深掘り分析

Gatewayリクエスト処理の全体像

Spring Cloud Gatewayでは、HTTPリクエストの受信から応答返却までの一連の処理が高度に最適化されたパイプラインで構成されています。今回はWebFluxフレームワーク上で動作するGatewayのコア処理フローを詳しく解説します。

リクエストハンドリングのエントリーポイント

Gatewayのコア処理はDispatcherHandlerのhandleメソッドから始まります。このメソッドでは、リクエストに適合するハンドラーを検索し、適切な処理チェーンを構築します。

public class RequestDispatcher {
    private final List<HandlerMapping> routeMappings;
    private final List<HandlerAdapter> processorAdapters;
    
    public Mono<Void> processRequest(ServerWebExchange ctx) {
        if (logger.isInfoEnabled()) {
            logRequestDetails(ctx.getRequest());
        }
        
        return findRouteHandler(ctx)
                .switchIfEmpty(Mono.error(new RouteNotFoundException()))
                .flatMap(handler -> executeHandler(ctx, handler))
                .flatMap(result -> finalizeResponse(ctx, result));
    }
    
    private Mono<Object> findRouteHandler(ServerWebExchange ctx) {
        return Flux.fromIterable(routeMappings)
                .filterWhen(mapping -> mapping.canHandle(ctx))
                .next()
                .cast(Object.class);
    }
}

ルート探索のメカニズム

ルート決定の中心となるのはRoutePredicateHandlerMappingクラスです。このクラスは設定されたルート定義を走査し、リクエストに適合するルートを見つけ出します。

public class RouteResolver {
    private final RouteDefinitionSource definitionSource;
    private final PredicateEvaluator predicateEvaluator;
    
    protected Mono<Route> resolveTargetRoute(ServerWebExchange exchange) {
        return fetchAllRoutes()
                .concatMap(route -> validateRouteMatch(route, exchange))
                .filter(Objects::nonNull)
                .next()
                .map(this::applyRouteConfiguration);
    }
    
    private Mono<Route> validateRouteMatch(Route route, ServerWebExchange ctx) {
        return Mono.just(route)
                .filterWhen(r -> {
                    ctx.getAttributes().put(CURRENT_ROUTE_ID, r.getIdentifier());
                    return r.getCondition().test(ctx);
                })
                .doOnError(err -> logError("Predicate evaluation failed", err))
                .onErrorResume(err -> Mono.empty());
    }
}

ルート定義の動的生成

設定ファイルから読み込まれたルート定義は、実行時にRouteオブジェクトに変換されます。この過程で断言(Predicate)とフィルター(Filter)がルートに関連付けられます。

public class RouteFactory {
    private final FilterRegistry filterRegistry;
    private final PredicateRegistry predicateRegistry;
    
    private Route constructRoute(RouteDefinition config) {
        CompositePredicate<ServerWebExchange> condition = buildPredicate(config);
        List<GatewayFilter> filters = collectFilters(config);
        
        return Route.builder()
                .id(config.getIdentifier())
                .uri(config.getTargetUri())
                .condition(condition)
                .filters(filters)
                .metadata(config.getMetadata())
                .build();
    }
    
    private List<GatewayFilter> collectFilters(RouteDefinition config) {
        List<GatewayFilter> result = new ArrayList<>(globalFilters);
        config.getFilterDefinitions()
                .stream()
                .map(filterRegistry::lookup)
                .forEach(result::add);
        sortFilters(result);
        return result;
    }
}

フィルターチェーンの実行

FilteringWebHandlerがフィルターチェーンの実行を担当します。グローバルフィルターとルート固有のフィルターが統合され、優先順位に従って実行されます。

public class FilterChainProcessor {
    private final List<GlobalFilter> systemFilters;
    
    public Mono<Void> process(ServerWebExchange exchange) {
        Route target = exchange.getRequiredAttribute(ROUTE_INFO);
        List<GatewayFilter> filters = prepareFilterChain(target);
        
        GatewayFilterChain chain = new FilterChainImpl(filters);
        return chain.filter(exchange);
    }
    
    private List<GatewayFilter> prepareFilterChain(Route route) {
        List<GatewayFilter> chain = new ArrayList<>(systemFilters);
        chain.addAll(route.getAppliedFilters());
        
        chain.sort(OrderAnnotationComparator.INSTANCE);
        return chain;
    }
}

主要グローバルフィルターの役割

ロードバランサーフィルター

LoadBalancerFilterは、"lb://"スキームを持つリクエストを処理し、サービスディスカバリから適切なインスタンスを選択します。

public class LoadBalancerFilter implements GlobalFilter {
    private final ServiceDiscovery discoveryClient;
    
    public Mono<Void> filter(ServerWebExchange ctx, GatewayFilterChain chain) {
        URI targetUri = ctx.getAttribute(REQUEST_TARGET_URI);
        
        if (!isLoadBalanced(targetUri)) {
            return chain.filter(ctx);
        }
        
        ServiceInstance selected = selectServiceInstance(targetUri);
        if (selected == null) {
            return Mono.error(new ServiceUnavailableException());
        }
        
        rebuildRequestUrl(ctx, selected);
        return chain.filter(ctx);
    }
    
    private ServiceInstance selectServiceInstance(URI uri) {
        String serviceName = uri.getHost();
        return discoveryClient.getInstances(serviceName)
                .stream()
                .findFirst()
                .orElse(null);
    }
}

HTTP転送フィルター

HttpRoutingFilterは実際のHTTPリクエストをターゲットサービスへ転送します。Nettyベースの非同期HTTPクライアントを活用します。

public class HttpRoutingFilter implements GlobalFilter {
    private final HttpClient httpClient;
    
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        URI target = exchange.getAttribute(PROXY_TARGET_URI);
        
        return performHttpRequest(exchange, target)
                .then(chain.filter(exchange));
    }
    
    private Mono<Void> performHttpRequest(ServerWebExchange ctx, URI target) {
        HttpRequest original = ctx.getRequest();
        
        return httpClient.sendRequest(
                target,
                request -> configureProxyRequest(request, original)
        ).flatMap(response -> handleProxyResponse(ctx, response));
    }
}

処理フローの要約

Gatewayのリクエスト処理は以下のシーケンスで実行されます:

  1. リクエスト受信とルート探索の実行
  2. 断言条件に基づく適合ルートの特定
  3. グローバルフィルターとルートフィルターの統合
  4. フィルターチェーンの順次実行
  5. ロードバランサーによるインスタンス選択
  6. ターゲットサービスへのリクエスト転送
  7. 応答のクライアントへの返却

このアーキテクチャにより、Gatewayは高パフォーマンスでスケーラブルなAPIゲートウェイを実現しています。WebFluxのリアクティブプログラミングモデルを活用することで、システムリソースを効率的に利用しながら多数の同時リクエストを処理できます。

タグ: Spring Cloud Gateway WebFlux Reactor Load Balancing

7月15日 16:18 投稿