LoadBalancer替代Ribbon实现负载均衡

关于Ribbon和LoadBalancer

本次试验spring boot版本2.6.1
配合SpringCloud版本为Jubilee(2021.0.0)
本来想用Ribbon做负载均衡,偶然间发现不导入ribbon也能通过RestTemplate+@LoadBalance实现负载均衡,心生好奇

  1. @LoadBalance注解在之前的springcloud版本中属于spring-cloud-starter-ribbon
    但在jubilee版本好像改成了org.springframework.cloud.client.loadbalancer
    后面去查了一下,原来是Ribbon目前已经停止维护,新版SpringCloud(2021.x.x)LoadBalancer替代了RibbonSpring Cloud全家桶在Spring Cloud Commons项目中,添加了Spring cloud Loadbalancer作为新的负载均衡器,并且做了兼容
  2. 在导入spring-cloud-starter-netflix-eureka-client这个包的时候,就已经包含了spring-cloud-starter-loadbalancer,因此不需要另外导入
<dependency>
	<groupId>org.springframework.cloud</groupId>
	<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
 <dependency>
      <groupId>org.springframework.cloud</groupId>
      <artifactId>spring-cloud-starter-loadbalancer</artifactId>
      <version>3.1.0</version>
      <scope>compile</scope>
</dependency>

使用

使用之前,先了解一下微服务之间的调用方式
Spring Cloud 中微服务调用默认是用 http 请求,主要通过一下三种 API:

API描述
RestTemplate同步 http API
WebClient异步响应式 http API
第三方封装如 openfeign
当项目中导入了 spring-cloud-starter-loadbalancer依赖,会自动在相关的Bean中加入负载均衡,对于以上三种请求方式加入负载均衡的方式如下:
  • 对于 RestTemplate,会自动对 @LoadBalanced 注解修饰的 RestTemplate Bean 增加 Interceptor 拦截器,从而加上了负载均衡的特性。
  • 对于 WebClient,通过加入 ReactorLoadBalancerExchangeFilterFunction 的方式加上负载均衡的特性。
  • 对于第三方封装,见百度

使用方式一

手动注入LoadBalanceClient,通过choose('生产服务名')选择一个服务方
根据服务方的hostport信息拼接url,手动new RestTemplate()发送请求获取响应

@RestController
@RequestMapping("/consumer")
public class ConsumeController {
    @Autowired
    private LoadBalancerClient loadBalancerClient;

    @RequestMapping("/interfaceOne")
    public String consumeOne(String msg) {
 		// 第二种调用方式: 通过loadBalancerClient按照一定规则选择服务方(默认为轮询模式)
		// 根据服务名(EurekaClient的服务名)获取服务列表,根据算法选取某个服务,并获得这个服务的网络信息。
		ServiceInstance serviceInstance = loadBalancerClient.choose("ServiceClient");
		String result = new RestTemplate().getForObject("http://" + serviceInstance.getHost()
                + ":" + serviceInstance.getPort() 
                + "/clientOne/interfaceOne?msg=" + msg, String.class);
        return result; 
    }
}

使用方式二

方式二是第一种方式的注解简化版.原理相同
因为SpringBoot不会自动注入RestTemplate对象,因此需要手动注入
SpringBoot自动注入了RestTempalateBuilder,所以可以用build()的方式创建restTemplate对象
同时在restTemplate对象加上@LoadBalance注解,叠加自动负载均衡
其实就是用@LoadBalance注解代替了手动注入loadBalanceClient对象去实现服务选择

@SpringBootApplication
public class ConsumerApplication {
    @Autowired
    private RestTemplateBuilder templateBuilder;

    public static void main(String[] args) {
        SpringApplication.run(ConsumerApplication.class, args);
    }

    @Bean
    @LoadBalanced
    public RestTemplate getTemplate() {
        return templateBuilder.build();
    }
}
@RestController
@RequestMapping("/consumer")
public class ConsumeController {
    @Autowired()
    private RestTemplate restTemplate;

    @RequestMapping("/interfaceOne")
    public String consumeOne(String msg) {
		String result = restTemplate.getForObject(
			"http://ServiceClient/clientOne/interfaceOne?msg=" + msg, String.class
		);
		return result;
    }
}

配置负载均衡策略

以前的Ribbon有多种负载均衡策略

策略类型类名
随机RandomRule
轮询RoundRobinRule
重试RetryRule
最低并发BestAvailableRule
可用过滤AvailabilityFilteringRule
响应时间加权重ResponseTimeWeightedRule
区域权重ZoneAvoidanceRule

但LoadBalancer貌似只提供了两种负载均衡器

  • RandomLoadBalancer 随机
  • RoundRobinLoadBalancer 轮询

不指定的时候默认用的是轮询

依据源码:LoadBalancerClientConfiguration类中的默认构造器

@Bean
@ConditionalOnMissingBean // 空Bean(未指定任何负载均衡器)时的默认情况
public ReactorLoadBalancer<ServiceInstance> reactorServiceInstanceLoadBalancer(Environment environment, LoadBalancerClientFactory loadBalancerClientFactory) {
String name = environment.getProperty("loadbalancer.client.name");
// 构造器返回的对象是轮询负载均衡器
return new RoundRobinLoadBalancer(loadBalancerClientFactory.getLazyProvider(name, ServiceInstanceListSupplier.class), name);
}

如果要切换使用随机或者自定义,需要手动配置一下

切换随机模式示例

1.先创建一个LoadBalancerConfig配置类
注意不加@Configuration注解(只针对单个微服务调用,而不是全局配置)

import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.loadbalancer.core.*;
import org.springframework.cloud.loadbalancer.core.RandomLoadBalancer;
import org.springframework.cloud.loadbalancer.support.LoadBalancerClientFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.core.env.Environment;

public class LoadBalanceConfig {
    @Bean
    ReactorLoadBalancer<ServiceInstance> randomLoadBalancer(Environment environment,
                                LoadBalancerClientFactory loadBalancerClientFactory) {
        // name取自@LoadBalancerClient中的name属性指定的服务提供方的服务名
        String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);
        return new RandomLoadBalancer(loadBalancerClientFactory.getLazyProvider(name, ServiceInstanceListSupplier.class), name);
    }
}

依据源码:LoadBalancerClientConfigurationRegistrar类

// 这是个Bean定义器,猜测其作用就是和@LoadBalancerClient注解配合,在注册RestTemplate对象并给其配置负载均衡器的时候,定义负载均衡器的核心属性
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
		// 上来简单明了地获取@LoadBalancerClients的name属性,说明之前没有猜错
        Map<String, Object> attrs = metadata.getAnnotationAttributes(LoadBalancerClients.class.getName(), true);
        // 这里说如果取name属性没取到,就取value属性,那说明除了name属性外,也可以用过value属性来指定服务提供方的服务名(有待试验)
        if (attrs != null && attrs.containsKey("value")) {
            AnnotationAttributes[] clients = (AnnotationAttributes[])((AnnotationAttributes[])attrs.get("value"));
            AnnotationAttributes[] var5 = clients;
            int var6 = clients.length;
            for(int var7 = 0; var7 < var6; ++var7) {
                AnnotationAttributes client = var5[var7];
                // 这里应该是用name和指定的configuration配置去注册一个负载均衡器,先不深究
                registerClientConfiguration(registry, getClientName(client), client.get("configuration"));
            }
        }
        // 没有拿到name和configuration等属性的时候,用 default+类名 作为name,加上默认的配置defaultConfiguration去注册负载均衡器
        if (attrs != null && attrs.containsKey("defaultConfiguration")) {
            String name;
            if (metadata.hasEnclosingClass()) {
                name = "default." + metadata.getEnclosingClassName();
            } else {
                name = "default." + metadata.getClassName();
            }
            registerClientConfiguration(registry, name, attrs.get("defaultConfiguration"));
        }
		// 这里为啥又重走一遍?不太明白
        Map<String, Object> client = metadata.getAnnotationAttributes(LoadBalancerClient.class.getName(), true);
        String name = getClientName(client);
        if (name != null) {
            registerClientConfiguration(registry, name, client.get("configuration"));
        }
    }
// 这个是上面那个方法调用的取值方法~看看就好
private static String getClientName(Map<String, Object> client) {
    if (client == null) {
        return null;
    } else {
        String value = (String)client.get("value");
        if (!StringUtils.hasText(value)) {
            value = (String)client.get("name");
        }
        if (StringUtils.hasText(value)) {
            return value;
        } else {
            throw new IllegalStateException("Either 'name' or 'value' must be provided in @LoadBalancerClient");
        }
    }
}

2.再创建一个RestTemplateConfig配置类(直接写在启动类也可以)
通过@LoadBalancerClient注解为当前的restTemplate对象指定负载均衡配置

@Configuration
@LoadBalancerClient(name = "provider-one", configuration = LoadBalanceConfig.class)
public class RestemplateConfig {
    @Bean
    @LoadBalanced
    public RestTemplate templateOne() {
        return new RestTemplate();
    }
}

需要注意:

  1. @LoadBalancerClient中的name属性是指服务提供方的服务名(即:spring.application.name),eureka是通过服务名去找对应的微服务的
  2. configuration 则是指定负载均衡器配置类

在这里插入图片描述

  • 3
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Ribbon 是一个客户端负载均衡器,它可以根据负载均衡算法自动将请求分发到多个服务提供者上,从而实现高可用和负载均衡。而 LoadBalancer 是一个服务端负载均衡器,它可以将请求分发到多个服务提供者上,从而实现高可用和负载均衡。 下面是 Ribbon 的一个代码案例: ```java @LoadBalanced @Bean public RestTemplate restTemplate() { return new RestTemplate(); } @Autowired private RestTemplate restTemplate; public String hello() { String url = "http://service-provider/hello"; return restTemplate.getForObject(url, String.class); } ``` 在这个案例中,我们使用了 Spring Cloud 中的 @LoadBalanced 注解来创建一个 Ribbon 负载均衡的 RestTemplate 对象,然后使用这个 RestTemplate 对象来发送请求。 希望这个回答能够帮助到你! ### 回答2: ribbonloadbalancer都是用于实现负载均衡的工具,但它们有些许不同之处。 首先,ribbon是一个客户端负载均衡的组件,它在客户端侧进行负载均衡,而loadbalancer是一种服务端负载均衡器,它位于服务侧进行负载均衡。 其次,ribbon可以基于多种负载均衡算法进行负载均衡,例如轮询、随机、权重等,而loadbalancer通常使用一种算法,如加权轮询。 再次,ribbon可以根据实际情况自动感知服务的变化,并动态更新负载均衡策略,而loadbalancer则需要手动配置并重启服务。 最后,ribbon可以与服务发现组件(如Eureka)进行集成,自动从服务注册中心获取服务列表,并根据服务状态进行负载均衡,而loadbalancer通常与服务注册中心无直接交互,需要手动配置服务列表。 以下是一个使用ribbon进行负载均衡的代码案例: ```java // 客户端配置类 @Configuration public class RibbonConfig { @Bean public IRule ribbonRule(){ return new RandomRule(); // 使用随机算法进行负载均衡 } } // 服务调用类 @Service public class HelloService { @Autowired private RestTemplate restTemplate; // 使用ribbon的RestTemplate @HystrixCommand(fallbackMethod = "fallback") // 使用Hystrix进行容错处理 public String sayHello() { // 使用客户端负载均衡调用服务 return restTemplate.getForObject("http://service-provider/hello", String.class); } public String fallback() { return "Service Unavailable"; } } // 启动类 @SpringBootApplication @EnableDiscoveryClient @RibbonClient(name = "service-provider", configuration = RibbonConfig.class) // 客户端负载均衡配置 public class RibbonApplication { public static void main(String[] args) { SpringApplication.run(RibbonApplication.class, args); } @Bean @LoadBalanced // 启用负载均衡 public RestTemplate restTemplate() { return new RestTemplate(); } } ``` 以上代码中,使用了ribbon的RestTemplate进行服务调用,通过在启动类上添加@RibbonClient注解指定了负载均衡配置类。同时,使用@LoadBalanced注解启用了负载均衡功能。当调用sayHello方法时,ribbon会根据负载均衡算法选择一个可用的服务节点进行调用,实现负载均衡的效果。 ### 回答3: ribbonloadbalancer都是用于实现负载均衡的工具,但它们的概念和使用方式有一定的差异。 Ribbon是Netflix开源的一款负载均衡客户端,主要用于在客户端进行负载均衡。它可以通过配置文件的方式定义多个服务器列表,并提供一些默认的负载均衡策略,如轮询、随机等。在请求发送时,Ribbon会根据负载均衡策略选择一台服务器发送请求。如果需要自定义负载均衡策略,可以通过继承Ribbon的相关类进行扩展。以下是一个使用Ribbon的代码案例: ``` @RestController public class UserController { private final RestTemplate restTemplate; @Autowired public UserController(RestTemplate restTemplate) { this.restTemplate = restTemplate; } @GetMapping("/user/{id}") public User getUser(@PathVariable("id") Long id) { String url = "http://service-provider/user/" + id; return restTemplate.getForObject(url, User.class); } } ``` 在上述案例中,通过RestTemplate发送GET请求时,使用了服务名(service-provider)替代了具体的服务器地址。Ribbon会根据服务名从配置文件中获取服务器列表,并且根据负载均衡策略选择一台服务器进行请求。 而LoadBalancer是一种服务器负载均衡的组件。它可以在服务端或者独立的服务器上进行配置,作为一个独立的网络设备,用于将请求分发到多个服务器上去执行。LoadBalancer可以通过不同的算法,如轮询、加权轮询、随机等,来选择一台服务器。以下是一个使用Spring Cloud的LoadBalancer进行服务间负载均衡的案例: ``` @RestController public class UserController { private final LoadBalancerClient loadBalancerClient; @Autowired public UserController(LoadBalancerClient loadBalancerClient) { this.loadBalancerClient = loadBalancerClient; } @GetMapping("/user/{id}") public User getUser(@PathVariable("id") Long id) { ServiceInstance serviceInstance = loadBalancerClient.choose("service-provider"); if (serviceInstance != null) { String url = "http://" + serviceInstance.getHost() + ":" + serviceInstance.getPort() + "/user/" + id; RestTemplate restTemplate = new RestTemplate(); return restTemplate.getForObject(url, User.class); } return null; } } ``` 在上述案例中,使用LoadBalancerClient选择一个可用的服务实例,然后通过服务实例的信息构建请求URL,并发送GET请求获取用户信息。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值