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提供如下配置(左边是接口,右边是默认实现):
- IClientConfig ribbonClientConfig: DefaultClientConfigImpl
- IRule ribbonRule: ZoneAvoidanceRule
- IPing ribbonPing: DummyPing
- ServerList<Server> ribbonServerList: ConfigurationBasedServerList
- ServerListFilter<Server> ribbonServerListFilter: ZonePreferenceServerListFilter
- ILoadBalancer ribbonLoadBalancer: ZoneAwareLoadBalancer
- ServerListUpdater ribbonServerListUpdater: 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
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。
- 使用NGINX和NGINX Plus速率限速
- 独家 | 教你用Scrapy建立你自己的数据集(附视频)
- 浏览器平台:Internet Explorer 8
- ASP.NET MVC 1.0 RC 那些事
- 优化SQLServer——表和分区索引(二)
- 关于UNPIVOT 操作符
- XML 在SQLServer中的使用
- 在asp.net mvc上应用新ASP.NET图表控件
- 自定义Unity对象生命周期管理集成ADO.NET Entity Framework
- Mono运行于IPhone之上
- Apache Hadoop入门
- 参数化(三):参数嗅探
- 什么是内存数据库以及它如何有效保存数据
- DATETIME类型和BIGINT 类型互相转换
- java教程
- Java快速入门
- Java 开发环境配置
- Java基本语法
- Java 对象和类
- Java 基本数据类型
- Java 变量类型
- Java 修饰符
- Java 运算符
- Java 循环结构
- Java 分支结构
- Java Number类
- Java Character类
- Java String类
- Java StringBuffer和StringBuilder类
- Java 数组
- Java 日期时间
- Java 正则表达式
- Java 方法
- Java 流(Stream)、文件(File)和IO
- Java 异常处理
- Java 继承
- Java 重写(Override)与重载(Overload)
- Java 多态
- Java 抽象类
- Java 封装
- Java 接口
- Java 包(package)
- Java 数据结构
- Java 集合框架
- Java 泛型
- Java 序列化
- Java 网络编程
- Java 发送邮件
- Java 多线程编程
- Java Applet基础
- Java 文档注释
- MapReduce之 FileInputFormat的切片策略(默认)
- MapReduce之片和块的关系
- MapReduce的常见输入格式之CombineTextInputFormat
- MapReduce之Job提交流程
- MapReduce之自定义InputFormat
- MapReduce之MapTask工作机制
- 数据链路层的三个基本问题
- DEBUG ArrayList
- 【PAT甲级】1002 A+B for Polynomials (25分)
- c/c++补完计划(一): 指针常量和常量指针
- 从0到1开发测试平台(六)增加登录接口
- FFmpeg 开发(04):FFmpeg + OpenGLES 实现音频可视化播放
- 【剑指offer】31.整数中1出现的次数
- 【剑指offer】35.数组中的逆序对
- 针对用友ic读卡器 web 脚本的重构