What is Spring Cloud Gateway Server MVC, and how does it differ from the reactive Gateway?
answer
- Servlet stack, thread-per-request
- RouterFunctions + predicates + HandlerFilterFunctions
- http() proxies via blocking RestClient
- Sibling of reactive Netty gateway
- Pairs well with virtual threads
basics
~10 sIt is the API gateway built on blocking Spring MVC (servlet stack) instead of reactive WebFlux/Netty. It runs on a servlet container with thread-per-request and proxies calls using a blocking RestClient.
solid answer
~40 sSpring Cloud Gateway Server MVC is the servlet-based flavor of Spring Cloud Gateway. Instead of running on WebFlux/Netty with a reactive, event-loop model, it runs on the Spring MVC servlet stack (Tomcat/Jetty) with the classic thread-per-request model. You define routes with the functional web MVC DSL (RouterFunctions), predicates decide which requests match, HandlerFilterFunctions transform request/response, and the terminal handler proxies upstream using a blocking RestClient. You choose it when your app and libraries are blocking, when you want to reuse the servlet/MVC ecosystem (servlet filters, Spring MVC debugging, blocking security), or you don't want to adopt reactive programming. Functionally it offers the same gateway concept (routing, predicates, filters) but with a synchronous programming model; the reactive gateway is a separate sibling module for non-blocking, high-concurrency workloads.
code
java · 18 linesimport org.springframework.cloud.gateway.server.mvc.handler.GatewayRouterFunctions;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.function.RouterFunction;
import org.springframework.web.servlet.function.ServerResponse;
import static org.springframework.cloud.gateway.server.mvc.handler.HandlerFunctions.http;
import static org.springframework.cloud.gateway.server.mvc.predicate.GatewayRequestPredicates.path;
@Configuration
class GatewayRoutes {
@Bean
RouterFunction<ServerResponse> ordersRoute() {
return GatewayRouterFunctions.route("orders")
.route(path("/orders/**"), http("https://orders-service:8080"))
.build();
}
}go deeper
Know it's the blocking/servlet version of the gateway, contrasted with reactive Netty.
Should name RouterFunctions, predicates, filters, and the RestClient proxy.
Explains threading tradeoffs and when to pick MVC vs reactive.
Frames it as an architectural choice tied to blocking ecosystems and virtual threads; knows the modules are mutually exclusive.
**What it is.** Spring Cloud Gateway is an API gateway: a service that sits in front of your microservices and routes/filters/transforms incoming HTTP requests. Historically Gateway was built only on **Spring WebFlux** (reactive, non-blocking, runs on Netty with an event loop). **Gateway Server MVC** is a second implementation built on the **Spring MVC servlet stack** — it runs inside a servlet container (Tomcat by default, or Jetty/Undertow) using the traditional **thread-per-request** model where each in-flight request occupies a platform thread until it completes. **Why two variants.** The reactive gateway is excellent for very high concurrency because a few event-loop threads handle thousands of connections, but it forces the whole request path to be non-blocking (reactive types, no blocking calls). Many teams have blocking codebases, blocking security, blocking libraries, or simply don't want reactive programming's complexity. Gateway Server MVC gives them the same gateway feature set on a familiar synchronous model. **How it is wired.** You add the starter `spring-cloud-starter-gateway-server-webmvc` (older coordinates: `spring-cloud-starter-gateway-mvc`). It is **not** interchangeable with the reactive starter — mixing them in one app is unsupported; pick one. Routes are expressed either in configuration (`spring.cloud.gateway.mvc.routes` / newer `spring.cloud.gateway.server.webmvc.routes`) or, more idiomatically, as `RouterFunction<ServerResponse>` beans built with `GatewayRouterFunctions.route(id)`. **Core building blocks:** - **Predicates** — `org.springframework.cloud.gateway.server.mvc.predicate.GatewayRequestPredicates` (e.g. `path()`, `host()`, `method()`, `header()`). They are `RequestPredicate`s from Spring MVC's functional web (`org.springframework.web.servlet.function`). A request must match to select the route. - **Handler** — the terminal that actually forwards the request. `org.springframework.cloud.gateway.server.mvc.handler.HandlerFunctions.http(...)` returns a `HandlerFunction<ServerResponse>` that proxies to the upstream URI using a **blocking RestClient**. - **Filters** — `HandlerFilterFunction<ServerResponse, ServerResponse>` applied `.before(...)`/`.after(...)`/`.filter(...)`, from factory classes like `BeforeFilterFunctions`, `AfterFilterFunctions`, `CircuitBreakerFilterFunctions`, `LoadBalancerFilterFunctions`. **Key differences vs reactive gateway (memorize):** 1. Runtime: servlet container + thread-per-request vs Netty + event loop. 2. Programming model: blocking/synchronous vs reactive `Mono`/`Flux`. 3. Proxy client: blocking `RestClient` vs reactive `WebClient`. 4. Web layer: Spring MVC functional (`org.springframework.web.servlet.function`) vs WebFlux functional (`org.springframework.web.reactive.function`). 5. Ecosystem reuse: servlet filters, `HandlerInterceptor`, blocking Spring Security vs their reactive equivalents. **Gotchas.** Under load the thread-per-request model can exhaust the thread pool while threads block on slow upstreams; pairing it with **virtual threads** (`spring.threads.virtual.enabled=true` on Java 21+) largely removes that cost. Don't try to add reactive filters or `WebFilter`s — the MVC variant uses servlet-world types. **When to use.** Choose MVC when your stack is blocking, you want MVC/servlet familiarity, or you run virtual threads; choose reactive when you need maximum connection scalability with a non-blocking pipeline end to end.
- Can you run the reactive and MVC gateways in the same application?No. They are separate starters with different (WebFlux vs servlet) web stacks; you pick one per application. Mixing them is unsupported and the wrong functional web types won't line up.
- What runtime concern does the MVC gateway have that the reactive one avoids, and how do you mitigate it?Thread-per-request means blocked upstream calls tie up platform threads, risking pool exhaustion under high concurrency. Mitigate with a larger pool, timeouts/circuit breakers, or (best) virtual threads via spring.threads.virtual.enabled=true on Java 21+.