欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Spring Cloud与Docker微服务架构实战(四)—— Ribbon客户端侧负载均衡

程序员文章站 2022-06-12 22:51:17
...

五、使用Ribbon实现客户端侧负载均衡

简介

为Ribbon配置服务提供者地址列表后Ribbon可以基于某种负载均衡算法,自动的帮服务消费者去请求,如轮询、随机等,也可以自定义负载均衡算法。

下图是Ribbon与Eureka联用的框架图

Spring Cloud与Docker微服务架构实战(四)—— Ribbon客户端侧负载均衡

为服务消费者整合Ribbon

为前文编写的电影微服务整合Ribbon

1.复制项目microservice-simple-consumer-movie,将ArtifactId修改为microservice-simple-consumer-movie-ribbon

2.引入Ribbon依赖,依赖如下

<!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-netflix-ribbon -->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
    <version>2.2.5.RELEASE</version>
</dependency>

3.为RestTemplate添加@LoadBalanced 注解

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

4.将Controller代码修改如下

package com.lzy.cloud.microservicesimpleconsumermovie.controller;

import com.lzy.cloud.microservicesimpleconsumermovie.pojo.User;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

import java.util.List;

@RestController
public class MovieController {
    private static final Logger LOGGER = LoggerFactory.getLogger(MovieController.class);

    @Autowired
    private RestTemplate restTemplate;

    @Autowired
    private DiscoveryClient discoveryClient;

    @Autowired
    private LoadBalancerClient loadBalancerClient;

    @GetMapping("/user/{id}")
    public User findById(@PathVariable Long id){
        return this.restTemplate.getForObject("http://microservice-simple-provider-user/" +id, User.class);
    }

    /*
     * 查询 microservice-simple-provider-user的实例信息
     * @return
     */
    @GetMapping("/user-instance")
    public List<ServiceInstance> showInfo(){
        return this.discoveryClient.getInstances("microservice-simple-provider-user");
    }

    /*
     * 打印选择了哪个节点
     */
    @GetMapping("/log-instance")
    public void logUserInstance(){
        ServiceInstance serviceInstance = loadBalancerClient.choose("microservice-simple-provider-user");
        MovieController.LOGGER.info("{}:{}:{}",serviceInstance.getServiceId(),serviceInstance.getHost(),
                serviceInstance.getPort());
    }

}

此时请求地址改成了"http://microservice-simple-provider-user/","microservice-simple-provider-user"是用户微服务的虚拟主机名,Ribbon和Eureka配合使用时,自动将虚拟主机名映射成微服务网络地址。

测试

启动eureka注册中心,并启动2个或多个microservice-simple-provider-user实例和Ribbon负载均衡功能的电影微服务。访问localhost:8761,结果如下

Spring Cloud与Docker微服务架构实战(四)—— Ribbon客户端侧负载均衡

多次访问localhost:8010/user/1,返回结果如下

{
"id": 1,
"username": "account1",
"name": "张三",
"age": 20,
"balance": 100
}

同时,两个微服务实例都会打印查询日志,但是是交替打印的,多次访问localhost:8010/log-instance,控制台打印如下日志,看到每次访问不同微服务,效果更加明显

2020-09-23 15:28:08.752  INFO 2464 --- [nio-8010-exec-1] c.l.c.m.controller.MovieController       : microservice-simple-provider-user:172.19.173.181:8082
2020-09-23 15:28:09.727  INFO 2464 --- [erListUpdater-0] c.netflix.config.ChainedDynamicProperty  : Flipping property: microservice-simple-provider-user.ribbon.ActiveConnectionsLimit to use NEXT property: niws.loadbalancer.availabilityFilteringRule.activeConnectionsLimit = 2147483647
2020-09-23 15:30:09.118  INFO 2464 --- [nio-8010-exec-6] c.l.c.m.controller.MovieController       : microservice-simple-provider-user:172.19.173.181:8082
2020-09-23 15:30:46.337  INFO 2464 --- [nio-8010-exec-7] c.l.c.m.controller.MovieController       : microservice-simple-provider-user:172.19.173.181:8081
2020-09-23 15:30:46.700  INFO 2464 --- [nio-8010-exec-8] c.l.c.m.controller.MovieController       : microservice-simple-provider-user:172.19.173.181:8082
2020-09-23 15:30:47.112  INFO 2464 --- [nio-8010-exec-9] c.l.c.m.controller.MovieController       : microservice-simple-provider-user:172.19.173.181:8081
2020-09-23 15:30:47.418  INFO 2464 --- [io-8010-exec-10] c.l.c.m.controller.MovieController       : microservice-simple-provider-user:172.19.173.181:8082
2020-09-23 15:30:47.641  INFO 2464 --- [nio-8010-exec-1] c.l.c.m.controller.MovieController       : microservice-simple-provider-user:172.19.173.181:8081

RestTemplate其实也是一个Ribbon客户端,包含和LoadBalancerClient一样的choose行为

  • 注意:虚拟主机名不能包含“_”之类的字符,否则Ribbon调用会出现异常

使用Java自定义Ribbon配置

Spring Cloud 中,Ribbon默认配置如下(格式是 BeanType beanName: ClassName):

  • IClientConfig ribbonClientConfig: DefaultClientConfigImpl
  • IRule ribbonRule: ZoneAvoidanceRule
  • IPing ribbonPing: NoOpPing
  • ServerList ribbonServerList: ConfigurationBasedServerList
  • ServerFilter ribbonServerListFilter: ZonePreferenceServerListFilter
  • IloadBalancer ribbonLoadBalancer: ZoneAwareLoadBalancer

简单说明:

@Bean
@ConditionalOnMissingBean
public IRule ribbonRule(IClientConfig config) {
  ZoneAvoidanceRule rule = new ZoneAvoidanceRule();
  rule.initWithNiwsConfig(config);
  return rule;
}

BeanType是IRule,beanName是ribbonRule,ClassName是ZoneAvoidanceRule,这是一种根据服务提供者所在Zone的性能以及服务提供者可用性综合计算,选择提供者节点的负载均衡规则。

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

下面来为microservice-provider-user的Ribbon客户端自定义配置

1.复制项目microservice-consumer-movie-ribbon,将ArtifactId修改为microservice-consumer-movie-ribbon-customizing

2.创建Ribbon配置类

import com.netflix.loadbalancer.IRule;
import com.netflix.loadbalancer.RandomRule;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/*
 * 该类不应该在主应用程序上下文的 @ComponentScan 中,应该手动排除不被扫描
 */
@Configuration
public class RibbonConfiguration {
    @Bean
    public IRule ribbonRule() {
        //修改负载均衡规则为随机
        return new RandomRule();
    }
}

3.创建空类,添加相应注解

import org.springframework.cloud.netflix.ribbon.RibbonClient;
import org.springframework.context.annotation.Configuration;

@Configuration
@RibbonClient(name = "microservice-provider-user", configuration = RibbonConfiguration.class)
public class TestConfiguration {
}

使用@RibbonClient 注解的configuration属性,可以指定名称Ribbon客户端的配置

测试

先启动eureka注册中心,再启动2个或更多microservice-provider-user实例

再启动microservice-consumer-movie-ribbon-customizing

多次访问http://localhost:8010/log-instance,可以得到如下日志

 1635 --- [nio-8010-exec-1] c.l.c.m.controller.MovieController       : microservice-simple-provider-user:172.19.173.181:8082
 1635 --- [nio-8010-exec-3] c.l.c.m.controller.MovieController       : microservice-simple-provider-user:172.19.173.181:8082
 1635 --- [nio-8010-exec-4] c.l.c.m.controller.MovieController       : microservice-simple-provider-user:172.19.173.181:8081
 1635 --- [nio-8010-exec-5] c.l.c.m.controller.MovieController       : microservice-simple-provider-user:172.19.173.181:8081
 1635 --- [nio-8010-exec-6] c.l.c.m.controller.MovieController       : microservice-simple-provider-user:172.19.173.181:8081
 1635 --- [nio-8010-exec-7] c.l.c.m.controller.MovieController       : microservice-simple-provider-user:172.19.173.181:8081
 1635 --- [nio-8010-exec-8] c.l.c.m.controller.MovieController       : microservice-simple-provider-user:172.19.173.181:8082

请求会随机分布到两个节点上,说明实现了Ribbon的自定义配置

使用属性自定义Ribbon配置

使用属性自定义Ribbon客户端,比Java代码配置更加方便

  • NFLoadBalancerClassName:配置ILoadBalancer的实现类
  • NFLoadBalancerRuleClassName:配置IRule的实现类
  • NFLoadBalancerPingClassName:配置Ping的实现类
  • NIWSServerListClassName:配置ServerList的实现类
  • NIWSServerListFilterClassName:配置ServerListFilter的实现类

采用属性来修改microservice-consumer-movie-ribbon-customizing,在项目的application.yml中添加以下内容即可:

microserivce-provider-user:
  ribbon:
    NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule

脱离Eureka使用Ribbon

Ribbon支持脱离Eureka使用,此时架构图如下:
Spring Cloud与Docker微服务架构实战(四)—— Ribbon客户端侧负载均衡

1.复制项目microservice-simple-consumer-movie-ribbon,将artifactId修改为microservice-simple-consumer-movie-ribbon-without-eureka

2.去掉Eureka的依赖,只使用Ribbon依赖,在项目pom文件中将eureka依赖注释掉

3.去掉启动类上的@EnableDiscoveryClient注解

4.修改application.yml如下:

server:
  port: 8010

spring:
  #  注册到服务发现中心的名称
  application:
    name: microservice-simple-consumer-movie

microservice-simple-provider-user:
  ribbon:
    listOfServers: localhost:8081, localhost:8082

listOfServers用于名为 microservice-simple-provider-user 的Ribbon客户端设置请求的地址列表

测试

启动两个user实例,启动microservice-simple-consumer-movie-ribbon-without-eureka

多次访问 http://localhost:8010/log-instance,控制台打印如下

[nio-8010-exec-5] c.l.c.m.controller.MovieController       : microservice-simple-provider-user:localhost:8081
[nio-8010-exec-6] c.l.c.m.controller.MovieController       : microservice-simple-provider-user:localhost:8082
[nio-8010-exec-7] c.l.c.m.controller.MovieController       : microservice-simple-provider-user:localhost:8081
[nio-8010-exec-8] c.l.c.m.controller.MovieController       : microservice-simple-provider-user:localhost:8082
[nio-8010-exec-9] c.l.c.m.controller.MovieController       : microservice-simple-provider-user:localhost:8081
[io-8010-exec-10] c.l.c.m.controller.MovieController       : microservice-simple-provider-user:localhost:8082

虽然电影微服务和用户微服务都没有注册到Eureka上,Ribbon仍然可以正常工作,请求依然会按照负载均衡策略分摊到两个用户微服务节点上