Spring Cloud入门教程-Ribbon实现客户端负载均衡
简介

我们继续以之前博客的代码为基础,增加Ribbon组件来提供客户端负载均衡。负载均衡是实现高并发、高性能、可伸缩服务的重要组成部分,它可以把请求分散到一个集群中不同的服务器中,以减轻每个服务器的负担。客户端负载均衡是运行在客户端程序中的,如我们的web项目,然后通过获取集群的IP地址列表,随机选择一个server发送请求。相对于服务端负载均衡来说,它不需要消耗服务器的资源。
基础环境
- JDK 1.8
- Maven 3.3.9
- IntelliJ 2018.1
- Git
项目源码
更新配置
我们这次需要在本地启动两个产品服务程序,用来验证负载均衡,所以需要为第二个程序提供不同的端口。Spring Cloud配置服务中心的配置默认会覆盖本地系统环境变量,而我们需要通过系统环境变量来设置产品服务的端口,所以需要在配置中心git仓库中修改产品服务的配置文件product-service.yml
server:
port: 8081
spring:
cloud:
config:
allow-override: true
override-system-properties: false
allow-override的默认值即为true,写出它来是想作说明,它的意思是允许远程配置中心的配置项覆盖本地的配置,并不是说允许本地的配置去覆盖远程的配置。当然我们可以把它设置成false,但是为了提供更精确的覆盖规则,这里保留了默认值。
我们添加了override-system-properties=false,即虽然远程配置中心的配置文件可以覆盖本地的配置,但是不要覆盖本地系统变量。修改完成后提交到git仓库。
另外,在productService项目的ProductController中添加一些log,用来验证负载均衡是否生效:
package cn.zxuqian.controllers;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class ProductController {
private static Logger log = LoggerFactory.getLogger(ProductController.class);
@RequestMapping("/products")
public String productList() {
log.info("Access to /products endpoint");
return "外套,夹克,毛衣,T恤";
}
}
为web配置Ribbon
首先在pom.xml中添加Ribbon的依赖:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
</dependency>
然后修改Application类,添加如下代码:
@EnableCircuitBreaker
@EnableDiscoveryClient
@RibbonClient(name = "product-service")
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
@Bean
@LoadBalanced
public RestTemplate rest(RestTemplateBuilder builder) {
return builder.build();
}
}
这里用到了@RibbonClient(name = "product-service")注解,用来标记此项目为Ribbon负载均衡的客户端,它需要选择产品服务集群中其中的一台来访问所需要的服务,这里的name属性对应于productService项目中配置的spring.application.name属性。
@LoadBalanced注解标明了RestTemplate会被配置为自动使用Ribbon的LoadBalancerClient来选择服务的uri并发送请求。
在我们在ProductService类中添加如下代码:
@Service
public class ProductService {
private final RestTemplate restTemplate;
@Autowired
private DiscoveryClient discoveryClient;
public ProductService(RestTemplate restTemplate) {
this.restTemplate = restTemplate;
}
@HystrixCommand(fallbackMethod = "backupProductList")
public String productList() {
List<ServiceInstance> instances = this.discoveryClient.getInstances("product-service");
if(instances != null && instances.size() > 0) {
return this.restTemplate.getForObject(instances.get(0).getUri() + "/products", String.class);
}
return "";
}
public String backupProductList() {
return "夹克,毛衣";
}
public String productListLoadBalanced() {
return this.restTemplate.getForObject("http://product-service/products", String.class);
}
}
这里新添加了一个productListLoadBalanced方法,跟之前的productList方法访问的是同一服务,只不过是用Ribbon Client去做了负载均衡,这里的uri的host变成了product-service即要访问的服务的名字,跟@RibbonClient中配置的name属性保持一致。最后在我们的ProductController中添加下面的代码:
@RestController
public class ProductController {
@Autowired
private ProductService productService;
@RequestMapping("/products")
public String productList() {
return productService.productList();
}
@RequestMapping("/productslb")
public String productListLoadBalanced() {
return productService.productListLoadBalanced();
}
}
来创建一个专门处理/productslb请求的方法,调用productServie提供负载均衡的方法。
到这里我们的代码就完成了,代码看似简单,其实是所有的配置都使用了默认值。Ribbon提供了编程式和配置式两种方式来配置Ribbon Client。现简单介绍下,后续深入Ribbon时再和大家一起看看如何修改它的配置。Ribbon提供如下配置(左边是接口,右边是默认实现):
IClientConfigribbonClientConfig: DefaultClientConfigImplIRule ribbonRule: ZoneAvoidanceRuleIPing ribbonPing: DummyPingServerList<Server>ribbonServerList: ConfigurationBasedServerListServerListFilter<Server>ribbonServerListFilter: ZonePreferenceServerListFilterILoadBalancerribbonLoadBalancer: ZoneAwareLoadBalancerServerListUpdaterribbonServerListUpdater: PollingServerListUpdater
因为我们这个项目用了Eureka,所以有些配置项和默认实现有所不同,如Eureka使用DiscoveryEnabledNIWSServerList取代ribbonServerList来获取在Eureka上注册的服务的列表。下边有一个简单的Congiguration类,来自Spring官网:
public class SayHelloConfiguration {
@Autowired
IClientConfig ribbonClientConfig;
@Bean
public IPing ribbonPing(IClientConfig config) {
return new PingUrl();
}
@Bean
public IRule ribbonRule(IClientConfig config) {
return new AvailabilityFilteringRule();
}
}
Ribbon默认不会发送Ping检查server的健康状态,默认均正常,然后IRune默认实现为ZoneAvoidanceRule用来避免AWS EC2问题较多的zone,这在本地测试环境来说是用不到的,然后替换成了AvailabilityFilteringRule,这个可以开启Ribbon自带的断路器功能,来过滤不正常工作的服务器。
测试
首先启动我们的configserver配置中心服务,然后启动registry Eureka注册与发现服务,然后启动两个productService,第一个我们可以正常使用spring-boot:run插件来启动,第二个我们需要给它提供一个新的端口,可以用如下命令启动:
$ SERVER_PORT=8082 mvn spring-boot:run
最后启动我们的web客户端项目,访问http://localhost:8080/productslb,然后刷新几次,会看到运行着productService的两个命令行窗口会随机出现我们的log:
Access to /products endpoint
欢迎访问我的博客张旭乾的博客
Spring Cloud入门教程-Ribbon实现客户端负载均衡的更多相关文章
- Spring Cloud入门教程(二):客户端负载均衡(Ribbon)
对于大型应用系统负载均衡(LB:Load Balancing)是首要被解决一个问题.在微服务之前LB方案主要是集中式负载均衡方案,在服务消费者和服务提供者之间又一个独立的LB,LB通常是专门的硬件,如 ...
- Spring Cloud官方文档中文版-客户端负载均衡:Ribbon
官方文档地址为:http://cloud.spring.io/spring-cloud-static/Dalston.SR2/#_spring_cloud_netflix 文中例子我做了一些测试在:h ...
- Spring Cloud Ribbon---微服务调用和客户端负载均衡
前面分析了Eureka的使用,作为服务注册中心,Eureka 分为 Server 端和 Client 端,Client 端作为服务的提供者,将自己注册到 Server 端,Client端高可用的方式是 ...
- 《Spring Cloud》学习(二) 负载均衡!
第二章 负载均衡 负载均衡是对系统的高可用.网络压力的缓解和处理能力扩容的重要手段之一.Spring Cloud Ribbon是一个基于 HTTP 和 TCP 的客户端负载均衡工具,它基于Netfli ...
- 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实现了断路器库 ...
- Spring Cloud 入门教程(九): 路由网关zuul
在微服务架构中,需要几个关键的组件,服务注册与发现.服务消费.负载均衡.断路器.智能路由.配置管理等,由这几个组件可以组建一个简单的微服务架构.客户端的请求首先经过负载均衡(zuul.Ngnix),再 ...
- Spring Cloud 入门教程(四): 分布式环境下自动发现配置服务
前一章, 我们的Hello world应用服务,通过配置服务器Config Server获取到了我们配置的hello信息“hello world”. 但自己的配置文件中必须配置config serve ...
随机推荐
- android之View绘制
Android系统的视图结构的设计也采用了组合模式,即View作为所有图形的基类,Viewgroup对View继承扩展为视图容器类,由此就得到了视图部分的基本结构--树形结构 View定义了绘图的基本 ...
- SpriteBuilder中使用GUI界面快速搭建RPG游戏中的地图名显示动画
大熊猫猪·侯佩原创或翻译作品.欢迎转载,转载请注明出处. 如果觉得写的不好请告诉我,如果觉得不错请多多支持点赞.谢谢! hopy ;) 在RPG游戏中我们在进入一个新的场景时,比如一个房间,一个村庄, ...
- 01安卓像素 dpi 、 dip 、分辨率、屏幕尺寸、px、density 关系以及换算
一.基本概念 dip : Density independent pixels ,设备无关像素. dp :就是dip px : 像素 dpi :d ...
- 【流媒体开发】VLC Media Player - Android 平台源码编译 与 二次开发详解 (提供详细800M下载好的编译源码及eclipse可调试播放器源码下载)
作者 : 韩曙亮 博客地址 : http://blog.csdn.net/shulianghan/article/details/42707293 转载请注明出处 : http://blog.csd ...
- python 内存数据库与远程服务
python 内存数据库与远程服务 需要import的python 内存数据库代码参考下面的链接: http://blog.csdn.net/ubuntu64fan/article/details/5 ...
- 深入理解android6.0 RunTime Permisstion
了解下runtime permission 2015.8 google发布了android 6.0,sdk版本为23,一款"为工作升级而生"的android系统.如6.0新加入的指 ...
- shell脚本中调用另一个脚本的三种不同方法(fork, exec, source)
fork ( /directory/script.sh) fork是最普通的, 就是直接在脚本里面用/directory/script.sh来调用script.sh这个脚本. 运行的时候开一个sub- ...
- sql一些常用的经典语句,最后是select as的用法
总结一些工作中用到或碰到的SQL语句,希望能与大家分享,同时也希望大家能提供更多的精妙SQL语句..... 1.delete table1 from (select * from table2) as ...
- PKU 3468 A Simple Problem with Integers
题目大意: 有N,M两个数 Q 表示查询, 后面两个数a b,表示查询从a 到b计算它们的和 C 表示增加 后面三个数a,b,c 表示从a开始,一直到b,每个数都增加c 除了查询要进行输出,增加不 ...
- 如何在Git中撤销一切 | 干货
翻译:李伟 审校:张帆 译自:Github JF杰微刊:如何在Git中撤销一切 任何一个版本控制系统中,最有用的特性之一莫过于 "撤销(undo)"操作.在Git中,"撤 ...