一、当单体应用遇上分布式之痛

五年前我刚接触电商项目时,系统还是单体架构的"巨无霸"。某次促销活动导致用户服务崩溃后,连带把订单、支付模块也拖下水,这让我深刻认识到分布式系统的必要性。这就像把鸡蛋放在多个篮子里,而Spring Cloud就是帮我们管理这些篮子的核心工具包。

二、Eureka服务治理实战(服务注册与发现篇)

2.1 搭建注册中心服务器

// Eureka服务器启动类
@SpringBootApplication
@EnableEurekaServer  // 激活Eureka服务端功能
public class RegistryServer {
    public static void main(String[] args) {
        SpringApplication.run(RegistryServer.class, args);
    }
}

// application.yml配置
server:
  port: 8761
eureka:
  client:
    register-with-eureka: false  # 不向自己注册
    fetch-registry: false        # 不获取注册信息
  instance:
    hostname: localhost

启动后访问http://localhost:8761,你会看到类似通讯录的界面,但此刻它还空空如也。

2.2 服务提供者实战

// 用户服务配置
@SpringBootApplication
@EnableDiscoveryClient  // 开启服务发现
public class UserService {
    public static void main(String[] args) {
        SpringApplication.run(UserService.class, args);
    }
}

// application.yml片段
spring:
  application:
    name: user-service  # 服务标识
eureka:
  client:
    service-url:
      defaultZone: http://localhost:8761/eureka  # 注册中心地址

启动两个实例观察负载均衡:

java -jar user-service.jar --server.port=8081
java -jar user-service.jar --server.port=8082

2.3 服务消费者调用

通过负载均衡的Ribbon客户端调用:

@RestController
public class OrderController {
    @Autowired
    private RestTemplate restTemplate;

    @Bean
    @LoadBalanced  // 启用客户端负载均衡
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }

    @GetMapping("/order/{userId}")
    public String getOrderInfo(@PathVariable String userId) {
        // 直接使用服务名调用
        return restTemplate.getForObject(
            "http://user-service/user/" + userId, 
            String.class
        );
    }
}

三、配置中心的全家桶方案

3.1 Config Server搭建

@SpringBootApplication
@EnableConfigServer  // 开启配置服务
public class ConfigServer {
    public static void main(String[] args) {
        SpringApplication.run(ConfigServer.class, args);
    }
}

// 仓库中的user-service-dev.yml
jdbc:
  url: jdbc:mysql://localhost:3306/user_db
  username: root
  password: 123456

3.2 客户端动态获取配置

// 用户服务新增配置类
@RefreshScope  // 支持动态刷新
@RestController
public class UserConfigController {
    @Value("${jdbc.url}") 
    private String jdbcUrl;

    @GetMapping("/config/show")
    public String showConfig() {
        return "当前数据库连接: " + jdbcUrl;
    }
}

// bootstrap.yml(优先级高于application.yml)
spring:
  cloud:
    config:
      uri: http://localhost:8888  # 配置中心地址
      name: user-service          # 配置文件前缀
      profile: dev                # 环境标识

触发配置刷新(无需重启):

curl -X POST http://localhost:8080/actuator/refresh

四、网关路由的流量调度艺术

4.1 Gateway网关配置

spring:
  cloud:
    gateway:
      routes:
        - id: user_route
          uri: lb://user-service  # lb表示负载均衡
          predicates:
            - Path=/api/user/**
          filters:
            - StripPrefix=1  # 去除前缀

4.2 实现熔断降级

// 降级处理配置
@Bean
public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
    return builder.routes()
        .route("order_route", r -> r.path("/api/order/**")
            .filters(f -> f.stripPrefix(1)
                .circuitBreaker(config -> config
                    .setName("orderCircuitBreaker")
                    .setFallbackUri("forward:/fallback/order")))
            .uri("lb://order-service"))
        .build();
}

// 降级控制器
@RestController
public class FallbackController {
    @GetMapping("/fallback/order")
    public ResponseEntity<String> orderFallback() {
        return ResponseEntity.status(503)
            .body("服务暂时不可用,请稍后重试");
    }
}

五、经典应用场景剖析

某电商平台采用该架构后:

  • 会员服务独立部署,通过网关实现鉴权
  • 价格服务通过配置中心管理促销策略
  • 不同区域用户请求被路由到最近的服务器 当某仓储服务崩溃时,网关自动切断流量避免雪崩效应

六、技术方案的双面性分析

优势组合拳

  1. 注册中心实现服务的自动发现
  2. 配置分离提升环境管理效率
  3. 网关统一管控入口流量

需要警惕的陷阱

  • Eureka集群节点数量建议3-5个为宜
  • 配置中心首次连接超时问题
  • 网关过滤器顺序影响业务逻辑

性能对比表

组件 启动耗时 内存消耗 适合场景
Eureka 较快 较低 中小型集群
Nacos 中等 较高 配置注册一体化
Consul 较慢 多数据中心

七、踩坑备忘录

  1. Spring Boot与Cloud版本兼容性问题(推荐使用2022.0.x + Boot 3.x组合)
  2. 注册中心高可用至少需要两个节点相互注册
  3. 配置中心的加密传输必须配置密钥
  4. 网关过滤器链的顺序影响业务逻辑
  5. 生产环境必须开启安全认证

八、架构演进思考

某社交平台采用该架构后:

  • 服务启动时间从3分钟缩短至30秒
  • 配置变更效率提升80%
  • 故障定位时间减少60% 但同时也增加了分布式事务等新挑战,需要结合Seata等组件完善