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

springcloud——客户端Eureka Client实现负载均衡 Ribbon

程序员文章站 2022-06-22 19:55:10
...

一、简介

Spring Cloud Ribbon是基于Netflix Ribbon实现的一套客户端负载均衡的工具。简单的说,Ribbon是Netflix发布的开源项目,主要功能是提供客户端的软件负载均衡算法,将Netflix的中间层服务连接在一起。Ribbon客户端组件提供一系列完善的配置项如连接超时,重试等。简单的说,就是在配置文件中列出Load Balancer(简称LB)后面所有的机器,Ribbon会自动的帮助你基于某种规则(如简单轮询,随机连接等)去连接这些机器。我们也很容易使用Ribbon实现自定义的负载均衡算法。

LB,即负载均衡(Load Balance),在微服务或分布式集群中经常用的一种应用。负载均衡简单的说就是将用户的请求平摊的分配到多个服务上,从而达到系统的HA(高可用)。常见的负载均衡有软件Nginx,LVS,硬件 F5等。相应的在中间件,例如:dubbo和SpringCloud中均给我们提供了负载均衡,SpringCloud的负载均衡算法可以自定义。 

负载均衡的实现方式有两种,分别是服务端的负载均衡和客户端的负载均衡:

服务端负载均衡:当浏览器向后台发出请求的时候,会首先向反向代理服务器发送请求,反向代理服务器会根据客户端部署的ip:port映射表以及负载均衡策略,来决定向哪台服务器发送请求,一般会使用到nginx反向代理技术。

客户端负载均衡:当浏览器向后台发出请求的时候,客户端会向服务注册器(例如:Eureka Server),拉取注册到服务器的可用服务信息,然后根据负载均衡策略,直接命中哪台服务器发送请求。这整个过程都是在客户端完成的,并不需要反向代理服务器的参与。

 

springcloud——客户端Eureka Client实现负载均衡 Ribbon
Eureka与Ribbon配合使用架构图


Ribbon在工作时分成两步:

  • 第一步先选择 EurekaServer ,它优先选择在同一个区域内负载较少的server
  • 第二步再根据用户指定的策略,在从server取到的服务注册列表中选择一个地址

其中Ribbon提供了多种策略:比如轮询、随机和根据响应时间加权。

二、为服务消费者实现Ribbon

(一)、创建一个microservicecloud-consumer-80工程

1、引入依赖

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <!-- Ribbon相关 -->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-eureka</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-ribbon</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-config</artifactId>
    </dependency>
</dependencies>

2、添加配置application.yml

server:
  port: 80
eureka:
  client:
    register-with-eureka: false
    service-url:
      defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/

3、自定义一个配置类ConfigBeans.java

@Configuration
public class ConfigBean{
    @Bean
    @LoadBalanced
    public RestTemplate getRestTemplate(){
        return new RestTemplate();
    }
}

只需要添加注解@LoadBalanced注解,就可以为RestTemplate整合Ribbon,使其具备负载均衡的能力

4、编写一个DeptController_Consumer.java

@RestController
public class DeptController_Consumer{
    // 可以直接用过服务名调用
    private static final String REST_URL_PREFIX = "http://MICROSERVICECLOUD-PROVIDER";

    @Autowired
    private RestTemplate restTemplate;

    @RequestMapping(value="/consumer/dept/get/{id}")
    public Dept get(@PathVariable("id") Long id){
        return restTemplate.getForObject(REST_URL_PREFIX+"/dept/get/"+id, Dept.class);
    }
}

MICROSERVICECLOUD-PROVIDER是用户微服务的虚拟主机名,当Ribbon和Eureka配合使用时,会自动将虚拟主机名映射成微服务的网络地址。

 

(二)、创建三个服务注册中心集群

工程名分别为:microservicecloud-discovery-eureka-7001,microservicecloud-discovery-eureka-7002,microservicecloud-discovery-eureka-7003

1、引入依赖

<dependencies>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-eureka-server</artifactId>
    </dependency>
</dependencies>
<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-dependencies</artifactId>
            <version>Dalston.SR1</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>


2、编写启动类,在启动类上添加一个@EnableEurekaServer注解,声明这是一个Eureka Server

启动类名称分别为:EurekaServer7001_App、EurekaServer7002_App、EurekaServer7003_App

@SpringBootApplication
@EnableEurekaServer//EurekaServer服务器端启动类,接受其它微服务注册进来
public class EurekaServer7001_App{
    public static void main(String[] args){
        SpringApplication.run(EurekaServer7001_App.class, args);
    }
}


3、在配置文件application.yml中添加以下内容 

7001:

server:
  port: 7001

eureka:
  instance:
    hostname: eureka7001.com #eureka服务端的实例名称
  client:
    register-with-eureka: false   #false表示不向注册中心注册自己,默认为true
    fetch-registry: false   #表示是否从Eureka Server获取注册表信息,默认为true。因为当前是一个单点Eureka Server,不需要同步其它Eureka Server节点的数据,所以设为false
    service-url:
      defaultZone: http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/   #设置与Eureka Server交互的地址查询服务和注册服务都需要依赖这个地址。

7002:

server:
  port: 7002

eureka:
  instance:
    hostname: eureka7002.com #eureka服务端的实例名称
  client:
    register-with-eureka: false #false表示不向注册中心注册自己。
    fetch-registry: false #false表示自己端就是注册中心,我的职责就是维护服务实例,并不需要去检索服务
    service-url:
      defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7003.com:7003/eureka/    #设置与Eureka Server交互的地址查询服务和注册服务都需要依赖这个地址。

7003:

server:
  port: 7003

eureka:
  instance:
    hostname: eureka7003.com #eureka服务端的实例名称
  client:
    register-with-eureka: false #false表示不向注册中心注册自己。
    fetch-registry: false #false表示自己端就是注册中心,我的职责就是维护服务实例,并不需要去检索服务
    service-url:
      defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/    #设置与Eureka Server交互的地址查询服务和注册服务都需要依赖这个地址。

(三)、创建三个服务提供者集群

工程名分别为:microservicecloud-provider-8001,microservicecloud-provider-8002,microservicecloud-provider-8003

<dependencies>
    <dependency>
        <groupId>org.mybatis.spring.boot</groupId>
        <artifactId>mybatis-spring-boot-starter</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-jetty</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
    </dependency>
    <!-- eureka -->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-eureka</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-config</artifactId>
    </dependency>
</dependencies>
<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-dependencies</artifactId>
            <version>Dalston.SR1</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-dependencies</artifactId>
            <version>1.5.9.RELEASE</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

2、编写启动类,在启动类上添加一个@EnableEurekaServer注解,声明这是一个Eureka Server

启动类名称分别为:DeptProvider8001_App、DeptProvider8002_App、DeptProvider8003_App

@SpringBootApplication
@EnableEurekaClient
public class DeptProvider8001_App{
    public static void main(String[] args){
        SpringApplication.run(DeptProvider8001_App.class, args);
    }
}

3、在配置文件application.yml中添加以下内容 

端口号分别为:8001、8002、8003

自定义服务名称:microservicecloud_provider_8001、microservicecloud_provider_8002、microservicecloud_provider_8003

server:
  port: 8001
spring:
  application:
    name: microservicecloud-dept
eureka:
  client:
    service-url:
      defaultZone:  http://localhost:7001/eureka,http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/ 
  instance:
    instance-id: microservicecloud_provider_8001   #自定义服务名称信息
    prefer-ip-address: true     #访问路径可以显示IP地址

4、编写一个DeptController_Provider.java

@RestController
public class DeptController {
  @Autowired
  private DeptService service;

  @RequestMapping(value = "/dept/get/{id}", method = RequestMethod.GET)
  public Dept get(@PathVariable("id") Long id) {
    return service.get(id);
  }
}

(四)、测试

分别启动Eureka Server、Provider、Consumer

访问随意一个Eureka Server:

springcloud——客户端Eureka Client实现负载均衡 Ribbon

可以发现集群已经成功启动

多次访问:localhost:80/consumer/dept/get/1

 

springcloud——客户端Eureka Client实现负载均衡 Ribbon

springcloud——客户端Eureka Client实现负载均衡 Ribbon

springcloud——客户端Eureka Client实现负载均衡 Ribbon

可以看到,此时请求会均匀分布到三个不同的微服务节点上,说明已经实现了负载均衡

 

 三、负载均衡策略

(一)、Ribbon提供的负载均衡策略

Ribbon的负载均衡策略在接口IRule下定义:

  • BestAvailableRule:最大可用策略,即先过滤出故障服务器后,选择一个当前并发请求数最小的;
  • AvailabilityFilteringRule:可用过滤策略,先过滤出故障的或并发请求大于阈值一部分服务实例,然后再以线性轮询的方式从过滤后的实例清单中选出一个;
  • RoundRobinRule:轮询策略,Ribbon以轮询的方式选择服务器,这个是默认值。所以示例中所启动的两个服务会被循环访问;
  • RandomRule:随机选择,也就是说Ribbon会随机从服务器列表中选择一个进行访问;
  • WeightedResponseTimeRule:带有加权的轮询策略,对各个服务器响应时间进行加权处理,然后在采用轮询的方式来获取相应的服务器;
  • RetryRule:对选定的负载均衡策略机上重试机制,在一个配置时间段内当选择server不成功,则一直尝试使用subRule的方式选择一个可用的server;
  • ZoneAvoidanceRule:区域感知策略,先使用主过滤条件(区域负载器,选择最优区域)对所有实例过滤并返回过滤后的实例清单,依次使用次过滤条件列表中的过滤条件对主过滤条件的结果进行过滤,判断最小过滤数(默认1)和最小过滤百分比(默认0),最后对满足条件的服务器则使用RoundRobinRule(轮询方式)选择一个服务器实例。

(二)、自定义负载均衡策略:

1、创建一个Ribbon的配置类:

@Configuration
public class MyRibbonRule {
    @Bean
    public IRule ribbonRule(){
        // 改成随机
        return new RandomRule();
    }

}

注意:该类不应该在主应用程序(启动类)上下文的@ComponentScan所扫描的包中!

2、在启动类上添加@RibbonClient主键

@RibbonClient(name = "MICROSERVICECLOUD-DEPT", configuration = MyRibbonRule.class)
@EnableEurekaClient
@SpringBootApplication
public class DeptConsumer80_Consumer {
    public static void main(String[] args){
        SpringApplication.run(DeptConsumer80_Consumer.class, args);
    }
}

@RibbonClient:

  • name:为特定名称的Ribbon服务提供者配置负载均衡
  • configuration:指定自定义负载均衡配置类

3、可以使用@RibbonClients注解为所有的Ribbon Client 提供默认配置

4、可以使用Application.yml配置文件自定义配置Ribbon

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

例子:

microservicecloud_dept
    ribbon:
        NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule

为microservicecloud_dept的Ribbon Client 的负载均衡策略改为随机

ribbon:
    NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule

把所有的Ribbon Client 的负载均衡策略改为随机

四、饥饿加载

springcloud会为每个微服务的RibbonClient维护一个子应用程序上下文,这个上下文默认是懒加载的。第一次请求时,对应的上下文才会被加载,因此首次请求往往会比较慢。

修改懒加载配置:

ribbon:
    eager-load:
        enabled: true
        clients: client1, client2

这样对于名为client1、client2的Ribbon Client将在启动时就加载对应的子应用程序上下文,从而提高了首次请求的访问速度