SpringCloud(2)---负载均衡 Ribbon(Greenwich版本)

​目录

1、负载均衡

2、Ribbon简介

3、IRULE的7中默认算法

4 、项目实践

4.1、准备工作

4.2、创建一个Ribbon的服务


1、负载均衡

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

  • 集中式LB

即在服务的消费方和提供方之间使用独立的LB设施(可以是硬件,如F5, 也可以是软件,如nginx), 由该设施负责把访问请求通过某种策略转发至服务的提供方。

  • 进程内LB

将LB逻辑集成到消费方,消费方从服务注册中心获知有哪些地址可用,然后自己再从这些地址中选择出一个合适的服务器。Ribbon就属于进程内LB,它只是一个类库,集成于消费方进程,消费方通过它来获取到服务提供方的地址。

Ribbon本地负载均衡和Nginx服务器端负载均衡的区别?

一、Ribbon本地负载均衡,在调用微服务接口时候,会在注册中心上获取信息服务列表之后缓存到jvm本地,从而在本地实现RPC远程服务调用技术。 

二、Nginx是在服务器端实现LB,客户端所有的请求都会交给Nginx,然后由Nginx实现转发请求。即LB由服务器端实现。

2、Ribbon简介

Spring Cloud Ribbon是基于Netflix Ribbon实现的一套客户端负载均衡的工具。 

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

​ Ribbon架构(1)

SpringCloud(2)---负载均衡 Ribbon(Greenwich版本)

Ribbon在工作时分成两步

第一步先选择 EurekaServer ,它优先选择在同一个区域内负载较少的server.

第二步再根据用户指定的策略,在从server取到的服务注册列表中选择一个地址。

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

总结:Ribbon其实就是一个软负载均衡的客户端组件,他可以和其他所需请求的客户端结合使用,和eureka结合只是其中的一个实例。

3、 IRULE的7中默认算法

IRule:根据特定算法中从服务器列表中选取一个要访问的服务,Ribbon默认的算法为轮询算法

Ribbon中的7种负载均衡算法:

  1. RoundRobinRule:轮询;
  2. RandomRule:随机;
  3. AvailabilityFilteringRule:会先过滤掉由于多次访问故障而处于断路器状态的服务,还有并发的连接数量超过阈值的服务,然后对剩余的服务列表按照轮询策略进行访问;
  4. WeightedResponseTimeRule:根据平均响应时间计算所有服务的权重,响应时间越快的服务权重越大被选中的概率越大。刚启动时如果统计信息不足,则使用RoundRobinRule(轮询)策略,等统计信息足够,会切换到WeightedResponseTimeRule;
  5. RetryRule:先按照RoundRobinRule(轮询)策略获取服务,如果获取服务失败则在指定时间内进行重试,获取可用的服务;
  6. BestAvailableRule:会先过滤掉由于多次访问故障而处于断路器跳闸状态的服务,然后选择一个并发量最小的服务;
  7. ZoneAvoidanceRule:复合判断Server所在区域的性能和Server的可用性选择服务器;

4 、项目实践

4.1、准备工作

继续用上一节的工程(SpringCloud-Project), 启动eureka-server,端口为8761; 启动eureka-client 两次,端口分别为8001 、8002。SpringBoot设置本地项目多端口启动

4.2、创建一个Ribbon的服务

1、在maven主工程springcloud-Project(SpringCloud(1)---服务的注册与发现 Eureka(Greenwich版本))下,新建module:service-ribbon,pom文件导入依赖。

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>SpringCloud-Project</artifactId>
        <groupId>com.boyue</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>
    <artifactId>service-feign</artifactId>
    <!--Feign默认集成了Ribbon,并和Eureka结合,默认实现了负载均衡的效果-->
    <dependencies>
        <!--Web的起步依赖spring-boot-starter-web-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!--Eureka客户端的起步依赖spring-cloud-starter-netflix-eureka-client-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
            <version>2.1.0.RELEASE</version>
        </dependency>
        <!--Feign的起步依赖spring-cloud-starter-openfeign-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>
    </dependencies>
</project>

代码提示:

springcloud更新换代比较快;

spring-cloud-starter-eureka-server是1.5以前的版本依赖;

spring-cloud-starter-netflix-eureka-server是最新版本的依赖(推荐)。

 

2、启动类上添加注解:@SpringBootApplication,@EnableDiscoveryClient,使用@LoadBalanced并配置负载均衡的配置信息类。

@SpringBootApplication
@EnableDiscoveryClient
public class ServiceRibbon {
    public static void main(String[] args) {
        SpringApplication.run(ServiceRibbon.class,args);
    }
    /**
     * 向程序的ioc注入一个bean: restTemplate;
     * 并通过@LoadBalanced注解表明这个restRemplate开启负载均衡的功能
     * 基于Ribbon
     * 此方法需要@Configuration注解,在@SpringBootApplication中已经包含
     */
    @Bean
    @LoadBalanced
    public RestTemplate restTemplate(){
        return  new RestTemplate();
    }
}

代码提示:

@EnableDiscoveryClient和@EnableEurekaClient的区别

简单区分下,他们在功能上是一致的:写在启动类的上,开启服务注册发现功能。

不同的是,当注册中心不一样时,像:eureka、consul、zookeeper,使用时也有了区别。

EnableDiscoveryClient注解在common包中,通过项目的classpath来决定使用哪种实现,而EnableEurekaClient注解在netflix包中,只会使用eureka这种实现方式;

所以,使用EnableDiscoverClient,对任何注册中心都适用。而EnableEurekaClient是为eureka服务的。

 

3、 配置文件application.yaml,其中需要将该服务注册Eureka注册中心:

server:
  port: 8001

  #我们可以指定微服务的名称后续在调用的时候,
  #  只需要使用该名称就可以进行服务的访问
spring:
  application:
      name: SERVICE-RIBBON
      #eureka是集群时候,只需要逗号,分隔即可
eureka:
  instance:
    hostname: localhost
    # 修改Eureka的默认描述信息
    # 我们让实例url指向 主机ip+端口号
    instance-id: http://${eureka.instance.hostname}:${server.port}
  client:
    service-url:
      defaultZone: http://localhost:8761/eureka/,http://localhost:7001/eureka/,http://localhost:7002/eureka/

4、写一个测试类UserService,通过之前注入ioc容器的restTemplate来消费EUREKA-CLIENT服务的“/info”接口,在这里我们直接用的程序名替代了具体的url地址,在ribbon中它会根据服务名来选择具体的服务实例,根据服务实例在请求的时候会用具体的url替换掉服务名,代码如下:

@Service
public class UserServiceImpl implements UserService {
    @Autowired
    private RestTemplate restTemplate;
    @Override
    public RespBean restGetInfo() {
//        通过之前注入ioc容器的restTemplate来消费eureka-client服务的“/info”接口,
//        在这里我们直接用的程序名替代了具体的url地址,
//        在ribbon中它会根据服务名来选择具体的服务实例,
//        根据服务实例在请求的时候会用具体的url替换掉服务名
        return restTemplate.getForObject("http://EUREKA-CLIENT/eurekaClient/user/eurekaClientInfo",RespBean.class);
//        return restTemplate.postForObject("http://EUREKA-CLIENT/eurekaClient/user/eurekaClientInfo","POST",RespBean.class);
    }
}

5、写一个controller,在controller中用调用HelloService 的restGetInfo()方法,代码如下:

@RestController
@RequestMapping("/rest")
public class RestServiceController {
    @Autowired
    private UserService userService;
    @GetMapping("/restGetInfo")
    public RespBean restGetInfo(){
        return userService.restGetInfo();
    }
}

6、EUREKA-CLIENT服务中eurekaClientInfo服务类:

@RestController
@RequestMapping("/user")
public class UserController {
​
    @Autowired
    private DiscoveryClient client;

    @Value("${server.port}")
    private String port;
    @Value("${spring.application.name}")
    private String applicationName;
    @GetMapping("/eurekaClientInfo")
    public RespBean eurekaClientInfo(){
       String info="被调用服务的名称:"+applicationName+",服务的端口:"+port;
        return RespBean.ok("服务信息如下:",info);
    }
}

7、我们可以在idea中,一个服务(EUREKA-CLIENT)按照多个端口同时启动。springboot设置本地项目多端口启动。在浏览器上多次访问http://localhost:8001/rest/restGetInfo,浏览器交替显示:

SpringCloud(2)---负载均衡 Ribbon(Greenwich版本)

这说明当我们通过调用restTemplate.getForObject("http://EUREKA-CLIENT/eurekaClient/user/eurekaClientInfo",RespBean.class)方法时,已经做了负载均衡,访问了不同的端口的服务实例。


扫码关注微信公众号:

SpringCloud(2)---负载均衡 Ribbon(Greenwich版本)

 

上一篇:SpringCloud 服务的平滑上下线


下一篇:Spring Cloud中使用Feign实现负载均衡--RandomRule