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

发布于:2025-08-01 ⋅ 阅读:(20) ⋅ 点赞:(0)

前言:

 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

//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 的配置类。(下面不加也是可以的,为默认的)

@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。

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

✅ 示例代码:

@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() 手动选择服务实例。

✅ 示例代码:

@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:❌ 否

✅ 示例代码:

@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 的配置类。

@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:✅ 是

✅ 示例代码:

@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:✅ 是

✅ 示例代码:

@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 的配置类。

@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:❌ 否

✅ 示例代码:

@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:❌ 否

✅ 示例代码:

@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

  • 通常写法

@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(基于服务名)

  • 通常写法

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


网站公告

今日签到

点亮在社区的每一天
去签到