进一步深究和分析设计和运行微服务系统的代价和复杂度,微服务并不是唯一通过分解和分布式实现涅槃的架构模式,但过去的一些尝试如SOA已经被认为是不成功的,现在提到微服务架构,也极大的增加了系统中运行模块的数量,在将功能和数据所有权分发到多个自治的服务上的同时,开发者也将整个应用的稳定性和安全操作的责任分配到了这些服务上,纵观全局大体有几个挑战:
- 识别和划定微服务范围需要大量专业的业务领域知识
- 正确识别服务间的边界和契约式很困难的,而一旦确定又很难进行改动
- 微服务是分布式系统,所以需要对状态、一致性和网络可靠性这些内容做不同的假设
- 跨网络分布式系统组件以及不断增长的技术差异性,会导致微服务出现新的故障形式
- 越来越难以了解和验证在正常运行过程中会发生什么事情
设计挑战
首当其冲的便是自治性,为了让服务实现自治,在设计上需要让服务从整体上开始松耦合的,而单独看每个服务,它内部封装了高度内聚的功能单元,这是一个不断演进的过程,服务的功能范围可能会随着时间而发生变化,开发者未来也可能会经常从现有的甚至可能将要下线的服务中剥离出新的功能来
划定微服务范围需要业务领域知识
每个微服务只负责一个功能,识别这些功能是需要业务领域知识的,在应用生命周期的初始阶段大部分开发者对业务领域知识知之甚少,甚至漠不关心,最糟糕的情况是认知是错误的
对问题领域的错误认知会导致错误的设计决策,和单体应用中的模块相比,微服务应用的服务边界更加僵化,这就意味着如果范围划定出错,可能给下游造成更到的代价
除了图中所示的重构外,还可能没有发现服务间的隐式依赖,导致在部署阶段出现错误或者不兼容的问题
服务契约的维护
每个微服务都应该独立于其他服务实现,这样才能实现技术的多样性和自治性,为了做到这一点,每个微服务都应该对外暴露一个契约(相当于面向对象设计中的接口),用于定义它期望接受和返回的消息
一个良好的契约应该具备如下几个特点:
- 完整:定义了交互所需的全部内容
- 简洁:除了必需的信息,没有多余的内容,这样消费者就能在合理的范围内组装消息
- 可预测:准确反映了所有实现的真实表现
实际上做到并不容易,随着时间的推移,开发者需要对契约逐步调整,但仍要保持向后兼容,稳定性和变化之间的矛盾很难把握
微服务应用是多个团队设计的
在规模比较大的组织中,微服务通常是由多个团队开发和运行的,而每个团队有自己的目标、工作方式和交付周期,因此协调任何庞大的微服务应用开发,都需要跨多个团队在优先级和时间层面达成一致
微服务应用是分布式系统
设计微服务系统也就意味着设计的是分布式系统,分布式系统就不得不考虑网络可靠性、网络延迟、带宽,数据传输成本等,在非分布式系统中做出的假设都不在适用了,开发者必须考虑延迟性、可靠性、以及应用中的状态一致性
一旦应用成为分布式,应用的状态数据就分布在了不同的地方,开发者不再能够保证操作的顺序,在多个服务上操作时,开发者也不再能像ACID那样继续保证事务,同时还会影响到应用层面的设计,开发者需要考虑服务如何在不一致的状态下进行操作以及如何在事务失败的情况下进行回滚
运维挑战
微服务方案本身会使系统中可能出现的故障点增多
微服务不能消除风险,而是将成本转移到了系统生命周期的后半阶段,降低了开发过程中的冲突,但增加了运维阶段系统部署、验证以及监控的复杂度
微服务的在实操中应该采用渐进式的方法,开发者可以在不修改现有服务的情况下,独立开发新的功能,从而将变更的代价和风险降到最低
但是在不断渐进的解耦系统中,要清楚地了解整体情况会越来越困难,这使得问题的诊断和支持变得更具挑战性,当出现故障时,开发者需要通过一系列的方式来跟踪系统到底发生了什么(调用了哪个服务,顺序是什么,输出是什么),同时还需要知道系统应该发生
难以实现的可观测性
这就是透明性的重要,但在微服务应用中,透明性会变得越来越困难,整个微服务系统是多个团队开发的,而开发者需要将许许多多的碎片拼接起来形成整体的蓝图,所以需要将每个服务所产生的数据关联并链接到一起,进而了解交付商业价值的整体的来龙去脉之后理解每个服务所做的事情
- 每个服务的日志提供了系统运行的部分视图,但开发者需要同时从微观细节和宏观整体两方面来更加全面的的理解系统
- 同时仍旧需要根据选择的部署方式,观测基础设施指标和微服务之间的关系,这些指标仍旧非常有用
不断增加的微服务使得故障点增多
如果说:”任何可能出现故障的东西最终肯定会出现故障“,这并不是悲观反而是非常重要,如果开发者提前认定构成系统的这些微服务是有缺陷或者脆弱的,那么就能够更好的提醒自己如何对系统进行设计、部署和监控,开发者需要考虑如何让系统在单个组件出问题的情况下继续运行,这意味着每个服务都需要更具鲁棒性(要考虑到错误勘察、故障切换和恢复),同样整个系统应该运行更加可靠,即便单个组件做不到100%可靠