Java · #java#spring-cloud#microservices#nacos#gateway

Spring Cloud微服务架构实战指南

2025.01.22 Java 10 min 3.9k
// 目录 · contents

引言

随着业务规模的增长,单体应用在可维护性、可扩展性和团队协作方面的局限性日益凸显。微服务架构通过将大型应用拆分为一组松耦合、可独立部署的服务,有效解决了这些痛点。Spring Cloud作为Java生态中最成熟的微服务框架,提供了一整套开箱即用的分布式系统解决方案。

本文将基于Spring Cloud Alibaba技术栈,从服务注册与发现、负载均衡、熔断降级、API网关、配置中心到分布式链路追踪,系统性地讲解微服务架构的核心组件及其实战应用。

微服务架构全景

在深入各个组件之前,我们先通过架构图了解Spring Cloud微服务系统的整体结构。

graph TB
    Client[客户端] --> Gateway[Spring Cloud Gateway]
    Gateway --> ServiceA[订单服务]
    Gateway --> ServiceB[用户服务]
    Gateway --> ServiceC[商品服务]

    ServiceA --> Nacos[Nacos 注册/配置中心]
    ServiceB --> Nacos
    ServiceC --> Nacos

    ServiceA --> Sentinel[Sentinel 熔断降级]
    ServiceB --> Sentinel
    ServiceC --> Sentinel

    ServiceA -.-> ServiceB
    ServiceA -.-> ServiceC

    ServiceA --> Sleuth[Sleuth + Zipkin 链路追踪]
    ServiceB --> Sleuth
    ServiceC --> Sleuth

    subgraph 基础设施层
        Nacos
        Sentinel
        Sleuth
    end

项目基础搭建

首先定义父工程的依赖管理,统一版本控制:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<!-- pom.xml (父工程) -->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.2.3</version>
</parent>

<properties>
<java.version>21</java.version>
<spring-cloud.version>2023.0.0</spring-cloud.version>
<spring-cloud-alibaba.version>2023.0.0.0</spring-cloud-alibaba.version>
</properties>

<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>${spring-cloud-alibaba.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>

服务注册与发现 - Nacos

Nacos(Dynamic Naming and Configuration Service)是阿里巴巴开源的服务注册与配置管理平台,它同时承担了注册中心和配置中心两个角色。

sequenceDiagram
    participant Service as 微服务实例
    participant Nacos as Nacos Server
    participant Consumer as 服务消费者

    Service->>Nacos: 1. 服务注册 (name, ip, port, metadata)
    Service->>Nacos: 2. 定时心跳 (每5秒)
    Consumer->>Nacos: 3. 查询服务列表
    Nacos-->>Consumer: 4. 返回健康实例列表
    Consumer->>Service: 5. 发起RPC调用

    Note over Nacos: 15秒无心跳标记不健康
    Note over Nacos: 30秒无心跳剔除实例

服务提供者配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# application.yml - 用户服务
server:
port: 8081

spring:
application:
name: user-service
cloud:
nacos:
discovery:
server-addr: localhost:8848
namespace: dev
group: DEFAULT_GROUP
# 临时实例(默认),基于心跳检测
ephemeral: true
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
@SpringBootApplication
@EnableDiscoveryClient
public class UserServiceApplication {
public static void main(String[] args) {
SpringApplication.run(UserServiceApplication.class, args);
}
}

@RestController
@RequestMapping("/api/users")
public class UserController {

@GetMapping("/{id}")
public User getUserById(@PathVariable Long id) {
// 模拟查询用户
return new User(id, "张三", "[email protected]");
}

@GetMapping("/{id}/detail")
public UserDetail getUserDetail(@PathVariable Long id) {
return new UserDetail(id, "张三", "[email protected]", "北京市朝阳区");
}
}

服务消费者 - 使用OpenFeign

OpenFeign提供了声明式的HTTP客户端,通过接口注解的方式简化了服务间调用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
// 定义Feign客户端接口
@FeignClient(name = "user-service", fallbackFactory = UserClientFallbackFactory.class)
public interface UserClient {

@GetMapping("/api/users/{id}")
User getUserById(@PathVariable("id") Long id);

@GetMapping("/api/users/{id}/detail")
UserDetail getUserDetail(@PathVariable("id") Long id);
}

// 降级工厂,提供容错处理
@Component
public class UserClientFallbackFactory implements FallbackFactory<UserClient> {

@Override
public UserClient create(Throwable cause) {
return new UserClient() {
@Override
public User getUserById(Long id) {
// 返回默认用户,防止级联失败
return new User(id, "未知用户", "");
}

@Override
public UserDetail getUserDetail(Long id) {
return new UserDetail(id, "未知用户", "", "");
}
};
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 订单服务中调用用户服务
@Service
@RequiredArgsConstructor
public class OrderService {

private final UserClient userClient;

public OrderVO createOrder(OrderCreateDTO dto) {
// 通过Feign调用用户服务获取用户信息
User user = userClient.getUserById(dto.getUserId());

Order order = new Order();
order.setOrderNo(generateOrderNo());
order.setUserId(user.getId());
order.setUserName(user.getName());
order.setAmount(dto.getAmount());
order.setStatus(OrderStatus.CREATED);

orderRepository.save(order);
return OrderVO.from(order);
}
}

负载均衡 - Spring Cloud LoadBalancer

Spring Cloud LoadBalancer替代了已停止维护的Netflix Ribbon,提供客户端负载均衡能力:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
// 自定义负载均衡策略:基于权重的随机选择
public class WeightedRandomLoadBalancer implements ReactorServiceInstanceListSupplier {

private final ServiceInstanceListSupplier delegate;

public WeightedRandomLoadBalancer(ServiceInstanceListSupplier delegate) {
this.delegate = delegate;
}

@Override
public Flux<List<ServiceInstance>> get() {
return delegate.get().map(this::selectByWeight);
}

private List<ServiceInstance> selectByWeight(List<ServiceInstance> instances) {
// 根据metadata中的weight字段进行加权随机选择
int totalWeight = instances.stream()
.mapToInt(inst -> Integer.parseInt(
inst.getMetadata().getOrDefault("weight", "1")))
.sum();

int random = ThreadLocalRandom.current().nextInt(totalWeight);
int current = 0;

for (ServiceInstance instance : instances) {
int weight = Integer.parseInt(
instance.getMetadata().getOrDefault("weight", "1"));
current += weight;
if (random < current) {
return Collections.singletonList(instance);
}
}
return Collections.singletonList(instances.get(0));
}

@Override
public String getServiceId() {
return delegate.getServiceId();
}
}

熔断降级 - Sentinel

Sentinel是面向分布式服务架构的流量治理组件,提供流量控制、熔断降级、系统自适应保护等功能。

stateDiagram-v2
    [*] --> Closed: 初始状态
    Closed --> Open: 错误率/慢调用比例超过阈值
    Open --> HalfOpen: 熔断时长结束
    HalfOpen --> Closed: 探测请求成功
    HalfOpen --> Open: 探测请求失败

    state Closed {
        [*] --> 正常处理请求
        正常处理请求 --> 统计异常比例
    }

    state Open {
        [*] --> 拒绝所有请求
        拒绝所有请求 --> 执行降级逻辑
    }

    state HalfOpen {
        [*] --> 放行少量探测请求
    }

Sentinel规则配置与使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
@RestController
@RequestMapping("/api/orders")
public class OrderController {

private final OrderService orderService;

public OrderController(OrderService orderService) {
this.orderService = orderService;
}

// 使用@SentinelResource注解定义资源并指定降级处理
@GetMapping("/{id}")
@SentinelResource(
value = "getOrder",
blockHandler = "getOrderBlockHandler",
fallback = "getOrderFallback"
)
public OrderVO getOrder(@PathVariable Long id) {
return orderService.getOrderById(id);
}

// 限流/降级时的处理方法(参数列表需与原方法一致,并多一个BlockException)
public OrderVO getOrderBlockHandler(Long id, BlockException ex) {
// 被限流或熔断时返回友好提示
throw new ServiceException("系统繁忙,请稍后重试");
}

// 业务异常时的降级方法
public OrderVO getOrderFallback(Long id, Throwable throwable) {
// 返回缓存数据或默认值
return OrderVO.defaultOrder(id);
}
}

// 编程式配置Sentinel规则
@Configuration
public class SentinelRuleConfig {

@PostConstruct
public void initRules() {
// 流控规则:每秒最多100个请求,超过则排队等待
FlowRule flowRule = new FlowRule();
flowRule.setResource("getOrder");
flowRule.setGrade(RuleConstant.FLOW_GRADE_QPS);
flowRule.setCount(100);
flowRule.setControlBehavior(RuleConstant.CONTROL_BEHAVIOR_RATE_LIMITER);
flowRule.setMaxQueueingTimeMs(500);
FlowRuleManager.loadRules(Collections.singletonList(flowRule));

// 熔断规则:10秒内慢调用比例超过50%则熔断5秒
DegradeRule degradeRule = new DegradeRule();
degradeRule.setResource("getOrder");
degradeRule.setGrade(CircuitBreakerStrategy.SLOW_REQUEST_RATIO.getType());
degradeRule.setCount(0.5); // 50%慢调用比例
degradeRule.setTimeWindow(5); // 熔断时长5秒
degradeRule.setSlowRatioThreshold(0.5);
degradeRule.setMinRequestAmount(10); // 最小请求数
degradeRule.setStatIntervalMs(10000); // 统计窗口10秒
DegradeRuleManager.loadRules(Collections.singletonList(degradeRule));
}
}

API Gateway - Spring Cloud Gateway

Spring Cloud Gateway是基于Spring WebFlux构建的API网关,提供路由、过滤、限流等能力。

graph LR
    Client[客户端请求] --> Predicate{路由断言匹配}
    Predicate -->|匹配成功| PreFilters[前置过滤器链]
    Predicate -->|匹配失败| NotFound[404]
    PreFilters --> ProxyService[代理到下游服务]
    ProxyService --> PostFilters[后置过滤器链]
    PostFilters --> Response[返回响应]

    subgraph 过滤器链
        PreFilters
        PostFilters
    end

网关路由与过滤器配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# Gateway application.yml
spring:
cloud:
gateway:
routes:
- id: user-service
uri: lb://user-service
predicates:
- Path=/api/users/**
filters:
- StripPrefix=0
- name: CircuitBreaker
args:
name: userServiceCB
fallbackUri: forward:/fallback/user

- id: order-service
uri: lb://order-service
predicates:
- Path=/api/orders/**
- Method=GET,POST
filters:
- StripPrefix=0
- AddRequestHeader=X-Request-Source, gateway
- name: RequestRateLimiter
args:
redis-rate-limiter.replenishRate: 100
redis-rate-limiter.burstCapacity: 200
key-resolver: "#{@userKeyResolver}"
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
// 自定义全局过滤器:JWT认证
@Component
@Order(-1)
public class AuthGlobalFilter implements GlobalFilter {

private static final List<String> WHITE_LIST = List.of(
"/api/users/login",
"/api/users/register",
"/health"
);

private final JwtUtil jwtUtil;

public AuthGlobalFilter(JwtUtil jwtUtil) {
this.jwtUtil = jwtUtil;
}

@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
String path = request.getURI().getPath();

// 白名单直接放行
if (WHITE_LIST.stream().anyMatch(path::startsWith)) {
return chain.filter(exchange);
}

// 从请求头获取Token
String token = request.getHeaders().getFirst("Authorization");
if (token == null || !token.startsWith("Bearer ")) {
return unauthorized(exchange, "缺少认证令牌");
}

try {
String jwt = token.substring(7);
Claims claims = jwtUtil.parseToken(jwt);

// 将用户信息传递给下游服务
ServerHttpRequest mutatedRequest = request.mutate()
.header("X-User-Id", claims.getSubject())
.header("X-User-Role", claims.get("role", String.class))
.build();

return chain.filter(exchange.mutate().request(mutatedRequest).build());
} catch (ExpiredJwtException e) {
return unauthorized(exchange, "令牌已过期");
} catch (JwtException e) {
return unauthorized(exchange, "令牌无效");
}
}

private Mono<Void> unauthorized(ServerWebExchange exchange, String message) {
ServerHttpResponse response = exchange.getResponse();
response.setStatusCode(HttpStatus.UNAUTHORIZED);
response.getHeaders().setContentType(MediaType.APPLICATION_JSON);

String body = """
{"code": 401, "message": "%s"}
""".formatted(message);

DataBuffer buffer = response.bufferFactory()
.wrap(body.getBytes(StandardCharsets.UTF_8));
return response.writeWith(Mono.just(buffer));
}
}

配置中心 - Nacos Config

Nacos Config支持配置的集中管理和动态刷新,无需重启服务即可更新配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# bootstrap.yml
spring:
application:
name: order-service
cloud:
nacos:
config:
server-addr: localhost:8848
namespace: dev
group: DEFAULT_GROUP
file-extension: yaml
# 共享配置(如数据库连接、Redis等公共配置)
shared-configs:
- data-id: common-db.yaml
group: DEFAULT_GROUP
refresh: true
- data-id: common-redis.yaml
group: DEFAULT_GROUP
refresh: true
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// 动态读取Nacos配置
@RestController
@RefreshScope
public class ConfigController {

@Value("${order.max-retry-times:3}")
private int maxRetryTimes;

@Value("${order.timeout-seconds:30}")
private int timeoutSeconds;

@GetMapping("/config/info")
public Map<String, Object> getConfig() {
return Map.of(
"maxRetryTimes", maxRetryTimes,
"timeoutSeconds", timeoutSeconds
);
}
}

// 监听配置变更
@Component
public class NacosConfigListener {

private static final Logger log = LoggerFactory.getLogger(NacosConfigListener.class);

@NacosConfigListener(dataId = "order-service.yaml", groupId = "DEFAULT_GROUP")
public void onConfigChange(String newConfig) {
log.info("检测到配置变更,最新配置内容: {}", newConfig);
// 执行自定义逻辑,如刷新本地缓存、重建连接池等
}
}

分布式链路追踪 - Micrometer Tracing

Spring Cloud 2023.x 已经用 Micrometer Tracing 取代了 Spring Cloud Sleuth。通过链路追踪我们可以清晰地看到一个请求在各微服务间的调用链路和耗时。

graph LR
    A[Gateway<br/>traceId=abc123] -->|spanId=1| B[Order Service<br/>traceId=abc123]
    B -->|spanId=2| C[User Service<br/>traceId=abc123]
    B -->|spanId=3| D[Product Service<br/>traceId=abc123]
    D -->|spanId=4| E[Inventory Service<br/>traceId=abc123]

    style A fill:#e1f5fe
    style B fill:#fff3e0
    style C fill:#e8f5e9
    style D fill:#fce4ec
    style E fill:#f3e5f5
1
2
3
4
5
6
7
8
9
10
11
12
# application.yml - 链路追踪配置
management:
tracing:
sampling:
probability: 1.0 # 生产环境建议设为0.1
zipkin:
tracing:
endpoint: http://localhost:9411/api/v2/spans

logging:
pattern:
level: "%5p [${spring.application.name},%X{traceId},%X{spanId}]"
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// 自定义Span,追踪关键业务逻辑
@Service
public class PaymentService {

private final Tracer tracer;
private final ObservationRegistry observationRegistry;

public PaymentService(Tracer tracer, ObservationRegistry observationRegistry) {
this.tracer = tracer;
this.observationRegistry = observationRegistry;
}

public PaymentResult processPayment(PaymentRequest request) {
// 创建自定义观测点
return Observation.createNotStarted("payment.process", observationRegistry)
.lowCardinalityKeyValue("payment.method", request.getMethod().name())
.observe(() -> {
// 调用第三方支付
PaymentResult result = callPaymentGateway(request);

// 在Span中添加关键业务信息
Span currentSpan = tracer.currentSpan();
if (currentSpan != null) {
currentSpan.tag("payment.orderId", request.getOrderId());
currentSpan.tag("payment.amount", request.getAmount().toString());
currentSpan.event("payment.completed");
}

return result;
});
}
}

最佳实践与注意事项

服务拆分原则

  1. 单一职责:每个服务只负责一个业务领域,避免出现”大服务”。
  2. 数据独立:每个服务拥有独立的数据存储,禁止跨服务直接访问数据库。
  3. 接口契约:服务间通过明确定义的API通信,接口变更需要版本管理。

性能与稳定性建议

关注点 建议
Feign超时 连接超时2s,读取超时5s,避免长时间等待
熔断阈值 根据实际QPS调整,生产环境不要使用默认值
线程隔离 对不同的下游服务使用独立线程池,防止线程池耗尽
配置刷新 高频变更的配置使用动态刷新,低频配置重启即可
链路采样率 生产环境设为10%-20%,全量采样会严重影响性能
网关限流 结合Redis实现分布式限流,单机限流在集群环境下不准确

容错设计

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
// 多级缓存 + 降级策略示例
@Service
public class ProductService {

private final ProductClient productClient;
private final RedisTemplate<String, Product> redisTemplate;
private final LoadingCache<Long, Product> localCache;

public ProductService(ProductClient productClient,
RedisTemplate<String, Product> redisTemplate) {
this.productClient = productClient;
this.redisTemplate = redisTemplate;
this.localCache = Caffeine.newBuilder()
.maximumSize(1000)
.expireAfterWrite(Duration.ofMinutes(5))
.build(this::loadProduct);
}

public Product getProduct(Long id) {
try {
// L1: 本地缓存
return localCache.get(id);
} catch (Exception e) {
// L3: 兜底默认值
return Product.defaultProduct(id);
}
}

private Product loadProduct(Long id) {
// L2: Redis缓存
String key = "product:" + id;
Product cached = redisTemplate.opsForValue().get(key);
if (cached != null) {
return cached;
}

// L3: 远程服务调用
Product product = productClient.getProduct(id);
redisTemplate.opsForValue().set(key, product, Duration.ofMinutes(30));
return product;
}
}

总结

本文系统性地介绍了基于Spring Cloud Alibaba的微服务架构核心组件:

  • Nacos同时承担服务注册发现与配置管理的职责,简化了基础设施的运维。
  • Spring Cloud LoadBalancer提供客户端负载均衡,支持自定义策略。
  • Sentinel实现了流量控制和熔断降级,保障系统在高并发和故障场景下的稳定性。
  • Spring Cloud Gateway作为统一入口,处理认证鉴权、限流和路由分发。
  • Micrometer Tracing实现分布式链路追踪,帮助快速定位跨服务调用的性能瓶颈。

微服务架构并非银弹,它在解决扩展性问题的同时,也引入了分布式系统的固有复杂性——网络不可靠、数据一致性、运维复杂度等。在实际落地时,应根据团队规模和业务复杂度谨慎评估,避免过度拆分。对于中小型项目,模块化的单体应用往往是更务实的起点。

作者 · authorzt
发布 · date2025-01-22
篇幅 · length3.9k 字 · 10 min
许可 · licenseCC BY-SA 4.0
$ echo "comments" · 评论