Robbin是在Spring Cloud中的一个组件,是由Netfix发布的负载均衡器,有助于控制HTTP和TCP客户端的行为。它给我们提供了默认的轮询、随机等负载均衡算法。同时也可以由我们定义自己的算法。

由于Robbin已经被集成在Eureka里面,因此我们这个样例的代码都是在《微服务Eureka使用详解》的基础上进行。

参考博客:https://blog.csdn.net/u013089490/article/details/83786844https://blog.csdn.net/dwhdome/article/details/86477961

负载均衡样例

(1)我们首先启动好在《微服务Eureka使用详解》中编写的三个服务:服务注册中心,user服务,roles服务。访问Eureka的管理页面可以看到如下内容:

(2)下面我们先来修改User服务(只修改controller):

    @GetMapping("users/{id}")
public String getUser(@PathVariable("id") String id) {
String str = "7001User.id" + id;
System.out.println(str);
return str;
}

启动服务,它的端口是7001。

然后再复制一个User项目,将Controller内容调整为如下:

    @GetMapping("users/{id}")
public String getUser(@PathVariable("id") String id) {
String str = "7002User.id" + id;
System.out.println(str);
return str;
}

以及将配置文件中的端口修改为7002

server.port=

启动该应用。

这时我们再查看Eureka服务页面:

可以清楚的看到USER服务的可用区域(Availability Zones)已经从(1)变成了(2)。状态(status)已经变成了两个服务地址7001和7002。

(3)Roles服务的负载均衡在《微服务Eureka使用详解》中已经配置过了,我们这里查看一下即可。

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

(4)访问Roles服务路径~/roles/{id},可用连续多次访问,这里假如我连续访问5次:

/roles/
/roles/
/roles/
/roles/
/roles/

可用看到返回的结果:

7001User.id1
7002User.id2
7001User.id3
7002User.id4
7001User.id5

两个User服务,7001端口和7002端口是默认处于一个轮询的状态。假设这一次访问7001端口,下一次就访问7002端口,以此类推。

修改负载均衡策略

负责负载均衡策略的顶级接口:

com.netflix.loadbalancer.IRule

所有的负责均衡算法均实现了这个接口,它的实现类如下:

默认情况下,使用的是

com.netflix.loadbalancer.RoundRobinRule:以轮询的方式进行负载均衡。

常用的还有

com.netflix.loadbalancer.RandomRule:随机策略
com.netflix.loadbalancer.RetryRule:重试策略。
com.netflix.loadbalancer.WeightedResponseTimeRule:权重策略。会计算每个服务的权重,越高的被调用的可能性越大。
com.netflix.loadbalancer.BestAvailableRule:最佳策略。遍历所有的服务实例,过滤掉故障实例,并返回请求数最小的实例返回。
com.netflix.loadbalancer.AvailabilityFilteringRule:可用过滤策略。过滤掉故障和请求数超过阈值的服务实例,再从剩下的实力中轮询调用。

如果我们要实现自己的策略,可以继承IRule接口,下面我们来以RoundRobinRule为例查看一下如何实现负载均衡策略。

(1)IRule接口

public interface IRule {

    // 返回经过负载均衡后最终调用的服务
Server choose(Object var1); void setLoadBalancer(ILoadBalancer var1); ILoadBalancer getLoadBalancer();
}

(2)RoundRobinRule类

我们先看最重要的choose(Object)方法

    public Server choose(Object key) {
return this.choose(this.getLoadBalancer(), key);
}

里面调用了我们另一个choose(ILoadBalancer, Object)方法

    public Server choose(ILoadBalancer lb, Object key) {
if (lb == null) {
log.warn("no load balancer");
return null;
} else {
Server server = null;
int count = ; while(true) {
if (server == null && count++ < ) {
List<Server> reachableServers = lb.getReachableServers();
List<Server> allServers = lb.getAllServers();
int upCount = reachableServers.size();
int serverCount = allServers.size();
if (upCount != && serverCount != ) {
int nextServerIndex = this.incrementAndGetModulo(serverCount);
server = (Server)allServers.get(nextServerIndex);
if (server == null) {
Thread.yield();
} else {
if (server.isAlive() && server.isReadyToServe()) {
return server;
} server = null;
}
continue;
} log.warn("No up servers available from load balancer: " + lb);
return null;
} if (count >= ) {
log.warn("No available alive servers after 10 tries from load balancer: " + lb);
} return server;
}
}
}

在上面的方法中,主要内容是在while(true)内获取下一个server,获取的方法是incrementAndGetModulo(int)。然后根据方法返回的服务下标,从服务集合中找到对应的server,如果server存在且存活,会直接使用这个server。如果server不存在或不存在,则会再循环获取下一个。直到循环10次,或着没有从服务注册中心找到可用的服务,会返回null。

核心的incrementAndGetModulo(int)方法

    private int incrementAndGetModulo(int modulo) {
int current;
int next;
do {
current = this.nextServerCyclicCounter.get(); //nextServerCyclicCounter是AtomicInteger对象,默认值0,可保证线程安全性
next = (current + ) % modulo; //每次往后移一位,取集合中的下一个server。这里要注意的是从1开始,即数组中的第二个server会被第一个调用。
} while(!this.nextServerCyclicCounter.compareAndSet(current, next)); //操作完成后用CAS操作将next赋值给nextServerCyclicCounter return next;
}

(3)可用顺便再看一下RetryRule类。

    public Server choose(ILoadBalancer lb, Object key) {
long requestTime = System.currentTimeMillis();
long deadline = requestTime + this.maxRetryMillis;
Server answer = null;
answer = this.subRule.choose(key); //内部仍然是使用了轮询策略。
if ((answer == null || !answer.isAlive()) && System.currentTimeMillis() < deadline) {
InterruptTask task = new InterruptTask(deadline - System.currentTimeMillis()); while(!Thread.interrupted()) {
answer = this.subRule.choose(key);
if (answer != null && answer.isAlive() || System.currentTimeMillis() >= deadline) {
break;
} Thread.yield();
} task.cancel();
} return answer != null && answer.isAlive() ? answer : null;
}

这个类采用的是重试策略,可以看到里面其实仍是采用了轮询策略,只不过如果轮询的server无法访问,或者不存活,会在指定的时间(500)内一直获取下一个server,直到找到一个存活的server。

注意:上面所说的故障服务,是由Eureka注册中心来判断。即使服务已经挂掉,但是Eureka的实例未过期,仍会被判断为正常。但是实际的返回可能是null等。

如果我们要实现自己的负载均衡策略,也可以通过继承IRule接口,在配置文件中进行配置。

Robbin负载均衡的更多相关文章

  1. 14 微服务电商【黑马乐优商城】:day02-springcloud(理论篇四:配置Robbin负载均衡)

    本项目的笔记和资料的Download,请点击这一句话自行获取. day01-springboot(理论篇) :day01-springboot(实践篇) day02-springcloud(理论篇一) ...

  2. TZ_13_负载均衡-Robbin

    1.但是实际环境中,我们往往会开启很多个user-service的集群.此时我们获取的服务列表中就会有多个,到底该访问哪一个呢? 一般这种情况下我们就需要编写负载均衡算法,在多个实例列表中进行选择. ...

  3. springcloud-alibaba手写负载均衡的坑,采用restTemplate,不能添加@loadbalanced注解,否则采用了robbin

    采用springcloud-alibaba整合rabbion使用DiscoveryClient调用restful时遇到的一个问题,报错如下: D:\javaDevlepTool\java1.8\jdk ...

  4. spring-cloud-feign负载均衡组件

    Feign简介: Feign是一个声明式的Web服务客户端,使用Feign可使得Web服务客户端的写入更加方便.它具有可插拔注释支持,包括Feign注解和JAX-RS注解.Feign还支持可插拔编码器 ...

  5. spring-cloud-ribbon负载均衡组件

    Ribbon简介: Spring Cloud Ribbon 是一个基于 HTTP 和 TCP 的客户端负载均衡工具,它基于 Netflix Ribbon 实现. 通过 Spring Cloud 的封装 ...

  6. spring cloud 使用ribbon简单处理客户端负载均衡

    假如我们的multiple服务的访问量剧增,用一个服务已经无法承载, 我们可以把Hello World服务做成一个集群. 很简单,我们只需要复制Hello world服务,同时将原来的端口8762修改 ...

  7. Spring Cloud 入门教程(五): Ribbon实现客户端的负载均衡

    接上节,假如我们的Hello world服务的访问量剧增,用一个服务已经无法承载, 我们可以把Hello World服务做成一个集群. 很简单,我们只需要复制Hello world服务,同时将原来的端 ...

  8. 【转】Nginx学习---负载均衡的原理、分类、实现架构,以及使用场景

    [原文]https://www.toutiao.com/i6593604356799463944/ [原文]https://www.toutiao.com/i6592741060194075143/ ...

  9. SpringCloud 进阶之Ribbon和Feign(负载均衡)

    1. Ribbon 负载均衡 Spring Cloud Ribbon是基于Netflix Ribbon实现的一套客户端,负载均衡的工具; 1.1 Ribbon 配置初步 1.1.1 修改 micros ...

随机推荐

  1. Ubuntu 12.04 Eclipse设置 Javadoc背景色

    在Ambiance主题下,eclipse弹出的tip是黑色背景的,这样压根就看不清java doc. 当然可以在外观改变系统主题为其他主题,相应的gtk-2.0/gtkrc要重新设置,比如Ubuntu ...

  2. Hibernate O/R 映射

    O/R 映射 目前为止我们已经通过应用 Hibernate 见识过十分基础的 O/R 映射了,但是还有三个更加重要的有关映射的话题需要我们更详细的探讨.这三个话题是集合的映射,实体类之间的关联映射以及 ...

  3. VS 断点不会命中的情况

    总结下遇到的几次断点无法命中的情况: 1.手误设置为release模式 如果是release模式的情况下,断点跳转命中情况是无法预知的,所以请修改成debug 2.与源文件不一致 这个情况是最常见的, ...

  4. Nodejs之路(二)—— Nodejs再入门

    一.在Node中使用模板引擎 js代码: // art-template不仅可以在浏览器使用,也可以在node中使用 // 1.安装 npm install art-temlate模板引擎 // 2. ...

  5. API 练习 第一篇

    练习API   CreateSemaphoreCreateEvent ReleaseSemapWaitForSingleObject  CloseHandleInitializeCriticalSec ...

  6. 跟我一起学习webpack输出动态HTML(三)

    跟着之前的项目来 我们没打包一次就会生成一个bundile.js,我们要更新最新的代码不希望有缓存,那么这个时候我们就是更改资源的URL, 每当代码发生变化时,相应的hash也会发生变化.这个时候我们 ...

  7. Drupal创建Omega 4.x 子主题layout笔记

    Adding a new region to your Omega 4.0 subtheme (Drupal) Drupal: Creating a custom layout with Omega ...

  8. Mobox 知识管理平台助推市长质量奖

    Mobox 知识管理平台助推市长质量奖 引言: 在过去的十多年中,国际上在管理领域出现两股浪潮:一是许多国家和地区采取设立政府质量奖的方式,来改善企业管理绩效,提升区域整体竞争力:二是这些国家和地区的 ...

  9. set_clock_latency

    set_clock_latancy用于定于虚拟时钟与真实时钟的延时 考虑最糟糕的情况,评估setup时数据会使用最大延时,时钟使用最小延时:评估hold时,数据使用最小延时,时钟使用最大延时.

  10. Ionic 分享功能(微博 微信 QQ)

    1.安装插件 cordova plugin add cordova-plugin-wechat --variable wechatappid=你申请微信appid cordova plugin add ...