微服务

什么是微服务

微服务就是一种架构风格

它将应用构建成一系列按业务领域划分模块的、小的自治服务。

简单来说,就是将一个系统按业务划分成多个子系统。

每个子系统都是完整的,可独立运行的。

子系统间的交互可通过HTTP协议进行通信(也可以采用消息队列来通信,如RoocketMQ,Kafaka等)。


微服务特点

  • 解耦:同一系统内的服务大部分可以被解耦。因此应用,作为一个整体,可以轻易地被构建、修改和扩展。
  • 组件化:微服务可以被看成相互独立的组件,这些组件可以被轻易地替换和升级。
  • 业务能力:微服务很小,它们可以专注于某种单一的能力
  • 自治:开发者和团队可以独立地工作,提高开发速度。
  • 持续交付:允许持续发布软件新版本,通过系统化的自动手段来创建、测试和批准新版本。
  • 职责明确:微服务不把应用看成一个又一个的项目。相反,它们把应用当成了自己需要负责的项目。
  • 去中心化管理:关注于使用正确的工具来完成正确的工作。这也就是说,没有标准化的方式或者技术模式。开发者们有权选择最好的工具来解决问题。
  • 敏捷性:微服务支持敏捷开发。任何新功能都可以被快速开发或丢弃。

微服务优势

  • 独立开发:基于各个微服务所独有的功能,它们可以被轻易开发出来。
  • 独立部署:基于它们所提供的服务,它们可以被独立地部署到应用中。
  • 错误隔离:即便其中某个服务发生了故障,整个系统还可以继续工作。
  • 混合技术栈:可以使用不同的语言和技术来为同一个应用构建不同的服务。
  • 按粒度扩展:可以根据需求扩展某一个组件,不需要将所有组件全部扩展。


聊聊为什么需要微服务

企业级应用通常被构建成三个主要部分

  1. 客户端用户界面(由运行在客户机器上的浏览器的 HTML 页面、Javascript 组成)。
  2. 数据库(由许多的表构成一个通用的、相互关联的数据管理系统)。
  3. 服务端应用。

服务端应用处理 HTTP 请求,执行领域逻辑(domain logic),检索并更新数据库中的数据,使用适当的 HTML 视图发送给浏览器。

服务端应用是完整的 ,是一个单独的的逻辑执行。

任何对系统的改变都涉及到重新构建和部署一个新版本的服务端应用程序

这样的整体服务是一种构建系统很自然的方式。

虽然你可以利用开发语基础特性把应用程序划分成类、函数、命名空间,但所有你处理请求的逻辑都运行在一个单独的进程中。

在某些场景中,开发者可以在的笔计本上开发、测试应用,然后利用部署通道来保证经过正常测试的变更,发布到产品中。

也可以使用横向扩展,通过负载均衡将多个应用部署到多台服务器上。

整体应用程序相当成功。


但是越来越多的人感觉到有点不妥,特别是在云中部署时。

变更发布周期被绑定了——只是变更应用程序的一小部分,却要求整个重新构建和部署。

随着时间的推移,很难再保持一个好的模块化结构,使得一个模块的变更很难不影响到其它模块。

扩展就需要整个应用程序的扩展,而不能进行部分扩展。

这导致了微服务架构风格的出现。把应用程序构建为一套服务。

事实是,服务可以独立部署和扩展,每个服务提供了一个坚实的模块边界,甚至不同的服务可以用不同的编程语言编写。

它们可以被不同的团队管理。

微服务风格不是什么新东西,它至少可以追溯到 Unix 的设计原则。

但是并没有太多人考虑微服务架构,如果他们用了,那么很多软件都会更好。



组件化(Componentization )与服务(Services)

当我们谈论组件时,可能会陷入一个困境——什么是组件。

我们的定义是,组件(component)是一个可独立替换和升级的软件单元。


组件化软件的主要方式是把它拆分成服务。

我们把库(libraries)定义为组件,这些组件被链接到程序。

并通过内存中函数调用来调用,而服务(services )是进程外组件。

他们利用某个机制通信,比如 WebService 请求,或远程过程调用。


组件和服务在很多面向对象编程中是不同的概念。

把服务当成组件(而不是组件库)的一个主要原因是,服务可以独立部署。

如果你的应用程序是由一个单独进程中的很多库组成,那么对任何一个组件的改变都将导致必须重新部署整个应用程序。

但是如果你把应用程序拆分成很多服务,那你只需要重新部署那个改变的服务。

当然,这也不是绝对的,有些服务会改变导致协调的服务接口。

但是一个好的微服务架构的目标就是通过在服务契约中解耦服务的边界和进化机制来避免这些。


另一个考虑是,把服务当组件将拥有更清晰的组件接口。

大多数开发语言都没有一个良好的机制来定义一个发布的接口。

发布的接口是指一个类向外公开的成员,比如 Java 中的声明为 Public 的成员,C# 中声明为非 Internal 的成员。

通常只有在文档和规范中会说明,这是为了让避免客户端破坏组件的封装性,阻止组件间紧耦合。

服务通过使用公开远程调用机制可以很容易避免这些。

像这样使用服务也有不足之处。

远程调用比进制内调用更消耗资源,因此远程 API 需要粗粒度,但这会比较难使用。

如果你需要调整组件间的职责分配,当跨越进程边界时,这样做将会很难。



围绕业务功能的组织

当寻找把一个大的应用程序拆分成小的部分时,通常管理都会集中在技术层面,UI团队、服务端业务逻辑团队和数据库团队。

当使用这种标准对团队进行划分时,甚至小小的更变都将导致跨团队项目协作,从而消耗时间和预算审批。

一个高效的团队会针对这种情况进行改善,两权相害取其轻。

设计一个系统的任何组织(广义上)都会产生这样一种设计,其结构是组织交流结构的复制。

​ ——Melvyn Conway, 1967

Melvyn Conway 的意思是,像下图所展示的,设计一个系统时,将人员划分为 UI 团队,中间件团队,DBA 团队。

那么相应地,软件系统也就会自然地被划分为 UI 界面,中间件系统,数据库。

image-20221012151130752

微服务的划分方法不同,它倾向围绕业务功能的组织来分割服务。

这些服务实现商业领域的软件,包括用户界面,持久化存储,任何的外部协作。

因此,团队是跨职能的,包含开发过程所要求的所有技能:用户体验、数据库和项目管理。

跨职能的团队同时负责构建和运营每个产品,每个产品被分割成许多单个的服务,这些服务通过消息总线通信。

大型的整体应用程序也可以按照业务功能进行模块化,尽管这样情况不常见。

当然,我们可以敦促一个构建整体应用程序的大型团队,按业务线来分割自己。

我们已经看到的主要问题是,这种组件形式会导致很多的依赖。

如果整体应用程序跨越很多模块边界,那么对于团队的每个成员短期内修复它们是很困难的。

此外,我们发现,模块化需要大量的强制规范。服务组件所要求的必需的更明确的分离使得保持团队边界清晰更加容易。



产品不是项目

大部分的软件开发者都使用这样的项目模式:至力于提供一些被认为是完整的软件。交付一个他们认为完成的软件。软件移交给运维组织,然后,解散构建软件的团队。

微服务的支持者认为这种做法是不可取的,并提议团队应该负责产品的整个生命周期。

Amazon 理念是“你构建,你运维(you build, you run it)”,要求开发团队对软件产品的整个生命周期负责。

这要求开发者每天都关注他们的软件运行如何,增加更用户的联系,同时承担一些售后支持。

产品的理念,跟业务能力联系起来。

不是着眼于完成一套功能的软件,而是有一个持续的关系,是如何能够帮助软件及其用户提升业务能力。

为什么相同的方法不能用在整体应用程序,但更小的服务粒度能够使创建服务的开发者与使用者之间的个人联系更容易。



强化终端弱化通道

当构建不同的进程间通信机制的时候,我们发现有许多的产品和方法能够把更加有效方法强加入的通信机制中。

比如企业服务总线(ESB),这样的产品提供更有效的方式改进通信过程中的路由、编码、传输、以及业务处理规则。

微服务倾向于做如下的选择:强化终端及弱化通道。

微服务的应用致力松耦合和高内聚:采用单独的业务逻辑,表现的更像经典Unix意义上的过滤器一样,接受请求、处理业务逻辑、返回响应。

它们更喜欢简单的REST风格,而不是复杂的协议,如WS或者BPEL或者集中式框架。

有两种协议最经常被使用到:包含资源API的HTTP的请求-响应轻量级消息通信协议

善于利用网络,而不是限制(Be of the web, not behind the web)。

​ ——Ian Robinson

微服务团队采用这样的原则和规范:基于互联网(广义上,包含Unix系统)构建系统。

这样经常使用的资源几乎不用什么的代价就可以被开发者或者运行商缓存。

第二种做法是通过轻量级消息总线来发布消息。

这种的通信协议非常的单一(单一到只负责消息路由),像RabbitMQ或者ZeroMQ这样的简单的实现甚至像可靠的异步机制都没提供。

以至于需要依赖产生或者消费消息的终端或者服务来处理这类问题。

在整体工风格中,组件在进程内执行,进程间的消息通信通常通过调用方法或者回调函数。

从整体式风格到微服务框架最大的问题在于通信方式的变更。

从内存内部原始的调用变成远程调用,产生的大量的不可靠通信。

因此,你需要把粗粒度的方法成更加细粒度的通信。



分散治理

集中治理的一种好处是在单一平台上进行标准化。

经验表明这种趋势的好处在缩小,因为并不是所有的问题都相同,而且解决方案并不是万能的。


我们更加倾向于采用适当的工具解决适当的问题,整体式的应用在一定程度上比多语言环境更有优势,但也适合所有的情况。

把整体式框架中的组件,拆分成不同的服务,我们在构建它们时有更多的选择。

你想用Node.js去开发报表页面吗?做吧。

用C++来构建时时性要求高的组件?很好。

你想以在不同类型的数据库中切换,来提高组件的读取性能?我们现在有技术手段来实现它了。

当然,你是可以做更多的选择,但也不意味的你就可以这样做,因为你的系统使用这种方式进行侵害意味着你已经有的决定。


采用微服务的团队更喜欢不同的标准。

他们不会把这些标准写在纸上,而是喜欢这样的思想:开发有用的工具来解决开发者遇到的相似的问题。

这些工具通常从实现中成长起来,并进行的广泛范围内分享,当然,它们有时,并不一定,会采用开源模式。

现在开源的做法也变得越来越普遍,git或者github成为了它们事实上的版本控制系统。


Netfix就是这样的一个组织,它是非常好的一个例子。

分享有用的、尤其是经过实践的代码库激励着其它的开发者也使用相似的方式来解决相似的问题,当然,也保留着根据需要使用不同的方法的权力。

共享库更关注于数据存储、进程内通信以及我们接下来做讨论到的自动化等这些问题上。


微服务社区中,开销问题特别引人注意。

这并不是说,社区不认为服务交互的价值。

相反,正是因为发现到它的价值。

这使得他们在寻找各种方法来解决它们。

Tolearant ReaderConsumer-Driven Contracts这样的设计模式就经常被微服务使用。

这些模式解决了独立服务在交互过程中的消耗问题。

使用Consumer-Driven Contracts增加了你的信心,并实现了快速的反馈机制。

事实上,我们知道澳大利亚的一个团队致力使用Consumer-Drvien Contracts开发新的服务。

他们使用简单的工程,帮助他们定义服务的接口。

使得在新服务的代码开始编写之前,这些接口就成为自动化构建的一个部分。

构建出来的服务,只需要指出这些接口适用的范围,一个优雅的方法避免了新软件中的'YAGNI '困境。

这些技术和工具在使用过程中完善,通过减少服务间的耦合,限制了集中式管理的需求。


也许分散治理普及于亚马逊“编译它,运维它”的理念。

团队为他们开发的软件负全部责任,也包含7*24小时的运行。

全责任的方式并不常见,但是我们确实发现越来越多的公司在他们的团队中所推广。

Netfix是另外一个接受这种理念的组件。

每天凌晨3点被闹钟吵醒,因为你非常的关注写的代码质量。

这在传统的集中式治理中这是一样多么不思议的事情呀。



分散数据管理

对数据的分散管理有多种不同的表现形式。

最为抽象层次,它意味着不同系统中的通用概念是不同的。

这带来的觉问题是大型的跨系统整合时,用户使用不同的售后支持将得到不同的促销信息。

这种情况叫做并没有给用户显示所有的促销手段。不同的语法确实存在相同的词义或者(更差)相同的词义。


应用之间这个问题很普遍,但应用内部这个问题也存在,特别是当应用拆分成不同的组件时。

对待这个问题非常有用的方式为Bounded Context的领域驱动设计。

DDD把复杂的领域拆分成不同上下文边界以及它们之间的关系。

这样的过程对于整体架构和微服务框架都很有用,但是服务间存在着明显的关系,帮助我们对上下文边界进行区分,同时也像我们在业务功能中谈到的,强行拆分。


当对概念模式下决心进行分散管理时,微服务也决定着分散数据管理。

当整体式的应用使用单一逻辑数据库对数据持久化时,企业通常选择在应用的范围内使用一个数据库,这些决定也受厂商的商业权限模式驱动。

微服务让每个服务管理自己的数据库:无论是相同数据库的不同实例,或者是不同的数据库系统。

这种方法叫Polyglot Persistence。你可以把这种方法用在整体架构中,但是它更常见于微服务架构中。

微服务音分散数据现任意味着管理数据更新。

处理数据更新的常用方法是使用事务来保证不同的资源修改数据库的一致性。

这种方法通常在整体架构中使用。

使用事务是因为它能够帮助处理一至性问题,但对时间的消耗是严重的,这给跨服务操作带来难题。

分布式事务非常难以实施,因此微服务架构强调服务间事务的协调,并清楚的认识一致性只能是最终一致性以及通过补偿运算处理问题。

选择处理不一致问题对于开发团队来说是新的挑战,但是也是一个常见的业务实践模式。

通常业务上允许一定的不一致以满足快速响应的需求,但同时也采用一些恢复的进程来处理这种错误。

当业务上处理强一致性消耗比处理错误的消耗少时,这种付出是值的的。



基础设施自动化

基础设施自动化技术在过去几年中得到了长足的发展:云计算,特别是AWS的发展,减少了构建、发布、运维微服务的复杂性。

许多使用微服务架构的产品或者系统,它们的团队拥有丰富的持集部署以及它的前任持续集成的经验。

团队使用这种方式构建软件致使更广泛的依赖基础设施自动化技术。

尽管这不是介绍自动部署的文章,但我们也打算介绍一下它的主要特征。

我们希望我们的软件应该这样方便的工作,因此我们需要更多的自动化测试。

流程中工作的软件改进意味着我们能自动的部署到各种新的环境中。

整体风格的应用相当开心的在各种环境中构建、测试、发布。

事实证明,一旦你打算投资一条整体架构应用自动化的的生产线,那么你会发现发布更多的应用似乎非不那么的可怕。

记住,CD(持续部署)的一个目标在于让发布变得无趣,因此无论是一个还是三个应用,它都一样的无趣。

另一个方面,我们发现使用微服务的团队更加依赖于基础设施的自动化。

相比之下,在整体架构也微服务架构中,尽管发布的场景不同,但发布工作的无趣并没有多大的区别。



容错性设计

使用服务作为组件的一个结果在于应用需要有能容忍服务的故障的设计。

任务服务可能因为供应商的不可靠而故障,客户端需要尽可能的优化这种场景的响应。

跟整体构架相比,这是一个缺点,因为它带来的额外的复杂性。

这将让微服务团队时刻的想到服务故障的情况下用户的体验。


Netflix 的Simian Army可以为每个应用的服务及数据中心提供日常故障检测和恢复。

这种产品中的自动化测试可以让大部分的运维团队正常的上下班。

这并不意味着整体构架的应用没有这么精巧的监控配置,只是在我们的经验中它并不常见。

由于服务可以随时故障,快速故障检测,乃至,自动恢复变更非常重要。

微服务应用把实时的监控放在应用的各个阶段中,检测构架元素(每秒数据库的接收的请求数)和业务相关的指标(把分钟接收的定单数)。

监控系统可以提供一种早期故障告警系统,让开发团队跟进并调查。

对于微服务框架来说,这相当重要,因为微服务相互的通信可能导致紧急意外行为。

许多专家车称赞这种紧急事件的价值,但事实是这种紧急行为有时是灾难。

监控是至关重要的,它能快速发现这种紧急不良行为,让我们迅速修复它。


整体架构,跟微服务一样,在构建时是通明的,实情上,它们就是这样子的。

它们不同之处在于,你需要清楚的认识到不同进程间运行的服务是不相关的。

库对于同一进程是透明的,也因此不那么重要了。

微服务团队期望清楚的监控和记录每个服务的配置,比如使用仪表盘显示上/下线状态、各种运维和业务相关的指标。

对断路器(circuit breaker)状态、目前的吞吐量和时延细节,我们也会经常遇到。



设计改进

微服务实践者,通常有不断改进设计的背景,他们把服务分解成进一步的工具。

这些工具可以让应用开发者在不改变速度情况下,控制都他们的应用的需求变更。

变更控制不意味首减少变更,而是使用适当的方式和工具,让它更加频繁,至少,很好让它变得可控。


不论如何,当你试图软件系统拆分成组件时,你将面临着如何拆分的问题。

那么我们的决定拆分我们应用的原则是什么呢?

首要的因素,组件可以被独立替换和更新的,这意味着我们寻找的关键在于,我们要想象着重写一个组件而不影响它们之前的协作关系。

事实上,许多的微服务小组给它进一步的预期:服务应该能够报废的,而不是要长久的发展的。


Guardian网站就是这方面的一个优秀的例子,它初期被设计和构建成一个整体架构,但它已经向微服务的发展了。

整体构架仍然是它网站的核心,但是他们使用微服务来增加那些使用整体架构API的新特性。

这种方法增加这些临时的特性非常方便,比如运动新闻的特稿。

这样站点的一个部分可以使用快速的开发语言迅速整合起来,当它过时后可以一次性移除。

我们发现一家金融机构用相似的方法增加新的市场营销活动,数周或者几个月后把它撤销。

可代替是模块化开发中的一个特例,它是用模块来应对需要变更的。

你希望让变更是相同模块,相同周期中进行变化而已。

系统的某些很小做变更部分,也应该放在不同的服务中,这样它们更容易让它们消亡。

如果你发现两个服务一直重复的变更时,这就是一个要合并它们的信号了。


把组件改成服务,增加了细化发布计划的一个机会。

整体构架的任务变更需要整个应用的完整的构建和发布。

然而,使用微服务,你只需要发布你要修改的服务就可以了,这将简化和加速你的发布周期。

缺点是你需要为一个变更服务发布可能中断用户的体验而担心。

传统的集成方法是使用版本来处理这些问题,但是微服务版本仅是最后的通告手段

我们需要在设计服务时尽可能的容忍供应商的变更,以避免提供多个版本。

posted @ 2022-10-12 17:02  克峰同学  阅读(125)  评论(1编辑  收藏  举报