第5章 使用Ribbon实现客户端负载均衡

8 篇文章 0 订阅
6 篇文章 0 订阅

第五章 使用Ribbon实现客户端负载均衡

  • 在实际的生产环境中,各个微服务都会部署多个实例。那么服务消费者要如何将请求分摊到多个服务提供者实例上呢?这就需要使用Ribbon

Ribbon概述

  • RibbonNetflix负载均衡器,它有助于控制HTTPTCP客户端的行为。为Ribbon配置服务者地址列表后,Ribbon就可以基于某种负载均衡算法,自动地帮助服务消费者去请求。Ribbon默认为我们提供了很多的负载均衡算法,如:轮询随机等。我们也可以为其实现自定义的负载均衡算法

  • RibbonEureka配合使用时,Ribbon可自动从Eureka Server获取服务提供者地址列表,并基于负载均衡算法,请求其中一个服务提供者实例。

在这里插入图片描述

为服务消费者整合Ribbon

  • 创建Maven项目

在这里插入图片描述

  • 复制项目microservice-consumer-movie,将ArtifactId修改为microservice-consumer-movie-ribbon。并添加maven依赖如下。

    • <dependency>
      	<groupId>org.springframework.cloud</groupId>
          <artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
      </dependency>
      
    • 由于之前的电影微服务添加了spring-cloud-starter-netflix-eureka-client,该依赖已经包含了spring-cloud-starter-netflix-ribbon,所以此处也可以不引入。

  • 修改启动类添加注解**@LoadBalanced**

    •     @Bean
          @LoadBalanced
      	//此注解可为RestTemplate整合Ribbin,使其具备负载均衡能力。
          public RestTemplate restTemplate() {
              return new RestTemplate();
          }
      
  • 修改MovieController代码。

    • @RestController
      public class MovieController{
          private static final Logger LOGGER = LoggerFactory.getLogger(MovieController.class);
          @Autowired
          private RestTemplate restTemplate;
          @Autowired
          private LoadBalancerClient loadBalancerClient;
          @GetMapping("/user/{id}")
          public User findById(@PathVariable Long id) {
              return this.restTemplate.getForObject("http://microservice-provider-user/"+id, User.class);
          }
          @GetMapping("/log-user-instance")
          public void logUserInstance() {
              ServiceInstance serviceInstance = this.loadBalancerClient.choose("microservice-provider-user");
              // 打印当前选择的是哪个节点
              MovieController.LOGGER.info("{}:{}:{}", serviceInstance.getServiceId(), serviceInstance.getHost(), serviceInstance.getPort());
          }
      }
      
    • 上面将请求地址修改为了http://microservice-provider-user/。此地址是用户微服务的虚拟主机名,当RibbonEureka配合使用时,会自动将虚拟主机名映射成微服务的网络地址。在新添加的log-user-instance()中可使用LoadBalancerClientAPI更加直观地获取当前选择的用户微服务节点。

  • 测试

    • 启动microservice-discovery-eureka

    • 启动两个或更多的microservice-provider-user实例

    • 启动microservice-movie-ribbon

    • 访问http://localhost:8761,如下。

      image-20200202223510099

    • 多次访问http://localhost:8010/user/1,返回如下

      image-20200202223634466

    • 同时注意启动的多个用户微服务下,有类似信息

      • Hibernate: select user0_.id as id1_0_0_, user0_.age as age2_0_0_, user0_.balance as balance3_0_0_, user0_.name as name4_0_0_, user0_.username as username5_0_0_ from user user0_ where user0_.id=?
        
    • 多次访问http://localhost:8010/log-user-instance,控制台输出如下。

      image-20200202224054793

    • 可以看到,此时请求会均匀分布到两个用户微服务节点上,说明已经实现了负载均衡。

  • 注:

    • 在默认情况下,虚拟主机名和服务名称是一致的,我们也可以使用配置属性

      eureka:
      	instance:
      		virtual-host-name: xxxxxxx
      		#或使用下面配置指定虚拟主机名
      eureka:
      	instance:		
      		secure-virtual-host-name: xxxxx
      
    • 不能将restTemplate.getForObject(…)loadBalancerClient.choose(…)写在同一个方法中,两者之间会有冲突。因为此时代码中的restTemplate实际上是一个Ribbon客户端,本身已经包含了"choose"的行为。

    • 虚拟主机名不能包含"__"之类的字符,否则Ribbon在调用时会报异常。

Ribbon配置自定义

  • Spring Cloud Edgware允许使用Java代码或属性自定义Ribbon的配置。

使用Java代码自定义Ribbon配置

配置指定名称的Ribbon Client
  • Spring Cloud中,Ribbon默认的配置如下(格式为BeanType beanName: ClassName)

    • IClientConfig ribbonClientConfig: DefaultClientConfigImpl
    • IRule ribbonRule: ZoneAvoidanceRule
    • IPing ribbonPing: DummyPing
    • ServerList ribbonServerList: ConfigurationBasedServerList
    • ServerListFilter ribbonServerListFilter: ZonePreferenceServerListFilter
    • ILoadBalancer ribbonLoadBalancer: ZoneAwareLoadBalancer
    • ServerListUpdater ribbonServerListUpdater: PollingServerListUpdater
  • 如,下面代码

    • //截取自org.springcframework.cloud.netflix.ribbon.RibbonClientConfiguration
      @Configuration
      @EnableConfigurationProperties
      @Import({HttpClientConfiguration.class, OkHttpRibbonConfiguration.class, RestClientRibbonConfiguration.class, HttpClientRibbonConfiguration.class})
      public class RibbonClientConfiguration {
          //......
           @Bean
          @ConditionalOnMissingBean
          public IRule ribbonRule(IClientConfig config) {
              if (this.propertiesFactory.isSet(IRule.class, this.name)) {
                  return (IRule)this.propertiesFactory.get(IRule.class, config, this.name);
              } else {
                  ZoneAvoidanceRule rule = new ZoneAvoidanceRule();
                  rule.initWithNiwsConfig(config);
                  return rule;
              }
          }
          //......
      }
      
    • BeanTypeIRule,beanNameribbonRuleClassNameZoneAvoidanceRule,这是一种根据服务提供者所在Zone的性能以及服务提供者可用性综合计算,选择提供者节点的负载均衡规则。

    • Spring cloud中,Ribbon默认的配置类是RibbonClientConfiguration。也可以使用一个POJO自定义的Ribbon配置(自定义配置会覆盖默认的配置)。这种配置是细粒度的,不同名称的Ribbon客户端可使用不同配置。

  • 创建Maven项目,使用Ribbon客户端自定义配置。

    在这里插入图片描述

    • 创建Ribbon的配置类

    • /**
       * 该类为Ribbon的配置类,
       * 注意该类不应该出现在应用程序上下文的@ComponentScan所扫描的包中。
       */
      @Configuration
      public class RibbonConfiguration {
          @Bean
          public IRule ribbonRule() {
      //        将负载均衡规则改为随机
              return new RandomRule();
          }
      }
      
    • 创建空类,如下

    • /**
       * 使用@RibbonClient,为特定name的Ribbon Client自定义配置。
       * 使用@RibbonClient的configuration属性,指定Ribbon的配置类
       */
      @Configuration
      @RibbonClient(name = "microservice-provider-user"
              ,configuration = RibbonConfiguration.class)
      public class TestConfiguration {
      }
      
      
  • 测试

    • 启动microservice-discovery-eureka
    • 启动多个microservice-provider-user
    • 启动microservice-consumer-movie-ribbon-customizing
    • 多次访问http://localhost:8010/log-user-instance,注意查看日志。如下。可以看到请求随机的分配到了用户微服务上。

    在这里插入图片描述

  • 注:

    • 上面RibbonConfiguration类不能存放主应用程序上下文的**@ComponentScan所扫描的包中,否则该类中的配置信息将被所有的@RibbonClient**共享。
    • 如果只想自定义某一个Ribbon客户端的配置,必须防止**@Configuration注解的类所在的包与@ComponentScan扫描的包重叠,或应显式指定@ComponentScan不扫描@Configuration**类所在的包。

使用属性自定义Ribbon配置

  • Spring Cloud Netflix 1.2.0开始,Ribbon支持使用属性自定义。这种方式比使用Java代码配置的方式更加方便。

  • 支持属性如下,配置的前缀是.ribbon。是Ribbon Client的名称,如果省略不写,则表示全局配置。

    • NFLoadBalancerClassName: 配置ILoadBalancer的实现类。
    • NFLoadBalancerRuleClassName: 配置IRule的实现类。
    • NFLoadBalancerPingClassName: 配置IPing的实现类。
    • NIWSServerListClassName: 配置ServerList的实现类。
    • NIWSServerListFilterClassName: 配置ServerListFilter的实现类。
  • 创建Maven项目

    在这里插入图片描述

    • 修改application.yml文件

    • server:
        port: 8010
      spring:
        application:
          name: microservice-consumer-movie
      eureka:
        client:
          serviceUrl:
            defaultZone: http://localhost:8761/eureka/
        instance:
          prefer-ip-address: true
      #  下面将microservice-provider-user的Ribbon Client的负载均衡设置为随机
      microservice-provider-user:
        ribbon:
          NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule
      #如果配置为下面的形式表示对所有Ribbon Client都使用RandomRule
      #ribbon:
      #   NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule
      
  • 测试

    • 启动microservice-discovery-eureka
    • 启动多个microservice-provider-user实例。
    • 启动microservice-consumer-movie-ribbon-customizing-properties
    • 多次访问http://localhost:8010/log-user-instance,查看控制台,可以看到请求随机的分配到了两个用户微服务上

    在这里插入图片描述

  • 注:属性配置的方式比Java代码配置的方式优先级更高

脱离Eureka使用Ribbon

  • 在一些遗留的微服务中,它们可能并没有注册到Eureka Server上,甚至根本不是使用Spring Cloud开发的,此时要想使用Ribbon实现负载均衡,就要脱离Eureka
  • Spring Cloud支持脱离Eureka使用,架构如下。

在这里插入图片描述

  • 创建Maven项目

在这里插入图片描述

  • 去除spring-cloud-starter-netflix-eureka-client依赖,并添加spring-cloud-starter-netflix-ribbon依赖。
 		  <dependency>
              <groupId>org.springframework.cloud</groupId>
              <artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
          </dependency>
  • 去除启动类上的**@EnableDiscoveryClient**注解

    • @SpringBootApplication
      public class ConsumerMovieApplication {
          @Bean
          @LoadBalanced
          public RestTemplate restTemplate() {
              return new RestTemplate();
          }
          public static void main(String[] args) {
              SpringApplication.run(ConsumerMovieApplication.class,args);
          }
      }
      
      
  • 修改application.yml

    • server:
        port: 8010
      spring:
        application:
          name: microservice-consumer-movie
      microservice-provider-user:
        ribbon:
          listOfServers: localhost:8000,localhost:8001
      #    microservice-provider-user.ribbon.listOfServers
      #    用于为名为microservice-provider-user的Ribbon客户端设置请求的地址列表
      
  • 测试

    • microservice-simple-provider-user进行打包。并启动多个实例,命令如下:

      • java -jar microservice-simple-provider-user-0.0.1-SNAPSHOT.jar
        
      • java -jar microservice-simple-provider-user-0.0.1-SNAPSHOT.jar --server.port=8001
        
    • 启动microservice-consumer-movie-without-eureka

    • 访问http://localhost:8010/log-user-instance,观察控制台打印信息。尽管微服务没有注册到Eureka上,Ribbon仍可正常工作,请求依旧会分摊到两个用户微服务节点上。

    在这里插入图片描述

饥饿加载

  • Spring Cloud会为每个名称的Ribbon Client维护一个子应用程序上下文,指定名称的Ribbon Client第一次请求时,对应的上下文才会被加载,因此,首次请求往往会比较慢。从Spring Cloud Dalston开始,可以在配置中配置饥饿加载,如:

    • ribbon:
      	eager-load:
      		enabled: true
      		clients: client1, client2
      
  • 这样对于名为client1,client2Ribbon Client,将在启动时就加载对应的子应用程序上下文,从而提高首次请求的访问速度。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值