分布式微服务--万字详解 微服务的各种负载均衡全场景以注意点

前言:

1. 使用方式分类总览

序号 使用形式 是否基于服务名调用 是否需 @LoadBalanced 备注
1 RestTemplate + 自定义负载均衡 ❌ 否(手动拼接URL) ❌ 否 手动选择服务实例
2 RestTemplate + Ribbon(非服务名) ❌ 否(手动拼接URL) ❌ 否 使用 DiscoveryClient+Ribbon手动选节点
3 RestTemplate + Spring Cloud LoadBalancer(非服务名) ❌ 否(手动拼接URL) ❌ 否 使用 LoadBalancerClient 手动选实例
4 RestTemplate + Ribbon(基于服务名) ✅ 是 ✅ 是 自动通过服务名解析
5 RestTemplate + Spring Cloud LoadBalancer(基于服务名) ✅ 是 ✅ 是 推荐替代 Ribbon
6 OpenFeign + Ribbon ✅ 是 ❌ 否 Spring Cloud 2020 以前使用
7 OpenFeign + Spring Cloud LoadBalancer ✅ 是 ❌ 否 Spring Cloud 2020 以后默认方案

2. @LoadBalanced 的作用

  • @LoadBalanced 是加在 RestTemplate@Bean 上的注解。

  • 作用:让这个 RestTemplate 支持通过服务名调用,并自动使用 Ribbon 或 Spring Cloud LoadBalancer 做客户端负载均衡

  • 必须加 @LoadBalanced 才能支持服务名调用 ,比如 restTemplate.getForObject("http://producer/getMember", String.class)

  • 如果没加,RestTemplate 就是普通的,只能用完整 URL(IP+端口)调用,自己拼 URL。

3. 使用Ribbon

java 复制代码
//1.不加也可以,是默认
    @Bean
    public IRule ribbonRule() {
        return new RandomRule(); //随机策略
        return new RoundRule(); // 轮询策略
        return new RoundRule(); // 自定义负载均衡策略
    }
//2. 也可以自定义负载均衡的方式
//@Component 建议采用@Bean方式注入
//原因:
    //1. 可读性差:别人一看 @Bean 没配置,可能误以为用的默认 RoundRobinRule。

    //2. 项目中多个服务时可能出现冲突:比如你给多个服务配置不同的 IRule,使用 @Component 会全局生效,反而不好控制。
public class BoyatopWeightLoadBalance extends AbstractLoadBalancerRule {
    @Override
    public void initWithNiwsConfig(IClientConfig iClientConfig) {

    }

    private AtomicInteger countAtomicInteger = new AtomicInteger(0);

    @Override
    public Server choose(Object key) {
        ILoadBalancer lb = getLoadBalancer();
        if (lb == null) {
            return null;
        }
        // 获取该服务接口地址 多个
        //获取的同时是该服务没有下线的地址  Server
        List<Server> upList = lb.getReachableServers();
        //目的是单纯server没有实现权重,只有选择nacos的server才可以实现权重
        ArrayList<NacosServer> newNacosServers = new ArrayList<>();
        upList.forEach((s) -> {
            NacosServer nacosServer = (NacosServer) s;
            double weight = nacosServer.getInstance().getWeight();
            for (int i = 0; i < weight; i++) {
                newNacosServers.add(nacosServer);
            }
        });
        return newNacosServers.get(countAtomicInteger.incrementAndGet() % newNacosServers.size());
    }
}

4. 使用Spring Cloud LoadBalancer

不同于 Ribbon,Spring Cloud LoadBalancer 不支持 IRule,如需更换策略需使用 RandomLoadBalancer

若要配置随机策略,需自定义 Spring Cloud LoadBalancer 的配置类。(下面不加也是可以的,为默认的)

java 复制代码
@Configuration
public class ProducerLoadBalancerConfig {

    @Bean
    public ReactorLoadBalancer<ServiceInstance> randomLoadBalancer(
        Environment environment,
        LoadBalancerClientFactory factory) {
        String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);
        return new RandomLoadBalancer(factory.getLazyProvider(name, ServiceInstanceListSupplier.class), name);
    }
}

application.yml:

复制代码
spring:
  cloud:
    loadbalancer:
      clients:
        producer:
          configuration: com.example.ProducerLoadBalancerConfig

1. RestTemplate + 自定义负载均衡(手动选择实例 + 手动拼接 URL)

✅ 是否基于服务名:❌ 否

✅ 是否需要 @LoadBalanced:❌ 否

✅ 特点:

  • 不依赖 Ribbon 或 LoadBalancer。

  • 手动从注册中心获取实例列表,实现负载均衡逻辑(如随机)。

✅ 示例代码:

java 复制代码
@RestController
public class OrderController {

    @Autowired
    private DiscoveryClient discoveryClient;

    @Autowired
    private RestTemplate restTemplate;

    @GetMapping("/orderToMember")
    public String orderToMember() {
        List<ServiceInstance> instances = discoveryClient.getInstances("producer");
        if (instances == null || instances.isEmpty()) return "No instance";

        // 随机负载均衡
        ServiceInstance instance = instances.get(new Random().nextInt(instances.size()));
        String url = instance.getUri() + "/getMember";

        return restTemplate.getForObject(url, String.class);
    }

    @Bean
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }
}

2. RestTemplate + Ribbon + 手动拼接 URL(非服务名调用)

✅ 是否基于服务名:❌ 否

✅ 是否需要 @LoadBalanced:❌ 否

✅ 特点:

  • 实际并未用到 Ribbon 的服务发现,仅使用 Ribbon 提供的负载策略(如 RandomRule)。

  • 使用 LoadBalancerClient.choose() 手动选择服务实例。

✅ 示例代码:

java 复制代码
@Configuration
public class RibbonConfig {
    //不加也可以,是默认
    @Bean
    public IRule ribbonRule() {
        return new RandomRule(); //随机策略
        return new RoundRule(); // 轮询策略
    }

    @Bean
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }
}

@RestController
public class OrderController {

    @Autowired
    private LoadBalancerClient loadBalancerClient;

    @Autowired
    private RestTemplate restTemplate;

    @GetMapping("/orderToMember")
    public String orderToMember() {
        ServiceInstance instance = loadBalancerClient.choose("producer");
        String url = instance.getUri() + "/getMember";
        return restTemplate.getForObject(url, String.class);
    }
}

3. RestTemplate + Spring Cloud LoadBalancer + 手动拼接 URL

✅ 是否基于服务名:❌ 否

✅ 是否需要 @LoadBalanced:❌ 否

✅ 示例代码:

java 复制代码
@RestController
public class OrderController {

    @Autowired
    private LoadBalancerClient loadBalancerClient;

    @Autowired
    private RestTemplate restTemplate;

    @GetMapping("/orderToMember")
    public String orderToMember() {
        ServiceInstance instance = loadBalancerClient.choose("producer");
        String url = instance.getUri() + "/getMember";
        return restTemplate.getForObject(url, String.class);
    }

    @Bean
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }
}

⚠️ 不同于 Ribbon,Spring Cloud LoadBalancer 不支持 IRule,如需更换策略需使用 RandomLoadBalancer

若要配置随机策略,需自定义 Spring Cloud LoadBalancer 的配置类。

java 复制代码
@Configuration
public class ProducerLoadBalancerConfig {

    @Bean
    public ReactorLoadBalancer<ServiceInstance> randomLoadBalancer(
        Environment environment,
        LoadBalancerClientFactory factory) {
        String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);
        return new RandomLoadBalancer(factory.getLazyProvider(name, ServiceInstanceListSupplier.class), name);
    }
}

application.yml:

复制代码
spring:
  cloud:
    loadbalancer:
      clients:
        producer:
          configuration: com.example.ProducerLoadBalancerConfig

4. RestTemplate + Ribbon(基于服务名)

✅ 是否基于服务名:✅ 是

✅ 是否需要 @LoadBalanced:✅ 是

✅ 示例代码:

java 复制代码
@Configuration
public class RibbonConfig {
    @Bean
    public IRule ribbonRule() {
        return new RandomRule();
    }

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

@RestController
public class OrderController {

    @Autowired
    private RestTemplate restTemplate;

    @GetMapping("/orderToMember")
    public String orderToMember() {
        return restTemplate.getForObject("http://producer/getMember", String.class);
    }
}

5. RestTemplate + Spring Cloud LoadBalancer(基于服务名)

✅ 是否基于服务名:✅ 是

✅ 是否需要 @LoadBalanced:✅ 是

✅ 示例代码:

java 复制代码
@Configuration
public class LoadBalancerConfig {
    @Bean
    @LoadBalanced
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }
}

@RestController
public class OrderController {

    @Autowired
    private RestTemplate restTemplate;

    @GetMapping("/orderToMember")
    public String orderToMember() {
        return restTemplate.getForObject("http://producer/getMember", String.class);
    }
}

若要配置随机策略,需自定义 Spring Cloud LoadBalancer 的配置类。

java 复制代码
@Configuration
public class ProducerLoadBalancerConfig {

    @Bean
    public ReactorLoadBalancer<ServiceInstance> randomLoadBalancer(
        Environment environment,
        LoadBalancerClientFactory factory) {
        String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);
        return new RandomLoadBalancer(factory.getLazyProvider(name, ServiceInstanceListSupplier.class), name);
    }
}

application.yml:

复制代码
spring:
  cloud:
    loadbalancer:
      clients:
        producer:
          configuration: com.example.ProducerLoadBalancerConfig

6. OpenFeign + Ribbon

✅ 是否基于服务名:✅ 是(自动)

✅ 是否需要 @LoadBalanced:❌ 否

✅ 示例代码:

java 复制代码
@FeignClient(name = "producer")
public interface MemberFeignClient {
    @GetMapping("/getMember")
    String getMember();
}

@RestController
public class OrderController {

    @Autowired
    private MemberFeignClient memberFeignClient;

    @GetMapping("/orderToMember")
    public String orderToMember() {
        return memberFeignClient.getMember();
    }
}

@Configuration
public class RibbonConfig {
    @Bean
    public IRule ribbonRule() {
        return new RandomRule();
    }
}

7. OpenFeign + Spring Cloud LoadBalancer

✅ 是否基于服务名:✅ 是(自动)

✅ 是否需要 @LoadBalanced:❌ 否

✅ 示例代码:

java 复制代码
@FeignClient(name = "producer")
public interface MemberFeignClient {
    @GetMapping("/getMember")
    String getMember();
}

@RestController
public class OrderController {

    @Autowired
    private MemberFeignClient memberFeignClient;

    @GetMapping("/orderToMember")
    public String orderToMember() {
        return memberFeignClient.getMember();
    }
}

// application.yml:
spring:
  cloud:
    loadbalancer:
      clients:
        producer:
          configuration: com.example.ProducerLoadBalancerConfig

随机策略方式同上使用 ReactorLoadBalancer


8. 总结对比表格

方式 基于服务名 需要 @LoadBalanced 策略配置方式 是否推荐
RestTemplate + 自定义负载均衡 手动逻辑(如随机) ✅ 灵活
RestTemplate + Ribbon(非服务名) IRule / RandomRule ⚠️不常见
RestTemplate + Spring Cloud LB(非服务名) ReactorLoadBalancer ⚠️一般用服务名
RestTemplate + Ribbon(服务名) IRule / RandomRule ❌ 已弃用
RestTemplate + Spring Cloud LB(服务名) ReactorLoadBalancer ✅ 推荐
OpenFeign + Ribbon IRule / RandomRule ❌ 已弃用
OpenFeign + Spring Cloud LB ReactorLoadBalancer ✅ 推荐

9. 举例说明:是否基于服务名的区别

以RestTemplate + Spring Cloud LoadBalancer + 手动拼接 URL和RestTemplate + Spring Cloud LoadBalancer(基于服务名)为例

1. RestTemplate + Spring Cloud LoadBalancer + 手动拼接 URL

  • 通常写法
java 复制代码
@Bean
public RestTemplate restTemplate() {
    return new RestTemplate();  // 普通RestTemplate,没有@LoadBalanced
}

@Autowired
private LoadBalancerClient loadBalancerClient;

public void call() {
    ServiceInstance instance = loadBalancerClient.choose("service-name");
    String url = instance.getUri() + "/api";
    restTemplate.getForObject(url, String.class);
}
  • 特点

    • RestTemplate 是普通实例,不支持基于服务名自动负载均衡。

    • 你手动通过 LoadBalancerClient 获取具体服务实例,再拼完整 URL。

    • 负载均衡由 LoadBalancerClient 实现,调用时需要自己拼接 URL。


2. RestTemplate + Spring Cloud LoadBalancer(基于服务名)

  • 通常写法
java 复制代码
@Bean
@LoadBalanced
public RestTemplate restTemplate() {
    return new RestTemplate();  // 带@LoadBalanced支持服务名调用
}

public void call() {
    restTemplate.getForObject("http://service-name/api", String.class);
}
  • 特点

    • RestTemplate 通过 @LoadBalanced 注解支持服务名解析和负载均衡。

    • 你直接调用服务名 URL,不用关心具体实例。

    • 负载均衡和服务实例选择完全交给 Spring Cloud LoadBalancer 内部管理。


3. 核心区别总结

特性 手动拼 URL(无 @LoadBalanced) 基于服务名调用(带 @LoadBalanced)
RestTemplate 是否带 @LoadBalanced
负载均衡策略执行点 LoadBalancerClient 手动调用实例选取 RestTemplate 内部自动调用负载均衡
请求时是否需手动拼接 URL 是,需要拼接具体实例的 URI 否,直接写服务名 URL
代码简洁性 代码相对复杂,需要手动管理实例和 URL 代码简洁,专注业务调用
适用场景 需要自定义实例选择或做扩展时使用 绝大多数场景推荐使用

4. 结论

是否加了 @LoadBalanced 是两种方式最主要的区别,但真正关键是:

  • @LoadBalancedRestTemplate 支持直接用服务名调用,负载均衡自动执行;

  • 不带 @LoadBalanced 时需要自己用 LoadBalancerClient 选实例,拼 URL 手动调用。

相关推荐
RainbowSea5 小时前
12. LangChain4j + 向量数据库操作详细说明
java·langchain·ai编程
RainbowSea5 小时前
11. LangChain4j + Tools(Function Calling)的使用详细说明
java·langchain·ai编程
考虑考虑9 小时前
Jpa使用union all
java·spring boot·后端
用户3721574261359 小时前
Java 实现 Excel 与 TXT 文本高效互转
java
浮游本尊10 小时前
Java学习第22天 - 云原生与容器化
java
稻草人222212 小时前
java Excel 导出 ,如何实现八倍效率优化,以及代码分层,方法封装
后端·架构
渣哥12 小时前
原来 Java 里线程安全集合有这么多种
java
间彧12 小时前
Spring Boot集成Spring Security完整指南
java
间彧12 小时前
Spring Secutiy基本原理及工作流程
java
数据智能老司机13 小时前
精通 Python 设计模式——创建型设计模式
python·设计模式·架构