`
QING____
  • 浏览: 2233456 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

《微服务设计》随心摘要备忘

 
阅读更多

第一章 微服务

1、随着领域驱动设计、持续交付(CD)、按虚拟化、基础设计自动化、小型自治团队、大型集群系统这些实践的流行,微服务应用而生。它并不是被发明出来的,而是从现实世界中总结出来的一种趋势或模式。

 

2、什么是微服务?微服务就是一些协同工作的小而自治的服务。

    很小、专注做好一件事:单一职责原则,“把因相同原因而变化的东西聚合在一起,而把因不同原因而变化的东西分离开来”。

    微服务将这个理念应用在独立的服务上。根据业务的边界来确定服务的边界,这样很容易确定某个功能代码应该放在哪里。而且由于该服务专注于某个边界之内,因此可以很好的避免由于代码库过大衍生出的很多相关问题。

    自治性:一个微服务就是一个独立的实体,它可以独立地部署在PAAS上,也可以作为一个操作系统进程存在。服务会暴露出API,然后服务之间通过这些API进行通信。API的实现技术应该避免与消费方耦合,这就意味着应该选择与具体技术不相关的API实现方式,以保证技术的选择不被限制。

 

3、微服务的好处:

    技术异构性:在一个由多个服务相互协作的系统中,可以在不同的服务中使用最适合该服务的技术。

 

    弹性:弹性工程学的一个关键改变就是“舱壁”;服务的边界就是一个很显眼的舱壁,对于单块服务的系统而言,可以通过将相同的实例运行在不同的机器上来降低功能完全不可用的概率,然而微服务系统本身就能够很好地处理服务不可用和功能降级问题。

 

    扩展:庞大的单块服务职能作为一个整体进行扩展。即使系统中只有一小部分存在性能问题,也需要对整个服务进行扩展。如果使用较小的服务,则可以只对需要扩展的服务进行扩展,这样可以把那些不需要扩展的服务运行在更小的、性能稍差的硬件上。

 

    简化部署:对于单体系统,即使只修改一行代码,也需要重新部署整个应用。这种部署影响很大、风险很高,因此相关干系人不敢轻易部署。部署的频率变低,这意味着两次发布之间功能差异就会很大、出错的概率更高。在微服务架构中,各个服务是独立部署的,这样可以更快地对特定部分的代码进行部署。如果真的出问题,也会会影响一个服务,并且容易快速回滚,这也意味着可以可以更快地使用我们开发的新特性。

 

    SOA面向服务架构是一种设计方案,其中包含多个服务,而服务之间通过配合最终会提供一系列功能。一个服务通常以独立的形式存在于操作系统进程中。服务之间通过网络调用,而非采用进程内调用的方法进行通信。

    你可以认为微服务架构是SOA的一种特定方法。

 

    微服务不是免费的午餐,根不是银弹,如果你想要得到一条通用准则,那么微服务是一个错误的选择。你需要面对所有分布式系统需要面对的复杂性。每个公司、组织和系统都不一样,微服务是否适合你,或者说你能够多大程度上采用微服务,取决于很多因素。

 

 

 

第二章 演化式架构

 

    架构师的一个重要职责是,确保团队有共同的技术愿景,以帮助我们向客户交付他们想要的系统。在某些场景下,架构师只需要和一个团队一起工作,这时他们等同于技术引领者。在其他情况下,他们要对整个项目的技术愿景负责,通常需要协调多个团队之间,甚至是真个组织内的工作。不管出于哪个层次,架构师这个角色都很微妙。在一般的组织中,非常出色的开发人员才能成为架构师,但是通常会比其他角色招致更多的批评。相比其他角色而言,架构师对多个方面都有更加直接的影响,比如所构建系统的质量、同事的工作条件、组织应对变化的能力等。这个角色很难做好,原因何在呢?

 

    我们发现,有一个角色可以更好地跟IT架构师相类比,这个想法是Erik Doenenburg告诉我的,他认为更好的类比是城市规划师,而不是建筑师。如果你玩过SimCity,那么你应该很熟悉城市规划师这个角色。城市规划师的职责是优化城镇布局,使其更易于现在居民生活,同事也会考虑一些未来的因素。为了达到这个目的,他需要收集各种各样的信息。规划师影响城市演化的方法很有趣,他不会直接说“在那个地方盖一栋这样的楼”,相反它会对城市进行分区。就像在SimCity中一样,你可能会把城市的某一部分规划成为工业区,另一部分规划成居民区,然后其他人会自己决定具体要盖什么建筑物。当然这个决定会受到一定的约束,比如工厂一定要盖在工业区。城市规划师更多考虑的是任何公共设施如何从一个区域移到另一个区域,而不是具体在每个区域中发生的事情。

    城市规划师应该尽量去预期可能发生的变化,但是也需要明白一个事实:尝试直接对各个方面进行控制往往不会奏效。

 

    未来的变化很难预见,所以与其对所有变化的可能性进行预测,不如做一个允许变化的计划。为此,应该避免对所有事情做出过于详尽的设计。城市这个系统应该让生活在其中的住户感到开心。借鉴Frank Buschmann的一个说法:架构师的职责之一就是保证该系统适合开发人员在其上工作。

 

    所以我们的架构师应该像城市规划师那样专注大方向上,只在很有限的情况下参与到非常具体的细节实现中来。他们需要保证系统不但能够满足当前的需求,还能够应对将来的变化。

 

    作为架构师,不应该过多的关注每个区域内发生的事情,而应该多关注区域之间的事情。这意味着我们应该考虑不同的服务之间如何交互、或者说保证我们能够对整个系统的健康状态进行监控。

 

    每一个服务内部可以允许团队自己选择不同的技术栈或者数据存储技术,当然其他的问题也需要考虑。但是事实上也不会无限制地允许团队选择任意技术栈,比如如果需要支持10种不同技术栈的话,可能在招聘上遇到困难,或者很难在不同的团队之间交换人员。

 

    “担心服务之间的交互,而不需要过于关注各个服务内部发生的事情”。

 

    有时候我们无法完全遵守技术愿景,比如为了发布一些紧急的特性,你可能会忽略一些约束,其实这仅仅是另一个需要做的取舍而已。我们的技术愿景有其本身的道理,所以偏离这个愿景短期肯能会带来利益,但是长期来看是要付出代价的。可以使用技术赵武的概念来帮助我们理解这个取舍,就像在真实世界中欠的赵武需要偿还一样,积累的技术债务也是如此。

 

    不光走捷径会引入技术债务,有时候系统的目标发生变化,并且与现有的实现不服,这种情况也会引入技术债务。

 

    架构师的职责就是从更高的层次出发,理解如何做权衡。理解债务的层次以及其对系统的影响非常重要。对于某些组织来说,架构师应该能够提供一些温和的指导,然后让团队自行决定如何偿还这些技术债务。而其他的组织就需要更加结构化的方式,比如维护一个债务列表,并且定期回顾。

 

    

    现实中的情况是多种多样的,但我个人非常支持使用拥有更好自治性的微服务团队,他们有更大的自由度来解决问题。如果你所在的组织对开发人员有非常多的限制,那么微服务可能并不是适合你。

 

    “治理通过评估干系人的需求、当前情况及下一步的可能性来确保企业目标的达成,通过排优先级和做决策来设定方向。对于已经达成一致的方向和目标进行监督”。--COBIT 5

 

     我坚定地相信,伟大的软件来自伟大的人。所以如果你只担心技术问题,那么恐怕你看到的问题远远不及一半。

 

第三章 如何建模服务

 

    什么样的服务是好服务?设计原则:松耦合、高内聚。

    

    当你在思考组织内的限界上下文时,不应该从共享数据的角度来考虑,而应该从这些上下文能够提供的功能来考虑。

    如果能把系统分解成为限界上下文来表示领域的话,那么对于某个功能所做的修改,就更倾向于局限在一个单独的微服务边界之外,这样就减小了修改的范围,并能够更快的进行部署。

 

 第四章 集成

 

    因为我认为,保证微服务之间通信方式的技术无关性是非常重要的。这意味着,不应该选择那种对微服务的具体实现技术有限制的集成方式。

 

    不要对远程调用过度抽象,以至于网络因素完全被隐藏起来;确保你可以独立地升级服务端的接口而不用强迫客户端升级,所以在编写客户端代码时要注意这方面的平衡。

 

    在微服务内部不要违反DRY,但在跨服务的情况下可以适当违反DRY。服务之间引入大量的耦合会比重复代码带来更糟糕的问题。

 

    客户端尽可能灵活地消费服务响应这一点符合Postel法则(也叫鲁棒法则),该法则认为,系统中的每个模块都应该“严进宽出”,即对自己发送的东西要严格,对接收的东西要宽容。这个原则最初的上下文时网络设备之间的交互,因为在这个场景中,所有奇怪的事情都有可能发生。在请求/响应的场景下,该原则可以帮助我们在服务发生改变时,减少消费方的修改。

 

    1)如果已经做了可以做的所有事情来避免对接口的修改(但还是无法避免),那么下一步的任务就是限制其影响。我们不想强迫客户端跟随服务端一起升级,因为希望微服务可以独立于彼此进行发布。我用过的一种比较成功的方法是,在同一个服务上使新接口和老接口同时存在。所以在发布一个破坏性修改时,可以部署一个同事包含新老接口的版本。(当老接口不再有人使用时,删除该接口以及相关代码即可)

 

    2)另一种经常被提起的版本管理的方法是,同时运行不同版本的服务,然后把老用户路由到老版本的服务,而新用户可以看到新版本的服务。短期内同时使用两个版本的服务是合理的,尤其是当你做蓝绿部署或者金丝雀发布时。在这些情况下,不同版本的服务可能只会共存几分钟或者几个小时,而且一般只会有两个版本。升级消费者到新版本的时间越长,就越应该考虑在同一个微服务中保留两套API的做法。    

 

第五章  分解单块系统

 

    处理分布式事务常用的算法是两阶段提交。在这种方式中,首先是投票。在这个阶段,每个参与者会(cohort)告诉事务管理器它是否应该继续。如果事务管理器收到的所有投片都是成功,则会告知它们进行提交操作。只要收到一个否定的投票,事务管理器就会让所有的参与者回退。这种方式会使得所有的参与者暂定并等待中央协调进程的指令,从而容易导致系统的中断。如果事务管理器宕机了,处于等待状态的事务就永远无法完成。如果一个cohort在投票阶段发送消息失败,则所有的其他参与者都会阻塞,投票结束之后的提交也有可能会失败。该算法隐式地认为上述情况不会发生,即如果一个cohort在投票阶段投了赞成票,则它一定能提交成功。cohort需要一种机制来保证这种事情的发生。这意味着此算法并不是万无一失的,而只是尝试捕获大部分的失败场景。

 

    所有这些方案都会增加复杂性。如你所见,分布式事务很容易出错,而且不利于扩展。这种通过重试和补偿达成最终一致性的方式,会使问题定位更加困难,而且有可能需要其他的补偿措施来修复潜在数据的不一致。

    如果现在有一个业务操作发生在跨系统的单个事务中,那么问问自己是否真的需要这么做。是否可以简单的把它们放到不同的本地事务中,然后依赖于最终一致性的概念?这种系统的构建和扩展都会比较容易。如果你遇到的场景确实需要保持一致性,那么尽量避免把它们放在不同的地方,一定要尽量这么做。如果实在不行,那么避免仅仅从纯技术(比如数据库事务)的角度考虑,而是显式地创建一个概念来表示这个事务。你可以把这个概念当做一个句柄或者钩子,在此之上,能够相对容易地进行类似的补偿事务这样的操作,这也是在系统中监控这些复杂概念的一种方式。

 

 

第十一章 规模化微服务

 

    如果等待太长时间来决定调用失败,整个系统会被拖慢。如果超时时间太短,你会将一个可能还在正常工作的调用错误认为是失败的。如果完全没有超时,一个宕掉的下游系统可能会让整个系统挂起。(断路器、舱壁隔离方案)

    舱壁:我们应该为每个下游服务的连接使用不同的连接池。我们可以把断路器看作一种密封一个舱壁的自动机制,它不仅保护消费者免受下游服务问题的影响,同时也使下游服务避免更多的调用,以防止可能产生的不利影响。例如,Hystrix允许你在一定条件下,实现拒绝请求的舱壁,以避免资源达到饱和,这被称为减载(load shedding)。有时候拒绝请求是避免重要系统变得不堪重负或者称为多个上游服务瓶颈的最佳方法。

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics