本文中我们主要介绍微服务开发框架——Spring Cloud。尽管Spring Cloud带有"Cloud"的字样,但它并不是云计算解决方案,而是Spring Boot的基础上构建的,用于快速构建分布式系统的通用模式的工具集。
Spring Cloud的特点
Spring Cloud有以下特点:
- 约定优于配置;
- 适用于各种环境。开发、部署PC Server或各种云环境(例如阿里云、AWS等)均可;
- 隐藏了组件的复杂性,并提供声明式、无xml的配置方式;
- 开箱即用,快速启动;
- 轻量级的组件。Spring Cloud整合的组件大多比较轻量。例如Eureka、Zuul等,都是各自领域轻量级的实现;
- 组件丰富,功能齐全。Spring Cloud 为微服务架构提供了非常完整的支持。例如、配置管理、服务发现、断路器、微服务网关等;
- 选型中立、丰富。例如,Spring Cloud支持使用Eureka、Zookeeper或Consul实现服务发现;
- 灵活。Spring Cloud的组成部分是解耦的,开发人员可以按需灵活挑选技术选型。
Spring Cloud版本简介
Spring Cloud版本由上图可知,Spring Cloud是以英文单词+SR+数字的形式命名版本号的。那么英文单词和SR分别表示什么呢?
因为Spring Cloud是一个综合项目,它包含很多子项目。由于子项目也维护着自己的版本号,Spring Cloud采用了这种命名方式,从而避免与子项目的版本混淆。其中英文单词如Edware是伦敦某地铁站名,它们按照字母顺序发行,可以将其理解为主版本的演进。SR表示"Service Release",一般表示Bug修复。
版本兼容性如下
版本内容
可参考官方文档:https://spring.io/projects/spring-cloud#overview
Spring Cloud分布式开发五大组件
- 服务发现——Netflix Eureka
- 客户端负载均衡——Netflix Ribbon
- 断路器——Netflix Hystrix
- 服务网关——Netflix Zuul
- 分布式配置——Spring Cloud Config
Eureka
Eureka我的上一篇博客(微服务理论篇)中谈到,对单体应用进行服务拆分得到各个微服务,而这些服务又是相互独立的,那么我们如何知道各个微服务的健康状态、如何知道某个微服务的存在呢?由此、一个拥有服务发现的框架显得尤为重要。这也就是Eureka诞生的原因。
- Eureka是由Netflix开发的服务发现框架,本身是一个基于RESTful的服务,主要用于定位运行在AWS域中的中间层服务。
- 由两个组件组成:Eureka Server和Eureka Client。Eureka Server提供服务注册服务,各个节点启动后,会在Eureka Server中进行注册,这样EurekaServer中的服务注册表中将会存储所有可用服务节点的信息,服务节点的信息可以在界面中直观的看到。Eureka Client即为微服务节点。
- Eureka Client启动后,将会注册到Eureka Server中,同时会定时发送心跳(默认无配置情况下为30s),如果Eureka Server在多个心跳周期内没有接收到某个节点的心跳,那么Eureka Server将会从服务注册表中把这个节点移除(默认90s)。
- Eureka Server之间通过复制的方式完成数据同步,Eureka还提供了客户端缓存机制,即使所有的Eureka Server都挂掉,客户端依然可以利用缓存中的信息消费其他服务的API。
综上,Eureka通过心跳检查、客户端缓存等机制,确保了系统的高可用性、灵活性和可伸缩性。
Ribbon
Ribbon通过使用Eureka已经实现了微服务的注册与发现。启动各个微服务时,Eureka Client会把自己的网络信息注册到Eureka Server上。似乎一切更美好了一些。然而,这样的架构依然有一些问题,如负载均衡。一般来说,各个微服务都会部署多个实例。那么服务消费者要如何将请求分摊到多个服务提供实例上呢?
- Ribbon(负载均衡器)的作用正是提供负载均衡机制,当为Ribbon配置服务提供者地址列表后,Ribbon就可以基于某种负载均衡算法,自动地帮助服务消费者去请求。
- Ribbon提供的负载均衡算法有多种,例如轮询、加权响应时间、随机和区域感知轮询。
-
Ribbon与Eureka配合使用时,Ribbon可自动从Eureka Server获取服务提供者地址列表,并基于负载均衡算法,请求其中一个服务提供者示例。(大致架构如下图)
Ribbon与Eureka配合使用
Hystrix
如果服务提供者相应非常慢,那么消费者对提供者的请求就会被强制等待,知道提供者响应或超时。在高负载场景下,如果不作任何处理,此类问题可能会导致服务消费者的资源耗竭甚至整个系统崩溃。
微服务架构的应用系统通常包含多个服务层。微服务之间通过网络进行通信,从而支撑起整个应用系统,因此,微服务之间难免存在依赖关系。而这种由于"基础服务故障"导致"级联故障"的现象称为雪崩效应。
雪崩效应
如图所示,A最为服务提供者(基础服务),B为A的服务消费者,C和D是B的服务消费者。当A不可用引起了B的不可用,并将不可用像滚雪球一样放大到C和D时,雪崩效应就形成了。
那么Hystrix是如何容错的呢?
- 为网络请求设置超时;
-
使用断路器模式:断路器可理解为对容易导致错误操作的代理。这种代理能够统计一段时间内调用失败的次数,并决定是正常请求依赖的服务还是直接返回;断路器可以实现快速失败,如果它在一段时间内检测到许多类似的错误(例如超时),就会在之后的一段时间内,强迫对该服务的调用快速失败,即不请求所依赖的服务。这样,应用程序就无须再浪费CPU时间去等待长时间的超时。断路器也可以自动诊断依赖的服务是否已经恢复正常,如果发现依赖的服务已经恢复正常,那么就会恢复请求该服务。
断路器状态转换图
以下对该图做个简单讲解:
- 正常情况下,断路器关闭,可以正常请求依赖的服务;
- 当一段时间内,请求失败率达到一定阈值(例如错误率达到50%,或100次/分钟等),断路器就会打开,此时,就不会再去请求依赖的服务;
- 断路器打开一段时间后,会自动进入"半开"状态。此时,断路器允许一个请求访问依赖的服务。如果请求能够调用成功,则关闭断路器;否则继续保持打开状态。
Zuul
Zuul作为微服务架构中的微服务网关。微服务架构经过前几个组件的组合,已经有了基本的雏形了,那么我们为什么还要使用微服务网关呢?我们可以想象,一般情况下我们一个业务并不是只调用一个接口就可以完成一个业务需求。
如果让客户端直接与各个微服务通信,会有以下问题:
- 客户端会多次请求不同的微服务,增加了客户端的复杂性;
- 存在跨域请求,在一定场景下处理相对复杂;
- 认证复杂,每个服务都需要独立认证;
- 难以重构,随着项目的迭代,可能需要重新划分微服务,如果直接与微服务通信,那么重构会很难实施;
如图,微服务网关封装了应用程序的内部结构,客户端只须跟网关交互,而无须直接调用特定微服务接口。同时,还有以下优点:
- 易于监控;
- 易于认证:可在微服务网关上进行认证,然后再将请求转发到后端的微服务,而无须在每个微服务中进行认证;
- 减少了客户端与各个微服务之间的交互次数。
Config
为什么要同一管理微服务配置?
对于传统的单体应用,常常使用配置文件管理所有配置。例如一个Spring Boot 项目开发的单体应用,可以将配置内容放到application.yml文件中。如果需要切换环境,可以设置多个Profile,并在启用应用时指定spring.profile.active={profile}。
而在微服务架构中,微服务的配置管理一般有以下需求:
- 集中管理配置:一个使用微服务架构的应用系统可能会包含成百上千个微服务,因此,集中管理配置是很有必要的;
- 不同环境不同配置。例如,数据源配置在不同的环境(开发、测试、预发布、生产等)中是不同的;
- 运行期间可动态调整:例如,可根据各个微服务的负载情况,动态调整数据源连接池的大小或熔断阈值,并且在调整配置时不停止微服务;
- 配置修改后可自动更新:如配置内容发生变化,微服务能够自动更新配置。
网友评论