Spring Cloud 负载均衡详解:Ribbon 与 OpenFeign ⭐⭐⭐

面试题:什么是负载均衡?Spring Cloud 如何实现负载均衡?

核心回答

负载均衡是将请求分发到多个服务实例的技术,分为服务端负载均衡(Nginx、F5)和客户端负载均衡(Ribbon)。Spring Cloud 通过 Ribbon 实现客户端负载均衡,OpenFeign 在 Ribbon 基础上提供了声明式 HTTP 客户端,使得服务调用更加简洁。

一、负载均衡基础

1.1 负载均衡分类

┌─────────────────────────────────────────────────────────────┐
│                   负载均衡分类                                │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  服务端负载均衡:                                             │
│  ┌─────────────────────────────────────────────────────┐   │
│  │                    负载均衡器                         │   │
│  │                 (Nginx/F5/HAProxy)                   │   │
│  └─────────────────────────────────────────────────────┘   │
│              │              │              │                │
│              ▼              ▼              ▼                │
│         服务实例1       服务实例2       服务实例3            │
│                                                             │
│  特点:                                                     │
│  ├── 集中式,独立部署                                       │
│  ├── 请求先到负载均衡器,再分发                              │
│  └── 需要维护负载均衡器高可用                                │
│                                                             │
│  客户端负载均衡:                                             │
│  ┌─────────────────────────────────────────────────────┐   │
│  │                     客户端                           │   │
│  │              (内置负载均衡逻辑)                       │   │
│  └─────────────────────────────────────────────────────┘   │
│              │              │              │                │
│              ▼              ▼              ▼                │
│         服务实例1       服务实例2       服务实例3            │
│                                                             │
│  特点:                                                     │
│  ├── 分布式,每个客户端都有负载均衡能力                      │
│  ├── 客户端直接选择服务实例                                  │
│  └── 无需独立的负载均衡器                                    │
│                                                             │
└─────────────────────────────────────────────────────────────┘

1.2 服务端 vs 客户端负载均衡

特性 服务端负载均衡 客户端负载均衡
部署位置 独立服务器 客户端内部
代表产品 Nginx、F5、HAProxy Ribbon、Spring Cloud LoadBalancer
请求路径 客户端 → LB → 服务 客户端 → 服务
性能 多一跳,稍慢 直连,更快
高可用 需要保证 LB 高可用 无需额外保证
运维成本 较高 较低
适用场景 外部流量入口 内部服务调用

1.3 负载均衡算法

┌─────────────────────────────────────────────────────────────┐
│                   常见负载均衡算法                            │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  1. 轮询(Round Robin)                                      │
│     ├── 按顺序依次分配请求                                   │
│     └── A → B → C → A → B → C                              │
│                                                             │
│  2. 随机(Random)                                          │
│     ├── 随机选择一个实例                                     │
│     └── 适合请求量大的场景                                   │
│                                                             │
│  3. 加权轮询(Weighted Round Robin)                        │
│     ├── 根据权重分配请求                                     │
│     └── 权重高的实例获得更多请求                             │
│                                                             │
│  4. 加权随机(Weighted Random)                             │
│     ├── 随机 + 权重                                         │
│     └── 概率与权重成正比                                     │
│                                                             │
│  5. 最少连接(Least Connections)                           │
│     ├── 选择当前连接数最少的实例                            │
│     └── 适合长连接场景                                       │
│                                                             │
│  6. 一致性哈希(Consistent Hash)                           │
│     ├── 根据请求特征(如 IP、用户 ID)计算哈希               │
│     └── 相同特征的请求总是路由到同一实例                     │
│                                                             │
│  7. 响应时间加权(Weighted Response Time)                  │
│     ├── 根据响应时间动态调整权重                            │
│     └── 响应快的实例获得更多请求                             │
│                                                             │
└─────────────────────────────────────────────────────────────┘

二、Ribbon 核心组件

2.1 Ribbon 简介

Ribbon 是 Netflix 开源的客户端负载均衡组件,主要功能:

2.2 Ribbon 核心组件架构

┌─────────────────────────────────────────────────────────────┐
│                  Ribbon 核心组件架构                          │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  ┌─────────────────────────────────────────────────────┐   │
│  │                  ILoadBalancer                       │   │
│  │                  (负载均衡器)                         │   │
│  └─────────────────────────────────────────────────────┘   │
│              │                              │               │
│              ▼                              ▼               │
│  ┌───────────────────┐          ┌───────────────────┐      │
│  │    ServerList     │          │      IRule        │      │
│  │   (服务列表)       │          │   (负载均衡策略)   │      │
│  └───────────────────┘          └───────────────────┘      │
│              │                              │               │
│              ▼                              ▼               │
│  ┌───────────────────┐          ┌───────────────────┐      │
│  │ ServerListFilter  │          │    IPing          │      │
│  │  (服务列表过滤)    │          │   (健康检查)       │      │
│  └───────────────────┘          └───────────────────┘      │
│                                                             │
│  组件说明:                                                  │
│  ├── ILoadBalancer:负载均衡器入口,协调各组件               │
│  ├── ServerList:获取服务实例列表                           │
│  ├── ServerListFilter:过滤服务实例                         │
│  ├── IRule:负载均衡策略,选择实例                          │
│  └── IPing:健康检查,剔除不可用实例                        │
│                                                             │
└─────────────────────────────────────────────────────────────┘

2.3 ILoadBalancer(负载均衡器)

public interface ILoadBalancer {
    
    // 添加服务实例
    void addServers(List<Server> newServers);
    
    // 选择服务实例
    Server chooseServer(Object key);
    
    // 标记服务下线
    void markServerDown(Server server);
    
    // 获取可用的服务列表
    List<Server> getReachableServers();
    
    // 获取所有服务列表
    List<Server> getAllServers();
}
// BaseLoadBalancer 实现
public class BaseLoadBalancer extends AbstractLoadBalancer {
    
    // 所有服务实例
    protected volatile List<Server> allServerList;
    
    // 可用服务实例
    protected volatile List<Server> upServerList;
    
    // 负载均衡策略
    protected IRule rule;
    
    // 健康检查
    protected IPing ping;
    
    @Override
    public Server chooseServer(Object key) {
        if (rule == null) {
            return null;
        }
        return rule.choose(key);
    }
}

2.4 ServerList(服务列表)

public interface ServerList<T extends Server> {
    
    // 获取初始服务列表
    public List<T> getInitialListOfServers();
    
    // 获取更新的服务列表
    public List<T> getUpdatedListOfServers();
}
// 基于 Eureka 的实现
public class DiscoveryEnabledNIWSServerList extends AbstractServerList<DiscoveryEnabledServer> {
    
    @Override
    public List<DiscoveryEnabledServer> getInitialListOfServers() {
        return fetchServersBasedOnConfiguration();
    }
    
    @Override
    public List<DiscoveryEnabledServer> getUpdatedListOfServers() {
        return fetchServersBasedOnConfiguration();
    }
    
    private List<DiscoveryEnabledServer> fetchServersBasedOnConfiguration() {
        // 从 Eureka 获取服务实例列表
        List<DiscoveryEnabledServer> result = new ArrayList<>();
        List<ServiceInstance> instances = discoveryClient.getInstances(serviceId);
        for (ServiceInstance instance : instances) {
            result.add(new DiscoveryEnabledServer(instance, false));
        }
        return result;
    }
}

2.5 ServerListFilter(服务列表过滤)

public interface ServerListFilter<T extends Server> {
    
    // 过滤服务列表
    public List<T> getFilteredListOfServers(List<T> servers);
}
// ZoneAffinityServerListFilter:同区域优先
public class ZoneAffinityServerListFilter<T extends Server> extends AbstractServerListFilter<T> {
    
    @Override
    public List<T> getFilteredListOfServers(List<T> servers) {
        // 获取当前实例所在区域
        String currentZone = zone;
        
        // 过滤同区域的实例
        List<T> filtered = servers.stream()
            .filter(server -> currentZone.equals(server.getZone()))
            .collect(Collectors.toList());
        
        // 如果同区域实例数量过少,返回全部实例
        if (filtered.size() < threshold) {
            return servers;
        }
        
        return filtered;
    }
}

2.6 IRule(负载均衡策略)

public interface IRule {
    
    // 选择服务实例
    public Server choose(Object key);
    
    // 设置负载均衡器
    public void setLoadBalancer(ILoadBalancer lb);
    
    // 获取负载均衡器
    public ILoadBalancer getLoadBalancer();
}

2.7 七种负载均衡策略详解

2.7.1 RoundRobinRule(轮询)

public class RoundRobinRule extends AbstractLoadBalancerRule {
    
    private AtomicInteger nextServerCyclicCounter;
    
    @Override
    public Server choose(Object key) {
        ILoadBalancer lb = getLoadBalancer();
        if (lb == null) {
            return null;
        }
        
        Server server = null;
        int count = 0;
        while (server == null && count++ < 10) {
            List<Server> reachableServers = lb.getReachableServers();
            List<Server> allServers = lb.getAllServers();
            
            int upCount = reachableServers.size();
            int serverCount = allServers.size();
            
            if (upCount == 0 || serverCount == 0) {
                return null;
            }
            
            // 计算下一个索引
            int nextServerIndex = incrementAndGetModulo(serverCount);
            server = allServers.get(nextServerIndex);
        }
        
        return server;
    }
    
    private int incrementAndGetModulo(int modulo) {
        for (;;) {
            int current = nextServerCyclicCounter.get();
            int next = (current + 1) % modulo;
            if (nextServerCyclicCounter.compareAndSet(current, next)) {
                return next;
            }
        }
    }
}

特点

2.7.2 RandomRule(随机)

public class RandomRule extends AbstractLoadBalancerRule {
    
    private Random random;
    
    @Override
    public Server choose(Object key) {
        ILoadBalancer lb = getLoadBalancer();
        if (lb == null) {
            return null;
        }
        
        Server server = null;
        while (server == null) {
            List<Server> upList = lb.getReachableServers();
            List<Server> allList = lb.getAllServers();
            
            int serverCount = allList.size();
            int upCount = upList.size();
            
            if (upCount == 0) {
                return null;
            }
            
            // 随机选择一个实例
            int index = random.nextInt(serverCount);
            server = upList.get(index);
        }
        
        return server;
    }
}

特点

2.7.3 WeightedResponseTimeRule(响应时间加权)

public class WeightedResponseTimeRule extends RoundRobinRule {
    
    private DynamicServerWeightLoadBalancer weightCalculator;
    
    @Override
    public Server choose(Object key) {
        // 获取权重列表
        List<Double> weights = weightCalculator.getWeights();
        List<Server> servers = getLoadBalancer().getAllServers();
        
        // 根据权重选择实例
        // 响应时间越短,权重越高
        double totalWeight = weights.stream().mapToDouble(Double::doubleValue).sum();
        double randomWeight = Math.random() * totalWeight;
        
        double currentWeight = 0;
        for (int i = 0; i < servers.size(); i++) {
            currentWeight += weights.get(i);
            if (randomWeight <= currentWeight) {
                return servers.get(i);
            }
        }
        
        return super.choose(key);
    }
    
    // 定时任务:计算权重
    class DynamicServerWeightLoadBalancer {
        void maintainWeights() {
            // 根据平均响应时间计算权重
            // 权重 = 总响应时间 - 当前实例响应时间
            // 响应时间越短,权重越高
        }
    }
}

特点

2.7.4 BestAvailableRule(最低并发)

public class BestAvailableRule extends ClientConfigEnabledRoundRobinRule {
    
    @Override
    public Server choose(Object key) {
        ILoadBalancer lb = getLoadBalancer();
        if (lb == null) {
            return null;
        }
        
        Server server = null;
        int minConcurrentConnections = Integer.MAX_VALUE;
        
        // 遍历所有实例,选择并发连接数最少的
        for (Server s : lb.getAllServers()) {
            ServerStats stats = loadBalancerStats.getSingleServerStat(s);
            int concurrentConnections = stats.getActiveRequestsCount();
            
            if (concurrentConnections < minConcurrentConnections) {
                minConcurrentConnections = concurrentConnections;
                server = s;
            }
        }
        
        return server;
    }
}

特点

2.7.5 AvailabilityFilteringRule(可用性过滤)

public class AvailabilityFilteringRule extends PredicateBasedRule {
    
    @Override
    public AbstractServerPredicate getPredicate() {
        // 过滤条件:
        // 1. 实例是可用的
        // 2. 并发连接数不超过阈值
        return compositePredicate;
    }
    
    @Override
    public Server choose(Object key) {
        int count = 0;
        Server server = null;
        
        // 使用轮询,但只从可用实例中选择
        while (count++ < 10) {
            List<Server> servers = getPredicate().getEligibleServers(
                getLoadBalancer().getAllServers(), key);
            
            if (servers.isEmpty()) {
                break;
            }
            
            int index = (int) (count % servers.size());
            server = servers.get(index);
            
            if (server != null && server.isAlive()) {
                return server;
            }
        }
        
        return null;
    }
}

特点

2.7.6 ZoneAvoidanceRule(区域感知)

public class ZoneAvoidanceRule extends PredicateBasedRule {
    
    @Override
    public AbstractServerPredicate getPredicate() {
        // 组合判断:
        // 1. 区域可用性
        // 2. 实例可用性
        return compositePredicate;
    }
    
    @Override
    public Server choose(Object key) {
        // 1. 计算各区域的可用性分数
        // 2. 选择最优区域
        // 3. 在最优区域内轮询选择实例
        return getPredicate().chooseRoundRobinAfterFiltering(
            getLoadBalancer().getAllServers(), key);
    }
}

特点

2.7.7 RetryRule(重试)

public class RetryRule extends AbstractLoadBalancerRule {
    
    private IRule subRule;
    private long maxRetryMillis;
    
    @Override
    public Server choose(Object key) {
        long startTime = System.currentTimeMillis();
        Server server = null;
        
        while (server == null && 
               (System.currentTimeMillis() - startTime) < maxRetryMillis) {
            
            // 使用子策略选择实例
            server = subRule.choose(key);
            
            if (server == null) {
                try {
                    Thread.sleep(100);  // 短暂等待后重试
                } catch (InterruptedException e) {
                    break;
                }
            }
        }
        
        return server;
    }
}

特点

2.8 IPing(健康检查)

public interface IPing {
    
    // 检查实例是否存活
    public boolean isAlive(Server server);
}
// DummyPing:不检查,默认存活
public class DummyPing implements IPing {
    @Override
    public boolean isAlive(Server server) {
        return true;
    }
}

// PingUrl:HTTP 检查
public class PingUrl implements IPing {
    
    private String expectedContent;
    
    @Override
    public boolean isAlive(Server server) {
        String url = "http://" + server.getHostPort() + "/";
        
        try {
            HttpURLConnection conn = (HttpURLConnection) new URL(url).openConnection();
            conn.setConnectTimeout(3000);
            conn.setReadTimeout(3000);
            
            int code = conn.getResponseCode();
            if (code == 200) {
                return true;
            }
        } catch (Exception e) {
            return false;
        }
        
        return false;
    }
}

2.9 Ribbon 配置

# application.yml
user-service:
  ribbon:
    # 负载均衡策略
    NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule
    
    # 服务列表刷新间隔
    ServerListRefreshInterval: 30000
    
    # 连接超时
    ConnectTimeout: 3000
    
    # 读取超时
    ReadTimeout: 5000
    
    # 最大重试次数
    MaxAutoRetries: 1
    
    # 切换实例重试次数
    MaxAutoRetriesNextServer: 1
    
    # 是否对所有操作重试
    OkToRetryOnAllOperations: false
// Java 配置
@Configuration
public class RibbonConfig {
    
    @Bean
    public IRule ribbonRule() {
        // 随机策略
        return new RandomRule();
    }
    
    @Bean
    public IPing ribbonPing() {
        // URL 检查
        return new PingUrl();
    }
}

三、Ribbon 执行流程

3.1 完整执行流程图

┌────────────────────────────────────────────────────────────────────┐
│                    Ribbon 执行流程图                                 │
├────────────────────────────────────────────────────────────────────┤
│                                                                    │
│  HTTP Request                                                      │
│  GET http://user-service/api/user/1                               │
│      ↓                                                             │
│  LoadBalancerInterceptor                                           │
│  ├── 拦截 RestTemplate 请求                                        │
│  └── 提取服务名:user-service                                      │
│      ↓                                                             │
│  RibbonLoadBalancerClient                                          │
│  ├── 获取 ILoadBalancer                                           │
│  │   └── SpringClientFactory.getInstance(serviceId)               │
│  ├── 执行 choose() 选择实例                                        │
│  │   └── IRule.choose(key)                                        │
│      ↓                                                             │
│  【选择实例详细流程】                                                │
│                                                                    │
│  1. ServerList.getUpdatedListOfServers()                           │
│     └── 从注册中心获取服务实例列表                                  │
│        [Instance1, Instance2, Instance3]                           │
│      ↓                                                             │
│  2. ServerListFilter.getFilteredListOfServers()                    │
│     └── 过滤服务实例                                               │
│        [Instance1, Instance2]                                      │
│      ↓                                                             │
│  3. IPing.isAlive()                                                │
│     └── 健康检查                                                   │
│        [Instance1(✓), Instance2(✓)]                                │
│      ↓                                                             │
│  4. IRule.choose()                                                 │
│     └── 根据策略选择实例                                           │
│        → Instance1                                                 │
│      ↓                                                             │
│  【返回选择结果】                                                    │
│                                                                    │
│  RibbonLoadBalancerClient                                          │
│  ├── 构建实际 URL                                                  │
│  │   http://user-service/api/user/1                               │
│  │   → http://192.168.1.1:8080/api/user/1                         │
│  └── 发送请求                                                      │
│      ↓                                                             │
│  HTTP Response                                                     │
│                                                                    │
└────────────────────────────────────────────────────────────────────┘

3.2 源码分析

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();
        
        // 负载均衡选择实例并执行请求
        return loadBalancer.execute(serviceName, 
            new LoadBalancerRequest<ClientHttpResponse>() {
                @Override
                public ClientHttpResponse apply(ServiceInstance instance) throws Exception {
                    // 构建实际 URL
                    URI uri = loadBalancer.reconstructURI(instance, originalUri);
                    HttpRequest newRequest = new HttpRequestAdapter(request, uri);
                    return execution.execute(newRequest, body);
                }
            });
    }
}

RibbonLoadBalancerClient

public class RibbonLoadBalancerClient implements LoadBalancerClient {
    
    @Override
    public <T> T execute(String serviceId, LoadBalancerRequest<T> request) throws IOException {
        // 获取负载均衡器
        ILoadBalancer loadBalancer = getLoadBalancer(serviceId);
        
        // 选择服务实例
        Server server = getServer(loadBalancer);
        if (server == null) {
            throw new IllegalStateException("No instances available for " + serviceId);
        }
        
        // 包装为 RibbonServer
        RibbonServer ribbonServer = new RibbonServer(serviceId, server);
        
        // 执行请求
        return execute(serviceId, ribbonServer, request);
    }
    
    protected Server getServer(ILoadBalancer loadBalancer) {
        if (loadBalancer == null) {
            return null;
        }
        // 调用负载均衡策略选择实例
        return loadBalancer.chooseServer("default");
    }
}

3.3 @LoadBalanced 注解原理

@Target({ ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Qualifier
public @interface LoadBalanced {
}
// LoadBalancerAutoConfiguration
@Configuration
@ConditionalOnClass(RestTemplate.class)
@ConditionalOnBean(LoadBalancerClient.class)
@EnableConfigurationProperties(LoadBalancerProperties.class)
public class LoadBalancerAutoConfiguration {
    
    @LoadBalanced
    @Autowired(required = false)
    private List<RestTemplate> restTemplates = Collections.emptyList();
    
    @Bean
    public LoadBalancerInterceptor loadBalancerInterceptor(
            LoadBalancerClient loadBalancerClient,
            LoadBalancerProperties properties) {
        return new LoadBalancerInterceptor(loadBalancerClient, properties);
    }
    
    @Bean
    public RestTemplateCustomizer restTemplateCustomizer(
            final LoadBalancerInterceptor loadBalancerInterceptor) {
        return restTemplate -> {
            // 为所有 @LoadBalanced 标注的 RestTemplate 添加拦截器
            List<ClientHttpRequestInterceptor> list = new ArrayList<>(
                restTemplate.getInterceptors());
            list.add(loadBalancerInterceptor);
            restTemplate.setInterceptors(list);
        };
    }
}

四、OpenFeign 详解

4.1 OpenFeign 简介

OpenFeign 是一个声明式 HTTP 客户端,通过注解定义 HTTP 请求,简化了服务调用代码。

特点

4.2 OpenFeign 使用

<!-- 依赖 -->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
// 启动类
@SpringBootApplication
@EnableFeignClients
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}
// Feign 客户端定义
@FeignClient(name = "user-service", path = "/api/user")
public interface UserClient {
    
    @GetMapping("/{id}")
    Result<User> getUser(@PathVariable("id") Long id);
    
    @GetMapping
    Result<List<User>> listUsers(@RequestParam("page") Integer page,
                                 @RequestParam("size") Integer size);
    
    @PostMapping
    Result<User> createUser(@RequestBody UserDTO userDTO);
    
    @PutMapping("/{id}")
    Result<User> updateUser(@PathVariable("id") Long id, 
                           @RequestBody UserDTO userDTO);
    
    @DeleteMapping("/{id}")
    Result<Void> deleteUser(@PathVariable("id") Long id);
}
// 使用 Feign 客户端
@Service
public class OrderService {
    
    @Autowired
    private UserClient userClient;
    
    public Order createOrder(Long userId, OrderDTO orderDTO) {
        // 调用用户服务
        Result<User> userResult = userClient.getUser(userId);
        if (!userResult.isSuccess()) {
            throw new RuntimeException("用户不存在");
        }
        
        User user = userResult.getData();
        
        // 创建订单...
        Order order = new Order();
        order.setUserId(user.getId());
        order.setUserName(user.getName());
        
        return orderRepository.save(order);
    }
}

4.3 Feign 核心组件

┌─────────────────────────────────────────────────────────────┐
│                   Feign 核心组件                              │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  Feign.Builder                                              │
│  ├── 构建 Feign 客户端                                      │
│  └── 配置各个组件                                           │
│                                                             │
│  Contract                                                   │
│  ├── 解析接口注解                                           │
│  └── SpringMvcContract:支持 Spring MVC 注解               │
│                                                             │
│  Encoder                                                    │
│  ├── 编码请求体                                             │
│  └── SpringEncoder:支持 JSON 序列化                        │
│                                                             │
│  Decoder                                                    │
│  ├── 解码响应体                                             │
│  └── SpringDecoder:支持 JSON 反序列化                      │
│                                                             │
│  Client                                                     │
│  ├── 执行 HTTP 请求                                         │
│  ├── Default:HttpURLConnection                             │
│  └── LoadBalancerFeignClient:集成 Ribbon                   │
│                                                             │
│  RequestInterceptor                                         │
│  ├── 请求拦截器                                             │
│  └── 添加统一 Header、认证信息                              │
│                                                             │
│  Logger                                                     │
│  ├── 日志记录                                               │
│  └── Slf4jLogger                                            │
│                                                             │
└─────────────────────────────────────────────────────────────┘

4.4 Feign 执行流程

┌────────────────────────────────────────────────────────────────────┐
│                    Feign 执行流程图                                  │
├────────────────────────────────────────────────────────────────────┤
│                                                                    │
│  userClient.getUser(1L)                                            │
│      ↓                                                             │
│  ReflectiveFeign.FeignInvocationHandler.invoke()                   │
│  ├── JDK 动态代理                                                  │
│  └── 拦截方法调用                                                  │
│      ↓                                                             │
│  SynchronousMethodHandler.invoke()                                 │
│  ├── 构建 RequestTemplate                                          │
│  │   ├── 解析 @GetMapping                                         │
│  │   ├── 解析 @PathVariable                                       │
│  │   └── 构建请求 URL 和参数                                      │
│      ↓                                                             │
│  RequestInterceptor.apply()                                        │
│  ├── 添加请求头                                                    │
│  ├── 添加认证信息                                                  │
│  └── 修改请求参数                                                  │
│      ↓                                                             │
│  Encoder.encode()                                                  │
│  └── 序列化请求体(POST/PUT)                                      │
│      ↓                                                             │
│  LoadBalancerFeignClient.execute()                                 │
│  ├── 解析服务名                                                    │
│  ├── Ribbon 选择实例                                               │
│  └── 发送 HTTP 请求                                                │
│      ↓                                                             │
│  HTTP 响应                                                         │
│      ↓                                                             │
│  Decoder.decode()                                                  │
│  └── 反序列化响应体                                                │
│      ↓                                                             │
│  返回结果 Result<User>                                             │
│                                                                    │
└────────────────────────────────────────────────────────────────────┘

4.5 Feign 配置

# application.yml
feign:
  client:
    config:
      default:
        # 连接超时
        connectTimeout: 5000
        # 读取超时
        readTimeout: 5000
        # 日志级别
        loggerLevel: FULL
      
      user-service:
        connectTimeout: 3000
        readTimeout: 10000
  
  # GZIP 压缩
  compression:
    request:
      enabled: true
      mime-types: application/json
      min-request-size: 2048
    response:
      enabled: true
  
  # Sentinel 集成
  sentinel:
    enabled: true
  
  # HTTP 客户端
  httpclient:
    enabled: true
    max-connections: 200
    max-connections-per-route: 50

4.6 Feign 日志配置

@Configuration
public class FeignConfig {
    
    @Bean
    public Logger.Level feignLoggerLevel() {
        // 日志级别
        // NONE:无日志
        // BASIC:请求方法、URL、响应状态码、执行时间
        // HEADERS:BASIC + 请求/响应头
        // FULL:HEADERS + 请求/响应体
        return Logger.Level.FULL;
    }
}
# 为特定 Feign 客户端配置日志级别
logging:
  level:
    com.example.client.UserClient: DEBUG

4.7 Feign 请求拦截器

@Configuration
public class FeignConfig {
    
    @Bean
    public RequestInterceptor authInterceptor() {
        return template -> {
            // 从当前上下文获取 Token
            String token = SecurityContextHolder.getContext()
                .getAuthentication()
                .getCredentials()
                .toString();
            
            // 添加认证头
            template.header("Authorization", "Bearer " + token);
        };
    }
    
    @Bean
    public RequestInterceptor traceInterceptor() {
        return template -> {
            // 添加链路追踪 ID
            String traceId = MDC.get("traceId");
            if (StringUtils.hasText(traceId)) {
                template.header("X-Trace-Id", traceId);
            }
        };
    }
}

4.8 Feign 降级配置

@FeignClient(name = "user-service", 
             fallback = UserClientFallback.class)
public interface UserClient {
    
    @GetMapping("/{id}")
    Result<User> getUser(@PathVariable("id") Long id);
}

@Component
public class UserClientFallback implements UserClient {
    
    @Override
    public Result<User> getUser(Long id) {
        return Result.error("用户服务不可用");
    }
}
// 使用 FallbackFactory 获取异常信息
@FeignClient(name = "user-service", 
             fallbackFactory = UserClientFallbackFactory.class)
public interface UserClient {
    
    @GetMapping("/{id}")
    Result<User> getUser(@PathVariable("id") Long id);
}

@Component
public class UserClientFallbackFactory implements FallbackFactory<UserClient> {
    
    @Override
    public UserClient create(Throwable cause) {
        return new UserClient() {
            @Override
            public Result<User> getUser(Long id) {
                log.error("调用用户服务失败", cause);
                
                if (cause instanceof FeignException.NotFound) {
                    return Result.error("用户不存在");
                }
                if (cause instanceof FeignException.ServiceUnavailable) {
                    return Result.error("用户服务不可用");
                }
                
                return Result.error("用户服务调用失败: " + cause.getMessage());
            }
        };
    }
}

4.9 Feign 性能优化

4.9.1 使用连接池

<!-- 替换默认的 HttpURLConnection -->
<dependency>
    <groupId>io.github.openfeign</groupId>
    <artifactId>feign-httpclient</artifactId>
</dependency>
feign:
  httpclient:
    enabled: true
    max-connections: 200          # 最大连接数
    max-connections-per-route: 50 # 每个路由的最大连接数
    connection-timeout: 5000      # 连接超时
    connection-timer-repeat: 3000 # 连接定时器重复间隔

4.9.2 GZIP 压缩

feign:
  compression:
    request:
      enabled: true
      mime-types: application/json,application/xml
      min-request-size: 2048      # 最小压缩大小
    response:
      enabled: true

4.9.3 超时配置

feign:
  client:
    config:
      default:
        connectTimeout: 5000      # 连接超时 5s
        readTimeout: 10000        # 读取超时 10s

4.10 自定义 Feign 配置

@FeignClient(name = "user-service", 
             configuration = UserFeignConfig.class,
             fallbackFactory = UserClientFallbackFactory.class)
public interface UserClient {
    // ...
}

public class UserFeignConfig {
    
    @Bean
    public Encoder encoder() {
        return new SpringEncoder(new SpringEncoderDelegate(messageConverters));
    }
    
    @Bean
    public Decoder decoder() {
        return new SpringDecoder(messageConverters);
    }
    
    @Bean
    public Contract contract() {
        return new SpringMvcContract();
    }
    
    @Bean
    public Logger.Level loggerLevel() {
        return Logger.Level.FULL;
    }
    
    @Bean
    public RequestInterceptor authInterceptor() {
        return template -> {
            // 自定义拦截逻辑
        };
    }
}

五、Spring Cloud LoadBalancer(Ribbon 替代方案)

5.1 背景

Ribbon 已进入维护模式,Spring Cloud 提供了新的负载均衡组件:Spring Cloud LoadBalancer。

5.2 添加依赖

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>

5.3 配置负载均衡策略

@Configuration
public class LoadBalancerConfig {
    
    @Bean
    public ReactorServiceInstanceLoadBalancer randomLoadBalancer(
            Environment environment, 
            LoadBalancerClientFactory factory) {
        String serviceId = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);
        return new RandomLoadBalancer(
            factory.getLazyProvider(serviceId, ServiceInstanceListSupplier.class), 
            serviceId);
    }
}
// 自定义负载均衡策略
public class CustomLoadBalancer implements ReactorServiceInstanceLoadBalancer {
    
    private final ServiceInstanceListSupplier serviceInstanceListSupplier;
    
    public CustomLoadBalancer(ServiceInstanceListSupplier supplier) {
        this.serviceInstanceListSupplier = supplier;
    }
    
    @Override
    public Mono<Response<ServiceInstance>> choose(Request request) {
        return serviceInstanceListSupplier.get()
            .next()
            .map(instances -> {
                // 自定义选择逻辑
                ServiceInstance instance = selectInstance(instances);
                return new DefaultResponse(instance);
            });
    }
    
    private ServiceInstance selectInstance(List<ServiceInstance> instances) {
        // 自定义算法
        // 如:加权、一致性哈希等
        return instances.get(0);
    }
}

📖 高频面试题

Q1: Ribbon 的七种负载均衡策略分别适用于什么场景?

答:

策略 适用场景
RoundRobinRule 服务器性能相近,请求均匀分布
RandomRule 请求量大时,随机分布足够均匀
WeightedResponseTime 服务器性能差异大,响应时间差距明显
BestAvailableRule 需要避免单实例过载
AvailabilityFilteringRule 需要剔除故障实例
ZoneAvoidanceRule 多区域部署,优先同区域调用
RetryRule 需要在选择失败时重试

Q2: @LoadBalanced 注解的作用是什么?

答:

1. 作用

2. 原理

// 通过 @Qualifier 注解,收集所有 @LoadBalanced 标注的 RestTemplate
@LoadBalanced
@Autowired(required = false)
private List<RestTemplate> restTemplates = Collections.emptyList();

// 为每个 RestTemplate 添加拦截器
restTemplate.setInterceptors(Arrays.asList(loadBalancerInterceptor));

3. 使用

@Configuration
public class RestTemplateConfig {
    
    @Bean
    @LoadBalanced
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }
}

@Service
public class UserService {
    
    @Autowired
    private RestTemplate restTemplate;
    
    public User getUser(Long id) {
        // 使用服务名替代具体地址
        return restTemplate.getForObject(
            "http://user-service/api/user/" + id, 
            User.class);
    }
}

Q3: Feign 如何实现负载均衡?

答:

Feign 默认集成 Ribbon 实现负载均衡:

Feign Client
    ↓
LoadBalancerFeignClient
    ↓
FeignLoadBalancer
    ↓
Ribbon (ILoadBalancer + IRule)
    ↓
选择服务实例
    ↓
发送请求

配置方式

user-service:
  ribbon:
    NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule
    ConnectTimeout: 3000
    ReadTimeout: 5000

Q4: Feign 和 RestTemplate 的区别?

答:

特性 Feign RestTemplate
使用方式 声明式接口 编程式调用
代码简洁度
负载均衡 内置 Ribbon 需要 @LoadBalanced
熔断降级 内置支持 需要额外配置
请求拦截 RequestInterceptor ClientHttpRequestInterceptor
性能 稍低(代理开销) 稍高
灵活性 较低 较高

选择建议

Q5: 如何优化 Feign 性能?

答:

1. 使用连接池

<dependency>
    <groupId>io.github.openfeign</groupId>
    <artifactId>feign-httpclient</artifactId>
</dependency>

2. 配置连接池参数

feign:
  httpclient:
    enabled: true
    max-connections: 200
    max-connections-per-route: 50

3. 开启 GZIP 压缩

feign:
  compression:
    request:
      enabled: true
    response:
      enabled: true

4. 设置合理的超时

feign:
  client:
    config:
      default:
        connectTimeout: 5000
        readTimeout: 10000

5. 合理配置日志级别

@Bean
public Logger.Level feignLoggerLevel() {
    return Logger.Level.BASIC;  // 生产环境使用 BASIC
}

参考链接: