Spring Cloud入门教程(二):客户端负载均衡(Ribbon)
对于大型应用系统负载均衡(LB:Load Balancing)是首要被解决一个问题。在微服务之前LB方案主要是集中式负载均衡方案,在服务消费者和服务提供者之间又一个独立的LB,LB通常是专门的硬件,如F5,或者是基于软件的,如VS、HAproxy等。LB上有所有服务的地址映射表,当服务消费者调用某个目标服务时,它先向LB发起请求,由LB以某种策略(比如:Round-Robin)做负载均衡后将请求转发到目标服务。
而微服务的出现,则为LB的实现提供了另外一种思路:把LB的功能以库的方式集成到服务消费方的进程内,而不是由一个集中的设备或服务器提供。这种方案称为软负载均衡(Soft Load Balancing)或者客户端负载均衡。在Spring Cloud中配合Eureka的服务注册功能,Ribbon子项目则为REST客户端实现了负载均衡。
接下来我们将通过扩展上一篇中的示例代码来搭建一个基于Ribbon的客户端负载均衡示例。
1. 示例代码
保持上一篇中的代码不变。
2. 如何启用Ribbon
在上一篇中的consumer-hello工程(也就是我们的服务消费方)的pom的依赖中声明了:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-ribbon</artifactId>
</dependency>
也就是说在本项目中将会启动Ribbon。
2.1 启动类
@EnableDiscoveryClient
@SpringBootApplication
public class Application {
@Bean
@LoadBalanced
RestTemplate restTemplate() {
return new RestTemplate();
}
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
在该启动类中:
@EnableDiscoveryClient注解,说明这是一个Eureka客户端,同时也可以获取到Eureka服务器中所注册的服务列表;- 在
RestTemplate中我们增加了一个@LoadBalanced注解,该注解就是能够让RestTemplate启用客户端负载均衡。
备注:如果你对Spring的RestTemplate使用还不是很了解,可以参考我的这篇文章《Spring Cloud入门教程(番外篇一):RestTemplate使用指南》。
3. 负载均衡测试
1) 启动Eureka服务器;
2) 启动多个服务提供者:SERVICE-HELLO,这里我启动了两个:
java -jar service-hello-1.0.0-SNAPSHOT.jar
java -jar service-hello-1.0.0-SNAPSHOT.jar --server.port=2200
通过Maven将Service-Hello打包成一个FatJar,可以使用上面的命令启动多个实例。这里SERVICE-HELLO的两个实例服务器端口分别为:2100和2200。
3) 启动服务消费者:consumer-hello;
4) 打开Eureka服务器管理页面:http://localhost:8260,可以看到已经有两个SERVICE-HELLO服务和一个CONSUMER-HELLO服务注册进来了。

5) 打开Consumer-Hello:http://localhost:8080/hello,多次刷新,在页面中我们可以看到下面两个输出交替出现:


说明了consumer-hello在交替访问SERVICE-HELLO的两个服务实例,也就是说我们的负载均衡测试是成功的。
4. 直接使用Ribbon的API
另外,除了使用@LoadBalanced注解外,我们还可以直接使用Ribbon所提供的LoadBalancerClient来实现负载均衡:
@RestController
public class HelloController {
protected Logger logger = LoggerFactory.getLogger(HelloController.class);
@Autowired
private RestTemplate restTemplate;
@Autowired
private LoadBalancerClient loadBalancerClient;
@RequestMapping(value = "/hello", method = RequestMethod.GET)
public String hello() {
return restTemplate.getForEntity("http://SERVICE-HELLO/hello", String.class).getBody();
}
@RequestMapping(value = "/helloEx", method = RequestMethod.GET)
public String helloEx() {
ServiceInstance instance = this.loadBalancerClient.choose("SERVICE-HELLO");
URI helloUri = URI.create(String.format("http://%s:%s/hello", instance.getHost(), instance.getPort()));
logger.info("Target service uri = {}. ", helloUri.toString());
return new RestTemplate().getForEntity(helloUri, String.class).getBody();
}
}
同样多次访问http://localhost:8080/helloEx后,在控制台中将会输出以下日志:
2017-06-26 21:31:30 891 [http-nio-8080-exec-3] INFO i.t.c.c.h.controller.HelloController - Target service uri = http://cd826dembp.lan:2100/hello.
2017-06-26 21:31:33 008 [http-nio-8080-exec-4] INFO i.t.c.c.h.controller.HelloController - Target service uri = http://cd826dembp.lan:2200/hello.
2017-06-26 21:31:34 447 [http-nio-8080-exec-6] INFO i.t.c.c.h.controller.HelloController - Target service uri = http://cd826dembp.lan:2100/hello.
2017-06-26 21:31:35 395 [http-nio-8080-exec-7] INFO i.t.c.c.h.controller.HelloController - Target service uri = http://cd826dembp.lan:2200/hello.
2017-06-26 21:31:36 220 [http-nio-8080-exec-8] INFO i.t.c.c.h.controller.HelloController - Target service uri = http://cd826dembp.lan:2100/hello.
说明负载均衡是一样启作用的。
注意:在helloEx方法中不能够使用之前自动织入的restTemplate,否则会报以下错误:
java.lang.IllegalStateException: No instances available for cd826dembp.lan
这个是因为自动织入的restTemplate会把服务器的逻辑名称当作服务名称来使用,造成找不到相应的服务实例,从而报错。
另外,我们还可以采用下面的方式:
@Primary
@Bean(name="lbcRestTemplate")
RestTemplate lbcRestTemplate() {
return new RestTemplate();
}
上面的类更改为:
@RestController
public class HelloController {
protected Logger logger = LoggerFactory.getLogger(HelloController.class);
@Autowired
@Qualifier(value = "restTemplate")
private RestTemplate restTemplate;
@Autowired
@Qualifier(value = "lbcRestTemplate")
private RestTemplate lbcRestTemplate;
@Autowired
private LoadBalancerClient loadBalancerClient;
@RequestMapping(value = "/hello", method = RequestMethod.GET)
public String hello() {
return restTemplate.getForEntity("http://SERVICE-HELLO/hello", String.class).getBody();
}
@RequestMapping(value = "/helloEx", method = RequestMethod.GET)
public String helloEx() {
ServiceInstance instance = this.loadBalancerClient.choose("SERVICE-HELLO");
URI helloUri = URI.create(String.format("http://%s:%s/hello", instance.getHost(), instance.getPort()));
logger.info("Target service uri = {}. ", helloUri.toString());
return this.lbcRestTemplate.getForEntity(helloUri, String.class).getBody();
}
}
5. Ribbon负载均衡策略
Ribbon本身提供了下面几种负载均衡策略:
- RoundRobinRule: 轮询策略,Ribbon以轮询的方式选择服务器,这个是默认值。所以示例中所启动的两个服务会被循环访问;
- RandomRule: 随机选择,也就是说Ribbon会随机从服务器列表中选择一个进行访问;
- BestAvailableRule: 最大可用策略,即先过滤出故障服务器后,选择一个当前并发请求数最小的;
- WeightedResponseTimeRule: 带有加权的轮询策略,对各个服务器响应时间进行加权处理,然后在采用轮询的方式来获取相应的服务器;
- AvailabilityFilteringRule: 可用过滤策略,先过滤出故障的或并发请求大于阈值一部分服务实例,然后再以线性轮询的方式从过滤后的实例清单中选出一个;
- ZoneAvoidanceRule: 区域感知策略,先使用主过滤条件(区域负载器,选择最优区域)对所有实例过滤并返回过滤后的实例清单,依次使用次过滤条件列表中的过滤条件对主过滤条件的结果进行过滤,判断最小过滤数(默认1)和最小过滤百分比(默认0),最后对满足条件的服务器则使用RoundRobinRule(轮询方式)选择一个服务器实例。
我们可以通过继承
ClientConfigEnabledRoundRobinRule,来实现自己负载均衡策略。
这里,大家先对Ribbon提供的负载均衡有一个概念,后续我们再详细分析各个负载均衡策略的具体处理逻辑及配置方式。
6. 小结
Ribbon的主要功能是为REST客户端实现负载均衡,其工作原理可以概括为下面四个步骤:
- Ribbon首先根据其所在Zone优先选择一个负载较少的Eureka Server;
- 定期从Eureka Server更新并过滤服务实例列表;
- 根据指定的负载均衡策略,从可用的服务器列表中选择一个服务实例的地址;
- 然后通过RestClient进行服务调用。
这时我们系统的架构变成为:
Spring Cloud入门教程(二):客户端负载均衡(Ribbon)的更多相关文章
- Spring Cloud第四篇 | 客户端负载均衡Ribbon
本文是Spring Cloud专栏的第四篇文章,了解前三篇文章内容有助于更好的理解本文: Spring Cloud第一篇 | Spring Cloud前言及其常用组件介绍概览 Spring Cl ...
- Spring Cloud 入门教程(二): 配置管理
使用Config Server,您可以在所有环境中管理应用程序的外部属性.客户端和服务器上的概念映射与Spring Environment和PropertySource抽象相同,因此它们与Spring ...
- Spring Cloud 入门教程(二): 服务消费者(rest+ribbon)
在上一篇文章,讲了服务的注册和发现.在微服务架构中,业务都会被拆分成一个独立的服务,服务与服务的通讯是基于http restful的.Spring cloud有两种服务调用方式,一种是ribbon+r ...
- 史上最全Spring Cloud Alibaba--Nacos教程(涵盖负载均衡、配置管理、多环境切换、配置共享/刷新、灰度、集群)
能够实现Nacos安装 基于Nacos能实现应用负载均衡 能基于Nacos实现配置管理 配置管理 负载均衡 多环境切换 配置共享 配置刷新 灰度发布 掌握Nacos集群部署 1 Nacos安装 Nac ...
- Spring Cloud 入门教程(三): 配置自动刷新
之前讲的配置管理, 只有在应用启动时会读取到GIT的内容, 之后只要应用不重启,GIT中文件的修改,应用无法感知, 即使重启Config Server也不行. 比如上一单元(Spring Cloud ...
- Spring Cloud 入门教程(一): 服务注册
1. 什么是Spring Cloud? Spring提供了一系列工具,可以帮助开发人员迅速搭建分布式系统中的公共组件(比如:配置管理,服务发现,断路器,智能路由,微代理,控制总线,一次性令牌,全局锁 ...
- Spring Cloud 入门教程(五): Ribbon实现客户端的负载均衡
接上节,假如我们的Hello world服务的访问量剧增,用一个服务已经无法承载, 我们可以把Hello World服务做成一个集群. 很简单,我们只需要复制Hello world服务,同时将原来的端 ...
- Spring Cloud 入门教程(六): 用声明式REST客户端Feign调用远端HTTP服务
首先简单解释一下什么是声明式实现? 要做一件事, 需要知道三个要素,where, what, how.即在哪里( where)用什么办法(how)做什么(what).什么时候做(when)我们纳入ho ...
- Spring Cloud 入门教程(七): 熔断机制 -- 断路器
对断路器模式不太清楚的话,可以参看另一篇博文:断路器(Curcuit Breaker)模式,下面直接介绍Spring Cloud的断路器如何使用. SpringCloud Netflix实现了断路器库 ...
随机推荐
- innosetup 安装前、卸载前判断是否有进程正在运行<转>
[Code] //安装前判断是否有进程正在运行,istask.dll文件与打包的exe文件一起 function RunTask(FileName: string; bFullpath: Boolea ...
- MFC中在picture control中显示CBitmap图像的方法
CStatic *pStatic1 = (CStatic *)GetDlgItem(IDC__IMAGE_STATIC2); CBitmap cbmp; cbmp.LoadBitmap(MAKEINT ...
- java配置slf4j日志系统
首先要导入的包: import: 每个类中加入下面,其中 RdiFtpDownload.class 是当前的类名.class 然后就可以打日志了: 配置 log4j.properties log4j. ...
- Rust语言学习笔记(6)
Traits(特质) // 特质 pub trait Summary { fn summarize(&self) -> String; } pub struct NewsArticle ...
- http post 接口
集团需求管理系统通过网状网与给各省公司需求管理系统进行交互.落地方为发起方提供访问的URL,发起方使用HTTP POST方法发送请求报文并得到应答报文,发起方作为落地方的HTTP客户端,落地方作为发起 ...
- JQUERY 简单易用的提示框插件
业务开发过程中,为了避免用户的误操作,提示框是必要的,于是琢磨出了下面这个使用,方便的提示框 还要引入遮罩层的样式如下: /*弹出层*/.input{height: 32px;border: 1px ...
- Hibernate 再接触 多对一与一对多
多对一单向关联 数据库设计: 错误做法:在多方加外键 在多这一方加外键 第一种 annotation Group.java package com.bjsxt.hibernate; import ja ...
- Missing parentheses in call to 'print'
这个消息的意思是你正在试图用python3.x来运行一个只用于python2.x版本的python脚本. print"Hello world" 上面的语法在python3中是错误的 ...
- C++ STL--queue 的使用方法
2.queuequeue 模板类的定义在<queue>头文件中.与stack 模板类很相似,queue 模板类也需要两个模板参数,一个是元素类型,一个容器类型,元素类型是必要的,容器类型是 ...
- webstocket 聊天
/** * 初始化socket **/ function initSocket(index_host){//端口号 if( !window.WebSocket ){ console.log(" ...