关键词:轻量、自治、
微服务是一些协同工作的小而自治到服务。
特点:小、自治。
功能迭代带来的问题:随着新功能的增加,代码库会越变越大。时间久了代码库会非常庞大,以至于想要知道该在什么地方做修改都很困难。
解决方法是:在一个单块系统内,通常会创建一些抽象层或者模块来保证代码的内聚性,从而避免上述问题。微服务将这个理念应用在独立的服务上。根据业务的边界来确定服务的边界,这样就很容易确定某个功能代码应该放在哪里。而且由于该服务专注于某个边界之内,因此可以很好地避免由于代码库过大衍生出的很多相关问题。
仓库大小:足够小即可,不要过小。
一个微服务就是一个独立的实体。
服务之间均通过网络调用进行通信,从而加强了服务之间的隔离性,避免紧耦合。
微服务有很多不同的好处,其中很多好处也适用于任何一个分布式系统。但相对于分布式系统或者面向服务的架构而言,微服务要更胜一筹,它会把这些好处推向极致。
在一个由多个服务相互协作的系统中,可以在不同的服务中使用最适合该服务的技术。
一个组件不可用不会导致级联故障。
更方便对有性能瓶颈的部分服务进行扩展。
服务独立部署,能降低部署风险,以及快速回滚。
小型代码库上工作的小团队更加高效。
易于重用已有功能,并有多种方法使用同一功能。
方便重写、删除服务
SOA (Service-Oriented Architecture,面向服务的架构)是一种设计方法,其中包含多个服务,而服务之间通过配合最终会提供一系列功能。一个服务通常以独立的形式存在于操作系统进程中。服务之间通过网络调用,而非采用进程内调用的方式进行通信。
SOA优点:可以用来应对臃肿的单块应用程序,从而提高软件的可重用性。
基于微服务的架构主要有两个优势:较小的颗粒度、解决问题的方法上能有更多选择。那其他的分解技术是否有相应的好处呢?
优点:可重用
缺点:无法技术异构性;可扩展性差;无法独立部署和变更
优点:支持热更新
缺点:无法技术异构性;耦合
微服务不是免费的午餐,更不是银弹。需要面对分布式系统面对的复杂性。
确保团队有共同的技术愿景,以帮助我们向客户交付他们想要的系统。
架构师类似城市规划师,优化城镇布局,使其更易于现有居民生活,同时也会考虑些未来的因素。
将架构师比作城市规划师,那在比喻中,区域的概念对应是什么呢?
它们应该是我们的服务边界,或者是一些粗粒度的服务群组。
基于要达到的目标去定义一些原则和实践对做设计来说非常有好处。接下来让我们对它们做一些讨论。
战略目标关心的是公司的走向以及如何才能让自己的客户满意。例如:“开拓东南亚的新市场”或者“让用户尽量使用自助服务”。
为了和更大的目标保持一致,我们会制定一些具体的规则,并称之为原则。
注意:
我们通过相应的实践来保证原则能够得到实施,这些实践能够指导我们如何完成任务。
特点:
对应一些东西可以理解为原则也可以理解为实践,关键是要有一些重要的原则来指导系统的演化,同时也要有一些细节来指导如何实现这些原则。
在考虑取舍时,需要注意一个很重要的因素:系统允许多少可变性。
能够清晰地描绘出跨服务系统的健康状态非常关键。这必须在系统级别而非单个服务级别进行考虑。
选用少数几种明确的接口技术有助于新消费者的集成。
所以必须保证每个服务都可以应对下游服务的错误。
怎么更好履行上面的要求标准
架构师提供温和的指导,团队自行决定然后偿还技术债务
维护一个债务列表,并且定期回顾
如果系统偏离了这些指导又会发生什么呢?
架构师的部分职责是治理,那么什么是治理?
治理是通过评估需求、当前情况、下一步可能性确保企业目标达成,通过排优先级和做决策来设定方向。对已达成一致的方向和目标进行监督。如果说,架构师的一个职责是确保有一个技术愿景,那么治理就是要确保我们构建的系统符合这个愿景,并在需要的时候对愿景进行演化。
能够独立修改及部署单个服务而不需要修改系统的其他部分。
把相关的行为聚集在一起,把不相关的行为放在别处。
限界上下文:任何一个给定的领域都包含多个限界上下文,每个限界上下文中的东西分成两部分,一部分不需要与外部通信,另一部分则需要。每个上下文都有明确的接口,该接口决定了它会暴露哪些模型给其他的上下文。
限界上下文:一个由显式边界限定的特定职责。
不应该从共享数据角度考虑划分;
应该从上下文能够提供的业务功能来考虑;
一开始你会识别出一些粗粒度的限界上下文,而这些限界上下文可能又包含一些嵌套的限界上下文。
方式
选型方法
组织架构和软件架构一致。如果拆分出细粒度的限界上下文都属于一个团队管理,嵌套结构更加合理。反之,拆分出细粒度的限界上下文属于不同团队管理,完全分离结构更加合理;
嵌套结构方便下游消费者整体打桩测试;
某个功能所要做的修改,应该更多的局限在一个单独的微服务边界之内,微服务之间如何就同一个业务概念进行通信需要考虑的点:
按照技术接缝对服务边界进行建模也并不总是错误的。比如,我见过当一个组织想要达到某个性能目标时,这种划分方式反而更合理。然而一般来讲,这不应该成为你考虑的首要方式。
集成把各个微服务、组件、外部服务全部关联起来,成为一个整体系统。
一个服务获取另一个服务信息,通过直接访问数据库;修改数据也是通过直接在数据库中修改。
微服务之间如何通信呢?
####4.1 通信模式
两种通信模式有着各自的协作风格,分为请求/响应方式 和 基于事件方式。
客户端发起一个请求,然后等待响应,通信方式:
发布事件,协作者收到消息自行决定处理方式。业务逻辑并非集中一处,而是分布在不同协作者中。
赖于某个中心大脑来指导并驱动整个流程。
仅仅会告知系统中各个部分各自的职责,而把具体怎么做的细节留给它们自己。
远程过程调用允许你进行一个本地调用,但事实上结果是由某个远程服务器产生的。RPC的主要卖点之一:易于使用。
避免技术耦合。有些RPC机制(如Java RMI)与特定的平台紧密绑定,对服务端和客户端的技术选型造成一定限制。
本地调用和远程调用并不相同,避免隐藏过头。隐藏过头会让开发人员会在不知道时远程调用情况下对其进行调用,以至于把网络因素忽略起来。
反脆弱性,服务方新增字段,不使用该字段的客户端也需要修改。
REST是受Web启发而产生的一种架构风格。REST风格包含了很多原则和限制,REST是RPC的一种替代方案。REST中最主要的概念是资源。
主要有两个部分需要考虑:微服务发布事件机制和消费者接收事件机制。
原则:尽量让中间件保持简单,而把业务逻辑放在自己的服务中。
事件驱动的系统看起来耦合非常低,而且伸缩性很好。但是这种编程风格也会带来一定的复杂性,这种复杂性并不仅仅包括对消息的发布订阅操作。
事件驱动架构和异步编程会带来一定的复杂性,所以我通常会很谨慎地选用这种技术。你需要确保各个流程有很好的监控机制,并考虑使用关联ID,这种机制可以帮助你对跨进程的请求进行追踪。
一般观念,如果代码在很多系统上都有重复实现,防止遗漏修改,一般都把重复性代码抽取出来成为共享库。但是在微服务不一定适合。微服务中重用会导致微服务和消费者之间的过渡耦合。
作者经验:在微服务内部不要违反DRY,但在跨服务的情况下可以适当违反DRY。服务之间引入大量的耦合会比重复代码带来更糟糕的问题。
微服务应该包含核心领域实体(比如客户)全生命周期的相关操作。从消息产生到消费期间数据可能发生变更,资源应该是使用引用以便于查询。
原则:应该在不确定数据是否能够保持有效的情况下,谨慎地进行处理。
避免过早地将客户端和服务端紧密绑定起来。
及早发现会对消费者产生破坏的修改非常重要,因为即使使用最好的技术,也难以避免破坏性修改的出现。
使用消费者驱动的契约来及早定位这些问题。
同时运行不同版本的服务,然后把老用户路由到老版本的服务,而新用户可以看到新版本的服务。
短期内同时使用两个版本的服务是合理的,尤其是当你做蓝绿部署或者金丝雀发布时。在这些情况下,不同版本的服务可能只会共存几分钟或者几个小时,而且一般只会有两个版本。升级消费者到新版本的时间越长,就越应该考虑在同一个微服务中暴露两套API的做法。
从接缝处可以抽取出相对独立的一部分代码,对这部分代码进行修改不会影响系统的其他部分。识别出接缝不仅仅能够清理代码库,更重要的是,这些被识别出的接缝可以成为服务的边界。
限界上下文就是一个非常好的接缝,因为它的定义就是组织内高内聚和低耦合的边界。所以第一步是开始识别出代码中的这些边界。
服务分解之后,各模块之间依赖可能存在依赖,而数据库数所有杂乱依赖的源头。
4.1 方法
找到数据库中的接缝,按照接缝进行分离。
先分离数据库表结构,暂时不对服务进行分离。好处是:可以随时选择回退这些修改或是继续做,而不影响服务的任何消费者;可能会带来的影响:访问次数可能会增多,破坏事务完整性。
保证一些事件要么都发生,要么都不发生。使用单块表结构时,所有的创建或者更新操作都可以在一个事务边界内完成。分离数据库之后,这种好处就没有了。我们有两种选择:
分布式事务背景:
手动编配补偿事务非常难以操作,一种替代方案是使用分布式事务。
分布式事务特点:
分布式事务会横跨多个事务,然后使用一个叫作事务管理器的工具来统一编配其他底层系统中运行的事务。就像普通的事务一样,一个分布式的事务会保证整个系统处于一致的状态。唯一不同的是,这里的事务会运行在不同系统的不同进程中,通常它们之间使用网络进行通信。
分布式事务算法:
常用的算法是两阶段提交。在这种方式中,首先是投票阶段。在这个阶段,每个参与者会告诉事务管理器它是否应该继续。如果事务管理器收到的所有投票都是成功,则会告知它们进行提交操作。只要收到一个否定的投票,事务管理器就会让所有的参与者回退。
两阶段提交提交算法问题:
扩展阅读——还有别的分布式事务吗?
除了两阶段提交(2PC)方案还有:
在对服务进行分离的同时,可能也需要对数据存储进行分离。但是就会在进行一个很常见的操作时出问题,这个操作就是报告。
报告通常需要来自组织内各个部分的数据来生成有用的输出。
存在的问题:
通过服务调用有些依赖第三方工具获取,不同的微服务暴露的API不一定能够很好地适用于报告这个场景。
解决方法:
对于某些服务暴露的资源来说,可以通过添加一些缓存头来加快数据的获取速度,还可以把这些数据缓存在反向代理之类的地方。但是报告天然就允许用户访问不同时期的历史数据,这意味着,如果用户访问的资源是别人没有访问过的(或者在很长一段时间内没有人访问),则缓存无法命中。
每个微服务可以在其管理的实体发生状态改变时发送一些事件。
实例:
我们的客户服务可能会在客户增删改时发送一些事件。对于这些暴露事件聚合(feed)的微服务来说,可以编写自己的事件订阅器把数据导出到报告数据库中。
优点:
与源微服务底层数据库之间的耦合就被消除掉了。我们只需要绑定到服务所发送的事件即可,而设计这些事件,本来就是用来暴露给外部消费者的。
缺点:
所有需要的信息都必须以事件的形式广播出去,所以在数据量比较大时,不容易像数据导出方式那样直接在数据库级别进行扩展。
建议:
考虑这种方式,因为它能够带来低耦合及更好的数据时效性。
大服务是怎么产生的呢?第一件需要理解的事情是,服务一定会慢慢变大,直至大到需要拆分。我们希望系统的架构随着时间的推移增量地进行变化。关键是要在拆分这件事情变得太过昂贵之前,意识到你需要做这个拆分。
CI (Continuous Integration,持续集成)能够保证新提交的代码与已有代码进行集成,从而让所有人保持同步。CI服务器会检
优点:
当持续集成遇上微服务时,需要考虑如何把CI的构建和每个微服务映射起来。所以如何在微服务、CI构建及源代码三者之间,建立起合适的映射呢?
CD (Continuous Delivery,持续交付),到了把一个构建分成多个阶段是很有价值的。将构建分解成为多个阶段,从而得到我们熟知的构建流水线。在第一个阶段运行快速测试,在第二个阶段运行耗时测试。
在微服务的世界,我们想要保证服务之间可以独立于彼此进行部署,所以每个服务都有自己独立的CI。在流水线中,构建物会沿着上线方向进行移动。
大多数技术栈都有相应的构建物类型,同时也有相关的工具来创建和安装这些构建物。不同技术栈生成的构建物各不相同,所以混合不同的构建物进行部署就会很复杂。
有一种方法可以避免多种技术栈下的构建物所带来的问题,那就是使用操作系统支持的构建物。
优点:
缺点:
建议:
尽量减少需要维护的操作系统的数量,最好只维护一种。
优点:
缺点:
服务需要一些配置。理想情况下,这些配置的工作量应该很小,而且仅仅局限于环境间的不同之处。如果配置较多需要使用配置中心。
存在的挑战:
1.监控会变得更加困难;
2. 不利于团队的自治;
3. 会限制部署构建物的选择;
把不同的服务放在同一个容器中,再把容器放置到单台主机上的模式
优点:
缺点:
优点:
缺点:
Docker是构建在轻量级容器之上的平台。
不管用于部署的底层平台和构建物是什么,使用统一接口来部署给定的服务都是一个很关键的实践。