0
点赞
收藏
分享

微信扫一扫

Spring Cloud的@LoadBalanced 与 RestTemplate

什么是 @LoadBalanced

@LoadBalanced 是 Spring Cloud 提供给 RestTemplateWebClient 等客户端的标记性注解(marker annotation),它的作用是:

开启客户端负载均衡(Client-Side Load Balancing)

📌 核心能力:

  • 将服务名(如 http://orderservice)解析为真实 IP + 端口
  • 从注册中心(如 Nacos、Eureka)获取服务实例列表
  • 按照负载均衡策略(如轮询、随机)选择一个实例
  • 自动完成 HTTP 请求的转发

核心原理:@LoadBalanced 如何工作?

1️⃣ 关键组件:LoadBalancerInterceptor

当你在 RestTemplate 上添加 @LoadBalanced 注解后,Spring Cloud 会自动为其注册一个 拦截器(Interceptor) —— LoadBalancerInterceptor

// 伪代码:拦截器逻辑
public ClientHttpResponse intercept(
    HttpRequest request, 
    byte[] body, 
    ClientHttpRequestExecution execution) {

    // 1. 获取服务名(如 "userservice")
    URI originalUri = request.getURI();
    String serviceName = originalUri.getHost();

    // 2. 从 LoadBalancerClient 获取实例
    ServiceInstance instance = loadBalancerClient.choose(serviceName);

    // 3. 替换 URL 为真实地址
    URI newUri = URI.create(
        originalUri.toString().replace(serviceName, 
            instance.getHost() + ":" + instance.getPort())
    );

    // 4. 执行真实请求
    return execution.execute(new Request(newUri, ...), body);
}

📌 本质
@LoadBalanced 并没有改变 RestTemplate 的发送逻辑,而是通过拦截 + URL 重写的方式实现负载均衡。

 实战:自定义负载均衡策略

场景:实现“同区域优先”调用(避免跨机房调用)

import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.loadbalancer.core.ReactorServiceInstanceLoadBalancer;
import org.springframework.cloud.loadbalancer.support.LoadBalancerClientFactory;
import reactor.core.publisher.Mono;

public class ZonePreferenceLoadBalancer implements ReactorServiceInstanceLoadBalancer {

    private final String localZone = "beijing"; // 本地机房

    @Override
    public Mono<Response<ServiceInstance>> choose(Request request) {
        return Mono.fromCallable(() -> {
            // 获取服务名
            String serviceId = (String) request.getContext().getServiceKey();
            
            // 获取所有实例
            List<ServiceInstance> instances = 
                LoadBalancerClientFactory.getInstances(serviceId);

            // 优先选择同区域实例
            List<ServiceInstance> preferred = instances.stream()
                .filter(instance -> "beijing".equals(instance.getMetadata().get("zone")))
                .collect(Collectors.toList());

            if (!preferred.isEmpty()) {
                return new DefaultResponse(
                    preferred.get(new Random().nextInt(preferred.size()))
                );
            }

            // 否则选择任意实例
            return new DefaultResponse(
                instances.get(new Random().nextInt(instances.size()))
            );
        }).onErrorReturn(new EmptyResponse());
    }
}

注册自定义策略

@Configuration
public class LoadBalancerConfig {

    @Bean
    @ConditionalOnMissingBean
    public ReactorServiceInstanceLoadBalancer zonePreferenceLoadBalancer(
        LoadBalancerClientFactory clientFactory) {
        String serviceId = clientFactory.get().getName();
        return new ZonePreferenceLoadBalancer();
    }
}

不要把 @LoadBalanced 当作“魔法注解”。理解其背后的设计思想,才能在复杂场景中游刃有余。它是 Spring Cloud 客户端负载均衡的基石,也是你掌握微服务调用链路的关键一环。

举报

相关推荐

0 条评论