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

前言:

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 手动调用。

相关推荐
我是廖志伟7 分钟前
【jar包启动,每天生成一个日志文件】
java·jar
Rookie小强10 分钟前
kafka的rebalance机制是什么
分布式·kafka
掉鱼的猫17 分钟前
Solon StateMachine 实现状态机使用示例详解
java·状态机
用户61354114601624 分钟前
BurpSuite 1.4.07.jar 怎么使用?详细安装和抓包教程(附安装包下载)
java
ankleless24 分钟前
Spring 框架深度解析:从核心原理到实战应用
java·spring
终端行者26 分钟前
jenkins实现分布式构建并自动发布到远程服务器上 jenkins实现自动打包编译发布远程服务器
服务器·分布式·jenkins
带刺的坐椅26 分钟前
Spring AOP 与 Solon AOP 有什么区别?
java·spring·solon·aop
华仔啊37 分钟前
Java序列化详解:什么情况下必须用它?
java
草莓熊Lotso1 小时前
【C++】--函数参数传递:传值与传引用的深度解析
c语言·开发语言·c++·其他·算法
Ice__Cai1 小时前
Flask 路由详解:构建灵活的 URL 映射系统
开发语言·python·flask