电子说
在过去40 年里,软件开发的世界日新月异,微服务日趋流行。本文为我们揭示了微服务的五大关键好处,看它们是如何帮助我们提升软件质量并适应新的业务需求。
弹性
维基百科将弹性定义为系统处理变化的能力。我对弹性的理解是在问题被解决后系统从异常状态(短暂的硬件故障以及意料之外的高网络延迟等)或压力期中优雅恢复,同时又不会影响系统性能的能力。
这虽然听上去很简单,但是在构建面向微服务软件的时候,问题源会由于系统的分布式特性而被放大,有时很难(甚至不可能)防止所有的异常情况。
弹性是从错误中优雅恢复的能力。但它同样也为系统带来了新的复杂度:如果一个微服务出现了问题,我们能否防止系统的常规故障?理想情况下,我们应该以这样一种方式来构建服务:仅对服务响应进行降级而非让系统出现常规故障,即使这样做也并不容易。
如今,各大公司的一个通病是系统存在可伸缩性问题。如果你之前曾与某个单块软件打过交道,我确信你在伴随公司的成长过程中,必定会在某些时刻遭遇到容量问题。
通常,这些问题并不涉及应用的每一层次或所有子系统。往往只有个别子系统或服务会明显慢于其余部分,一旦没有处理好容量问题就会导致整个应用发生故障。下图描述了我们是如何对微服务进行扩展(扩展成两个邮件服务)的,同时又不牵扯系统的其余部分:
让我们来看一个关于车险的场景,用于计算指定风险因素列表报价的服务便是该类问题的一个例子。通过扩展整个应用来满足对某个特定部分的需求是否有意义?如果你脑海中的答案是“否”,那么你离拥抱微服务更近了一步。微服务可以让你仅仅按需扩展系统的一部分,从而只加大系统特定部分的处理能力。让我们来看一个关于车险的场景,用于计算指定风险因素列表报价的服务便是该类问题的一个例子。通过扩展整个应用来满足对某个特定部分的需求是否有意义?如果你脑海中的答案是“否”,那么你离拥抱微服务更近了一步。微服务可以让你仅仅按需扩展系统的一部分,从而只加大系统特定部分的处理能力。
如果该保险系统是一个面向微服务的系统,那么我们只需要创建更多的微服务实例来负责计算就能解决报价计算需求过旺的问题。请记住,扩展服务会给运维团队增加开销。
技术多样性
软件的世界每几个月就会更新换代。新语言进入业界成为某类系统事实标准的节奏片刻未停。几年前, Ruby onRails面世并在 2013年成为在各种新项目中使用昀多的 Web框架。Golang(由 Google创建的一门语言)因其结合了强大的性能与优雅简洁的语法而成为当前的一种趋势,任何只要拥有一门编程语言经验的人都可以在几天内学会它。
在过去,我也曾使用 Python和 Java成功编写过微服务。
尤其是 Java,自从 Spring Boot发布之后,它成为在编写敏捷微服务方面相当有吸引力的技术栈。
Django是一款强大且可用于编写微服务的 Python框架,与 Ruby on Rails非常相似。通过它我们可以自动化地进行数据库迁移,并可以非常轻松地完成创建 CRUD(创建、读取、更新及删除)服务的工作。
Node.js利用了著名语言 JavaScript的优势,创建了一个新的服务端技术栈,从而改变了工程师们编写新软件的方式。
那么,将这些技术都结合起来会有什么问题吗?平心而论,这是一个优势:我们可以选择合适的工具来做相对应的工作。
只要待集成的技术是标准化的,面向微服务的架构便可以帮你实现这一点。正如我们在上文中已经了解到的,一个微服务是非常小的,并且是一个自主运维的软件中的独立部分。
下图展示了微服务是如何隐藏数据的存取逻辑的,两个服务在存取数据方面共用同一个通信点,从而能很好地互相解耦(一个服务实现发生变化时并不涉及任何其他服务):
此前我们曾讨论到性能的问题。通常,系统的某些部分会比其他部分承受更多的压力。通过利用当代的多核 CPU进行并行(并发)编程可以解决其中的一些性能问题。然而, Node.js并不是一门适合执行并行任务的语言。针对那些处于压力之下的微服务来说,我们可以选择一门更加适合的语言来进行开发,比如 Erlang,从而可以以一种更加优雅的方式来管理并发。这样做,花不了你两周的时间。
在同一系统中使用多种技术存在着一个问题:开发人员和系统管理员需要知道所有的(或一部分)相关技能。拥抱微服务的公司通常可以秉持一门核心技术(在本书中,我们将会使用 Node.js),并辅以一些其他技术(我们除了使用 Docker来管理部署之外,还可以采用 Capistrano或 Fabricator来管理发布)。
可替换性
可替换性是指替换系统中某个组件而不影响系统行为的一种能力。当我们在讨论软件的时候,可替换性往往是与低耦合密不可分的。在编写微服务的时候不能将内部逻辑暴露给调用服务,服务实现对客户端来说是透明的,客户端了解的只有接口。让我们来看看下面的例子,该接口是用 Java编写的,仅需通过观察接口就能识别出它存在着什么问题。
public interface GeoIpService {
/**
*检查IP是否属于指定ISO代码所对应的国家
**/
boolean isIn(String ip, String isoCode) throws
SOAPFaultException;
}
初看该接口可以发现它是自描述的。它将检查特定 IP是否属于特定的国家,一旦服务出现重大问题会抛出 SOAPFaultException。
如果我们构建客户端来消费该接口,需要考虑到服务的上述逻辑,捕获并处理 SoapFaultException。这等同于将服务内部实现的细节暴露给了外部世界,从而很难再替换掉 GeoIpService接口。同样的,事实上我们创建的某个服务如果关联了应用逻辑的某个部分则表明创建了一个限界上下文:即一个高内聚的服务或服务集(通过集合所辖服务的协同工作可以达成一个目标)。
独立性
不管我们怎么努力,人类的大脑都不擅长解决复杂问题。人类大脑昀有效的运作模式是同一时间只做一件事情,所以我们可以将复杂问题拆解成更小的问题。面向微服务的架构应该也遵从这一方式:所有服务应该都是独立的,它们通过接口进行交互。除了协定确认接口这一环节之外,不同的工程师团队可以在无须交流的情况下完成对服务的开发。一家采用了微服务的公司可以根据业务的需求来调整工程师团队的规模,从而能敏捷地响应业务的高峰期或静默期。
为什么可替换性如此重要
在前面的一个小节中,我们讨论了该如何确定微服务的合理规模。按照普遍的经验而言,一个团队应该能在一个 sprint内完成一个微服务的重写和部署。这样做的背后的根本原因就是技术债务。
我会将技术债务定义为在一个既定计划的周期内,初始技术设计与预期交付功能之间的偏差。某些方面的牺牲或错误假设会导致编写的软件非常糟糕,这样的软件需要全盘重构或重写。在前面的例子中,接口在暴露给外部世界时明确表明必须使用 SOAP来调用 Web服务。一旦需要将客户端代码改造成 REST客户端,REST客户端根本无法处理 SOAP异常。
易于部署
微服务应当易于部署。作为软件开发者,我们知道在软件的部署过程中很多事情都可能会出现问题。正如前面所提到的,微服务是非常易于部署的,原因如下:
•少量的业务逻辑(从经验上来说是只需两周即可完成从无到有的编写)导致更易于部署。
•微服务是自治的工作单元,所以升级一个服务对于复杂系统来说是一个局部可控的问题。无须重新部署整个系统。
•微服务架构中的基础设施和配置应该尽可能自动化。在本书的后续部分中,我们将学习如何使用 Docker来部署微服务,以及这样做相比于传统部署技术会有怎样的优势。
全部0条评论
快来发表一下你的评论吧 !