为何使用微服务方法构建应用程序?
对于软件开发人员而言,将应用程序分解成多个组成部分可谓司空见惯。 通常采用一种分层方法,其中包括后端存储、中间层业务逻辑和前端用户界面 (UI)。 过去几年来的变化是开发人员开始为云构建分布式应用程序。
不断变化的业务需求包括:
- 一项为吸引新地理区域的客户而大规模构建和操作的服务。
- 更快速地提供特性与功能,灵活应对客户的需求。
- 提高资源利用率以降低成本。
这些业务需求影响我们生成应用程序的 方式 。
有关在 Azure 中构建微服务的方法的详细信息,请阅读微服务:由云支持的应用程序变革。
单一式设计方法与微服务设计方法
应用程序会随着时间而发展。 成功的应用程序因为有实用性而发展。 失败的应用程序不会发展,最终将被取代。 问题在于:现在对要求了解多少,以及未来有何变化? 例如,假设你要为公司的某个部门构建一个报表应用程序。 可以确定的是,该应用程序只会在公司范围内使用,而且报表的保留期不会很长。 那么,选择的方法将不同于构建服务向数千万个客户传送视频内容的方式。
有时,向外寻求概念证明才是驱动因素。 你知道,以后可以重新设计应用程序。 过度设计永不使用的功能并没有太大意义。 另一方面,公司在构建云时会预料到成长和使用量。 成长和规模是不可预测的。 我们希望能够快速创建原型,同时还了解我们正在通往未来成功的路上。 这是简练的启动方法:生成、测量、学习、迭代。
在客户端/服务器时代,我们倾向专注于生成分层式应用程序,每一层采用特定的技术。 已针对这些方法派生出“单一式”应用程序一词。 接口通常存在于各层之间,而在一层内的各组件之间采用更紧密耦合的设计。 开发人员设计并分解类,将类编译成库,并链接起来成为一些可执行文件和 DLL。
这类单一式设计方法有一些优点。 单一式应用程序的设计通常更为简单,组件之间通常通过进程间通信 (IPC) 进行调用,因此调用更快。 此外,每个人都只测试单一产品,人力资源运用更有效率。 缺点是分层之间紧密耦合,无法缩放单独组件。 如果需要执行修复或升级,必须等待其他人完成其测试, 因此更难以发挥灵活性。
微服务解决了这些缺点,更密切配合上述业务要求。 但它们本身也有优缺点。 微服务的优点是通常各自封装较为简单的业务功能,可独立进行横向缩放、测试、部署和管理。 微服务方法的一个重要优点是团队更倾向于以业务方案为导向,而非以技术为导向。 较小的团队可以根据客户方案开发微服务,使用他们所需的任何技术。
换句话说,组织不需要为了维护微服务应用程序而将技术标准化。 拥有服务的单独团队可以根据团队的专业知识,或什么最适合解决问题,各自发挥所长。 实际上,最好使用一组建议的技术,例如特定的 NoSQL 存储或 Web 应用程序框架。
微服务的缺点包括需要管理越来越多的独立实体、处理更复杂的部署和版本控制。 微服务之间的网络流量以及相应的网络延迟不断增加。 大量琐碎的服务可能会给性能带来灾难。 如果没有工具帮助查看这些依赖性,很难“看到”整个系统。
若要让微服务方法奏效,必须在以下方面制定标准:在通信方式上达成共识,只注重需要从服务获得什么,不在乎僵化的约定。 必须在设计的初期定义这些约定,因为之后服务将各自独立更新。 在设计微服务方法时出现的另一个描述是“面向服务的精细体系结构 (SOA)”。
简而言之,微服务设计方法是分离的服务联合,各自独立更改,并达成一致的通信标准。
随着越来越多云应用程序的生成,人们发现从长远来看,这种将整体应用程序分解成独立、方案焦点式服务的做法是较好的方法。
应用程序开发方法的比较
单一式应用程序包含域特定的功能,通常按照功能层划分,例如 Web、业务和数据。
单一式应用程序可通过复制到多个服务器/虚拟机/容器上进行扩展。
微服务应用程序将单独功能分隔成单独的较小服务。
微服务方法可通过独立部署每个服务而扩大,跨服务器/虚拟机/容器创建这些服务的实例。
使用微服务方法进行设计并非适用于所有项目,但确实更符合前面所述的业务目标。 如果确定以后有机会根据微服务设计重写代码,可以从单一式方法入手。 更常见的是,从单一式应用程序入手,分阶段慢慢分解它(从需要提高可缩放性或敏捷性的功能区域开始)。
使用微服务方法时,将以许多小服务来组成应用程序。 这些服务在部署于计算机群集上的容器中运行。 较小的团队可针对方案来开发服务,且每个服务独立进行测试、版本控制、部署和缩放,因此整个应用程序可以不断改进。
什么是微服务?
微服务有不同的定义。 但是,微服务的以下大部分特征已被广为接受:
- 封装客户方案或业务方案。 要解决哪种问题?
- 由小型工程团队开发。
- 使用任何编程语言编写并使用任何框架。
- 由独立控制版本、部署及缩放的代码和(可选)状态组成。
- 通过定义完善的接口和协议与其他微服务交互。
- 具有用来解析位置的唯一名称 (URL)。
- 在出现故障时可保持一致且可用。
总而言之:
微服务应用程序由独立控制版本和可缩放的、以客户为中心的服务组成,这些服务通过标准协议和定义完善的接口彼此通信。
使用任何编程语言编写并使用任何框架
作为开发人员,我们希望根据本身的技能和要创建的服务的需求,自由选择语言或框架。 对于某些服务来说,你可能会认为 C++ 的性能优点胜于一切。 对于其他服务来说,C# 或 Java 的简易管理开发可能更重要。 在某些情况下,可能需要使用特定合作伙伴库、数据存储技术,或向客户端公开服务的方式。
在选择技术之后,需考虑服务的运营管理或生命周期管理和缩放。
允许独立控制版本、部署及缩放的代码和状态
无论你以何种方式编写微服务,代码和(可选)状态都应该独立部署、升级和缩放。 此问题难以解决,因为这涉及到所选的技术。 在缩放方面,难以了解如何分区(或分片)代码和状态。 当代码和状态使用不同的技术时(目前的普遍情况),微服务的部署脚本必须能够妥善缩放两者。 这种区分也涉及灵活性和弹性,因此可以升级某些微服务,无需一次性全部升级。
接下来,我们来比较一下整体方法和微服务方法。 下图显示了各种状态存储方法的差异:
这两种方法的状态存储
左侧的单一式方法具有单一数据库和多层的特定技术。
右侧的微服务方法显示互连的微服务图,其中状态通常以微服务为范围,并使用各种技术。
在单一式方法中,应用程序通常使用单一数据库。 使用一个数据库的优点是位置单一,容易部署。 每个组件可以通过单个表存储其状态。 困难之处在于团队必须严格区分状态。 某人无可避免地就想将某个列添加到现有客户表、在表之间执行联接,并且对存储层形成依赖性。 发生这种情况后,无法缩放各个组件。
在微服务方法中,每个服务都管理并存储自己的状态。 每个服务将负责同时缩放代码和状态,以满足服务的需求。 不足的是,需要创建应用程序数据的视图或查询时,必须跨多个状态存储进行查询。 为了解决此问题,通常由一个独立的微服务生成一个跨许多微服务的视图。 如果需要对数据运行多个即席查询,应考虑将每个微服务的数据写入数据仓库服务以供脱机分析。
微服务的版本受控。 微服务的不同版本可以同时运行。 较新版的微服务可能在升级期间失败,并需要回滚到旧版。 版本控制还有助于执行 A/B 测试,其中不同的用户将体验到不同版本的服务。 例如,在更广泛推出新功能之前,通常先对一组特定的客户升级微服务以测试新功能。
通过定义完善的接口和协议与其他微服务交互
过去 10 年来,行业已经发布了大量的文献用于介绍面向服务的体系结构中的通信模式。 一般而言,服务通信使用 REST 方法,并配合 HTTP 与 TCP 协议及 XML 或 JSON 作为序列化格式。 从接口观点来看,这涉及到采用 Web 设计方法。 但是,你仍然可以使用二进制协议或自己的数据格式。 只需注意,如果这些协议和格式非公开可用,则微服务的使用就很困难。
具有用来解析位置的唯一名称 (URL)
微服务无论在何处运行,都必须可寻址。 若要在计算机上运行特定微服务,很快就会陷入困境。
就像 DNS 解析特定计算机的特定 URL 一样,微服务需要有唯一的名称来发现它目前所在的位置。 微服务需要有可寻址的名称才能独立于它们运行所在的基础结构之外。 这意味着服务的部署和发现方式之间互相影响,因为需要有服务注册表。 当计算机发生故障时,注册表服务需要指出服务已移到何处。
在出现故障时可保持一致且可用
处理意外的故障是最难解决的问题之一,特别是在分布式系统中。 开发人员编写的代码大多是在处理异常。 在测试期间,开发人员还是将最多的时间花费在异常处理上。 该过程比编写代码来处理故障更复杂。 当运行微服务的计算机发生故障时,该怎么办? 需要检测这种故障,这本身就是棘手的问题。 但是,还需要重启微服务。
出于可用性,微服务必须能够从故障复原,并能够在另一台计算机上重启。 除了这些复原要求以外,数据不能丢失,并且需要保持一致。
如果在应用程序升级期间发生故障,则很难实现复原。 在配合部署系统一起运行时,微服务不需要恢复。 它需要确定是要继续升级到更新版本,还是回滚到旧版以维持一致的状态。 需要考虑一些问题,例如,是否有足够的计算机用于继续升级,以及如何恢复旧版的微服务。 需要微服务发出运行状况信息才能做出这些决定。
报告运行状况和诊断
微服务需要报告其运行状况和诊断,这一点看似明显,但却经常被忽视。 否则,难以从操作角度深入了解其运行状况。 面临的难题是关联一组独立服务的诊断事件并修正计算机时钟偏差以识别事件顺序。 同样地,通过议定的协议和数据格式来与微服务交互时,需要将运行状况和诊断事件的记录方式标准化,这些事件最终将写入可供查询和查看的事件存储。 在微服务方法中,不同的团队需要同意采用一种日志记录格式。 需要通过一致的方法查看整个应用程序中的诊断事件。
运行状况与诊断不同。 运行状况是微服务报告其当前状态以采取适当的措施。 一个很好的例子便是使用升级和部署机制保持可用性。 当前服务可能由于进程崩溃或计算机重新启动而状况不正常,但仍可运行。 不应该执行升级而让情况恶化。 最好是先进行调查,或让微服务有时间恢复。 微服务的运行状况事件有助于我们做出明智的决策,实际上有助于创建自我修复的服务。
在 Azure 上设计微服务的指导
请访问 Azure 体系结构中心,获取有关在在 Azure 中设计和构建微服务的指导。
Service Fabric 作为微服务平台
Azure 从提供盒装产品(通常是单一式)转换到提供服务后,Azure Service Fabric 横空问世。 构建和运营 Azure SQL 数据库和 Azure Cosmos DB 等大型服务的经验造就了 Service Fabric。 该平台随着越来越多服务采用它而不断发展变化。 Service Fabric 不仅要在 Azure 中运行,还要在独立的 Windows Server 部署中运行。
Service Fabric 旨在解决构建和运行服务方面的难题,并有效地使用基础结构资源,使团队可以使用微服务方法来解决业务问题。
Service Fabric 可帮助你构建使用微服务方法的应用程序,它提供:
- 提供系统服务的平台,用于部署、升级、检测和重启失败的服务、发现服务、路由消息、管理状态和监视运行状况。
- 能够部署在容器中运行或作为进程运行的应用程序。 Service Fabric 是容器和进程 Orchestrator。
- 生产编程 API,用于帮助你将应用程序构建为微服务:ASP.NET Core、Reliable Actors 和 Reliable Services。 例如,可以获取运行状况和诊断信息,或利用内置的高可用性。
Service Fabric 与服务生成方式无关,可以使用任意技术。不过,它确实提供内置编程 API,以便用户可以更轻松地生成微服务。
将现有应用程序迁移到 Service Fabric
Service Fabric 允许重用现有代码,可以通过新的微服务对现有代码进行现代化。 应用程序现代化分为五个阶段,可以在任意阶段开始和停止操作。 阶段包括:
- 从传统单一式应用程序入手。
- 迁移。 使用容器或来宾可执行文件在 Service Fabric 中托管现有代码。
- 现代化。 将新微服务与现有容器化代码一起添加。
- 创新。 根据需求将单一式应用程序分解成微服务。
- 将应用程序转换为微服务。 转换现有的单一式应用程序,或构建新领域应用程序。
请记住,可以在上述任意阶段启动和停止。 并不强求继续执行到下一阶段。
让我们看看每个阶段的示例。
迁移
许多公司出于两个原因将现有单一式应用程序迁移到容器中:
- 由于整合和移除现有硬件或以较高密度运行的应用程序,因此成本降低。
- 开发和运营遵循一致的部署协定。
成本降低是立竿见影的。 在 Microsoft 内部,大量现有应用程序正在容器化,节省的成本高达数百万美元。 虽然部署一致性难以评估,但同样很重要。 也就是说,开发人员仍可以自由选择适合自己的技术;不过,运营人员只接受使用一种方法来部署和管理应用程序。 这可以减轻运营压力,免除支持不同技术而造成的复杂性,同时,不必强制要求开发人员选择特定的技术。 实质上,每个应用程序都容器化到独立式部署映像中。
许多组织止步于这一阶段。 它们已享受到容器带来的优势,Service Fabric 能够提供完整的管理体验,包括部署、升级、版本控制、回滚和运行状况监视等。
现代化
现代化是指将新服务与现有容器化代码一起添加。 若要编写新代码,最好是在微服务之旅上略进几步。 可以添加新的 REST API 终结点,也可以添加新的业务逻辑。 这样,便可以开始构建新的微服务,并进行开发和部署。
创新
微服务方法可适应不断变化的业务需求。 在此阶段,需要确定是将单一式应用程序拆分为服务,还是进行创新。 此处的典型示例是正用作工作流队列的数据库在处理方面成为瓶颈的情况。 随着工作流请求数量不断增加,需要分布工作,以实现缩放。 对于不缩放或需要频繁更新的特定应用程序部分,可将其拆分为微服务,并进行创新。
将应用程序转换为微服务
在此阶段,应用程序完全由微服务组成或拆分为微服务。 到此已完成微服务之旅。 虽然可以从此阶段开始,但如果没有微服务平台提供帮助,这会是一项巨额投资。
微服务适合我的应用程序吗?
也许。 随着 Microsoft 中越来越多团队开始出于业务原因而以云为目标构建应用程序,许多团队已认识到采用类似微服务的方法所带来的优点。 例如,多年以来,必应一直在使用微服务。 微服务方法对于其他团队而言相当新颖。 团队发现需要解决一些疑难问题,但这并非他们的强项。 这就是为什么 Service Fabric 受到重视而成为构建服务的适当技术。
Service Fabric 的目标是将构建微服务应用程序的复杂性降低,使你不需要经历许多耗费成本的重新设计工作。 从小规模开始,按需缩放,淘汰旧服务,添加新服务,并随着客户用量的增加而改进。 我们也知道,还需要解决许多其他问题,才能让微服务更易为大部分开发人员所接受。 容器和执行组件编程模型都是朝此目标前进的一小步。 我们确信将涌现出更多的创新,使微服务方法变得更简单。