wayne
wayne
发布于 2025-07-15 / 3 阅读
0
0

Spring Cloud - OpenFeign - 初始化与负载均衡

初始化

一、初始化触发点:@EnableFeignClients

Spring Boot 应用启动时,@EnableFeignClients 注解会触发 Feign 的初始化:

@SpringBootApplication
@EnableFeignClients // 关键启动注解
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

二、核心初始化流程

1. 扫描阶段 - FeignClientsRegistrar

关键代码 (FeignClientsRegistrar.registerBeanDefinitions):

public void registerBeanDefinitions(...) {
    // 扫描所有@FeignClient接口
    Set<BeanDefinition> candidateComponents = scanner.findCandidateComponents(basePackage);
    
    for (BeanDefinition candidateComponent : candidateComponents) {
        // 为每个Feign客户端创建Bean定义
        registerFeignClient(registry, annotationMetadata, attributes);
    }
}

2. 创建 FeignClient 工厂 - FeignClientFactoryBean

每个 @FeignClient 接口都会关联一个 FeignClientFactoryBean

class FeignClientFactoryBean implements FactoryBean<Object> {
    @Override
    public Object getObject() {
        return getTarget(); // 创建代理对象
    }
    
    protected <T> T getTarget() {
        // 创建Feign.Builder
        Feign.Builder builder = feign(context);
        
        // 配置目标URL
        Targeter targeter = get(context, Targeter.class);
        return targeter.target(this, builder, context, 
                Target.HardCodedTarget.create(type, name, url));
    }
}

3. 构建 Feign 实例 - Feign.Builder

创建实际的 Feign 客户端:

protected Feign.Builder feign(FeignContext context) {
    // 1. 获取基础组件
    FeignLoggerFactory loggerFactory = get(context, FeignLoggerFactory.class);
    Logger logger = loggerFactory.create(this.type);
    
    // 2. 配置契约(解析注解)
    Contract contract = get(context, Contract.class);
    
    // 3. 配置编码器/解码器
    Encoder encoder = getOptional(context, Encoder.class);
    Decoder decoder = getOptional(context, Decoder.class);
    
    // 4. 创建Builder
    Feign.Builder builder = get(context, Feign.Builder.class)
        .logger(logger)
        .encoder(encoder)
        .decoder(decoder)
        .contract(contract);
    
    // 5. 配置拦截器
    applyInterceptors(context, builder);
    
    return builder;
}

4. 生成动态代理 - ReflectiveFeign

核心代理创建过程:

public class ReflectiveFeign extends Feign {
    public <T> T newInstance(Target<T> target) {
        // 创建方法映射
        Map<Method, MethodHandler> methodToHandler = new LinkedHashMap<>();
        for (Method method : target.type().getMethods()) {
            if (method.getDeclaringClass() == Object.class) continue;
            methodToHandler.put(method, createHandler(method, target));
        }
        
        // 创建动态代理
        InvocationHandler handler = factory.create(target, methodToHandler);
        T proxy = (T) Proxy.newProxyInstance(
            target.type().getClassLoader(),
            new Class<?>[] {target.type()},
            handler);
        
        return proxy;
    }
}

5. 负载均衡集成 - LoadBalancerFeignClient

当使用 Ribbon 时,会创建负载均衡客户端:

class LoadBalancerFeignClient implements Client {
    private final Client delegate;
    private final LoadBalancerClient loadBalancer;
    
    @Override
    public Response execute(Request request, Request.Options options) {
        // 解析服务名
        final URI originalUri = URI.create(request.url());
        String serviceId = originalUri.getHost();
        
        // 通过负载均衡选择实例
        ServiceInstance instance = loadBalancer.choose(serviceId);
        URI uri = loadBalancer.reconstructURI(instance, originalUri);
        
        // 创建新请求
        Request newRequest = buildRequest(request, uri);
        
        // 委托给底层客户端执行
        return delegate.execute(newRequest, options);
    }
}

总结

负载均衡核心步骤

Ribbon负载均衡核心步骤

  1. 服务发现
    从注册中心(如Eureka)拉取服务实例列表。

  2. 策略选择
    根据配置的IRule实现类选择实例,默认策略为RoundRobinRule(轮询)。

  3. 请求转发
    将Feign请求中的服务名(如http://user-service/api)替换为具体实例地址(如http://192.168.1.2:8080/api)。

负载均衡策略配置

# 示例:配置随机策略
user-service:  # 服务名
  ribbon:
    NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule

常用策略

策略类

算法

适用场景

RoundRobinRule

轮询

默认均匀分发

RandomRule

随机

避免热点实例

WeightedResponseTimeRule

根据服务实例的历史响应时间动态分配权重,响应越快的实例获得更高的被选概率。

性能敏感型系统

RetryRule

在基础策略(如轮询)失败时自动重试其他实例,提高可用性。

高可用要求场景


评论