☁️ Spring Cloud微服务组件:分布式系统的全家桶!

15 阅读7分钟

副标题:Eureka、Ribbon、Feign、Hystrix...一站式解决方案!🎯


🎬 开场:Spring Cloud是什么?

微服务架构全景图

传统单体应用:
┌──────────────────────────────┐
│     Monolithic Application   │
│  ┌────────┬────────┬────────┐│
│  │ 用户   │ 订单   │ 商品   ││
│  │ 模块   │ 模块   │ 模块   ││
│  └────────┴────────┴────────┘│
│           数据库              │
└──────────────────────────────┘

微服务架构(Spring Cloud):
┌──────────────────────────────────────┐
│        服务注册中心(Eureka)        │
└─────────────┬────────────────────────┘
              │
    ┌─────────┼─────────┬─────────┐
    │         │         │         │
┌───▼───┐ ┌──▼───┐ ┌───▼───┐ ┌──▼───┐
│用户服务│ │订单服务│ │商品服务│ │支付服务│
│Feign  │ │Ribbon │ │Hystrix│ │Config│
└───┬───┘ └──┬───┘ └───┬───┘ └──┬───┘
    │        │         │        │
┌───▼────────▼─────────▼────────▼───┐
│       配置中心(Config Server)    │
└────────────────────────────────────┘

📚 核心组件

Spring Cloud组件全家桶

服务注册与发现:
├── Eureka(Netflix,推荐)
├── Consul(HashiCorp)
└── Nacos(阿里)

服务调用:
├── Ribbon(客户端负载均衡)
├── Feign(声明式HTTP客户端)
└── OpenFeign(Feign的Spring Cloud版本)

服务熔断降级:
├── Hystrix(Netflix,已停更)
├── Resilience4j
└── Sentinel(阿里,推荐)

API网关:
├── Zuul(Netflix,已停更)
├── Gateway(Spring官方,推荐)
└── Kong

配置中心:
├── Config(Spring官方)
├── Nacos(阿里,推荐)
└── Apollo(携程)

链路追踪:
├── Sleuth(Spring官方)
├── Zipkin
└── Skywalking(Apache)

🎯 Eureka:服务注册中心

架构原理

Eureka架构:

┌──────────────────────────────────────┐
│         Eureka Server(注册中心)     │
│                                      │
│   服务注册表(Registry)             │
│   ┌─────────────────────────────┐   │
│   │ user-service: [ip1, ip2]    │   │
│   │ order-service: [ip3, ip4]   │   │
│   │ product-service: [ip5, ip6] │   │
│   └─────────────────────────────┘   │
└──────┬───────────────────┬───────────┘
       │                   │
   注册/续约             获取服务列表
       │                   │
┌──────▼──────┐      ┌─────▼─────────┐
│ 服务提供者   │      │  服务消费者    │
│ (Provider)  │      │  (Consumer)   │
│             │◀─────│               │
│  user-service│  调用 │ order-service│
└─────────────┘      └───────────────┘

Eureka Server搭建

/**
 * Eureka Server(注册中心)
 */
@SpringBootApplication
@EnableEurekaServer  // 启用Eureka Server
public class EurekaServerApplication {
    public static void main(String[] args) {
        SpringApplication.run(EurekaServerApplication.class, args);
    }
}
# application.yml
server:
  port: 8761  # Eureka默认端口

eureka:
  instance:
    hostname: localhost
  client:
    register-with-eureka: false  # 不注册自己
    fetch-registry: false        # 不拉取注册表
    service-url:
      defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/

Eureka Client(服务提供者)

/**
 * 服务提供者
 */
@SpringBootApplication
@EnableEurekaClient  // 启用Eureka Client
public class UserServiceApplication {
    public static void main(String[] args) {
        SpringApplication.run(UserServiceApplication.class, args);
    }
}

@RestController
public class UserController {
    
    @GetMapping("/user/{id}")
    public User getUser(@PathVariable Long id) {
        return new User(id, "张三");
    }
}
# application.yml
server:
  port: 8001

spring:
  application:
    name: user-service  # 服务名

eureka:
  client:
    service-url:
      defaultZone: http://localhost:8761/eureka/  # 注册中心地址
  instance:
    prefer-ip-address: true  # 使用IP注册

核心原理

/**
 * Eureka核心机制
 */

// 1. 服务注册
// 服务启动时,向Eureka Server注册
POST /eureka/apps/{appName}
{
  "instance": {
    "instanceId": "user-service:8001",
    "hostName": "localhost",
    "app": "USER-SERVICE",
    "ipAddr": "192.168.1.100",
    "port": 8001,
    "status": "UP"
  }
}

// 2. 心跳续约(每30秒)
// 服务定期发送心跳,表示还活着
PUT /eureka/apps/{appName}/{instanceId}?status=UP&lastDirtyTimestamp=xxx

// 3. 服务下线
// 服务关闭时,主动下线
DELETE /eureka/apps/{appName}/{instanceId}

// 4. 服务剔除
// Eureka Server定期(默认60秒)检查心跳
// 90秒没有心跳,剔除服务

// 5. 获取服务列表
// 消费者定期(默认30秒)拉取服务列表
GET /eureka/apps

// 6. 自我保护机制
// 15分钟内85%的服务没有心跳
// Eureka认为是网络问题,不剔除服务
// 保护服务注册表不被误删

🎯 Ribbon:客户端负载均衡

原理

Ribbon负载均衡流程:

1. 服务消费者启动
   ↓
2. 从Eureka获取服务列表
   user-service: [192.168.1.100:8001, 192.168.1.101:8002]
   ↓
3. 使用负载均衡策略选择实例
   ↓
4. 发起HTTP请求
   ↓
5. 获取响应

负载均衡策略:
- RoundRobinRule(轮询,默认)
- RandomRule(随机)
- RetryRule(重试)
- WeightedResponseTimeRule(响应时间权重)
- BestAvailableRule(最低并发)

使用方式

/**
 * 使用Ribbon调用服务
 */
@SpringBootApplication
@EnableEurekaClient
public class OrderServiceApplication {
    
    /**
     * RestTemplate + @LoadBalanced
     * 
     * @LoadBalanced:启用负载均衡
     */
    @Bean
    @LoadBalanced  // 关键注解!
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }
    
    public static void main(String[] args) {
        SpringApplication.run(OrderServiceApplication.class, args);
    }
}

@Service
public class OrderService {
    
    @Autowired
    private RestTemplate restTemplate;
    
    /**
     * 调用用户服务
     */
    public User getUser(Long userId) {
        // 使用服务名调用(Ribbon会自动负载均衡)
        String url = "http://user-service/user/" + userId;
        
        return restTemplate.getForObject(url, User.class);
        // Ribbon会从Eureka获取user-service的实例列表
        // 然后使用负载均衡策略选择一个实例
        // 最后将URL替换为实际的IP:Port
    }
}

核心源码

/**
 * Ribbon核心拦截器
 * 
 * 源码:LoadBalancerInterceptor
 */
public class LoadBalancerInterceptor implements ClientHttpRequestInterceptor {
    
    private LoadBalancerClient loadBalancer;
    
    @Override
    public ClientHttpResponse intercept(
        HttpRequest request, 
        byte[] body, 
        ClientHttpRequestExecution execution) throws IOException {
        
        URI originalUri = request.getURI();
        String serviceName = originalUri.getHost();  // 获取服务名
        
        // 使用LoadBalancerClient选择实例
        return loadBalancer.execute(
            serviceName,
            requestFactory.createRequest(request, body, execution)
        );
    }
}

/**
 * 负载均衡客户端
 */
public class RibbonLoadBalancerClient implements LoadBalancerClient {
    
    @Override
    public <T> T execute(String serviceId, LoadBalancerRequest<T> request) {
        // 1. 获取负载均衡器
        ILoadBalancer loadBalancer = getLoadBalancer(serviceId);
        
        // 2. 选择服务实例
        Server server = getServer(loadBalancer);
        
        // 3. 执行请求
        return execute(serviceId, server, request);
    }
    
    protected Server getServer(ILoadBalancer loadBalancer) {
        // 使用负载均衡策略选择实例
        return loadBalancer.chooseServer("default");
    }
}

/**
 * 轮询策略
 */
public class RoundRobinRule extends AbstractLoadBalancerRule {
    
    private AtomicInteger nextServerCyclicCounter;
    
    @Override
    public Server choose(Object key) {
        ILoadBalancer lb = getLoadBalancer();
        
        // 获取所有可用实例
        List<Server> allServers = lb.getAllServers();
        
        // 轮询选择
        int index = incrementAndGetModulo(allServers.size());
        return allServers.get(index);
    }
    
    private int incrementAndGetModulo(int modulo) {
        for (;;) {
            int current = nextServerCyclicCounter.get();
            int next = (current + 1) % modulo;
            if (nextServerCyclicCounter.compareAndSet(current, next)) {
                return next;
            }
        }
    }
}

🎯 Feign:声明式HTTP客户端

原理

Feign简化服务调用:

传统方式(Ribbon):
String url = "http://user-service/user/" + userId;
User user = restTemplate.getForObject(url, User.class);

Feign方式:
@FeignClient("user-service")
interface UserClient {
    @GetMapping("/user/{id}")
    User getUser(@PathVariable Long id);
}

// 直接调用
User user = userClient.getUser(userId);

就像调用本地方法一样简单!✅

使用方式

/**
 * 定义Feign客户端
 */
@FeignClient(name = "user-service")  // 服务名
public interface UserClient {
    
    @GetMapping("/user/{id}")
    User getUser(@PathVariable("id") Long id);
    
    @PostMapping("/user")
    User createUser(@RequestBody User user);
    
    @PutMapping("/user/{id}")
    User updateUser(@PathVariable("id") Long id, @RequestBody User user);
    
    @DeleteMapping("/user/{id}")
    void deleteUser(@PathVariable("id") Long id);
}

/**
 * 启用Feign
 */
@SpringBootApplication
@EnableFeignClients  // 启用Feign客户端
public class OrderServiceApplication {
    public static void main(String[] args) {
        SpringApplication.run(OrderServiceApplication.class, args);
    }
}

/**
 * 使用Feign客户端
 */
@Service
public class OrderService {
    
    @Autowired
    private UserClient userClient;  // 注入Feign客户端
    
    public void createOrder(Long userId) {
        // 直接调用,就像本地方法
        User user = userClient.getUser(userId);
        
        // 创建订单
        Order order = new Order();
        order.setUserId(user.getId());
        order.setUserName(user.getName());
        
        orderMapper.insert(order);
    }
}

核心原理

/**
 * Feign动态代理原理
 */

// 1. 扫描@FeignClient注解
// Spring启动时扫描所有@FeignClient接口

// 2. 创建代理对象
// 为每个接口创建JDK动态代理

Proxy.newProxyInstance(
    classLoader,
    new Class<?>[] { UserClient.class },
    new FeignInvocationHandler()
);

// 3. 拦截方法调用
class FeignInvocationHandler implements InvocationHandler {
    
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) {
        // 解析方法上的注解(@GetMapping等)
        RequestTemplate template = parseMethod(method);
        
        // 设置请求参数
        template.resolve(args);
        
        // 使用Ribbon负载均衡选择实例
        String url = loadBalancer.choose("user-service");
        
        // 发送HTTP请求
        Response response = client.execute(template, url);
        
        // 解析响应
        return decoder.decode(response, method.getReturnType());
    }
}

🎯 Hystrix:服务熔断降级

熔断器原理

熔断器状态机:

     关闭(Closed)
          │
    正常调用,统计失败率
          │
      失败率超过阈值
          ↓
     打开(Open)
          │
    快速失败,直接降级
          │
      等待一段时间
          ↓
    半开(Half-Open)
          │
    尝试调用,判断是否恢复
      │           │
    成功         失败
      ↓           ↓
    关闭        打开

使用方式

/**
 * 启用Hystrix
 */
@SpringBootApplication
@EnableCircuitBreaker  // 启用熔断器
public class OrderServiceApplication {
    public static void main(String[] args) {
        SpringApplication.run(OrderServiceApplication.class, args);
    }
}

/**
 * 使用@HystrixCommand
 */
@Service
public class OrderService {
    
    @Autowired
    private UserClient userClient;
    
    /**
     * 调用用户服务(带熔断)
     * 
     * @HystrixCommand:标记熔断方法
     * fallbackMethod:降级方法
     */
    @HystrixCommand(
        fallbackMethod = "getUserFallback",  // 降级方法
        commandProperties = {
            // 熔断阈值:10秒内20次请求
            @HystrixProperty(name = "circuitBreaker.requestVolumeThreshold", value = "20"),
            // 失败率:50%
            @HystrixProperty(name = "circuitBreaker.errorThresholdPercentage", value = "50"),
            // 熔断时间:5秒
            @HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds", value = "5000")
        }
    )
    public User getUser(Long userId) {
        // 调用远程服务
        return userClient.getUser(userId);
    }
    
    /**
     * 降级方法
     * 
     * 当服务调用失败或熔断打开时执行
     */
    public User getUserFallback(Long userId) {
        // 返回默认值
        User user = new User();
        user.setId(userId);
        user.setName("默认用户");
        return user;
    }
}

Feign + Hystrix

/**
 * Feign集成Hystrix
 */
@FeignClient(
    name = "user-service",
    fallback = UserClientFallback.class  // 降级实现类
)
public interface UserClient {
    
    @GetMapping("/user/{id}")
    User getUser(@PathVariable("id") Long id);
}

/**
 * 降级实现类
 */
@Component
public class UserClientFallback implements UserClient {
    
    @Override
    public User getUser(Long id) {
        // 降级逻辑
        User user = new User();
        user.setId(id);
        user.setName("服务降级");
        return user;
    }
}
# 启用Feign的Hystrix支持
feign:
  hystrix:
    enabled: true

🎯 Gateway:API网关

架构

Gateway架构:

客户端请求
    ↓
┌───────────────────────────────────┐
│        Gateway(网关)             │
│                                   │
│  ┌─────────────────────────────┐ │
│  │  路由(Route)               │ │
│  │  - 断言(Predicate)         │ │
│  │  - 过滤器(Filter)          │ │
│  └─────────────────────────────┘ │
└───────┬────────────┬──────────────┘
        │            │
┌───────▼──┐   ┌────▼──────┐
│用户服务  │   │  订单服务  │
└──────────┘   └───────────┘

使用方式

/**
 * Gateway配置
 */
@SpringBootApplication
public class GatewayApplication {
    public static void main(String[] args) {
        SpringApplication.run(GatewayApplication.class, args);
    }
}
# application.yml
server:
  port: 8080

spring:
  application:
    name: api-gateway
  cloud:
    gateway:
      routes:
        # 用户服务路由
        - id: user-service
          uri: lb://user-service  # lb表示负载均衡
          predicates:
            - Path=/user/**  # 路径匹配
          filters:
            - StripPrefix=1  # 去掉第一个路径前缀
            
        # 订单服务路由
        - id: order-service
          uri: lb://order-service
          predicates:
            - Path=/order/**
          filters:
            - StripPrefix=1

路由示例

请求:http://localhost:8080/user/123
     ↓
Gateway路由匹配:/user/**
     ↓
转发到:lb://user-service/123Ribbon负载均衡选择实例
     ↓
实际请求:http://192.168.1.100:8001/123

🎉 总结

组件对比

组件作用推荐度替代方案
Eureka服务注册发现⭐⭐⭐⭐Nacos
Ribbon负载均衡⭐⭐⭐⭐LoadBalancer
Feign声明式调用⭐⭐⭐⭐⭐-
Hystrix熔断降级⭐⭐⭐Sentinel
GatewayAPI网关⭐⭐⭐⭐⭐-
Config配置中心⭐⭐⭐Nacos

核心原理

1. Eureka
   - 服务注册、心跳续约、服务发现
   - 自我保护机制

2. Ribbon
   - 客户端负载均衡
   - 从Eureka获取服务列表
   - 使用策略选择实例

3. Feign
   - 动态代理
   - 结合Ribbon负载均衡
   - 简化HTTP调用

4. Hystrix
   - 熔断器状态机
   - 降级处理
   - 资源隔离

5. Gateway
   - 路由转发
   - 过滤器链
   - 统一入口

记忆口诀

Spring Cloud微服务全家桶,
组件丰富功能强。

Eureka注册中心,
服务注册和发现。
心跳续约保活,
自我保护防误删。

Ribbon负载均衡,
客户端来实现。
轮询随机权重,
策略可以选。

Feign声明式客户端,
接口注解就搞定。
动态代理来实现,
像调本地方法一样简单。

Hystrix熔断降级,
服务故障不蔓延。
熔断器状态机,
打开关闭半开状态。

Gateway统一网关,
路由转发和过滤。
统一鉴权限流,
微服务入口。

微服务架构复杂,
Spring Cloud来帮忙!

愿你的微服务架构简单优雅,Spring Cloud护航! ☁️✨