vlambda博客
学习文章列表

读书笔记《developing-microservices-with-node-js》微服务架构

Chapter 1. Microservices Architecture

微服务正变得越来越流行。如今,几乎所有参与新建项目的工程师都应该考虑使用微服务来提高他们构建的系统的质量。他们应该了解涉及 此类系统的架构原则。我们将揭示微服务与面向服务的架构SOA )。我们还将介绍一个很棒的平台来编写微服务,Node.js,它将允许 我们可以毫不费力地创建高性能的微服务。

在本章中,您将从架构的角度了解微服务:

  • 什么是微服务?

  • 面向微服务的架构

  • 主要优势

  • SOA 与微服务

  • 为什么选择 Node.js?

Need for microservices


软件开发的世界在过去 40 年中发展迅速。这种演变的关键点之一是这些系统的规模。从 MS-DOS 时代开始,我们在目前的系统中实现了一百倍的飞跃。这种规模的增长产生了对组织代码和软件组件的更好方法的需求。通常,当公司因业务需求而增长时,即有机增长< /a>software 组织在一个整体架构上,因为它是构建软件的最简单和最快捷的方式。几年(甚至几个月)后,由于所创建软件的耦合性质,添加新功能变得更加困难。

Monolithic software

Amazon 或 Netflix 等新兴高科技公司的自然趋势是使用微服务构建他们的新软件,这是理想的场景:他们在面向微服务的软件中获得巨大优势(通过本书,您将了解如何)为了不费吹灰之力扩大他们的新产品。问题是并非所有公司都可以预先规划他们的软件。这些公司没有计划,而是根据所经历的有机增长来构建软件:很少有软件组件按亲和力对业务流进行分组。拥有两大软件组件的公司并不罕见:面向用户的网站和内部管理 工具。这通常称为单体软件架构

其中一些公司在尝试扩展工程团队时面临着很大的问题。很难协调构建、部署和维护单个软件组件的团队。发布冲突和重新引入错误是一个常见问题,会消耗团队的大量精力。这个问题的解决方案之一(它带来了好处)是将单体软件拆分为微服务,以便团队能够专注于一些较小的模块以及可以在没有版本控制、更新和部署的情况下进行版本控制、更新和部署的独立软件组件干扰公司的其他系统。

将单体架构拆分为微服务使工程团队能够创建独立且自主的工作单元,这些工作单元高度专业化于特定任务,例如发送电子邮件、处理卡支付等。

Microservices in the real world

微服务是专门用于一项任务并协同工作以实现更高级别任务的小型软件组件。暂时忘掉软件,想想公司是如何运作的。当有人在公司申请工作时,他申请的是给定的职位:软件工程师、系统管理员、办公室经理。其原因可以用一个词来概括:专业化。如果你习惯了软件工程师的工作,你会随着经验变得更好,并为公司增加更多的价值。您不知道如何与客户打交道这一事实不会影响您的表现,因为这不是您的专业领域,并且几乎不会为您的日常工作增加任何价值。

Tip

专业化往往是提高效率的关键。做一件事并把它做好是软件开发的口头禅之一。

微服务是一个自治的工作单元,可以执行一项任务而不会干扰系统的其他部分,类似于公司的职位。这有许多好处,可用于有利于工程团队,以帮助扩展公司的系统。

如今,数百个系统是使用面向微服务的架构构建的,如下所示:

  • Netflix:这个 是最受欢迎的流媒体服务之一,它拥有构建了一个完整的应用程序生态系统,这些应用程序相互协作,以提供在全球范围内使用的可靠且可扩展的流媒体系统。

  • Spotify:这是世界领先的音乐流媒体服务之一,它使用微服务构建了这个应用程序。应用程序的每个小部件(使用 Chromium Embedded Framework 公开为桌面应用程序的网站)都是可以单独更新的不同微服务。

Microservice-oriented architectures

面向微服务的架构具有一些特殊性,使其适合任何希望保持其 IT 系统弹性和向上/向下扩展的大中型公司 -就绪状态。

How is it better?

它们不是软件工程的圣杯,但如果小心处理,它们会成为解决技术面临的大多数重大问题的完美方法-附属公司。

牢记面向微服务架构设计的关键原则很重要,例如弹性、可组合性、弹性等;否则,您最终可能会得到一个分散在不同机器上的单一应用程序,这会产生问题,而不是一个优雅的解决方案。

Shortcomings

还有一些关于面向微服务的架构的批评,因为它们引入了一些需要处理的问题,例如延迟、可追溯性和不存在的配置管理使用基于单片机的软件。部分问题描述如下:

  • 网络延迟:微服务具有分布式特性,因此必须考虑网络延迟

  • 操作开销:更多的服务器意味着更多的维护

  • 最终一致性:在高度事务性的系统上,我们需要在实现时考虑数据可能在一段时间内不一致的事实(我们将讨论本章稍后会介绍)

一般来说,工程师应该尝试评估这种方法的优缺点,并决定是否使用微服务以满足业务需求。

面向微服务的架构有一些需要考虑的特殊性。当软件工程师编写单体软件时,由于所构建软件的性质,有一些会被完全忽略。

例如,假设我们的软件需要发送电子邮件。在单体软件中,我们只需将功能添加到应用程序的核心。我们甚至可以选择创建一个专门的模块来处理电子邮件(这似乎是个好主意)。现在,假设我们正在创建一个微服务,而不是向大型软件工件添加功能,我们创建一个可以独立部署和版本控制的专用服务。在这种情况下,我们将有一个不需要考虑的额外步骤,即 网络延迟,以到达新的微服务。

在前面的示例中,无论您采用哪种方法(单体或微服务)来构建软件,都不是什么大问题。例如,如果一封电子邮件丢失了,这并不是世界末日。根据定义,不能保证电子邮件的发送,因此我们的应用程序仍然可以工作,尽管我们可能会收到客户的一些投诉。

Key design principles


在构建微服务时需要考虑一些关键设计原则。没有黄金法则,而且由于微服务是一个最近的概念,有时甚至对要遵循的实践缺乏共识。一般来说,我们可以假设以下设计原则:

  • 微服务是对公司流程进行建模的业务单元。

  • 它们是包含业务逻辑并使用简单通道和协议进行通信的智能端点。

  • 面向微服务的架构根据定义是分散的。这有助于构建强大且有弹性的软件。

Business units, no components

软件工程 最令人愉快的方面之一是创建一个新项目。在这里,您可以发挥所有创造力,尝试新的 架构概念、框架或方法。不幸的是,这在成熟的公司中并不常见。通常,我们所做的是在现有软件中创建新组件。在创建新组件时可以遵循的最佳设计原则之一是保持与软件其余部分的耦合尽可能低,以便它作为一个独立的单元工作。

Tip

保持低水平的耦合允许软件组件轻松转换为微服务。

考虑一个真实的例子:您公司的应用程序现在需要能够处理付款。

这里的逻辑决策是创建一个新模块,该模块知道如何处理所选支付提供商(信用卡、PayPal 等),并允许我们将所有与支付相关的业务逻辑保留在其中。让我们在下面的代码中定义接口:

public interface PaymentService {
  PaymentResponse processPayment(PaymentRequest request) throws MyBusinessException;
}

这个简单的接口大家都能看懂,但它是迈向微服务的关键。我们将所有业务知识封装在一个接口后面,这样理论上我们可以在不影响应用程序的其余部分的情况下切换支付提供商——实现细节对外界是隐藏的。

以下是我们迄今为止所知道的:

  • 我们知道方法名称,因此,我们知道如何调用服务

  • 该方法可能会抛出 MyBusinessException 类型的异常,强制调用代码处理它

  • 我们知道输入参数是一个 PaymentRequest 实例

  • 响应是已知对象

我们创建了一个高度内聚和松散耦合的业务单元。让我们在下面证明这个肯定:

  • 高度内聚:所有支付模块内的代码只会做一个事物,即处理支付和调用第三方服务的所有方面(连接处理、响应代码等),例如借记卡处理器。

  • 松散耦合:如果由于某种原因我们需要切换到新的 会发生什么支付处理器?界面是否有任何信息流出?我们是否需要因为合约的变化而改变调用代码?答案是不。支付服务接口的实现将始终是一个模块化的工作单元。

下面的 图表显示了由许多 组件组成的系统如何获取其中一个(支付服务)剥离成一个微服务:

读书笔记《developing-microservices-with-node-js》微服务架构

一旦实现了这个 模块,我们将能够处理付款和我们的单体应用程序将具有另一个功能,可以很好地提取到微服务中。

现在,我们可以推出新版本的支付服务,只要界面没有改变,以及与世界其他地方(我们的系统或第三方)的合同没有改变。这就是为什么保持实现独立于接口如此重要的原因,即使该语言不提供对接口的支持。

我们还可以根据需要扩展和部署尽可能多的支付服务,以便我们可以满足业务需求,而无需不必要地扩展可能没有压力的应用程序的其余部分。

Tip

下载示例代码

您可以从 http://www.packtpub.com< /一>。如果您在其他地方购买了这本书,您可以访问 http://www.packtpub.com/support< /a> 并注册以将文件直接通过电子邮件发送给您。

您可以按照以下步骤下载代码文件:

  • 使用您的电子邮件地址和密码登录或注册我们的网站。

  • 将鼠标指针悬停在顶部的 SUPPORT 选项卡上。

  • 点击代码下载&勘误表

  • Search 框中输入书名。

  • 选择您要为其下载代码文件的图书。

  • 从您购买此书的下拉菜单中进行选择。

  • 点击代码下载

您还可以通过在 Packt Publishing 网站的图书网页上单击 Code Files 按钮来下载代码文件。在 Search 框中输入书名即可访问此页面。请注意,您需要登录到您的 Packt 帐户。

下载文件后,请确保使用以下最新版本解压缩或解压缩文件夹:

  • WinRAR / 7-Zip for Windows

  • Zipeg / iZip / UnRarX for Mac

  • 适用于 Linux 的 7-Zip / PeaZip

Smart services, dumb communication pipes

超文本传输​​协议 (HTTP) 是最好的协议之一互联网上曾经发生过的事情。想象一个设计为无状态的协议,但通过使用cookies来保持状态。那是在 Web 1.0 时代,当时没有人谈论 REST API 或移动应用程序。让我们看一个 HTTP 请求的示例:

读书笔记《developing-microservices-with-node-js》微服务架构

如您所见,它是一个人类可读协议,无需解释即可理解。

如今,人们普遍认为 HTTP 不仅限于在 Web 中使用,而且由于它是 设计的,现在它被用作通用用途<一个 id="id30" class="indexterm"> 协议,用于将数据从一个端点传输到另一个端点。 HTTP 是微服务之间通信所需的全部内容:一种传输数据和从传输错误中恢复(如果可能)的协议。

在过去的几年里,特别是在企业界,一直在努力创建智能 通信机制,例如 <强>BPEL。 BPEL 代表 Business Process Execution Language,它不关注通信操作,而是关注围绕业务步骤的操作。

这在通信协议中引入了某种程度的复杂性,并使应用程序的业务逻辑从端点渗入其中,从而导致端点之间存在某种程度的耦合。

业务逻辑应保留在端点内,而不是渗入通信通道,以便系统可以轻松测试和扩展。多年来的经验教训是,通信层必须是一个简单明了的协议,以确保数据和端点(微服务)的传输。这些端点应该在其实现中嵌入服务可能会关闭一段时间的事实时间(这称为弹性,我们将在本章后面讨论)或网络可能导致通信问题。

在构建面向微服务的软件时,HTTP 通常是最常用的协议,但另一个需要探索的有趣选项是使用队列,例如 Rabbit MQ 和 Kafka,以促进端点之间的通信。

排队技术提供了一种干净的方法来以缓冲的 方式管理通信,封装了 的复杂性在高度事务性的系统上确认消息。

Decentralization

单体应用程序的主要缺点之一是将所有内容集中在单个(或少数)软件组件和数据库上。这通常会导致需要根据公司需求和流的集中治理来复制和扩展庞大的数据存储。

微服务旨在实现去中心化。与其拥有庞大的数据库,不如将数据按照前面解释的业务单位进行拆分?

一些读者可能会将事务性作为不这样做的主要原因之一。考虑以下场景:

  1. 客户在我们面向微服务的在线商店购买商品。

  2. 为商品付款时,系统会发出以下调用:

    1. 调用公司的财务系统以创建与付款的交易。

    2. 调用仓库系统发送图书。

    3. 调用邮件系统为客户订阅时事通讯。

在单体软件中,所有调用都将包装在一个事务中,因此如果由于某种原因任何调用失败,其他调用的数据将不会保存在数据库中。

当您学习设计数据库时,ACID 首字母缩略词总结了首要且最重要的原则之一:

  • 原子性:每个 事务将是全部或全部。如果某一部分失败,则不会在数据库上保留任何更改。

  • 一致性:通过事务对数据的更改需要保证其一致性。

  • 隔离:交易并发执行的结果导致系统状态如果交易是连续执行的,那将获得。

  • 持久性:一旦事务被提交,数据就会持续存在。

在面向微服务的软件上,ACID 原则并不能在全球范围内得到保证。微服务将在本地提交事务,但没有任何机制可以保证全局事务的 100% 完整性。可以在不处理付款的情况下发送图书,除非我们考虑到特定规则来防止它发生。

在面向微服务的架构上,无法保证数据的事务性,因此我们需要将故障因素考虑到实现中。解决这个问题的一种方法(虽然,变通方法是一个更合适的词)是分散治理和数据存储。

在构建微服务时,我们需要在设计中嵌入一个根据软件的可用性,或更多组件可能会发生故障并降低功能。

让我们看一下下面的图表:

读书笔记《developing-microservices-with-node-js》微服务架构

此图表示单片软件的执行顺序。一个顺序的调用列表,无论如何,都将按照 ACID 原则执行:要么所有调用(事务)都成功,要么都不成功。

这只有在框架和数据库引擎设计者已经开发出事务的概念来保​​证数据的事务性时才有可能。

在使用微服务时,我们需要考虑工程师所说的最终一致性。在事务部分失败后,每个微服务实例都应存储恢复事务所需的信息,以便信息最终保持一致。按照前面的例子,如果我们在不处理支付的情况下发送书籍,支付网关将有一个失败的交易,稍后有人会处理,使数据再次一致。

解决此问题的最佳方法 是分散治理。每个端点都应该能够做出影响事务全局范围的本地决策。我们将在 第 3 章 中详细讨论这个主题, 从单体应用到微服务

Technology alignment

在构建一个新软件时,每个开发者都应该保持一个的概念牢记:标准

标准保证您的服务将在技术上独立,以便使用不同的编程语言或技术轻松构建集成。

使用微服务对系统进行建模的优点之一是我们可以为正确的工作选择正确的技术,这样我们在解决问题时就可以非常高效。在构建单体软件时,将我们可以做的技术与微服务相结合是相当困难的。通常,在单体软件中,我们与一开始选择的技术联系在一起。

Java 远程方法调用 (RMI) 就是一个例子如果您希望您的系统对新技术开放,则应避免使用 的非标准协议。这是连接用 Java 编写的软件组件的好方法,但开发人员将很难(如果没有失败)使用 Node.js 调用 RMI 方法。这会将我们的架构与给定的语言联系起来,从微服务 的角度来看,这将扼​​杀一个最有趣的优势:技术异质性

How small is too small?

一旦我们决定将我们的系统建模为一组微服务,就会有总是有一个问题需要回答:到底有多小才算太小?

答案总是很棘手,而且可能令人失望:这取决于

给定系统中微服务的正确规模取决于公司的结构以及创建易于由一小群开发人员管理的软件组件的能力。这也取决于技术需求。

想象一个接收和处理银行文件的系统;您可能知道,银行之间的所有付款都以特定已知格式的文件发送(例如 Single Euro Payments AreaSEPA) 用于欧元支付)。这种类型的系统的特点之一是大量不同的文件系统需要知道如何处理。

解决此问题的第一种方法是从微服务的角度解决它,将其与任何其他服务分开,创建一个工作单元,并为每种类型的文件创建一个微服务。它将使我们能够在不干扰系统其余部分的情况下对现有文件处理器进行修改。它还将使我们能够 继续处理文件,即使其中一项服务遇到问题。

微服务应该尽可能小,但请记住,每个微服务都会给需要管理新服务的运营团队增加开销。尝试从可管理性、可扩展性和专业化方面回答到底有多小?这个问题。微服务应该足够小,以便在不影响系统其余部分的情况下由一个人快速管理和扩展(或缩减);它应该只做一件事。

Tip

作为一般规则,微服务应该足够小,以便在冲刺中完全重写。

Key benefits


在上一个主题中,我们谈到了面向微服务的架构是什么。我还公开了我从经验中学到的设计原则,并展示了这种架构的一些好处。

现在,是时候概述这些关键优势并展示它们将如何帮助我们提高软件质量,以及能够快速适应新的业务需求。

Resilience

弹性 在维基百科中被定义为系统应对变化的能力。我喜欢将弹性视为系统从异常中正常恢复的能力 (暂时性硬件故障、意外的高网络延迟等)或压力期,一旦情况得到解决,不会影响系统的性能。

虽然听起来很简单,但在构建面向微服务的软件时,由于系统的分布式特性,问题的根源扩大了,有时很难(甚至不可能)防止所有异常情况。

弹性是从错误中优雅恢复的能力。它还增加了另一个 级别的复杂性:如果一个微服务遇到问题,我们能否防止出现一般故障?理想情况下,我们应该以降低服务响应而不是导致一般故障的方式构建我们的系统,尽管这并不总是那么容易。

Scalability

如今,公司中 的常见问题之一是系统的 可扩展性。如果您之前从事过单体软件的工作,我相信您在某个时候遇到过容量问题,以及公司的发展。

通常,这些问题不会跨越应用程序的所有层或子系统。总是有一个子系统或服务的执行速度明显慢于其他子系统,如果它无法满足需求,就会导致整个应用程序失败。

下图描述了如何在不干扰系统其余部分的情况下扩展微服务(两个邮件服务):

读书笔记《developing-microservices-with-node-js》微服务架构

Tip

汽车保险领域中这些弱点的一个例子是计算给定风险因素列表的报价的服务。仅仅为了满足这个特定部分的需求而扩展整个应用程序是否有意义?如果您心中的答案是,那么您离拥抱微服务又近了一步。微服务使您能够随着系统特定区域的需求增加而扩展系统的各个部分。

如果我们的 保险系统是一个面向微服务的软件,那么唯一需要解决对报价计算的高需求将产生更多负责计算的微服务(或微服务)实例。请记住,扩展服务可能会增加运营它们的开销。

Technology heterogeneity

软件世界每隔几个月就会发生变化。新语言作为特定类型的事实上的标准进入行业的系统。几年前,Ruby on Rails 出现在现场,并在 2013 年成为新项目最常用的 Web 框架之一。Golang(一种由 Google 创建的语言)正在成为当今的一种趋势,因为它结合了巨大的性能和优雅而优雅的特性。任何对另一种编程语言有一定经验的人都可以在几天内学会简单的语法。

过去,我曾使用 Python 和 Java 作为编写微服务的成功替代方案。

尤其是 Java,自 Spring Boot 发布以来,它是编写敏捷(编写和操作)微服务的有吸引​​力的技术堆栈。

Django,也是一个在 Python 上编写微服务的强大框架。与 Ruby on Rails 非常相似,它可以自动化数据库迁移并创建 CRUD 创建读取更新删除)服务非常简单。

Node.js 利用一种著名的语言 JavaScript 创建了一个新的服务器端堆栈,该堆栈正在改变工程师创建新软件的方式。

那么,将它们全部组合起来有什么问题呢?平心而论,这是一个优势:我们可以为正确的工作选择正确的工具

只要集成技术是标准的,面向微服务的架构就可以做到这一点。正如您之前所了解的,微服务是可以自行运行的小型独立软件

下图显示了微服务如何隐藏数据存储/收集,只有公共通信点——使它们成为低耦合的一个很好的例子(一个服务实现更改不会干扰任何其他服务):

读书笔记《developing-microservices-with-node-js》微服务架构

我们之前谈到了性能。 我们系统的某些部分总是比其他部分承受更大的压力。使用现代多核 CPU,并行(并发)编程可以解决其中一些性能问题,但是,Node.js 并不是一种用于并行化任务的好语言。我们可以选择在压力下使用更合适的语言(例如 Erlang)重写微服务,以更优雅的方式管理并发。完成它应该不超过两周。

在同一系统上使用多种技术有一个缺点:开发人员和系统管理员需要了解所有(或少数)技术。采用微服务的公司通常会尝试使用一种核心技术(在本书中,我们将使用 Node.js)和一些辅助技术(虽然我们将使用 Docker 来管理部署,但我们可以使用 Capistrano 或 Fabricator 来管理发布)。

Replaceability

可替换性是在不干扰系统行为方式的情况下更改系统的一个组件的能力。

当谈到关于软件时,可替换性伴随着低耦合。我们应该以一种内部逻辑不会暴露给调用服务的方式编写我们的微服务,这样给定服务的客户端就不需要知道它是如何实现的,只需要知道接口。让我们看一下下面的例子。它是用 Java 编写的,因为我们只需要查看界面即可识别陷阱:

public interface GeoIpService {
  /**
   * Checks if an IP is in the country given by an ISO code.
   **/
  boolean isIn(String ip, String isoCode) throws SOAPFaultException;
}

乍一看,这个界面是不言自明的。它检查给定 IP 是否在给定国家/地区并抛出 SOAPFaultException,这是一个大问题。

如果我们构建使用此服务的客户端,考虑到它们的逻辑、捕获和处理 SoapFaultException,我们会将内部实现细节暴露给外部世界,并且很难替换 GeoIpService 接口。此外,我们正在创建与应用程序逻辑的一部分相关的服务这一事实表明 >有界上下文:一种高度内聚的服务或一组协同工作以实现一个目的的服务。

Independence

无论我们如何努力,人脑都不是为解决复杂问题而设计的问题。人类大脑最有效的运作模式是当时的一件事,以便我们将复杂的问题分解成更小的问题。面向微服务的架构应该遵循这种方法:所有服务应该是独立的,并通过接口交互,直到它们可以由不同的工程师组开发而无需任何交互,除了同意接口。这将使采用微服务的公司能够根据业务需求扩大或缩小工程团队,使业务能够灵活地响应需求高峰或平静期。

Why is replaceability important?

在上一节中,我们讨论了微服务的正确大小。作为一般经验法则,团队应该能够在冲刺中重写和部署微服务。其背后的原因是技术债务

我会将 技术债务定义为在计划的期限内交付预期功能的原始技术设计的偏差。其中一些牺牲或错误假设通常会导致需要完全重构或重写的糟糕的软件。

在前面的示例中,接口向外部世界公开我们正在使用 SOAP 来调用 Web 服务,但是我们需要更改客户端的代码,因为 REST 客户端肯定与 SOAP 异常无关。

Easy to deploy

微服务 应该易于部署。

作为软件开发人员,我们很清楚很多事情都可能出错,从而导致软件无法部署。

如前所述,由于多种原因,微服务应该易于部署,如下表所述:

  • 少量的业务逻辑(记住 两周从头开始重写的经验法则)导致更简单的部署。

  • 微服务是自治的工作单元,因此升级服务是复杂系统中的一个包含问题。无需重新部署整个系统。

  • 微服务架构上的基础设施和配置应尽可能自动化。在本书的后面,我们将学习如何使用 Docker 部署微服务,以及与传统部署技术相比有哪些好处。

SOA versus microservices


面向服务的架构 (SOA)年数。 SOA 是一个很好的软件设计原则。它们是自包含的组件,为其他组件提供服务。正如我们之前所说的,这一切都是为了在系统的不同模块上保持低耦合,就好像它是一个难题一样,这样我们就可以在不对整个系统造成太大影响的情况下更换各个部分。

原则上,SOA 看起来与微服务架构非常相似。那么区别是什么呢?

微服务是细粒度的 SOA 组件。换句话说,单个 SOA 组件 可以分解 到多个微服务中可以一起工作以提供相同级别的功能:

读书笔记《developing-microservices-with-node-js》微服务架构

Tip

微服务是细粒度的 SOA 组件。它们是关注范围狭窄的轻量级服务。

微服务和 SOA 之间的另一个区别是用于互连和编写服务的技术。

J2EE 是一种技术堆栈,旨在编写 SOA 架构,因为它强制执行企业标准。 Java 命名和目录接口、企业 Java Bean 和 企业服务总线 (ESB ) 是构建和维护 SOA 应用程序的生态系统。虽然 ESB 是一种标准,但 2005 年以后毕业的工程师很少听说过 ESB,使用过的工程师更是少之又少,而如今的 Ruby on Rails 等现代框架甚至不考虑这么复杂的软件。

另一方面,微服务强制使用广为人知且可广泛互操作的标准(例如 HTTP)。我们可以选择正确的语言或工具来构建组件(微服务),遵循本章前面在技术异构中解释的主要好处之一部分。

除了 从技术 堆栈和服务的大小之外,还有一个偶数SOA 和微服务之间更大的区别:领域模型。在本章前面,我们已经讨论了去中心化。治理的去中心化,而且,数据的去中心化。在基于微服务的软件中,每个微服务都应该在本地存储自己的数据,将领域模型隔离到单个服务;而在面向 SOA 的软件中,数据通常存储在几个大型数据库中,并且服务共享领域模型。

Why Node.js?


几年前,我不相信 Node.js。对我来说,这不仅仅是一种解决问题的真正工具,而是一种趋势……服务器中的 JavaScript?那看起来不太对劲。平心而论,我什至不喜欢 JavaScript。然后,诸如 jQuery 或 Angular.js 之类的现代框架出现了。他们 解决了其中一个问题,即跨浏览器兼容性。在我们需要考虑至少三个不同的浏览器之前,在 jQuery 之后,所有这些逻辑都很好地封装在一个库中,这样我们就不需要担心兼容性,只要我们遵循 jQuery 文档。

然后,JavaScript 变得越来越流行。突然间,所有的内部工具都是用 Single-Page Application (SPA< /span>) 框架,大量使用 JavaScript,因此,现在的大多数开发人员,无论哪种方式,都精通 JavaScript。

然后,有人决定将 JavaScript 从浏览器中移除,这是一个好主意。 Rhino、Node.js 和 Nashorn 是可以执行独立 JavaScript 的运行时示例。其中一些甚至可以与 Java 代码交互,使开发人员能够将 Java 类导入 JavaScript 程序,这使您可以访问已经用 Java 编写的无穷无尽的框架集。

让我们关注 Node.js。出于多种原因,Node.js 是面向微服务架构的完美候选者,如下表所述:

  • 易于学习(尽管可能很难掌握)

  • 易于扩展

  • 高度可测试

  • 易于部署

  • 通过 npm 进行依赖管理

  • 有数百个库可以与大多数标准协议集成

这些原因以及 以及我们将在接下来的章节中介绍的其他原因,使 Node.js 成为构建可靠微服务的完美候选者。

API aggregation

Seneca 是我在接下来的章节中选择的开发框架. Seneca 最吸引人的特性之一是 API 聚合。

API 聚合是一种高级技术,通过聚合不同的功能(插件、方法等)来组合接口

让我们看一下下面的例子:

var express = require('express');
var app = express();

app.get('/sayhello', function (req, res) {
  res.send('Hello World!');
});
app.get('/saygoodbye', function(req, res) {
  res.send('Bye bye!');
});

var server = app.listen(3000, function () {
  var host = server.address().address;
  var port = server.address().port;
  console.log('App listening at http://%s:%s', host, port);
});

前面的示例使用 Express,这是一个非常流行的 Node.js 网络框架。该框架也是围绕 API 聚合技术构建的。我们来看看第四行和第七行。在这些行中,开发人员注册了两个方法,当有人点击 URL /sayhello/saygoodbye获取请求。换句话说,应用程序由不同的较小且独立的实现组成,这些实现在单个接口上暴露给外部世界,在这种情况下,应用程序侦听 3000 端口。

在接下来的章节中,我将解释为什么这个属性很重要,以及在构建(和扩展)微服务时如何利用它。

The future of Node.js

JavaScript 最初被设计为一种在网络浏览器中执行的语言。对于那些工作或学习的人来说,使用 C/C++ 是非常熟悉的,这是将其作为 Web 2.0 中动态操作文档的标准采用的关键。 异步 JavaScript 和 XML (AJAX) 是 JavaScript 的导火索 增长。不同的浏览器对请求对象有不同的实现,因此开发人员很难编写跨浏览器的代码。

缺乏标准导致创建了许多框架来封装 AJAX 背后的逻辑,从而使跨浏览器脚本易于编写。

JavaScript 是一种脚本语言。它不是为面向对象而设计的,也不是为大型应用程序选择的语言,因为代码往往会变得混乱,并且很难在不同的公司之间强制执行代码应该如何布局的标准。我工作过的每一家公司都有不同的最佳实践和其中的一些甚至是矛盾的。

欧洲计算机制造商协会ECMA)前来救援。 ECMAScript 6ECMA 语言(JavaScript、ActionScript、 Rhino 等)引入了类、继承、集合的概念,以及许多有趣的特性,这些特性将使 JavaScript 软件的开发比实际的 V8 规范更容易和更标准。

我认为其中一个更有趣的功能是引入了 class 关键字,它允许我们使用对象对 JavaScript 软件进行建模。

目前,大多数浏览器都支持大量这些功能,但对于 Node.js,只有少数是默认实现的,其中一些是通过向解释器传递特殊标志(和谐标志)来实现的。 )。

在本书中,我将尽量避免 ECMAScript 6 的特性,坚持大多数开发人员都广为人知的 V8 规范,一旦有人了解 JavaScript V8,就很容易在 ECMAScript 6 上升级。

Summary


在本章中,我们研究了围绕微服务的关键概念,以及在设计高质量软件组件以构建强大且有弹性的软件架构以使我们能够快速响应业务需求时应遵循的最佳实践。

您还了解了关键的好处,例如在面向微服务的架构上为正确的服务(技术异构性)使用正确的语言的可能性,以及可能使我们的生活变得更加艰难的一些陷阱,例如在由面向微服务的架构的分布式特性引起的操作方面。

最后,我们讨论了为什么 Node.js 是构建微服务的绝佳工具,以及我们如何从 JavaScript 中受益,通过 API 聚合等技术构建高质量的软件组件。

在接下来的章节中,我们将开发本章中讨论的概念,并提供代码示例和对我多年来所学主题的进一步解释。

如前所述,我们将专注于 JavaScript 的 V8 版本,但我将提供一些关于如何轻松编写可升级组件以支持 ECMAScript 6 的提示。