Repository navigation
Description
Goal
Allow explicitly configured HTTP workloads that call the same upstream origin to use independent, bounded connection and acquisition pools. A slow or saturated bulk workload should not consume the connection/acquisition capacity reserved for an interactive workload merely because both use the same backend host.
This is a focused transport-resource feature, not a second rate-limiter platform or a general resource-governance tracker.
Existing behavior and source evidence
Reviewed source: apache/shenyu@750673e3f619e335333150ff1b2d14070d5d5c31 (2.7.3-SNAPSHOT).
- shenyu-spring-boot-starter/shenyu-spring-boot-starter-plugin/shenyu-spring-boot-starter-plugin-httpclient/src/main/java/org/apache/shenyu/springboot/starter/plugin/httpclient/HttpClientFactory.java creates one
ConnectionProviderfrom the process-wideshenyu.httpclient.poolconfiguration and builds the HTTP client around it. - shenyu-spring-boot-starter/shenyu-spring-boot-starter-plugin/shenyu-spring-boot-starter-plugin-httpclient/src/main/java/org/apache/shenyu/springboot/starter/plugin/httpclient/HttpClientPluginConfiguration.java publishes that client and supplies one client instance to the configured WebClient or Netty forwarding plugin at lines 101–136.
- shenyu-plugin/shenyu-plugin-httpclient/src/main/java/org/apache/shenyu/plugin/httpclient/WebClientPlugin.java and shenyu-plugin/shenyu-plugin-httpclient/src/main/java/org/apache/shenyu/plugin/httpclient/NettyHttpClientPlugin.java retain that client for forwarding. There is no first-class, explicitly named pool-group selection in the built-in Divide HTTP path.
- Existing bounds such as
maxConnections, acquisition timeout andmaxPendingAcquiresalready exist. Reactor Netty pools are origin-specific; calls to different origins are not all forced into one physical socket pool. The missing use case is isolating workloads within the same origin.
This is a source-grounded enhancement proposal. No production incident or measured throughput improvement is claimed.
Scope: small first delivery
- Define a finite set of named pool groups in trusted startup configuration. Each group owns its connection provider and finite connection/acquisition settings; the historical default client remains available. Reject invalid limits and excessive group count at startup. Do not create pools from request IDs, untrusted tenant headers, raw paths or arbitrary per-request group names.
- Let an optional Divide rule-handle field select one of those configured groups. Keep existing selector/rule matching and routing semantics. An absent selection uses the current default path. Reject or visibly fail an unknown group reference; do not silently fall back to another group's capacity.
- Pin the chosen group/client for the request's actual lifetime, including its HTTP retries/failover and response body. A rule update selects a group for future work; it must not move or close an already running response. The first delivery does not dynamically add/remove/reconfigure the group registry: changing its definitions requires restart, avoiding a second hot-retirement mechanism.
- Support both built-in
nettyandwebClientforwarding strategies with equivalent group selection. Preserve TLS/proxy/trace/header behavior, keep-alive, bounded body replay, existing configuration and custom-client extension behavior. Clearly reject unsupported combinations rather than bypassing a user-supplied client or its policy. - Own and close only factory-created providers at shutdown. Reuse the ownership/cancellation fixes in PR [ISSUE #7644] Bound HTTP connection pools and acquisition capacity #7653 where applicable. A cancelled pending acquisition must not hold queue capacity indefinitely. Avoid a new per-group event-loop/thread pool in this first delivery.
- Publish bounded group metrics for active/idle connections, pending acquisition, acquisition rejection and wait time. Group names come only from the finite configuration. Document that limits are per gateway and per origin/provider as applicable, not a cluster-wide request/stream-concurrency guarantee. HTTP/2 multiplexing must not be presented as equivalent to one connection per RPC.
Acceptance criteria
- A real loopback test uses two rules and two groups against the same backend origin. Workload A holds all of its configured connections/queue slots; a B request still completes using B's own capacity before A is released. Also exercise the shared-default control to prove that the test detects the intended boundary.
- Enforce each group's configured connection/acquisition limits; test saturation, timeout, cancellation, recovery and retry/failover without creating unbounded groups, queues or per-request clients.
- Both built-in HTTP strategies route through the selected group. TLS/proxy/trace and custom-client compatibility are covered or explicitly scoped as unsupported.
- Rule rebinding does not terminate an existing HTTP/SSE response; shutdown releases every owned provider exactly once, with no use-after-dispose or closing externally owned clients.
- Disabled/default-only deployments retain existing behavior; document configuration, finite cardinality, per-origin/replica scope and migration/rollback. Include a fixed-workload isolation measurement following [Task][Performance] Establish reproducible benchmarks and track gateway optimizations #7650 rather than promising a universal p99 improvement.
Relationship to existing issues
- [Task][Performance] Define bounded HTTP connection-pool and acquisition policies #7644 / PR [ISSUE #7644] Bound HTTP connection pools and acquisition capacity #7653 bound a provider's connections and pending acquisition; reuse them. They do not establish named same-origin workload isolation.
- [Feature] Add opt-in latency-based adaptive upstream concurrency control #7930 owns latency-feedback admission, explicitly separate from connection pools. This task is static pool partitioning, not adaptive concurrency or distributed quotas.
- [Feature] Share an end-to-end request deadline across plugins and upstream attempts #7931 owns cross-plugin deadlines; pool acquisition remains subject to its remaining-budget contract when integrated.
- [Feature] Define bounded stream retirement policies for configuration reloads #8062 owns dynamic revision/stream retirement. Startup-only group definitions keep that policy out of this first delivery.
- The existing Redis concurrent rate limiter remains useful. This proposal does not claim ShenYu lacks concurrency limiting.
Non-goals
AI Proxy's separate transport, native gRPC/WebSocket pools, per-tenant automatically created pools, CPU/thread isolation, global connection limits across replicas, new credential routing, or a generic plugin-resource framework. Extend to other transports only after this bounded HTTP implementation is useful and measured.
Activity
konlue commentedon Nov 3, 2026
/assign
[ISSUE #8194] Add opt-in named upstream connection-pool groups for wo…