Spring Cloud Ecosystem: Component Map, Version Traps & Request Flow

This article provides a comprehensive overview of the Spring Cloud ecosystem, detailing its six-layer component architecture, strict version compatibility rules, end-to-end request flow, core abstraction interfaces, recommended project structure, common configuration pitfalls, and a structured learning path with interview questions.

Architecture Pony
Architecture Pony
Architecture Pony
Spring Cloud Ecosystem: Component Map, Version Traps & Request Flow

Two Classic Incidents

Incident 1: Version Hell. A team upgraded Spring Boot to 3.2 while Spring Cloud remained on 2021.0.x, causing NoSuchMethodError at startup. The root cause: Spring Cloud versions are strictly bound to Spring Boot versions; the auto-configuration contracts between the two frameworks had changed.

Incident 2: Gateway Blamed for Everything. Frequent 502 errors led the team to suspect the gateway. The actual cause was a downstream service registered in a different namespace, so the gateway could not find instances by service name. Any misalignment among registry, namespace, or gateway routing manifests as "service unavailable".

Common Pattern: Microservice failures are amplified — a single configuration error can surface at any hop, while the symptom is often far from the root cause. Therefore, this series starts by establishing two maps: a component panorama (who does what) and a version matrix (what works with what).

Technical Analysis: What Microservices Solve

Benefits and Costs of Splitting

Benefits: Independent deployment (change order without redeploying product), independent scaling (scale only order and inventory for flash sales), fault isolation (product service down doesn't affect ordering), technology heterogeneity, clear team boundaries.

Costs: Unreliable network (timeouts, retries, idempotency), data consistency (see Seata), longer chains hard to debug, distributed transactions and locks, operational complexity grows exponentially.

Conclusion: Microservices are not a "more advanced architecture" but a trade of operational complexity for organizational and business complexity . For teams under 20 people or businesses without independent scaling needs, a modular monolith is superior. Spring Cloud merely packages the problems that must be solved after splitting into components.

Component Panorama: Six Layers

Access Layer: Spring Cloud Gateway (routing, filtering, rate limiting, auth entry).

Registry & Config Layer: Nacos (registry + config), Eureka, Consul, Config Server.

Invocation Layer: OpenFeign (declarative HTTP), Spring Cloud LoadBalancer (client-side load balancing), RestTemplate/RestClient.

Fault Tolerance Layer: Sentinel (rate limiting, circuit breaking, degradation, hot params), Resilience4j (official Hystrix replacement).

Observability Layer: Micrometer + Micrometer Tracing (replaces Sleuth from Boot 3), SkyWalking, Prometheus + Grafana.

Messaging & Transaction Layer: Spring Cloud Stream, RocketMQ/RabbitMQ, Seata (AT/TCC).

Key Insight: Spring Cloud implements almost no capabilities itself; it is a layer of abstraction and auto-configuration . DiscoveryClient , LoadBalancerClient , CircuitBreaker are interfaces — Nacos, LoadBalancer, Sentinel provide the implementations. Understanding the abstraction layer makes swapping implementations painless.

Netflix Retirement and Migration

Eureka 2.0 stopped, Ribbon entered maintenance, Hystrix development halted, Zuul 1 hit performance bottlenecks. The replacement path is clear:

// Netflix OSS -> Modern Replacement
Eureka   -> Nacos / Consul
Ribbon   -> Spring Cloud LoadBalancer
Hystrix  -> Sentinel / Resilience4j
Zuul 1   -> Spring Cloud Gateway (WebFlux)
Sleuth   -> Micrometer Tracing

Core Constraint: Version Matrix Is the Foundation

Spring Cloud uses city names for versions (2020.0 Ilford, 2021.0 Jubilee, 2022.0 Kilburn, 2023.0 Leyton, 2024.0 Moorgate). Each version is compatible only with specific Spring Boot major versions. The official compatibility matrix must be consulted; selection order: Boot -> Cloud -> Alibaba .

// Boot -> Cloud Mapping
3.2.x -> 2023.0.x
3.3.x -> 2023.0.x
3.4.x -> 2024.0.x
3.5.x -> 2024.0.x

// Spring Cloud Alibaba
2023.0.1.0 -> Boot 3.2.x
2023.0.3.2 -> Boot 3.3/3.4
2022.0.0.0 -> Boot 3.0/3.1

Three Iron Laws: ① Never write Spring Cloud component versions manually; only import BOMs. ② Alibaba version must align with Cloud major version (2023 with 2023). ③ Upgrade Boot / Cloud / Alibaba together; upgrading one alone guarantees breakage.

Correct Parent POM

<properties>
<boot.version>3.2.5</boot.version>
<cloud.version>2023.0.1</cloud.version>
<alibaba.version>2023.0.1.0</alibaba.version>
</properties>

<dependencyManagement>
<dependencies>
<!-- Three BOMs in order -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!-- cloud-dependencies / alibaba same structure, omitted -->
</dependencies>
</dependencyManagement>

Import order matters: Boot -> Cloud -> Alibaba . Later BOMs override earlier ones. Reversing the order causes Alibaba's BOM to be overwritten by Cloud's, leading to issues like "Nacos client version newer than server".

Core Flow: One Request Across Microservices

Hop 1: Gateway. External request hits Gateway:8080, matches route by Path=/api/order/**, strips prefix, Sentinel filter chain rate-limits, forwards to service name mall-order.

Hop 2: Load Balancing. Gateway fetches mall-order instances from Nacos (local cache + push subscription), LoadBalancer round-robins an instance, sends real HTTP request.

Hop 3: Service Internal. Order service executes business; needs to deduct inventory via Feign client, again "fetch instance -> select instance -> send request"; XID / TraceId / user context propagated via headers.

Hop 4: Fault Tolerance. Inventory service times out or throws exception; Sentinel triggers circuit breaker per rules, Feign fallback returns fallback result, preventing cascade failure.

Hop 5: Observability. Every hop records a span; TraceId stitches full chain; metrics to Prometheus, logs to ELK.

Return Path: Response travels back up call stack; gateway wraps response uniformly, records latency; exceptions converted to standard error codes by global handler (see validation article).

Core Source Code: Four Abstract Interfaces

Auto-Configuration: Boot 3 Entry Point Changed

// Boot 2.x: META-INF/spring.factories
// Boot 3.x: New auto-configuration manifest
// META-INF/spring/
// org.springframework.boot
// .autoconfigure.AutoConfiguration
// .imports
// Spring Cloud components register via this file
// Troubleshoot "auto-config not effective" by checking
// if this file exists in classpath

Service Discovery: DiscoveryClient

public interface DiscoveryClient {
// Get instance list by service name
List<ServiceInstance>
getInstances(String serviceId);
}
// Nacos: NacosDiscoveryClient
// Internally uses NamingService
// .selectInstances(...)
// Local ServiceInstanceListSupplier
// Cache + push subscription, avoid
// requesting registry on every call

Load Balancing: RoundRobinLoadBalancer

// Reactor choose implementation
return serviceInstances.get()
.map(list -> {
if (list.isEmpty()) {
return new EmptyResponse();
}
// Atomic increment modulo -> round robin
int i = Math.abs(
pos.incrementAndGet()
% list.size());
return new DefaultResponse(
list.get(i));
});

Note: pos.incrementAndGet() is a globally shared atomic variable — all requests in the same process share one round-robin sequence, naturally uniform. To switch to weighted or response-time-based balancing, replace the LoadBalancer implementation; callers remain unaware.

Declarative Invocation: Feign's Two-Layer Proxy

@FeignClient(name = "mall-stock",
path = "/stock",
fallbackFactory = StockFallback.class)
public interface StockClient {
@PostMapping("/deduct")
Result<Void> deduct(
@RequestBody DeductDTO dto);
}

// FeignClientFactoryBean does:
// 1. Create child container per service name (isolate config)
// 2. Contract parses annotations -> MethodMetadata
// 3. ReflectiveFeign.newInstance -> JDK dynamic proxy
// 4. Invocation -> RequestTemplate -> Client sends request
//    // Default HttpURLConnection, production swap
//    OkHttp / Apache HttpClient
Child Container Mechanism (NamedContextFactory): Each @FeignClient gets an independent Spring child container; Encoder/Decoder/Interceptor are isolated. But this introduces a pitfall — child containers do not inherit parent beans by default . A RequestInterceptor defined in the main container will not take effect; it must be declared in a class referenced by the configuration attribute.

Engineering Skeleton: Module Structure

mall/
mall-common/  # Response wrapper / exceptions / utils
mall-api/     # Feign interfaces + DTOs
mall-gateway/ # :8080 routing & auth
mall-order/   # :8081 order
mall-stock/   # :8082 inventory
mall-product/ # :8083 product

Why separate mall-api? Feign interfaces are contracts depended on by multiple services. Placing them in the provider module creates reverse dependency (order depends on stock's implementation jar); putting them in common makes common bloat endlessly. A dedicated api layer holds only interfaces and DTOs — anyone can depend without implementation coupling.

Minimum Runnable Configuration

spring:
application:
name: mall-order
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
namespace: dev
config:
server-addr: 127.0.0.1:8848
file-extension: yaml
namespace: dev
config:
import: optional:nacos:mall-order.yaml

Three confusing points: ① namespace must be the ID (UUID from console), not the display name; ② config.import is Boot 2.4+ mechanism — without it config center won't activate; ③ file-extension must match the DataId suffix in Nacos, otherwise config won't be fetched.

Eight High-Frequency Pitfalls

Version Misalignment. Boot 3.2 with Cloud 2021 -> immediate NoSuchMethodError. Check official compatibility matrix first.

Manual Component Versions. Explicitly declaring nacos-client version conflicts with BOM; Maven's nearest-wins strategy often picks the wrong one.

Namespace as Name. Nacos expects namespace ID; writing the name equals no namespace, service registers to public.

Config Center Ineffective. Missing spring.config.import or DataId suffix mismatch with file-extension.

Feign First Call Timeout. Lazy loading + default 1s timeout -> cold start first call always times out. Warm up or increase connectTimeout.

Interceptor Not Working. Feign child container doesn't inherit parent beans; RequestInterceptor must be in configuration class.

Gateway Route & Controller Path Duplication. Gateway strips prefix then forwards; controller also has full path -> becomes /api/api/order.

Circular Service Calls. A calls B, B calls A; with retries causes avalanche. Decouple via events/messaging (see event & MQ articles).

Troubleshooting Three Steps

Check Nacos console service list — instances present? healthy? namespace/group correct? Half the issues end here.

Enable Feign logging ( loggerLevel: FULL) and Gateway GlobalFilter logs to see which hop the request reaches.

Use TraceId to stitch full chain (SkyWalking / Micrometer Tracing) to pinpoint timeout, circuit break, or 5xx.

Learning Path and Interview Questions

Four-Phase Roadmap

Beginner (1 week): Spin up Nacos + two services + gateway; complete minimal loop "Gateway -> Service A -> Feign calls Service B".

Intermediate (2 weeks): Master version matrix, BOM imports, config center import mechanism; write custom LoadBalancer and RequestInterceptor.

Advanced (1 month): Read DiscoveryClient, LoadBalancer, Feign proxy, NamedContextFactory source code; understand registry push/pull model.

Practice (ongoing): Perform a splitting exercise on existing monolith (cut 3 services by business domain + gateway), equip with timeout/retry/circuit breaker/distributed tracing quartet.

Twelve Interview Questions

Q1: Microservices benefits and costs? When not to split?

Answer: Benefits: independent deployment, scaling, fault isolation, clear team boundaries. Costs: unreliable network (timeout/retry/idempotency), distributed transactions, longer chains hard to debug, operational cost up. Teams under 20 people or no independent scaling demand -> modular monolith is better. Microservices trade operational complexity for organizational complexity.

Q2: Spring Cloud and Spring Boot version mapping?

Answer: Cloud uses city names, strictly bound to Boot major versions: Boot 3.2/3.3 -> Cloud 2023.0.x; Boot 3.4/3.5 -> 2024.0.x; Cloud Alibaba 2023.0.1.0 -> Boot 3.2.x. Rely on official compatibility matrix, not memory.

Q3: Why only import BOM, not write component versions?

Answer: BOM locks the entire compatible component set at once, preventing version drift and transitive dependency conflicts. Manual versions get picked by Maven's nearest-wins, often not the expected version, resulting in NoSuchMethodError or class not found.

Q4: Six layers and their components?

Answer: Access: Gateway; Registry/Config: Nacos; Invocation: OpenFeign + Spring Cloud LoadBalancer; Fault Tolerance: Sentinel / Resilience4j; Observability: Micrometer Tracing + SkyWalking; Messaging/Transaction: Spring Cloud Stream + Seata.

Q5: Netflix replacements?

Answer: Eureka -> Nacos / Consul; Ribbon -> Spring Cloud LoadBalancer; Hystrix -> Sentinel (stronger rate limiting) or Resilience4j (official recommendation); Zuul 1 -> Spring Cloud Gateway (WebFlux, non-blocking).

Q6: Registry vs Gateway responsibilities? Why gateway shouldn't connect to DB?

Answer: Registry stores instances and metadata, provides service discovery. Gateway handles routing, filtering, rate limiting, auth, protocol conversion. Gateway connecting to DB couples access layer to data model, loses scaling and security boundaries, pushes business logic into infrastructure.

Q7: Request hops from gateway to downstream?

Answer: Gateway matches route and runs filter chain (auth/rate limit) -> LoadBalancer picks instance from local cache -> Target service processes business -> Feign again "fetch instance -> pick instance -> send request" -> Fault tolerance components apply circuit breaker/fallback -> Every hop records Span, TraceId stitches full chain.

Q8: Does DiscoveryClient hit registry every call?

Answer: No. First full pull cached locally; thereafter incremental updates via push subscription or long polling (Nacos uses UDP push + periodic pull fallback). Business calls read only local cache, so brief registry unavailability doesn't affect existing calls.

Q9: LoadBalancer round-robin implementation? How to extend to weighted?

Answer: RoundRobinLoadBalancer uses a global AtomicInteger increment modulo instance count, naturally uniform. Weighted can switch to WeightedRoundRobin or custom ReactorServiceInstanceLoadBalancer selecting by weight, response time, or zone affinity; callers unchanged.

Q10: Feign proxy generation? Why configuration attribute needed?

Answer: FeignClientFactoryBean creates child container per client, Contract parses method annotations to MethodMetadata , ReflectiveFeign builds JDK dynamic proxy, invocation becomes RequestTemplate handed to Client . Child container doesn't inherit parent beans, so RequestInterceptor , Decoder must be declared in configuration class to take effect.

Q11: Boot 3 auto-configuration manifest file?

Answer: META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports , replacing Boot 2.x's spring.factories . When troubleshooting "auto-config not effective", first verify this file exists in classpath and class isn't excluded by conditional annotations.

Q12: Service circular dependency consequences? Solution?

Answer: A calls B, B calls A creates infinite recursion; combined with retries triggers avalanche, also couples deployment order. Solutions: extract common logic to separate service or SDK, use events/messaging for async decoupling, merge service boundaries if necessary — don't mask by increasing timeouts.

One-Page Cheat Sheet

Six Layers: Gateway / Registry-Config / Invocation
Fault Tolerance / Observability / Messaging-Transaction
Version Iron Law: Boot -> Cloud -> Alibaba
All three upgrade together, only import BOM
Request Five Hops: Gateway -> LB -> Service
-> Feign -> Fault Tolerance -> Trace
Four Abstractions: DiscoveryClient /
LoadBalancer / CircuitBreaker
/ Feign
One Sentence: Cloud implements no capabilities,
it only defines abstractions and wiring

Summary

The complexity of a microservice system lies not in code but in boundaries : version boundaries, service boundaries, responsibility boundaries. Internalize the component panorama and version matrix; every subsequent component is just a link on this chain. Next article dives into the registry: Nacos Service Registration and Config Center — AP Model, Heartbeat Renewal, and Config Hot Reload .

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

MicroservicesService DiscoveryNacosSpring BootFeignVersion CompatibilityGatewaySpring Cloud
Architecture Pony
Written by

Architecture Pony

Learning notes

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.