Developer Playground

Spring Actuator Port Separation: MVC and WebFlux Have Different Levels of "Isolation"

Updated: January 27, 2026

The Problem

While operating Spring Cloud Gateway (SCG) in an EKS environment, I separated the Actuator port (management.server.port) for stability. I naturally assumed that "even if the main service crashes due to traffic overload, the health check would still be alive" - just like in the Spring MVC (Tomcat) days.

But then a question arose:

"Netty is based on Event Loops - does just changing the port actually separate the threads?"

Starting from this question, I dug into the thread model differences between MVC and WebFlux, and what port separation means in each environment.

MVC vs WebFlux: Fundamental Thread Model Differences

The bottom line is: "Spring Context and server instances are separated, but in WebFlux, threads (Event Loops) are shared."

Characteristic MVC (Tomcat) WebFlux (Netty)
Thread Model Thread-per-Request Event Loop
On Port Separation Separate Tomcat Connector + Thread Pool Separate HttpServer + Same Event Loop
Thread-Level Isolation Yes (Possible) No (Not Possible)
Context/Instance Isolation Yes Yes

MVC (Tomcat): Thread-per-Request and Thread-Level Isolation

Spring MVC uses Tomcat as its default embedded server and follows the Thread-per-Request model. Each request is assigned a thread from the thread pool for processing.

Effect of Port Separation in MVC

  • Separate Connector Created: Setting management.server.port creates a separate Tomcat Connector.
  • Separate Thread Pool Allocated: Each Connector gets an independent thread pool (Executor).
  • Real Isolation: Even if all 200 threads on the main port (8080) are exhausted, the management port (9090)'s thread pool remains available, so health checks respond normally.

MVC Actuator Port Separation Configuration (application.yml)

server:
  port: 8080  # Main application port

management:
  server:
    port: 9090  # Actuator dedicated port
  endpoints:
    web:
      exposure:
        include: health,info,metrics

With this configuration, MVC environments achieve complete thread-level isolation. Main service load does not affect Actuator responses.

WebFlux (Netty): Event Loop and Global Resource Sharing

Spring WebFlux uses Netty as its default server and follows the Event Loop model. It handles many concurrent connections with a small number of threads (usually equal to CPU cores).

Key Point

When you separate ports in WebFlux, a separate HttpServer object is created and a separate Child ApplicationContext is spawned. However, Netty shares the Global Event Loop (Worker Threads) through ReactorResourceFactory for resource efficiency.

How Port Separation Actually Works in WebFlux

  • Separate HttpServer: A separate Netty HttpServer instance is created for each port.
  • Separate ApplicationContext: A Child Context is created for the management port.
  • Same Event Loop Shared: But the Worker Threads (Event Loops) that actually process requests are shared!

Result: If a blocking call accidentally occurs in the main logic and blocks the Event Loop, the Actuator (9090) using the same Event Loop also freezes.

Event Loop Sharing Structure in WebFlux

Netty Server Context - Event Loop Sharing

⚠️ Both ports share the same Event Loop!

Analogy: Same Kitchen, Different Entrances

The WebFlux situation becomes clearer with a restaurant analogy.

MVC (Tomcat) - Complete Separation

[Front Door (8080)] → [Kitchen A] ← Chef Team A
[Back Door (9090)] → [Kitchen B] ← Chef Team B

→ Completely separate kitchens with independent chefs

WebFlux (Netty) - Only Entrances Separated

[Front Door (8080)] ─┬→ [Same Kitchen] ← Same Chefs
[Back Door (9090)] ─┘

→ Different entrances, but same kitchen with same chefs

The Result: If there are too many customers at the front door and the chefs (Threads) are overwhelmed, the staff meal request (Health Check) coming through the back door also cannot be processed.

The Real Value of Port Separation in WebFlux

So is port separation meaningless in WebFlux? No. It's still essential. (Especially in EKS environments)

Even without performance isolation, the benefits in 'security' and 'operations' are clear.

Real Benefits of Port Separation

  • Security Breach Prevention:

    Sensitive endpoints like /actuator/env and /actuator/heapdump can be blocked from external load balancer exposure at the network level (K8s Service).

  • Clear Probe Configuration:

    K8s Liveness Probe targets 9090, actual traffic goes to 8080, cleanly separating traffic paths.

  • Simplified Firewall Rules:

    NetworkPolicy configuration is easier when the Actuator port is only accessible within the cluster.

Isolation Type MVC WebFlux
Thread/Performance Isolation Yes No
Network/Security Isolation Yes Yes
Context/Instance Isolation Yes Yes

The Right Approach: Eliminating Blocking Code with BlockHound

To prevent Gateway freezes in WebFlux, instead of relying on port separation, the right approach is to eliminate blocking code in the main logic.

BlockHound is a library created by the Project Reactor team that detects blocking code running on Event Loop threads at runtime.

Adding BlockHound Dependency (build.gradle.kts)

dependencies {
    // Use only in test environment
    testImplementation("io.projectreactor.tools:blockhound:1.0.9.RELEASE")
}

BlockHound Configuration (Test Code)

import reactor.blockhound.BlockHound
import org.junit.jupiter.api.BeforeAll

class GatewayBlockingDetectionTest {

    companion object {
        @JvmStatic
        @BeforeAll
        fun setUp() {
            BlockHound.install()
        }
    }

    @Test
    fun `should detect blocking call`() {
        // BlockingOperationError is thrown if blocking code exists
        Mono.fromCallable {
            Thread.sleep(100)  // This code gets detected!
        }.subscribeOn(Schedulers.parallel())
         .block()
    }
}

Common Blocking Patterns Detected by BlockHound

  • Thread.sleep()
  • JDBC driver calls (when using JDBC instead of R2DBC)
  • Synchronous I/O with InputStream/OutputStream
  • Waiting in synchronized blocks
  • Lock waiting with ReentrantLock, etc.

Note: BlockHound has performance overhead, so it should only be used in test/development environments, not production.

Conclusion and Recommendations

Key Takeaway

Port separation in WebFlux is for 'security/management isolation', not 'performance isolation'.

Recommendations

  • 1
    When using MVC: Port separation provides real thread isolation. Use it with confidence.
  • 2
    When using WebFlux: Use port separation only for security/management purposes. Don't expect thread isolation.
  • 3
    The right approach for WebFlux: Detect and eliminate blocking code with BlockHound.
  • 4
    Monitoring Strategy: Monitor Actuator endpoint response times separately for early detection of Event Loop blocking.

One-Liner for Interviews/Tech Sharing

"Separating Actuator ports creates separate Netty Server Contexts, but Event Loop Resources are shared."


Advertisement