SpringCloud学习之Ribbon负载均衡

    SpringCloud Ribbon 是一个基于HTTP和TCP的客户端负载均衡工具,它基于Netflix Ribbon实现。通过Spring Cloud的封装,可以让我们轻松的,面向服务的REST模板请求自动转换成客户端负载均衡的服务调用。
    负载均衡在系统架构中很重要,负载均衡是对系统的高可用,网络压力的环节和处理能力扩容的重要手段。在高并发的web请求中,大量用户同时点击一个登陆按钮,导致短时间内网络带宽急剧增加,服务器负载过重,这个时候我们就要对这些大量的请求做一个引流,把这些请求分发到不同的服务器上去,在这个过程中,使用什么样的算法把这些请求分发到不同的服务器上去,这是负载均衡需要做的。
    在Spring Cloud 构建的微服务集群中,不同服务之间的通信是通过HTTP的rest请求完成的,可能出现A服务对B的大量rest请求,这个时候就要对A服务做客户端的负载均衡,B服务要创建多个实例来就收这些负载均衡后的Rest请求。在客户端负载均衡中,所有客户端节点都维护这自己要访问的服务端清单,而这些服务端的清单来自于服务注册中心。
 

    下面使用一个例子,来演示Ribbon的负载均衡这一过程 ,使用上面这图的架构来搭建第一步:建立高可用的服务注册中心,使用SpringCloud组件之Eureka中创建的高可用注册中心,并在本地启动

第二步:创建一个接受负载均衡的SpringBoot服务(上面的B服务)

pom.xml文件

<parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.5.3.RELEASE</version>
        <relativePath/>
</parent>
<dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-tomcat</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <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-commons</artifactId>
        </dependency>
    </dependencies>
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Dalston.SR3</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>
application.yaml
server:
  port: 8015 #端口号
spring:
  application:
    name: loadbalance-service # 服务名称

#取消Ribbon使用Eureka
ribbon:
  eureka:
    enabled: false;

#设置一个服务客户端的Ribbon配置
helloword-service:
  ribbon:
    #配置Ribbon能访问 的微服务节点,多个节点用逗号隔开
    listOfServers: localhost:8001,localhost:8002,localhost:8003,localhost:8004,localhost:8005
    #配置Ribbon负载均衡策略
    NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule;

由于不需要Eureka组件,所以需要取消Ribbon使用Eureka,由于没有注册中心,需要手动添加一个服务客户端的所有实例,最后设置负载均衡策略

LoadBalanceController类

@RestController
@RequestMapping("loadbalance")
public class LoadBalanceController {
    @Autowired
    RestTemplate restTemplate;
    @Autowired
    private LoadBalancerClient loadBalancer;

    @RequestMapping(value = "/hello",method = RequestMethod.GET)
    public String getRequest(){
        ServiceInstance instance = loadBalancer.choose("helloword-service");
        String host = instance.getHost();
        int port = instance.getPort();
        System.out.println("负载均衡请求:" + host + ":" +port);
        return "负载均衡请求:" + host + ":" +port;
    }
}

LoadBalanceMain类

@SpringBootApplication
public class LoadBalanceMain {
    @Bean
    @LoadBalanced
    RestTemplate getrestTemplate(){
        return new RestTemplate();
    }
    @Bean
    public IRule ribbonRule() {
        return new RandomRule();
    }
    public static void main(String[] args) {
        SpringApplication.run(LoadBalanceMain.class,args);
    }
}

    ribbonRule()方法的返回值表示Ribbon选择的复杂你均衡策略,同时将该对象加载到 Spring容器中,启动main,在浏览器中输入http://localhost:8015/loadbalance/hello,多次请求,可以看到页面呈现不同的请求路径,而且这些请求都是随机出现,查看后台打印。

2. 负载均衡策略

(1)RoundRobinRule策略(com.netflix.loadbalancer.RandomRule)

    该策略实现了按照线性轮询的方式一次轮询服务清单上的每个服务实例。结合上面的例子,修改两个部分,(1)application.yaml中

NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RoundRobinRule,(2)LoadBalanceMain 中 修改ribbonRule()的返回值

(2)RetryRule策略(com.netflix.loadbalancer.RetryRule)

    该策略具备重试机制的实例选择功能,在给定时间内能够得到选择到具体的服务实例就返回,当超过时间还有没选到就返回null,参数maxRetryMillis控制这个超时时间。

(3)WeightedResponseTimeRule策略(com.netflix.loadbalancer.WeightedResponseTimeRule)

    该策略是对RoundRobinRule的扩展,增加了根据实例的响应时间来计算权重,并从权重中选择对应的实例。该策略实现主要有三个核心内容

  • 定时任务

    WeightedResponseTimeRule策略在初始化的时候会启动一个定时任务,默认每隔30秒计算一次每个服务实例的权重

  • 权重计算

    累计所有实例的响应时间,得到总的totalResponseTime,然后为实例清单中的每个实例逐个计算权重,计算公式为
weightSoFar = weightSoFar + totalResponseTime - 该实例的平均响应时间,weightSoFar起始为零

例子
有A,B,C,D四个实例,他们的平均响应时间是10,40,80,100,
计算总的响应时间10+40+80+100 =230
计算各个实例的权重
A: 230-10=220
B:220+(230-40)=410
C:410+(230-80)=560
D:560+(230-100)=690;
计算各个实例的权重区间
A:[0,220]
B:(220,410]
C:(410,560]
D:(560,690)
  • 实例选择

    WeightedResponseTimeRule策略会在[0,最大权重值)之间随机选取一个数,然后在看这个数落在哪个实例的权重区间内,接着WeightedResponseTimeRule就会去选择该实例。可以看出,响应时间越短,获取的权重区间就会越大,随机数落到的概率就会越大

(4)ClientConfigEnableRoundRobinRule策略(com.netflix.loadbalancer.ClientConfigEnableRoundRobinRule)

    该策略一般不直接使用,有些高级的策略会继承该类,完成一些高级的策略,ClientConfigEnableRoundRobinRule策略默认使用
RoundRibinRule的线性轮询机制

(5)BestAvailableRule策略(com.netflix.loadbalancer.BestAvailableRule)

    BestAvailableRule策略继承ClientConfigEnableRoundRobinRule,通过遍历负载均衡中维护的所有服务实例,会过滤掉故障实例,并找出并发数请求数最小的实例,所以该策略的特性就是选出最空闲的实例

(6)PredicateBasedRule策略(com.netflix.loadbalancer.PredicateBasedRule)

    PredicateBasedRule策略继承ClientConfigEnableRoundRobinRule,该策略主要特性是“先过滤,在轮询”,也就是先过滤掉一些实例,得到过滤后的实例清单,然后轮询该实例清单,PredicateBasedRule中“过滤”功能没有实现,需要继承它的类完成,也就是说不同继承PredicateBasedRule的类有不同的“过滤特性”

(7)AvailabilityFilteringRule策略(com.netflix.loadbalancer.PredicateBasedRule)

    AvailabilityFilteringRule策略继承PredicateBasedRule策略的“先过滤,在轮询”特性,
AvailabilityFilteringRule策略的过滤特性是
1:是否故障,即断路器是否生效已断开
2:实例的并发请求数大于阈值,默认2的32次方减一,该阈值可以通过
<clientName>.<nameSpace>.ActiveConnectionsLimit来设置,只要满足其中一个那么就会过滤掉

3. RestTemplate

    RestTemplate是一个REST请求的模板,封装了不同Rest请求类型,该对象会使用Ribbon的自动化配置,同时通过配置@LoadBalanced还能够开启客户端的负载均衡。下面介绍RestTemplate的不同请求类型
  • Get请求
RestTemplate restTemplate = new RestTemplate();
ResponseEntity<String> responseEntity = restTemplate .getForEntity(String “http://HELLO-SERVICE/helllo/hello?name={1}”,String.class,"李四");
String body = responseEntity .getBody();

第一个参数:表示 Rest请求的URL 通常使用服务的服务名称,服务通过服务名称获取注册中心上该服务名称下的所有实例,在服务中心中保存着各个实例的元数据包括IP和端口号,这样就可以组成一个url,然后实行负载均衡。

第二个参数:表示Rest请求的响应返回值类型,上面这个例子表示返回值是String类型。当然也可以是一个实体类

第三个参数:便是URL中映射的参数值 可以使使用Map<参数名:参数值> 如果没有可以不填

getForEntity方法返回的是整个Rest请求响应的结果,包含响应的body,当然还有一些其他的参数:如响应状态,等信息。

    对于只要求获取响应body的情况可以直接使用getForObject()方法,该方法在getForEntity()方法的基础上封装,直接返回响应body值

RestTemplate restTemplate = new RestTemplate();

String body = restTemplate.getForObject(String “http://HELLO-SERVICE/helllo/hello?name={1}”,String.class,"李四");

  • POST请求

多了一个postForLocation()方法,该方法实现以POST请求提交资源,并返回新资源的URL

User user = new User("李四",33);

URI responseUrl = restTemplate.postForLocation(url,user);

该方法不需要指定返回值类型,因为返回值类型指定为URI

4. 总结

在微服务架构中使用客户端负载均衡调用是需要两个步骤

(1)服务提供者只需要启动多个服务实例并注册到一个注册中心或者多个关联的注册中心

(2)服务消费者直接通过调用被@LoadBalanced注解修饰过的RestTemplate对象实现面向服务的接口调用




转自:https://www.jianshu.com/p/be57acb72d271

posted @ 2020-02-05 15:39  天~宇~翱~翔  阅读(453)  评论(0编辑  收藏  举报