副标题: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/123
↓
Ribbon负载均衡选择实例
↓
实际请求:http://192.168.1.100:8001/123
🎉 总结
组件对比
| 组件 | 作用 | 推荐度 | 替代方案 |
|---|---|---|---|
| Eureka | 服务注册发现 | ⭐⭐⭐⭐ | Nacos |
| Ribbon | 负载均衡 | ⭐⭐⭐⭐ | LoadBalancer |
| Feign | 声明式调用 | ⭐⭐⭐⭐⭐ | - |
| Hystrix | 熔断降级 | ⭐⭐⭐ | Sentinel |
| Gateway | API网关 | ⭐⭐⭐⭐⭐ | - |
| 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护航! ☁️✨