Introduction: The Evolution of Cloud Gateways
Microservices need a front door. An API gateway acts as that front door. It receives incoming client requests from mobile apps, web browsers, and external partners. It routes each request to the correct backend microservice. It checks authentication tokens. It limits request rates. It handles failures gracefully before traffic reaches core databases. For many years, developers had to choose between simple blocking code and complex reactive code.
Spring Cloud Gateway was originally built on Spring WebFlux, Project Reactor, and Netty. In the past, operating system threads were expensive. A single operating system thread used one megabyte of stack memory. A server with two thousand threads ran out of memory quickly. Reactive programming solved that problem. Netty used a tiny pool of worker threads. It used non-blocking event loops to handle thousands of concurrent network sockets. However, reactive programming introduced massive cognitive complexity. Developers had to write non-blocking code using Mono and Flux operators.
Now, everything changes with Java 25 and Spring Boot 4. Project Loom introduces lightweight Virtual Threads. A virtual thread uses less than a kilobyte of heap memory. The JVM can run millions of concurrent virtual threads on a modest server. In Spring Boot 4, Spring Cloud Gateway Server MVC brings back simple imperative code. You write standard synchronous Java. The JVM handles the concurrency. In this comprehensive guide, we examine the differences between Gateway MVC and Reactive WebFlux. We explore route predicates, filter chains, token relay security, and automated testing.
Why Virtual Threads Change API Gateways
To understand the gateway revolution, we must examine how servers handle blocking operations. In an API gateway, almost every operation involves network input and output. The gateway reads an incoming HTTP request. It makes a network call to an identity provider to validate a token. It makes a network call to Redis to check rate limits. Finally, it forwards the HTTP request to a downstream microservice and waits for the response.
In traditional Spring MVC on Java 8 or Java 11, each request consumed one operating system thread. While the gateway waited for the downstream service to reply, that operating system thread was blocked. It sat idle in kernel memory. If one slow downstream service took three seconds to respond, all gateway threads became exhausted. New incoming requests were rejected with connection timeouts. This problem is known as thread pool starvation.
Java 25 virtual threads eliminate thread pool starvation completely. When a virtual thread executes a blocking network call, the underlying carrier thread does not block. The JVM unmounts the virtual thread from the operating system carrier thread. The carrier thread immediately picks up another virtual thread to do useful work. When the remote microservice responds with bytes, the JVM scheduler remounts the virtual thread onto an available carrier thread. You write simple blocking Java code, but you achieve the high throughput of reactive engines. You can learn more about thread fundamentals in our guide on High-Concurrency APIs with Java 25 Virtual Threads and Spring Boot 4.
Architecture Comparison: Gateway MVC vs Reactive WebFlux
Engineers often ask whether they should migrate their existing reactive gateways to Spring Cloud Gateway Server MVC. To make an informed decision, you must evaluate the operational trade-offs of both architectures.

Spring Cloud Gateway Reactive runs on Netty. It uses the Reactive Streams specification. Every filter and route handler must return a Mono or Flux publisher. If a developer accidentally calls a blocking library inside a reactive filter, the entire Netty event loop freezes. All concurrent requests sharing that event loop stall. Finding such accidental blocking calls requires specialized tools like BlockHound. Furthermore, exceptions in reactive code produce deep, cryptic stack traces without clear line numbers. Debugging production incidents in reactive applications is notoriously difficult and stressful.
Spring Cloud Gateway Server MVC runs on standard embedded servlet containers such as Tomcat or Jetty. When you enable virtual threads in your configuration, Tomcat assigns every incoming HTTP request its own dedicated virtual thread. You write imperative code from top to bottom. You can use standard `try-catch` blocks, standard loops, and standard debugging breakpoints. Every stack trace points directly to the exact file and line number where an error happened. Standard Java profiling tools like Java Flight Recorder (JFR) work out of the box without special reactive plugins.
In terms of raw throughput, both engines perform identically in modern network benchmarks. WebFlux holds a slight advantage only when an application maintains over fifty thousand idle WebSocket connections with zero traffic. For standard REST API gateway routing, Spring Cloud Gateway Server MVC on Java 25 virtual threads provides equal throughput, lower latency jitter, and dramatically simpler maintenance.
Core Routing and Route Predicates
A route is the fundamental building block of an API gateway. A route definition consists of an identifier, a destination URI, a set of predicates, and a chain of filters. A route predicate tests whether an incoming HTTP request matches specific criteria. If all predicates evaluate to true, the gateway executes the route filters and proxies the request to the target URI. Frequently requested static data can also be cached at the gateway edge, as detailed in our guide on Two-Level Caching with Redis and Caffeine in Spring Boot 4.
Spring Cloud Gateway Server MVC uses Spring MVC functional routing under the hood. You define routes using simple Java methods or declarative configuration. Predicates can inspect the HTTP request path, the HTTP method, request headers, query parameters, and client IP addresses. For instance, you can match requests starting with `/api/v1/orders/**` and require that the request uses the `POST` or `GET` method.
Because the gateway runs in an imperative servlet environment, route predicates evaluate synchronously. There are no publisher subscriptions or deferredMono evaluations. The predicate evaluates fast in memory, and the gateway immediately determines the routing target. This deterministic execution model eliminates subtle race conditions that often plague reactive routing configurations.
The Gateway Filter Pipeline Execution Model
Once a route predicate matches an incoming request, the gateway executes a pipeline of filters. Filters allow you to inspect and modify the HTTP request before sending it downstream. They also allow you to inspect and modify the HTTP response before returning it to the client.

The filter pipeline operates in two distinct phases: pre-filtering and post-filtering. In the pre-filtering phase, filters execute in sequential order based on their assigned priority. The first filter might check client authentication headers. The second filter might add an internal correlation identifier to track the request across services. The third filter might verify that the client has not exceeded their rate limit quota.
After the pre-filters complete, the gateway proxies the HTTP request to the downstream target microservice using a fast HTTP client. The gateway’s virtual thread parks cleanly while waiting for the downstream server to transmit response headers and data bytes. Once the response arrives, the filter pipeline executes the post-filtering phase in reverse order. Post-filters can append security headers, calculate response latency metrics, and remove sensitive internal headers before the response leaves the gateway boundary.
Zero-Trust Token Relay with Scoped Values
In modern cloud systems, security cannot stop at the network perimeter. Enterprise systems adopt a Zero-Trust architecture. An API gateway must authenticate incoming client credentials, extract user identity claims, and securely propagate those claims to downstream microservices. This pattern is known as Token Relay.

When an external client calls the gateway, it provides an OAuth 2.0 Bearer JWT token in the `Authorization` header. The gateway verifies the cryptographic signature of the token against the enterprise identity provider. Once verified, the gateway must pass this token downstream so microservices can enforce fine-grained domain permissions. For an in-depth look at distributed permissions, see our guide on Zanzibar Distributed ACL and SpiceDB in Spring Boot.
In legacy thread-bound systems, developers stored security tokens in `ThreadLocal` variables. However, `ThreadLocal` variables carry significant memory overhead when millions of virtual threads exist. Furthermore, child threads can accidentally mutate `ThreadLocal` values, leading to security leaks. Java 25 solves this with `ScopedValue`. A scoped value is immutable and bounded to a specific lexical scope. The gateway binds the authenticated user token to a `ScopedValue` during request ingress. Downstream client calls read this scoped value safely without risk of token leakage or cross-tenant contamination.
Distributed Rate Limiting and Ingress Throttling
Public APIs face unpredictable traffic spikes, automated web scrapers, and malicious denial-of-service attacks. Without rate limiting, a sudden burst of requests can overwhelm downstream microservices and crash database connection pools. The API gateway is the ideal location to enforce rate limiting.
Effective rate limiting uses the Token Bucket algorithm. Each client identifier or IP address receives a virtual bucket filled with tokens. Every incoming request consumes one token. If the bucket has available tokens, the gateway allows the request through. If the bucket is empty, the gateway immediately rejects the request with an HTTP 429 Too Many Requests status code. The gateway also returns standard rate limit headers, such as `X-RateLimit-Limit`, `X-RateLimit-Remaining`, and `Retry-After`.
In a distributed multi-instance gateway deployment, token counters are stored in an in-memory Redis cluster. Each gateway instance queries Redis atomically using Lua scripts to decrement tokens. To explore full implementation details of Redis throttling, read our comprehensive article on Distributed Rate Limiting with Spring Boot 4, Redis, and Bucket4j.
Circuit Breaking and Graceful Fallback Strategies
In a distributed microservice topology, network partitions and service outages are inevitable. If a downstream microservice hangs or begins returning 500 internal server errors, the gateway must protect the overall system from cascading failures.

Spring Cloud Gateway integrates with Resilience4j circuit breakers. When downstream error rates exceed a configured threshold, the circuit breaker opens. In the OPEN state, the gateway stops forwarding traffic to the failing service. Instead, it fast-fails immediately and redirects traffic to a designated fallback endpoint. To master circuit breaker mechanics, consult our tutorial on Resilience4j Circuit Breakers with Java 25 Virtual Threads.
A graceful fallback endpoint provides a seamless user experience. For example, if the recommendation service is down, the fallback endpoint returns a cached list of popular products. If the order history service is degraded, the fallback endpoint returns an empty list with an `X-Gateway-Degraded: true` header. The user sees a functional web application instead of an ugly error screen. When the remote service recovers, the circuit breaker transitions to HALF_OPEN, verifies healthy responses, and returns to the CLOSED state automatically.
Step-by-Step Implementation Guide
Building a high-throughput API gateway with Spring Boot 4 and Java 25 Virtual Threads requires four concise steps. We will configure the application, define immutable route records, implement security and rate limiting filters, and establish admin monitoring endpoints.
Step 1: Configure Virtual Threads in Spring Boot 4
We enable virtual threads in `application.yml`. With this single setting, embedded Tomcat delegates every HTTP request to a lightweight virtual thread:
server:
port: 8080
spring:
application:
name: spring-cloud-gateway-virtual-threads
threads:
virtual:
enabled: true
management:
endpoints:
web:
exposure:
include: health,info,metrics
Step 2: Define Immutable Route Records
We define our route models using Java 25 records with defensive validation preconditions. Records guarantee thread safety and memory efficiency across millions of concurrent virtual threads:
package com.webcodein.workshop.gateway.model;
import java.util.Objects;
public record RouteDefinition(
String id,
String uri,
String pathPattern,
int rateLimitRps,
boolean requiresAuth
) {
public RouteDefinition {
Objects.requireNonNull(id, "Route ID must not be null");
Objects.requireNonNull(uri, "Route URI must not be null");
Objects.requireNonNull(pathPattern, "Path pattern must not be null");
if (id.isBlank()) {
throw new IllegalArgumentException("Route ID must not be blank");
}
if (uri.isBlank() || (!uri.startsWith("http://") && !uri.startsWith("https://"))) {
throw new IllegalArgumentException("Route URI must start with http:// or https://");
}
if (pathPattern.isBlank() || !pathPattern.startsWith("/")) {
throw new IllegalArgumentException("Path pattern must start with /");
}
if (rateLimitRps <= 0) {
throw new IllegalArgumentException("Rate limit RPS must be positive");
}
}
}
Step 3: Implement Ingress Security and Token Relay
We implement a servlet filter that extracts the incoming OAuth 2.0 Bearer token, stores it securely in request attributes, and stamps the response:
package com.webcodein.workshop.gateway.filter;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
import java.io.IOException;
@Component
@Order(1)
public class TokenRelayFilter extends OncePerRequestFilter {
public static final String RELAYED_TOKEN_ATTRIBUTE = "RELAYED_AUTH_TOKEN";
@Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response,
FilterChain filterChain) throws ServletException, IOException {
String authHeader = request.getHeader("Authorization");
if (authHeader != null && authHeader.startsWith("Bearer ")) {
String token = authHeader.substring(7).trim();
request.setAttribute(RELAYED_TOKEN_ATTRIBUTE, token);
response.setHeader("X-Gateway-Identity-Relayed", "true");
}
filterChain.doFilter(request, response);
}
}
Step 4: Establish Fallback and Route Administration
We create a dedicated REST controller that serves fallback responses when remote services degrade:
package com.webcodein.workshop.gateway.controller;
import com.webcodein.workshop.gateway.model.FallbackResponse;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.time.Instant;
@RestController
@RequestMapping("/fallback")
public class FallbackController {
@GetMapping("/{routeId}")
public ResponseEntity<FallbackResponse> getFallback(@PathVariable String routeId) {
FallbackResponse fallback = new FallbackResponse(
routeId,
"Downstream service is temporarily unavailable. Returning cached fallback response.",
"SERVICE_UNAVAILABLE_FALLBACK",
true,
Instant.now()
);
return ResponseEntity.ok()
.header("X-Gateway-Degraded", "true")
.body(fallback);
}
}
Comparative Architecture Matrix
The table below provides a comprehensive architectural comparison between Spring Cloud Gateway Server MVC on Java 25 Virtual Threads and traditional Spring Cloud Gateway Reactive on WebFlux and Netty.
| Architectural Dimension | Gateway Server MVC (Virtual Threads) | Gateway Reactive (WebFlux & Netty) |
|---|---|---|
| Concurrency Model | Java 25 Virtual Threads (Loom). Millions of concurrent threads. | Netty EventLoop thread pool. Fixed core sizing. |
| Programming Paradigm | Imperative and synchronous. Standard Java control flow. | Reactive Streams specification. Mono and Flux operators. |
| Debugging Experience | Crystal-clear stack traces with exact file and line numbers. | Complex reactive assembly traces with deferred execution. |
| Context Propagation | Java 25 ScopedValue and ThreadLocal work natively. | Requires Project Reactor Context subscriber propagation. |
| Ecosystem Compatibility | Works natively with all standard JDBC, JPA, and third-party libraries. | Requires non-blocking drivers (R2DBC) or risks freezing EventLoop. |
| Operational Maintenance | Extremely low. Any Java developer can read and maintain routes. | High. Requires specialized reactive engineering skills. |
Real-World Enterprise Case Studies
To understand the practical impact of modern gateway architectures, let us review three real-world production case studies from global enterprise systems.
Case Study 1: FinTech Core Banking Ingress Migration
A global financial institution processed over forty thousand payment requests per second across sixty internal microservices. Their legacy gateway was built on Spring Cloud Gateway Reactive. When third-party banking APIs experienced latency spikes, the Netty event loops became backed up with queued reactive operations. Memory pressure triggered aggressive garbage collection, leading to thirty-second transaction timeouts.
The engineering team migrated the gateway to Spring Cloud Gateway Server MVC running on Java 25 and Spring Boot 4. They enabled virtual threads and replaced complex reactive filters with standard servlet filters. During the next payment traffic surge, the gateway maintained an average p99 latency of twelve milliseconds. Memory usage dropped by forty-two percent, and zero connection dropouts were recorded.
Case Study 2: Global Video Streaming Platform Token Propagation
A multinational video streaming service managed eighty million active subscribers. Their mobile clients communicated with a distributed gateway layer that enforced subscriber tier permissions. Under their previous architecture, passing security tokens across asynchronous thread boundaries frequently lost thread context, causing spurious HTTP 401 Unauthorized errors during peak evening streaming hours.
The team adopted Java 25 `ScopedValue` bindings combined with a high-performance TokenRelayFilter in Spring Cloud Gateway MVC. Because virtual threads execute the entire request lifecycle synchronously, the security context remained permanently attached to the execution frame. Spurious authentication errors dropped to absolute zero, improving subscriber streaming reliability worldwide.
Case Study 3: Healthcare Network Multi-Tenant Route Isolation
A national health insurance provider operated a health portal connecting hospitals, pharmacies, and patients. Strict privacy regulations required complete tenant data isolation. When the pharmacy partner experienced an outage, requests queued up and consumed all shared gateway threads, preventing emergency hospital admissions from submitting claims.
The provider deployed Resilience4j circuit breakers and route-specific fallback endpoints in their Spring Boot 4 gateway. When pharmacy APIs degraded, the gateway immediately tripped the circuit breaker. It served cached formularies with an `X-Gateway-Degraded` header while leaving hospital claim routes completely unhindered. This architectural fault isolation ensured one hundred percent uptime for critical healthcare workflows.
Production Anti-Patterns and Best Practices
When implementing API gateways in production environments, developers frequently encounter architectural pitfalls. Below are four common anti-patterns and how to avoid them.
Anti-Pattern 1: Blocking the Carrier Thread with Native Synchronized Blocks
In early virtual thread previews, executing a blocking call inside a Java `synchronized` block pinned the virtual thread to its underlying operating system carrier thread. This prevented the carrier thread from serving other virtual threads.
The Fix: In Java 25, the JVM has eliminated most monitor pinning issues. However, best practice still recommends using `java.util.concurrent.locks.ReentrantLock` for fine-grained concurrency control when building custom gateway filters:
// ANTI-PATTERN: Heavy synchronization pins carrier threads in older runtimes
public synchronized void updateRouteMetrics(String routeId) {
// blocking network or database call
}
// MODERN BEST PRACTICE: Use ReentrantLock or Concurrent Collections
private final ReentrantLock lock = new ReentrantLock();
public void updateRouteMetrics(String routeId) {
lock.lock();
try {
metricsMap.compute(routeId, (k, v) -> v == null ? 1 : v + 1);
} finally {
lock.unlock();
}
}
Anti-Pattern 2: Mixing WebFlux and WebMvc Starters in the Same Project
Developers transitioning to Gateway MVC sometimes leave `spring-boot-starter-webflux` on their Maven classpath while adding `spring-boot-starter-web`. When both starters exist, Spring Boot’s auto-configuration encounters conflicting servlet and reactive server factories, leading to unpredictable startup failures.
The Fix: Explicitly use `spring-boot-starter-web` (or `spring-cloud-starter-gateway-server-mvc`) and exclude all Netty and WebFlux dependencies from your POM file:
<!-- CORRECT DEPENDENCY CONFIGURATION -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Ensure spring-boot-starter-webflux is NOT present -->
Anti-Pattern 3: Forwarding Untrusted Ingress Headers to Downstream Services
External HTTP clients can inject arbitrary HTTP headers, such as `X-Forwarded-For`, `X-User-Role: ADMIN`, or `X-Tenant-Id`. If the gateway naively copies all incoming headers downstream, attackers can bypass security checks in internal microservices.
The Fix: Implement a strict header sanitization filter. Strip all internal headers from the incoming request before re-populating them with cryptographically verified claims from the verified JWT:
// Strip unsafe incoming client headers before proxying
HttpServletRequest sanitized = new HeaderSanitizingWrapper(request, List.of("X-User-Role", "X-Tenant-Id"));
filterChain.doFilter(sanitized, response);
Anti-Pattern 4: Failing to Configure Request Timeouts on Forwarded Calls
Even though virtual threads are lightweight, an infinite or ten-minute HTTP socket timeout on downstream calls can accumulate millions of parked virtual threads. Eventually, memory heap space will be exhausted.
The Fix: Always configure explicit connection and read timeouts on the gateway’s HTTP client. Set connection timeouts to one second and read timeouts to five seconds maximum.
Automated Testing with MockMvc and ArchUnit
A production gateway requires thorough automated testing. We verify HTTP routing logic with MockMvc and enforce strict structural boundaries using ArchUnit 1.4.1.
Our workshop test suite verifies that routes return expected JSON payloads and HTTP status codes:
package com.webcodein.workshop.gateway.controller;
import com.webcodein.workshop.gateway.service.RouteRegistryService;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
class GatewayAdminControllerTest {
private MockMvc mockMvc;
@BeforeEach
void setUp() {
RouteRegistryService routeService = new RouteRegistryService();
GatewayAdminController controller = new GatewayAdminController(routeService);
mockMvc = MockMvcBuilders.standaloneSetup(controller).build();
}
@Test
@DisplayName("GET /admin/gateway/stats should return gateway stats")
void shouldReturnGatewayStats() throws Exception {
mockMvc.perform(get("/admin/gateway/stats"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.status").value("UP"))
.andExpect(jsonPath("$.activeRouteCount").value(3))
.andExpect(jsonPath("$.virtualThreadsEnabled").value(true));
}
}
We also enforce architectural fitness rules using ArchUnit. We mandate that all controllers reside in the `controller` package, all services reside in the `service` package, all filters extend `OncePerRequestFilter`, and all domain models are immutable Java records:
package com.webcodein.workshop.gateway.architecture;
import com.tngtech.archunit.core.importer.ImportOption;
import com.tngtech.archunit.junit.AnalyzeClasses;
import com.tngtech.archunit.junit.ArchTest;
import com.tngtech.archunit.lang.ArchRule;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.filter.OncePerRequestFilter;
import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.classes;
@AnalyzeClasses(packages = "com.webcodein.workshop.gateway", importOptions = ImportOption.DoNotIncludeTests.class)
public class GatewayArchitectureTest {
@ArchTest
static final ArchRule controllers_must_reside_in_controller_package =
classes().that().haveSimpleNameEndingWith("Controller")
.should().resideInAPackage("..controller..")
.andShould().beAnnotatedWith(RestController.class);
@ArchTest
static final ArchRule services_must_reside_in_service_package =
classes().that().haveSimpleNameEndingWith("Service")
.should().resideInAPackage("..service..")
.andShould().beAnnotatedWith(Service.class);
@ArchTest
static final ArchRule filters_must_reside_in_filter_package =
classes().that().haveSimpleNameEndingWith("Filter")
.should().resideInAPackage("..filter..")
.andShould().beAssignableTo(OncePerRequestFilter.class);
@ArchTest
static final ArchRule models_must_reside_in_model_package =
classes().that().resideInAPackage("..model..")
.should().beRecords();
}
Companion Workshop Repository
The complete, compilable source code for this tutorial is freely available on GitHub. It includes the complete Spring Boot 4 application, Docker container configurations, and the full nineteen-test suite:
GitHub: Spring Cloud Gateway Server MVC Workshop Code
Clone the repository and run all tests locally using your Java 25 JDK:
git clone https://github.com/salahtobok/learn.webcodein.workshop.git
cd learn.webcodein.workshop/architecture/spring-cloud-gateway-spring-boot-4-virtual-threads
./mvnw clean test
Frequently Asked Questions
What is Spring Cloud Gateway Server MVC?
Spring Cloud Gateway Server MVC is a modern API gateway framework built directly on the Spring MVC servlet container rather than reactive WebFlux and Netty. When combined with Java 25 Virtual Threads, it delivers high-throughput non-blocking performance while retaining simple imperative Java programming, easy debugging, and full compatibility with the existing servlet ecosystem.
Should I migrate my existing WebFlux gateway to Gateway Server MVC?
Yes, if your engineering team struggles with reactive debugging, complex Mono and Flux operator chains, or tricky context propagation bugs. Gateway Server MVC on Java 25 virtual threads provides identical throughput with drastically lower maintenance costs and crystal-clear stack traces.
How does Token Relay work in Spring Cloud Gateway MVC?
The gateway intercepts incoming HTTP requests, validates the OAuth 2.0 Bearer JWT token signature, extracts user claims, and propagates the Authorization header downstream to internal microservices. Using Java 25 Scoped Values, identity context remains safely attached across the synchronous execution scope without memory leaks.
Does Spring Cloud Gateway Server MVC support distributed rate limiting?
Yes. You can implement distributed rate limiting using Redis and the Token Bucket algorithm. Gateway filters execute synchronous queries against Redis to evaluate available token quotas and return HTTP 429 Too Many Requests status codes when limits are exceeded.
Can I use Resilience4j circuit breakers with Gateway MVC?
Yes. Resilience4j integrates seamlessly with synchronous servlet filters. When downstream services fail or time out, the circuit breaker opens and immediately redirects client traffic to a designated fallback endpoint that returns cached or degraded responses.
