上一讲我们对 Nacos 的集群环境与实现原理进行了讲解,我们已经可以轻松将单个微服务接入到 Nacos 进行注册,但是微服务本不是孤岛,如何实现有效的服务间稳定通信是本文即将介绍的主要内容,本次我们将主要学习三方面知识:

  1. 介绍 Ribbon 负载均衡器;
  2. 讲解 Ribbon+RestTemplate 两种实现方式;
  3. 讲解 Ribbon 的负载均衡策略。

Ribbon 负载均衡器

在介绍 Ribbon 之前,咱们先来认识下负载均衡以及它的两种实现方式。
负载均衡顾名思义,是指通过软件或者硬件措施。它将来自客户端的请求按照某种策略平均的分配到集群的每一个节点上,保证这些节点的 CPU、内存等设备负载情况大致在一条水平线,避免由于局部节点负载过高产生宕机,再将这些处理压力传递到其他节点上产生系统性崩溃。

负载均衡按实现方式分类可区分为:服务端负载均衡与客户端负载均衡
服务端负载均衡顾名思义,在架构中会提供专用的负载均衡器,由负载均衡器持有后端节点的信息,服务消费者发来的请求经由专用的负载均衡器分发给服务提供者,进而实现负载均衡的作用。目前常用的负载均衡器软硬件有:F5、Nginx、HaProxy 等。

客户端负载均衡是指,在架构中不再部署额外的负载均衡器,在每个服务消费者内部持有客户端负载均衡器,由内置的负载均衡策略决定向哪个服务提供者发起请求。说到这,我们的主角登场了,Netfilx Ribbon 是 Netflix 公司开源的一个负载均衡组件,是属于客户端负载均衡器。目前Ribbon 已被 Spring Cloud 官方技术生态整合,运行时以 SDK 形式内嵌到每一个微服务实例中,为微服务间通信提供负载均衡与高可用支持。为了更容易理解,我们通过应用场景说明 Ribbon 的执行流程。假设订单服务在查询订单时需要附带对应商品详情,这就意味着订单服务依赖于商品服务,两者必然产生服务间通信,此时 Ribbon 的执行过程如下图所示:

  1. 订单服务(order-service)与商品服务(goods-service)实例在启动时向 Nacos 注册;
  2. 订单服务向商品服务发起通信前,Ribbon 向 Nacos 查询商品服务的可用实例列表;
  3. Ribbon 根据设置的负载策略从商品服务可用实例列表中选择实例;
  4. 订单服务实例向商品服务实例发起请求,完成 RESTful 通信;
    了解了 Ribbon 执行流程后,咱们通过代码方式体现这个完整流程

Ribbon+RestTemplate 实现服务间高可用通信

开始前,我们首先介绍下 Spring Cloud 自带的 RestTemplate 对象,RestTemplate 对象是Spring Cloud 封装的 RESTful 通信对象,它封装了基于 HTTP 协议的操作,通过简单的API便可发起 HTTP 请求并自动处理响应。RestTemplate 天然与 Ribbon 兼容,两者配合可以极大简化服务间通信过程。Ribbon + RestTemplate 提供了两种开发模式:代码模式,注解模式。

代码模式

第一种代码模式是指通过纯 Java 代码实现微服务间通信,虽然工作中代码模式很少使用,但它却是理解 Ribbon+RestTemplate 最直观的途径,所以我对它首先进行讲解。该模式使用主要分为两个阶段:

第一阶段,创建服务提供者,服务提供者是请求的实际处理者,也是标准的 Spring Boot 工程,利用 Controller 对外暴露 RESTful API 供服务消费者调用。

第一步,利用 Spring Initializr 向导创建 provider-service 微服务。

其中 pom.xml 要确保引入 web 与 nacos-discovery 两个依赖。

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId></dependency>

第二步,在 application.yml 中调整微服务与 Nacos 的通信配置。

spring:application:name: provider-service #应用/微服务名字cloud:nacos:discovery:server-addr: 192.168.31.102:8848 #nacos服务器地址username: nacos #用户名密码password: nacosserver:port: 80

第三步,创建 ProviderController,通过 Controller 控制器对外暴露接口。

package com.lagou.providerservice.controller;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.RestController;@RestControllerpublic class ProviderController {@GetMapping("/provider/msg")public String sendMessage(){return "This is the message from provider service!";}}

到这里服务提供者 provider-service 已经开发完毕,细心的你应该已注意到,服务提供者就是标准的 Spring Cloud 微服务,向 Nacos 进行注册的同时对外暴露 msg 接口并返回一段静态文本,并没有任何与 Ribbon 相关的内容。确实,Ribbon 与 RestTemplate 应出现在服务消费者,而非提供者一端。

下面为了演示需要,我们需要准备五台虚拟机:

在 provider-service 工程中利用 maven package 命令生成 provider-service-0.0.1-SNAPSHOT.jar。

分别上传至到 111 至 113 节点后执行 Java 命令启动微服务实例。

java -jar provider-service-0.0.1-SNAPSHOT.jar

三个节点启动成功,在 Nacos 控制台应见到三个健康实例。

单独访问任意节点,都能看到相同的返回文本。

http://192.168.31.111/provider/msghttp://192.168.31.112/provider/msghttp://192.168.31.113/provider/msg


到这里,第一阶段开发服务提供者 provider-service 告一段落。

第二阶段,开发服务消费者 consumer-service,服务消费者说白了就是服务的使用方,我们需要在服务消费者内置 Ribbon+RestTemplate 实现服务间高可用通信。

第一步,利用 Spring Initializr 创建 consumer-service微服务。

pom.xml 确保引入以下三个依赖。

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId></dependency><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-ribbon</artifactId><version>${spring-cloud-alibaba.version}</version></dependency>

这里需要重点说明,starter-netflix-ribbon 就是通过 Spring Boot Starter 向当前微服务工程集成 Ribbon,无须做其他额外配置。与此同时,用于 RESTful 通信的 RestTemplate 对象已被集成到 starter-web 模块,无须额外依赖。

第二步,配置 application.yml,与 provider-service 除微服务 id 外并无其他变化。

spring:application:name: customer-service #应用/微服务名字cloud:nacos:discovery:server-addr: 192.168.31.102:8848 #nacos服务器地址username: nacos #用户名密码password: nacosserver:port: 80

第三步,利用 Spring Java Config 方式声明 RestTemplate。在 ConsumerServiceApplication 类中新增以下声明代码。

@SpringBootApplicationpublic class ConsumerServiceApplication {//Java Config声明RestTemplate对象//在应用启动时自动执行restTemplate()方法创建RestTemplate对象,其BeanId为restTemplate。@Beanpublic RestTemplate restTemplate(){return new RestTemplate();}public static void main(String[] args) {SpringApplication.run(ConsumerServiceApplication.class, args);}}

第四步,创建 ConsumerController,通过 Ribbon+RestTemplate 实现负载均衡通信,重要的代码我通过注释进行说明。

package com.lagou.consumerservice.controller;...@RestControllerpublic class ConsumerController {private Logger logger = LoggerFactory.getLogger(ConsumerController.class);//注入 Ribbon 负载均衡器对象//在引入 starter-netflix-ribbo n后在 SpringBoot 启动时会自动实例化 LoadBalancerClient 对象。//在 Controlle 使用 @Resource 注解进行注入即可。@Resourceprivate LoadBalancerClient loadBalancerClient;@Resource//将应用启动时创建的 RestTemplate 对象注入 ConsumerControllerprivate RestTemplate restTemplate;@GetMapping("/consumer/msg")public String getProviderMessage() {//loadBalancerClient.choose()方法会从 Nacos 获取 provider-service 所有可用实例,//并按负载均衡策略从中选择一个可用实例,封装为 ServiceInstance(服务实例)对象//结合现有环境既是从192.168.31.111:80、192.168.31.112:80、192.168.31.113:80三个实例中选择一个包装为ServiceInstanceServiceInstance serviceInstance = loadBalancerClient.choose("provider-service");//获取服务实例的 IP 地址String host = serviceInstance.getHost();//获取服务实例的端口int port = serviceInstance.getPort();//在日志中打印服务实例信息logger.info("本次调用由provider-service的" + host + ":" + port + " 实例节点负责处理" );//通过 RestTemplate 对象的 getForObject() 方法向指定 URL 发送请求,并接收响应。//getForObject()方法有两个参数://1. 具体发送的 URL,结合当前环境发送地址为:http://192.168.31.111:80/provider/msg//2. String.class说明 URL 返回的是纯字符串,如果第二参数是实体类, RestTemplate 会自动进行反序列化,为实体属性赋值String result = restTemplate.getForObject("http://" + host + ":" + port + "/provider/msg", String.class);//输出响应内容logger.info("provider-service 响应数据:" + result);//向浏览器返回响应return "consumer-service 响应数据:" + result;}}

第五步,利用 Maven Package 打包生成 Jar。

部署至 120 虚拟机,执行启动命令:

java -jar consumer-service-0.0.1-SNAPSHOT.jar

启动成功后,在 Nacos 中确认 consumer-service 已注册。
在浏览器输入http://192.168.31.120/consumer/msg,F5 多次刷新,看日志会得到以下结果

本次调用由 provider-service 的 192.168.31.111:80 实例节点负责处理consumer-service 获得数据:This is the message from provider service!本次调用由 provider-service 的 192.168.31.112:80 实例节点负责处理consumer-service 获得数据:This is the message from provider service!本次调用由 provider-service 的 192.168.31.113:80 实例节点负责处理consumer-service 获得数据:This is the message from provider service!本次调用由 provider-service 的 192.168.31.111:80 实例节点负责处理consumer-service 获得数据:This is the message from provider service!本次调用由 provider-service 的 192.168.31.112:80 实例节点负责处理consumer-service 获得数据:This is the message from provider service!

不难看出,因为在 Nacos 中存在 3 个 provider-service 的可用实例,默认 Ribbon 是以轮询方式按顺序逐次发送。如果遇到某个 provider-service 实例宕机,Nacos 心跳机制会检测到并将其剔除,同时通知所有 consumer-service 实例,服务提供者节点状态发生变化,之后 consumer-service 便不会向宕机节点发出请求。

以上便是代码模式的处理过程。它清晰的说明了 Ribbon 的执行过程,先从 Nacos 获取可用服务提供者实例信息,再通过 RestTemplate.getForObject() 向该实例发起 RESTful 请求完成处理。但可能你也感觉到了,代码模式使用复杂,需要自己获取可用实例 IP、端口信息,再拼接 URL 实现服务间通信,那有没有更简单的办法呢?答案是肯定的,利用 @LoadBalanced 注解可自动化实现这一过程。

注解模式

注解模式仍然分为两阶段:

第一阶段,创建服务提供者 provider-service,因服务提供者并不涉及 Ribbon,所以与代码模式一阶段代码完全相同,这里不再复述。

第二阶段,创建新的服务消费者 consumer-service。

第一步,利用 Spring Initializr 创建 consumer-service 微服务。

同样引用 3 个依赖。

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId></dependency><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-ribbon</artifactId><version>${spring-cloud-alibaba.version}</version></dependency>

第二步,配置 application.yml。在原有代码模式基础上,将 Debug 级别日志输出,这样便可看到负载均衡实例信息。

spring:application:name: consumer-service #应用/微服务名字cloud:nacos:discovery:server-addr: localhost:8848 #nacos服务器地址username: nacos #用户名密码password: nacoslogging:level:root: debug

第三步,关键点来了,在 Spring 初始化 RestTemplate 实例时增加 @LoadBalanced 注解,使 RestTemplate 进行服务通信时自动与 Ribbon 整合,自动实现负载均衡。

@SpringBootApplicationpublic class ConsumerServiceApplication {@Bean@LoadBalanced //使RestTemplate对象自动支持Ribbon负载均衡public RestTemplate restTemplate(){return new RestTemplate();}public static void main(String[] args) {SpringApplication.run(ConsumerServiceApplication.class, args);}}

第四步,在 Controller 发起通信时,原有 RestTemplate.getForObject() 方法书写 URL 时,将 IP 端口部分要替换为服务名,如下所示:

@RestControllerpublic class ConsumerController {private Logger logger = LoggerFactory.getLogger(ConsumerController.class); @Resourceprivate RestTemplate restTemplate;@GetMapping("/consumer/msg")public String getProviderMessage() {//关键点:将原有IP:端口替换为服务名,RestTemplate便会在通信前自动利用Ribbon查询可用provider-service实例列表//再根据负载均衡策略选择节点实例String result = restTemplate.getForObject("http://provider-service/provider/msg", String.class);logger.info("consumer-service获得数据:" + result);return   "consumer-service获得数据:" + result;}}

在新的 getProviderMessage 代码中,不再出现 LoadBalancerClient 与 ServiceInstance 对象,这一切都被 @LoadBalanced 进行封装,在 RestTemplate 查询前自动处理。

第五步,重新部署 consumer-service,多次访问地址http://192.168.31.120/consumer/msg,在控制台会看到 Debug 级别日志,通过实际IP地址也同样印证 Ribbon 默认采用轮询策略进行分配。

s.n.www.protocol.http.HttpURLConnection : {GET /provider/msg ...}{Accept: ...}{User-Agent: ...}{Host: 192.168.31.111:80}...s.n.www.protocol.http.HttpURLConnection : {GET /provider/msg ...}{Accept: ...}{User-Agent: ...}{Host: 192.168.31.112:80}...s.n.www.protocol.http.HttpURLConnection : {GET /provider/msg ...}{Accept: ...}{User-Agent: ...}{Host: 192.168.31.113:80}

以上便是注解模式的使用办法,相比代码模式是不是简单很多啊。对了,你注意到了吗?无论注解模式还是代码模式,默认的负载均衡策略都是轮询,即按顺序依次访问,作为 Ribbon 还支持哪些其他负载均衡策略呢?我们又该如何设置呢?本次最后一小节,我将带领你学习这块知识。

如何配置 Ribbon 负载均衡策略

Ribbon 内置多种负载均衡策略,常用的分为以下几种。

  • RoundRobinRule:轮询策略,Ribbon 默认策略。默认超过 10 次获取到的 server 都不可用,会返回⼀个空的 server。
  • RandomRule:随机策略,如果随机到的 server 为 null 或者不可用的话。会不停地循环选取。
  • RetryRule:重试策略,⼀定时限内循环重试。默认继承 RoundRobinRule,也⽀持自定义注⼊,RetryRule 会在每次选取之后,对选举的 server 进⾏判断,是否为 null,是否 alive,并且在 500ms 内会不停地选取判断。而 RoundRobinRule 失效的策略是超过 10 次,RandomRule 没有失效时间的概念,只要 serverList 没都挂。
  • BestAvailableRule:最小连接数策略,遍历 serverList,选取出可⽤的且连接数最小的⼀个 server。那么会调用 RoundRobinRule 重新选取。
  • AvailabilityFilteringRule:可用过滤策略。扩展了轮询策略,会先通过默认的轮询选取⼀个 server,再去判断该 server 是否超时可用、当前连接数是否超限,都成功再返回。
  • ZoneAvoidanceRule:区域权衡策略。扩展了轮询策略,除了过滤超时和链接数过多的 server,还会过滤掉不符合要求的 zone 区域⾥⾯的所有节点,始终保证在⼀个区域/机房内的服务实例进行轮询。
    这里所有负载均衡策略名本质都是 com.netflix.loadbalancer 包下的类:

    要更改微服务通信时采用的负载均衡策略也很简单,在 application.yml 中采用下面格式书写即可。
provider-service: #服务提供者的微服务idribbon:NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule #设置对应的负载均衡类

当采用随机策略,运行时得到如下日志,杂乱的顺序说明随机策略已生效。

本次调用由 provider-service的192.168.31.112:80 实例节点负责处理consumer-service 获得数据:This is the message from provider service!本次调用由 provider-service 的 192.168.31.112:80 实例节点负责处理consumer-service 获得数据:This is the message from provider service!本次调用由 provider-service 的 192.168.31.113:80 实例节点负责处理consumer-service 获得数据:This is the message from provider service!本次调用由 provider-service 的 192.168.31.111:80 实例节点负责处理consumer-service 获得数据:This is the message from provider service!本次调用由 provider-service 的 192.168.31.113:80 实例节点负责处理consumer-service 获得数据:This is the message from provider service!

讲到这里,想必你对 Ribbon 已经有了直观的认识,在项目中合理的使用 Ribbon 负载均衡可以使系统性能有显著的提升,最后我们来做下总结。

小结与预告

本文我们介绍了三方面知识,开始介绍了 Ribbon 负载均衡器的作用,之后讲解了 Ribbon 的两种开发模式,最后讲解了 Ribbon 的负载均衡策略与设置办法。

这里给你留一道课外题:如果 Ribbon 自带的负载均衡策略不能满足实际项目的需要,我们如何自定义 Ribbon 负载均衡策略呢?你可以自行查阅资料,将学到的知识分享在评论区中。

下一节课,我们将学习除 RestTemplate 之外另一项重要的服务间通信技术 OpenFeign,看它又提供了哪些高级特性。

04 | 负载均衡:Ribbon 如何保证微服务的高可用相关推荐

  1. Spring Cloud Alibaba 负载均衡:Ribbon 如何保证微服务的高可用

    上一讲我们对 Nacos 的集群环境与实现原理进行了讲解,我们已经可以轻松将单个微服务接入到 Nacos 进行注册,但是微服务本不是孤岛,如何实现有效的服务间稳定通信是本文即将介绍的主要内容,本次我们 ...

  2. SpringCloud Alibaba 实战之《负载均衡:Ribbon 如何保证微服务的高可用》

    上一讲我们对 Nacos 的集群环境与实现原理进行了讲解,我们已经可以轻松将单个微服务接入到 Nacos 进行注册,但是微服务本不是孤岛,如何实现有效的服务间稳定通信是本文即将介绍的主要内容,本次我们 ...

  3. 负载均衡服务器性能,服务器负载均衡:确保应用服务的高性能与高可用

    随着互联网的快速发展和业务量的不断提升,基于网络的数据访问流量迅速增长,特别数据中心.大型企业及门户网站,日益丰富的内容和信息输出大大加速了服务器的资源消耗.加上大部分网站都需要提供24小时不间断服务 ...

  4. 云原生时代微服务的高可用架构设计

    简介: 在8月20日"阿里巴巴技术质量精品课"上,来自蚂蚁的经国分享了对云原生时代微服务的高可用架构设计的全面解析,为大家介绍了应用架构演进路径.云原生时代的技术福利.高可用架构的 ...

  5. 蚂蚁金服资深技术专家经国:云原生时代微服务的高可用架构设计

    经国 蚂蚁金服数字金融线担任技术风险架构师 读完需要 15 分钟 速读仅需 5 分钟 经国,蚂蚁金服资深技术专家,毕业于浙江大学. 2014 年加入蚂蚁金服,先后负责过支付宝的单元化.弹性.去 ORA ...

  6. 微服务治理 高可用 HA (High Availability) 的一些理解

    文章目录 微服务治理 高可用 HA (High Availability) 的一些理解 1.简介 2.Nginx 高可用 3.站点层的高可用 4.服务层 Service 的高可用 5.缓存层的高可用 ...

  7. 如何保证 HBase 服务的高可用?看看这份 HBase 可用性分析与高可用实践吧!

    来源 | 阿丸笔记 责编 | Carol 头图 | CSDN 下载自视觉中国 HBase作为一个分布式存储的数据库,它是如何保证可用性的呢?对于分布式系统的CAP问题,它是如何权衡的呢? 最重要的是, ...

  8. 运维企业专题(8)LVS高可用与负载均衡后篇——LVS健康检查与高可用详解

    实验准备 1.下面的实验使用的是rhel6系列(rhel6.5)的虚拟机,因此你需要有对应的镜像和yum源 2.准备三台虚拟机,为了区分主机名与IP分别为 server1 172.25.6.1 ser ...

  9. 为了做到微服务的高可用,鬼知道我出了多少张牌

    高可用并不是一套整体解决方案,而是由诸多环节组成,一环扣一环,鬼知道为了这些串联起来的环节,我得出多少张牌去应对,才能最终组成一个整个系统的高可用落地方案. 图片来自 Pexels 什么是高可用 在定 ...

最新文章

  1. Docker学习之网络篇
  2. 专升本c语言程序设计网课_2020年宜春学院专升本招生信息
  3. NDIS与WinSock关系之自我扫盲
  4. django-分页的演示
  5. oracle11gr2配置监听,Windows环境配置Oracle 11gR2 Listener
  6. Java虚拟机(二)——垃圾回收与内存分配
  7. JDK5.0新特性系列---11.5.4线程 同步装置之Exchanger
  8. HashTable VS Dictionary
  9. 阿里云爬虫风险管理产品商业化,为云端流量保驾护航 1
  10. Apache 2.4.28的安装
  11. 开源软件之screen的第一次使用
  12. layui结合json viewer实现代码格式化
  13. 【Ubuntu】Ubuntu16.04 安装显卡驱动重启黑屏问题处理
  14. 计算机模拟光速,中国科大模拟出“超光速通讯”
  15. 47、SimpleOrientationSensor
  16. 【cdq分治】cdq分治与整体二分学习笔记Part2.cdq分治
  17. Mybatis超强大的动态 SQL 大全
  18. 不少专车司机考虑退出
  19. 苹果机上使用onenote2007
  20. Java14发布,16大新特性,代码更加简洁明快

热门文章

  1. 事务日志已满,原因为“ACTIVE_TRANSACTION”
  2. 一次绕过360+诺顿的提权过程
  3. 截取中文字符长度(中文、字母都有效)
  4. C#Windows 服务制作安装删除. 用户注销后,程序继续运行 (转载)
  5. 开源云计算平台 abiCloud
  6. ASP.NET中实现模版的动态加载
  7. 三十岁前不必在乎的三十件事
  8. java编写单词数_JAVA flink小试——单词计数
  9. 简述python垃圾回收机制_python中的垃圾回收机制简述
  10. js调用php函数兵每秒刷新,深入理解JavaScript立即调用函数表达式(IIFE)