近十年来,Spring因其提供的依赖注入功能而广受Java开发者的欢迎,因其可以帮助大家开发出松散耦合的系统。简单来说,用户只需要专注于接口所提供的抽象,即可获得具体的实例。如今随着云计算越发流行,如何充分利用云环境提供的自动伸缩能力,并与特定云供应商实现松散的耦合,这已成为一个非常有趣的挑战。“云原生”这个概念应运而生。我们先来看看“云原生”和“微服务”到底是什么?
很多人认为,云原生仅仅是用云提供商的服务来运行你的现有应用?
答案不是这么简单。云原生会全面改变应用程序的设计、实现、部署和运维过程。
现在比较流行的云原生定义如下:
在开发Spring框架和云平台的软件公司Pivotal看来,云原生的定义是:
云原生是一种构建和运行应用程序的方法,这种方法可以完全发挥出云计算模型的优势。
致力于创建和推动云原生编程范式的组织CNCF (Cloud Native Computing Foundation),对云原生的定义是:
云原生技术可以帮助企业通过公有云、私有云、混合云等现代化的动态环境构建并运行可伸缩的应用。例如:容器、服务网格(Service Mesh)、微服务、不可变基础架构(Immutable infrastructure)以及声明式API。
总结一下,云原生应用能充分利用云计算模型带来的各种优势, 而微服务是其中的一种实现形式。下面的定义会帮助你更清晰地理解:
云原生应用是专门面向云计算环境设计的应用,而非简单的应用迁移上云。
“微服务架构风格是一种将单个应用程序开发成一套小型服务的方法,每个小型服务都在自己的进程中运行,并通过轻量级机制(通常为HTTP资源API)相互通信。这些服务围绕业务功能构建,可通过全自动部署机制独立部署。此外,这些服务至少应该能集中管理,可以用不同编程语言编写,并可以使用不同的数据存储技术。” ——知名软件工程师,敏捷开发方法创始人之一,Martin Fowler
由此可见微服务是一种可以协同工作的小型、专注、自治的服务。
小型、专注体现了微服务的单一职责(Single Responsibility)。一个服务只需要将一件事做好就够了。自治则意味着容错能力,每个服务可以彼此独立地演化和部署。
微服务就其本质来说与云计算平台的关联极为密切,但微服务这个概念本身并不是新事物。这个概念多年前就出现了。
微服务的概念虽然出现已久,但并未真正流行。主要是因为传统的本地化部署使微服务的落地相当困难。而云计算的出现提供了更好的扩展性、可靠性和可维护性,所以基于云计算的微服务实现和维护更加便利,也就流行起来。目前我们可以从微服务里获得收益包括但不限于这些:
微服务有很多优点,但是构建微服务的过程依然相当复杂。为了使微服务架构轻松实现,业界定义了一些通用的模式。比较知名的有,集中化的中心化配置管理、服务注册和发现、异步消息驱动以及分布式追踪。Spring Cloud将这些微服务架构模式融合到具体实践中,帮助我们更好地遵循云原生最佳实践。如下图:
Spring Cloud独特的价值主要体现在这几方面:
那么Spring Cloud的模块是如何融入微服务模式的?
为满足 Store config in environment
(存储配置于环境中)以及微服务架构的要求,我们需要将所有服务的配置存储在一个集中的位置。此外还需要具备下列功能:
维持变更历史并能轻松回退至老版本。这是一个很实用的功能,可以帮我们撤销生产环境中错误的改动。
Spring Cloud Config通过两个简单的注解来支持这些功能。我们只需要在Config Server中注解@EnableConfigServer,并在其他服务中包含Starter即可启用客户端。详情可参阅Spring Cloud Config文档 ( http://cloud.spring.io/spring-cloud-config/single/spring-cloud-config.html )。
服务发现是大部分分布式系统和面向服务的架构中一个重要的组件。这个问题看起来很简单:客户端如何确定运行于多个主机上的服务所使用的IP和端口。但随着在云环境中部署的服务越来越多,问题开始变得复杂了。
确定服务位置,包含两部分:
那么又该如何选择服务发现解决方案?此时有很多因素需要考虑:
Spring Cloud为注册和发现提供了通用的抽象,我们只需要使用@EnableDiscoveryClient,相应的客户端会自动绑定。Spring Cloud Discovery Eureka和Spring Cloud Discovery Zookeeper提供了服务发现的具体实现。我们需要根据具体地业务场景选择不同的实现,详情可参阅Spring cloud Discovery文档 ( https://cloud.spring.io/spring-cloud-commons/multi/multi__spring_cloud_commons_common_abstractions.html )。
既然我们有了众多的微服务,那么它们肯定需要互相通信。传统的同步调用显然不能满足微服务多变的复杂环境,所以异步的消息驱动是必然趋势。其实,一切请求都可以视作是消息,因此也就诞生了使用不同格式和API的消息中间件。让这些消息中间件相互通信无异于一场噩梦。解决这个问题其实很简单,只需要定义统一的消息接口,随后为每个中间件提供适配器,让它们知道如何在自己的消息和标准格式之间进行转换就行了。这就是Spring Integration的核心设计理念。
Spring Integration的目标主要在于:
同时Spring Integration的设计主要遵循了下列原则:
详情可参阅Spring Integration文档 ( https://docs.spring.io/spring-integration/reference/htmlsingle/ )。
不过目前Spring Integration依然专注于底层,并包含很多晦涩难懂的术语。所以这种编程模型不像其他Spring技术那么易用,于是又诞生了Spring Cloud Stream。Spring Cloud Stream基于标准消息格式和Spring integration提供的多种适配器,可以工作在高层抽象中,借此以更简单的方式产生、处理和使用消息。它也类似于Unix管道,我们只需要关注如何处理消息,消息将按照我们的预期来之,去之。详情可参阅Spring Cloud Stream文档 ( https://docs.spring.io/spring-cloud-stream/docs/current/reference/htmlsingle/ )。
在微服务架构下,由于服务进行了拆分,一个请求很可能涉及多个服务,这些服务可能部署在不同的机器之上。尽管很多解决方案都实现了集中的日志存储和查询,但是如果遇到调用失败,想快速定位问题仍然非常困难。一般的做法是多次反复在日志中查询相关的关键字来寻找线索。这种方法费时费力,而且容易出错。实际上,我们需要的是一个可以帮助我们整合一次调用链的所有相关信息的系统。
Spring Cloud Sleuth 通过引入Span和Trace的概念来实现这种聚合。通俗地讲,一个Span就是一次服务的调用,而Trace就是包含多个Span的树形结构,比如一次分布式调用会包含多个服务调用。Sleuth会把相应的SpanId和TraceId记录在对应的日志中。如下图:
实际中,会被记录的操作包括:
然而,有了这些信息还远远不够,我们还需要把这些信息整合,处理,并以简单直观的方式展现出来。这就是Zipkin发挥作用的地方。它提供的存储模块和UI界面可以帮助我们理解整个调用链。如下图所示。
Spring Cloud 虽然对通用的模式提供了很好的实现,但如果想在实际的云服务上面使用还有一定的距离。所以,Spring Cloud Azure遵循了Spring Cloud提供的最佳实践和通用抽象,并在此基础上更进一步提供了自动化的资源配置和自动配置Azure服务相关属性的能力。借此用户只需要从较高层面了解Azure服务即可顺利使用,而无需涉及有关配置和SDK API的底层细节。以Azure EventHub为例,我们只需要知道这是一种在设计上与Kafka类似的消息服务,随后即可使用Azure EventHub的Spring Cloud Stream Binder生成并使用消息。
Spring Cloud Azure的设计理念主要包括:
配置,这可能是开发者最不愿做的工作之一。配置每个属性前,开发者必须完整阅读相关文档并全面了解每个属性的含义,随后小心谨慎地从一个位置复制每个属性,然后粘贴到应用的属性文件中。然而麻烦还没完,他们还需要为每个属性提供必要的备注,以便让其他开发者明白在每种场景下需要更改哪个属性,并且怎样做才不会出错。我们想要解决这个痛点,因此基于Spring boot提供了自动配置功能。
举例来说,如果想使用Azure EventHub,此时并不需要了解连接字符串是什么,只需输入EventHub的名称空间(类似于Kafka的集群名称)和EventHub名称(类似于Kafka的话题名称),其他都会自动配置。当然,我们也可以通过自定义配置覆盖默认值。
云最大的优点之一在于可以通过可编程的API创建并查询自己的资源。这也是实现自动化的关键。Spring Cloud Azure可借助Azure资源管理器实现自动化的资源配置。其实资源的范畴很大,例如AzureEventHub的Consumer group。当我们有一个新服务使用另一个新的Consumer Group时,无需手工创建。
在了解了Spring Cloud Stream的优势后,假设你开始使用它,但希望将其迁移至Azure,此时该怎么做?你可能已经使用了Kafka或RabbitMQ Binder,但Azure似乎并未提供此类托管式的Kafka或RabbitMQ服务。那么怎样用最快捷的方法来迁移?
实际上我们并不需要关注自己到底使用了哪种消息中间件,只要有一个组件能提供类似的功能并满足性能要求就行了。因此我们只需要将依赖项从Kafka Binder改为Azure EventHub Binder就行,完全不用更改任何代码即可平滑迁移。Azure EventHub Binder提供了丰富地高级功能,如下:
与Apache Kafka类似,EventHub也为轻量级Consumer组提供了类似的支持,但实现上略有差异。Kafka会将所有已提交的偏移量(Offset)存储在Broker中,但EventHub偏移量的存储工作需要手工进行。EventHub SDK提供了在Azure存储帐户中保存偏移量的功能。
EventHub提供了与Kafka类似的物理分区概念,但与Kafka会在Consumer和分区之间自动再均衡的做法不同,EventHub使用了一种抢占模式。Azure存储帐户可以充当「租约」来决定每个分区是哪个Consumer所拥有的。当新Consumer启动后,它会尝试着从负载最重的Consumer「抢占」一部分分区,借此实现负载均衡。
在分布式发布-订阅消息系统中,主要存在三种消息语义:最少一次(At-least-once)、最多一次(At-most-once)以及严格一次(Exactly-once)。目前我们只考虑了使用方:
Manual
检查点模式为消息处理完毕之后的手工检查点操作提供了支持。 Batch
检查点模式。 通过使用自定义消息头暴露Checkpointer,Azure EventHub Binder即可支持不同的消息使用语义。详情可参阅Spring Cloud Stream Event Hub binder文档 或者也可以通过范例自行尝试。
Spring资源为 UrlResource
、 ClassPathResource
和 FileSystemResource
等基于流的资源的操作提供了通用接口。很明显,Azure存储Blob很适合充当这种 BlobResource
。在这种 Resource
中,所有实现方面的细节均已隐藏,不存在的文件也可以自动创建。
详情可参阅Spring Resource with Azure Storage文档,或通过范例自行尝试。
尽管Spring Cloud提供了很好的微服务构建支持,但是对于首次尝鲜微服务的小白,想迅速搭建一个基于Spring Cloud的可以运行的微服务仍然很有挑战,通常包含以下工作:
为了解决上述问题,我们构建了 Spring Cloud Azure Playground来帮助大家轻松构建微服务。提供的功能如下:
你可以通过以下链接访问: https://aka.ms/springcloud 。如下图:
目前,Spring Cloud Azure已全面开源并已发布至Github ( https://github.com/Microsoft/spring-cloud-azure )。欢迎感兴趣的朋友进一步了解、使用或提出意见建议。
参考链接:
Spring Resource with Azure Storage文档: https://github.com/Microsoft/spring-cloud-azure/tree/release/1.0.0.M2/spring-cloud-azure-starters/spring-azure-starter-storage
Spring Cloud Stream Event Hub binder文档: https://github.com/Microsoft/spring-cloud-azure/tree/release/1.0.0.M2/spring-cloud-azure-eventhub-stream-binder
范例1: https://github.com/Microsoft/spring-cloud-azure/tree/release/1.0.0.M2/spring-cloud-azure-samples/spring-cloud-azure-eventhub-binder-sample
范例2: https://github.com/Microsoft/spring-cloud-azure/tree/release/1.0.0.M2/spring-cloud-azure-samples/spring-cloud-azure-storage-sample
朱仲玮,Azure资深工程师,主要致力于改进Java开发者在Azure的体验,Spring Cloud Azure的主要贡献者。曾就职于惠普,eBay和Bing,对分布式架构,微服务和大数据有丰富的经验。
感谢张婵对本文的审校。