为了降低用户构建和维护分布式系统的难度,推动微服务的落地,Spring Cloud提供了快速构建分布式微服务系统的一些常用功能,如配置管理、服务发现、断路器、智能路由、服务代理、控制总线等提供的一套开发工具。这些工具就相当于分布式系统的样板,Spring Cloud的使用者可以使用这些样板工具快速构建服务以及相关应用。这些工具能够在任何分布式环境中良好运行,如开发者的计算机、数据中心以及类似Cloud Foundry这样的管理平台。
Spring Cloud适用于以下场景:配置管理、服务发现、断路器、智能路由、微代理、控制总线、一次性令牌、全局锁、领导选举、分布式会话、集群状态等。
接下来从服务化架构演进的角度来讲述为什么Spring Cloud更适应微服务架构。
我们先从Nginx说起,了解为什么需要微服务。最初的服务化解决方案是给相同服务提供一个统一的域名,然后服务调用者向这个域发送HTTP请求,由Nginx负责请求的分发和跳转。
这种架构存在很多问题:Nginx作为中间层,在配置文件中耦合了服务调用的逻辑,这削弱了微服务的完整性,也使得Nginx在一定程度上变成了一个重量级的ESB。图1-7标识出了Nginx的转发信息流走向。
服务的信息分散在各个系统,无法统一管理和维护。每一次的服务调用都是一次尝试,服务消费方并不知道有哪些实例在给他们提供服务。这带来了一些问题:
图1-7 Nginx转发的信息流
·无法直观地看到服务提供方和服务消费方当前的运行状况与通信频率;
·消费方的失败重发、负载均衡等都没有统一策略,这加大了开发每个服务的难度,不利于快速演化。
为了解决上面的问题,我们需要一个现成的中心组件对服务进行整合,将每个服务的信息汇总,包括服务的组件名称、地址、数量等。服务的调用方在请求某项服务时首先通过中心组件获取提供服务的实例信息(IP、端口等),再通过默认或自定义的策略选择该服务的某一提供方直接进行访问,所以考虑引入Dubbo。
Dubbo是阿里开源的一个SOA服务治理解决方案,文档丰富,在国内的使用度非常高。图1-8为Dubbo的基本架构图,使用Dubbo构建的微服务已经可以较好地解决上面提到的问题。
图1-8 Dubbo的基本架构图
从图1-8中,可以看出:
·调用中间层变成了可选组件,消费方可以直接访问服务提供方;
·服务信息被集中到Registry中,形成了服务治理的中心组件;
·通过Monitor监控系统,可以直观地展示服务调用的统计信息;
·服务消费者可以进行负载均衡、服务降级的选择。
但是对于微服务架构而言,Dubbo并不是十全十美的,也有一些缺陷。
·Registry严重依赖第三方组件(ZooKeeper或者Redis),当这些组件出现问题时,服务调用很快就会中断。
·Dubbo只支持RPC调用。这使得服务提供方与调用方在代码上产生了强依赖,服务提供方需要不断将包含公共代码的Jar包打包出来供消费方使用。一旦打包出现问题,就会导致服务调用出错。
笔者认为,Dubbo和Spring Cloud并不是完全的竞争关系,两者所解决的问题域并不一样: Dubbo的定位始终是一款RPC框架,而Spring Cloud的目标是微服务架构下的一站式解决方案。 如果非要比较的话,Dubbo可以类比到Netflix OSS技术栈,而Spring Cloud集成了Netflix OSS作为分布式服务治理解决方案,但除此之外Spring Cloud还提供了配置、消息、安全、调用链跟踪等分布式问题解决方案。
当前由于RPC协议、注册中心元数据不匹配等问题,在面临微服务基础框架选型时Dubbo与Spring Cloud只能二选一,这也是大家总是拿Dubbo和Spring Cloud做对比的原因之一。Dubbo已经适配到Spring Cloud生态,比如作为Spring Cloud的二进制通信方案来发挥Dubbo的性能优势,Dubbo通过模块化以及对HTTP的支持适配到Spring Cloud。
作为新一代的服务框架,Spring Cloud提出的口号是开发“面向云的应用程序”,它为微服务架构提供了更加全面的技术支持。结合我们一开始提到的微服务的诉求,参见表1-1,把Spring Cloud与Dubbo进行一番对比。
表1-1 Spring Cloud与Dubbo功能对比
Spring Cloud抛弃了Dubbo的RPC通信,采用的是基于HTTP的REST方式。严格来说,这两种方式各有优劣。虽然从一定程度上来说,后者牺牲了服务调用的性能,但也避免了上面提到的原生RPC带来的问题。而且REST相比RPC更为灵活,服务提供方和调用方,不存在代码级别的强依赖,这在强调快速演化的微服务环境下显得更加合适。
很明显,Spring Cloud的功能比Dubbo更加强大,涵盖面更广,而且作为Spring的拳头项目,它也能够与Spring Framework、Spring Boot、Spring Data、Spring Batch等其他Spring项目完美融合,这些对于微服务而言是至关重要的。前面提到,微服务背后一个重要的理念就是持续集成、快速交付,而在服务内部使用一个统一的技术框架,显然比将分散的技术组合到一起更有效率。更重要的是,相比于Dubbo,它是一个正在持续维护的、社区更加火热的开源项目,这就可以保证使用它构建的系统持续地得到开源力量的支持。
下面列举Spring Cloud的几个优势。
·Spring Cloud来源于Spring,质量、稳定性、持续性都可以得到保证。
·Spirng Cloud天然支持Spring Boot,更加便于业务落地。
·Spring Cloud发展得非常快,从开始接触时的相关组件版本为1.x,到现在将要发布2.x系列。
·Spring Cloud是Java领域最适合做微服务的框架。
·相比于其他框架,Spring Cloud对微服务周边环境的支持力度最大。对于中小企业来讲,使用门槛较低。
我们从整体上来看一下Spring Cloud各个组件如何配套使用,如图1-9所示。
图1-9 Spring Cloud各个组件协同示意图
从图1-9可以看出,Spring Cloud各个组件相互配合,合作支持了一套完整的微服务架构。
Spring Cloud从设计之初就考虑了绝大多数互联网公司架构演化所需的功能,这些功能都是以插拔的形式提供,方便合理选择需要的组件进行集成,从而使用过程中更加平滑、顺利。
Spring Cloud提供了标准化的、一站式的技术方案。下面按照Spring Cloud子项目和其提供的分布式方案来分别了解Spring Cloud价值。
系统一旦走向分布式,其复杂程度成倍增长,传统单体应用那种只考虑业务逻辑的开发方式已经不再适用。正因其复杂性,目前只有业务需求大的大型互联网公司才会(被迫)采用,而且需要投入大量的技术力量来开发基础设施,也造成了小公司“用不起”分布式架构的情况。现在这一局面正在逐渐被打破,因为Netflix开源了经过实战考验的一系列基础设施构件,加上Spring Cloud的大力支持,开发分布式系统已经不再像以前那样可怕了。
(1)服务的注册发现和软负载均衡
Spring Cloud Netflix通过Eureka Server实现服务注册中心,通过Ribbon实现软负载均衡。
(2)分区治理
Eureka支持Region和Zone的概念。其中一个Region可以包含多个Zone。Eureka在启动时需要指定一个Zone名,即当前Eureka属于哪个Zone,如果不指定则属于defaultZone。Eureka Client也需要指定Zone,Client(当与Ribbon配置使用时)在向Server获取注册列表时会优先向自己所在Zone的Eureka发请求,只有自己Zone中的Eureka全挂了才会尝试向其他Zone发请求。当获取到远程服务列表后,Client也会优先向同一个Zone的服务发起远程调用。Region和Zone可以对应于现实中的大区和机房,如在华北地区有10个机房,在华南地区有20个机房,那么分别为Eureka指定合理的Region和Zone能有效避免跨机房调用,而一个地区的Eureka坏掉也不会导致整个该地区的服务都不可用。
(3)Ribbon软负载均衡
Ribbon在工作时分成两步:第一步先选择Eureka Server,优先选择同一个Zone且负载较少的Server,第二步再根据用户指定的策略,在从Server取到的服务注册列表中选择一个地址。其中Ribbon默认提供了三种策略:轮询、断路器和根据响应时间加权。
(4)声明式HTTP RESTful客户端Feign
Feign与Apache Http Client这类客户端最大的不同,是它允许通过定义接口的形式构造HTTP请求,不需要手动拼参数,使用起来与正常的本地调用没有什么区别:
@FeignClient(name = "ea") public interface AdvertGroupRemoteService { @RequestMapping(value = "/group/{groupId}", method = RequestMethod.GET) AdvertGroupVO findByGroupId(@PathVariable("groupId") Integer adGroupId); }
这里只需要调用AdvertGroupRemoteService.findByGroupId()方法就能完成向目标主机发送HTTP请求并封装返回结果的效果。
(5)断路器
断路器(Cricuit Breaker)是一种能够在远程服务不可用时自动熔断(打开开关),并在远程服务恢复时自动恢复(闭合开关)的设施,Spring Cloud通过Netflix的Hystrix组件提供断路器、资源隔离与自我修复功能。
(6)资源隔离
Hystrix对每一个依赖服务都配置了一个线程池,对依赖服务的调用会在线程池中执行。例如,我们设计服务I的线程池大小为20,那么Hystrix会最多允许有20个容器线程调用服务I,如果超出20,Hystrix会拒绝并快速失败。这样即使服务I长时间未响应,容器最多也只能堵塞20个线程,剩余80个线程仍然可以处理用户请求。
(7)快速失败
快速失败是防止资源耗尽的关键一点。当Hystrix发现在过去某段时间内对服务I的调用出错率达到某个阈值时,Hystrix就会“熔断”该服务,后续任何发向服务I的请求都会快速失败,而不是白白让调用线程去等待。
(8)自我修复
处于熔断状态的服务,在经过一段时间后,Hystrix会让其进入“半关闭”状态,即允许少量请求通过,然后统计调用的成功率。如果这个请求能成功,Hystrix会恢复该服务,从而达到自我修复的效果。其中,在服务被熔断到进入半关闭状态之间的时间,就是留给开发人员排查错误并恢复故障的时间,开发人员可以通过监控措施得到提醒并线上排查。
(9)监控方案
监控是保障分布式系统健康运行必不可少的方案。基于Spring Cloud,我们可以从两个纬度进行监控:Hystrix断路器的监控和每个服务监控状况的监控。
图1-10是Hystrix提供的Dashboard图形化监控。
可见图1-10中监控信息应有尽有(调用成功率、平时响应时间、调用频次、断路器状态等)。可以通过编程的方式定时获取该信息,并在断路器熔断时通过短信、邮件等方式通知开发者。
Hystrix的监控数据默认保存在每个实例的内存中,Spring Boot提供了多种方式,可以导入到Redis、TSDB以供日后分析使用。
图1-10 Hystrix的Dashboard图形化监控界面
除此之外,Spring Cloud还提供了对单个实例的监控,如图1-11所示。其中包含了接口调用频次、响应时间、JVM状态、动态日志等各种开发者关心的信息。
(1)版本命名
之前提到过,Spring Cloud是一个拥有诸多子项目的大型综合项目,原则上其子项目也都维护着自己的发布版本号。每一个Spring Cloud的版本都会包含不同的子项目版本,为了管理每个版本的子项目清单,避免版本名与子项目的发布号混淆,没有采用版本号的方式,而是通过命名的方式。这些版本采用了伦敦地铁站的名字,根据字母表的顺序来对应版本时间顺序,比如:最早的Release版本为Angel,第二个Release版本为Brixton,依此类推。
(2)版本号
经过上面的解释,不难猜出,Angel.SR6和Brixton.SR5中的SR6、SR5就是版本号了。
当一个版本的Spring Cloud项目的发布内容积累到临界点或者一个严重bug解决后,就会发布一个“service releases”版本,简称SRX版本,其中X是一个递增数字。
(3)当前版本
我们在选择Spring Boot与Spring Cloud版本的时候,还是需要尽可能按照Spring Cloud官方版本依赖关系来选择:
·Brixton版本对应Spring Boot 1.3.x;
·Camden版本对应Spring Boot 1.4.x;
·Dalston版本对应Spring Boot 1.5.x。
图1-11 监控界面
在开始正式介绍每个项目之前,需要准备如表1-2所示的环境。
表1-2 必备工具