15158846557 在线咨询 在线咨询
15158846557 在线咨询
所在位置: 首页 > 营销资讯 > 网站运营 > 微服务架构深度解析与最佳实践

微服务架构深度解析与最佳实践

时间:2022-08-11 14:42:01 | 来源:网站运营

时间:2022-08-11 14:42:01 来源:网站运营

微服务架构的概念,现在对于大家应该都不陌生,无论使用 Apache Dubbo、还是 Spring Cloud,都可以去尝试微服务,把复杂而庞大的业务系统拆分成一些更小粒度且独立部署的 Rest 服务。但是这个过程,具体应该怎么做?现有的条件下到底要不要做微服务?服务拆分成什么粒度才是合适的?遗留的老系统需要如何考虑重构改造?有哪些坑需要我们注意?系统怎么在分布式服务下实现数据的一致性和服务的高可用可伸缩?拆分的过程中系统数量增多,测试、部署、运维、监控,又应该如何处理?

(个人微信号:Robynn-D , 欢迎交流)本文将从这些问题的深度分析出发,阐述微服务架构落地的一些设计原则和利弊取舍,结合微服务架构过程的很多最佳实践经验,希望给读者带来一定的启发和思考,避免在实际应用过程中走弯路,能够多快好省的落地实现微服务架构。内容涉及:

  1. 微服务架构的发展过程简介
  2. 微服务架构的特点与常见特性
  3. 微服务架构的常见技术与简单示例
  4. 微服务架构存在的一些问题
  5. 如何合理拆分微服务
  6. 遗留系统应该如何改造
  7. 怎么考虑拆分后的数据一致性
  8. 系统和服务的高可用可伸缩如何实现
  9. 拆分过程的测试和部署如何处理
  10. 拆分后的运维和监控如何处理

第一部分:微服务深度解析

微服务架构的发展过程简介







微服务架构发展的五个关键时间节点

特别值得一提的是,在微服务架构发展的过程中,还有两位技术大牛的影响不可忽视:

微服务架构的发展趋势




软件架构的发展趋势,简单的说可以分为这几个阶段(详细介绍可以参见我的上一个文章《软件架构发展历程分享》或者《高可用可伸缩微服务架构:基于 Dubbo、Spring Cloud 和 ServiceMesh》一书):







我们可以从 Google 的趋势图看到,从 2014 年起,微服务架构架构的热度就直线上升,成为最热门的技术之一。从国内的情况来看,一方面;另一方面,一直到现在,每年的 QCon 大会都会有微服务架构版本,跟大家分享最新的微服务架构知识和实践经验。

什么是微服务架构

说了这么多,那么到底什么是微服务和微服务架构呢?

按 Martin Fowler 和 James Lewis 的定义,微服务架构风格是通过实现一系列微小的服务的方式,开发一个独立应用系统的方法,每个服务运行在自己的进程内,通过轻量级的机制与其他服务通信,通常是 HTTP 资源 API 的方式。这些服务都是围绕业务能力来构建,通过全自动部署工具来实现独立部署。这些服务,其可以使用不同的编程语言和不同的数据存储技术,并保持最小化集中管理。

具体包含如下特征:

Chris Richardson 则简化了这种说法,认为微服务是一种架构风格,通过一组服务的方式构造系统,同时需要满足如下条件:

Peter Lawyer 说,微服务很多地方特别像是 Mike Gancarz 总结的 Unix 哲学:

微服务架构就是 Unix 哲学在分布式系统里的应用。微服务架构的哲学本质上等于 Unix 哲学里的“让程序只做好一件事”:

从这里我们可以得出一个结论,一个微服务架构的系统需要满足一系列的条件和原则,而不仅仅是说使用了某个微服务的技术框架就是微服务架构。微服务这个词目前也过于流行以致于有些泛滥了。很多技术组件或者框架,例如可以用来暴露服务,可以用来自动化部署,可以用来管理配置等等,它们都可以用来作为微服务架构设计时的某个组成部分。但是单独用一个,并不代表我们实现了微服务设计,而只能说明我们借鉴了一些微服务的思想。另一方面,我们在做微服务的时候,也不用把市面上所有的微服务组件都拿来用。就像是我们写个业务系统不会用到 JDK 的所有 API,画一幅画之前我们买了一盒 24 支的水彩笔,实际上我们可能做一幅作品最终只用到了 5-6 个颜色的水彩笔。

微服务架构的特点、优势和常见技术

微服务的四个特点和六个能力

现在让我们分析一下上一节里的各个技术大牛们阐述的技术观点,从设计开发、系统部署、测试运维和服务治理四个主要方面来考虑微服务架构的特点,那么这四个方面就可以总结为下图:

微服务架构首先是一个分布式的架构,其次我们要暴露和提供业务服务能力,然后我们需要考虑围绕这些业务能力的各种非功能性的能力。这些分散在各处的服务本身需要被管理起来,并且对服务的调用方透明,这样就有了服务的注册发现的功能需求。

同样地,每个服务可能部署了多台机器多个实例,所以,我们需要有路由和寻址的能力,做负载均衡,提升系统的扩展能力。有了这么多对外提供的不同服务接口,我们一样需要有一种机制对他们进行统一的接入控制,并把一些非业务的策略做到这个接入层,比如权限相关的,这就是服务网关。同时我们发现随着业务的发展和一些特定的运营活动,比如秒杀大促,流量会出现十倍以上的激增,这时候我们就需要考虑系统容量,服务间的强弱依赖关系,做服务降级、熔断,系统过载保护等措施。

以上这些由于微服务带来的复杂性,导致了应用配置、业务配置,都被散落到各处,所以分布式配置中心的需求也出现了。最后,系统分散部署以后,所有的调用都跨了进程,我们还需要有能在线上做链路跟踪,性能监控的一套技术,来协助我们时刻了解系统内部的状态和指标,让我们能够随时对系统进行分析和干预。这六种能力总结如下图:




微服务的优势

为什么我们要从单体系统走向微服务架构呢?我们先来看一个图。

这个图 x 轴是系统复杂度,y 轴是开发的生产力,我们可以看到:

也就是说微服务应用在复杂度低的情况下,生产力反而比单体系统低。在复杂度高的地方,情况恰恰相反。

随着复杂度升高,单体架构的生产力快速下降,而微服务相对平稳,所以,复杂度越高的业务系统,越适合使用微服务架构。

搞清楚了微服务架构与单体架构的生产力的区别以后,我们再来看看微服务有哪些优势,我总结了一下有以下几点:

常见的微服务技术框架

具体怎么做微服务呢?我们先看看大家最常简单见到的微服务的图:

(个人微信号:Robynn-D , 欢迎交流)在互联网出行业务中,用户通过 API 网关访问系统的乘客、司机、出行三个 REST 服务,这三个服务再通过 REST 访问计费、支付和通知三个服务。看起来很简单,也好理解,但是实际的业务系统里,设计和实现一般会是这样:







Sping Cloud 与 Apache Dubbo、Spring Cloud Alibaba

Spring Cloud 是一个较为成熟的微服务框架,定位为开发人员提供工具,以快速构建分布式系统中的某些常见模式(例如,配置管理,服务发现,断路器,智能路由,微代理,控制总线,一次性令牌,全局锁,Leader 选举,分布式会话,群集状态)。其技术栈大致如下:

可以看到很多组件都是由 Netflix 贡献的。

而 Apache Dubbo 定位是一款高性能、轻量级的开源 Java RPC 框架,它提供了三大核心能力:面向接口的远程方法调用,智能容错和负载均衡,以及服务自动注册和发现。所以,我们可以看到 Dubbo 提供的能力只是 Spring Cloud 的一部分子集。

同时 Dubbo 项目重新维护以后,捐献给了 Apache,项目的导师就是 Spring Cloud 的核心人员。自这时候起 Dubbo 项目就开始在适合 Spring Cloud 体系,结果就是 Alibaba 也基于自己的一系列开源组件和技术,实现了 Spring Cloud Alibaba,并顺利从 Spring Cloud 孵化器毕业。详见:https://spring.io/projects/spring-cloud-alibaba

Spring Cloud Alibaba 致力于提供微服务开发的一站式解决方案。此项目包含开发分布式应用微服务的必需组件,方便开发者通过 Spring Cloud 编程模型轻松使用这些组件来开发分布式应用服务。主要功能和开源技术栈:

可以看到,基本上功能都比较完备了。

第二部分:微服务最佳实践

微服务架构不是银弹







《管理的常识》一书里说,管理的核心是不断的解决(推进工作过程中出现的各种)问题。同样地,我认为架构的核心则是不断的解决(系统设计实现与演化过程中的各种)问题。
Fred Brooks 在《人月神话》里说,“没有银弹”,现在依然成立,微服务也并不是只有优点,没有副作用,把系统拆分了了很多部分,每一个部分简单了,但是整体的关系变复杂了。前面介绍了那么多微服务的特点和优势,以及相关技术,我们再来分析一下微服务存在的问题,进而讨论什么场景适合使用微服务,什么场景不适合,以及最佳的实践方式。

微服务架构首先是一个分布式系统架构。分布式系统的发展由来已久,但是近年来产生了理论和实现的大爆发。

究其原因:分布式系统的发展得益于廉价 pc 硬件使得堆机器成为可能,而单机的成本/容量是非线性的,所以分布式的核心是线性的水平扩展整个集群的功能,以及带来的协调机制,管理复杂度,数据和状态一致性,容错和故障恢复,容量与弹性伸缩,这些通用性的基础能力建设。

简单的翻译一下:单个机器堆资源,升级 CPU 内存加大一倍,想要增加一倍的处理能力且成本不超过一倍,不仅很难、而且不现实了。这样,我们就需要思考怎么通过进一步对于系统里不同功能的部分,拆解开,单独扩展这些能水平扩展的部分,从而在控制成本的前提下,提升整个系统的处理能力。

另外,我们现在都知道,设计系统如果对可靠性,可用性有非常高的要求,那么需要先假设上下游都是不可靠的,依赖的基础设施和网络也是不可靠的,这样就考虑分布式后的分片、复制、故障转移、灾难恢复等,分布式系统下,我们可以对系统的不同性质的节点、不同可靠性可用性要求的组件,做针对性的单独处理。

很多系统看起来是分布式的,其实是一个大单机。 很多系统看起来是微服务的,其实是一个大单体。
就像人月神话里说的,往已经延期的项目,加人手,可能会导致更延期。问题不是出现在应不应该加人,或者应不应该使用分布式上。而是实施的人,没搞清楚关键。比如系统拆成分布式或微服务,然后某个关键地方依然卡住了业务流程,可能整体还不如单机的,扩展性失效,多加机器还不如单机。

为什么?

因为如果希望用多个机器来扩展业务,最后发现各个机器上运行的程序没有划清楚边界职责,多个机器合起来并不比单个机器有更大处理能力,这就是一个大单机。

同样的,如果我们拆分了很多更小独立的服务,但是一个业务请求进来,还是在一些地方被卡住,导致有一些瓶颈使得整个拆分后并不比之前有整体的改进,那么其实是费劲的做了一个大单体系统。

所以合理的拆分微服务,使得我们能够更好的扩展系统是关键,同时如果业务简单,流量不大,不扩展也可以很好的应对,系统对于可用性和一致性要求也不是特别高,那么分布式和微服务,也不是必须的。

微服务系统适合的场景

以下几类系统,比较适合使用微服务架构,或者使用微服务架构改造:

反过来,以下几类系统,不太适合一开始就做微服务,

微服务带来的一些问题




Chris Richardson 在 http://blog.daocloud.io/microservices-1/提到了微服务的几个不足:

Peter Lawyer 有在 https://vanilla-java.github.io/2016/03/22/Micro-services-for-performance.html 中提出,微服务的几个问题:

其实可以看到,大家的想法和分析都比较一致,微服务架构带来的固有复杂性和人为复杂性如何管理:

  1. 如何合理拆分微服务,粒度如何控制:对业务按粒度和边界拆解的问题,这决定了我们的服务是不是合理,开发和维护是不是方便。核心思路是深入了解业务。
  2. 遗留系统应该如何改造,从哪儿下手,如何推动:改造遗留系统一般来说比重新做一个新系统更复杂,如何平滑的、最大代价的将老系统改造成微服务,也是一个巨大挑战。
  3. 拆分后的性能应该如何保障:性能有两个指标,关键是区分出来如何处理不同特性的数据,关注不同的指标,做好优化和选型,针对具体细节具体对待。
  4. 怎么考虑拆分后的数据一致性,分布式事务的选取和取舍:多个服务间的业务数据一致性的问题,事务是个需要重点考虑的问题,主要是考虑强一致性的分布式事务还是可以使用最终一致的弱一致性。对于一般的业务,可能使用补偿冲正类的做法,在业务允许的一定时间内数据达到一致状态即可,比如 30s,或者 10 分钟。
  5. 系统和服务的高可用可伸缩如何实现:高可用意味着系统稳定健壮,可伸缩意味着弹性,资源有效使用,如何做到无状态、不共享,是实现可高用可伸缩的关键。
  6. 拆分过程的测试和部署如何处理,怎么提升管理能力,降低风险:跨系统的协作问题、测试的问题,这对于技术能力和研发成熟度较低的团队,会带来很大麻烦。核心思路定义好业务边界、系统间接口与数据标准,提升自动化测试水平。微服务架构由于拆分粒度较细,测试是个大问题。在保持每块职责单一的同时,关键是保证接口稳定,做好自动化测试(特别是 UT 和接口的自动化)和持续集成,尽量少全流程的人工回归测试。部署单元太多导致维护成本上升的问题,要管理的点多了,问题自然复杂了,核心思路是自动化部署与运维。
  7. 拆分后的运维和监控如何处理:怎么应对系统的故障,关键是保障核心技术的指标,以及业务指标,做好预警报警,积累问题、寻找根因,控制和杜绝低级失误。

七个关键问题的应对策略

如何合理拆分微服务




当一个系统服务化的时候,就会面临一个问题:如何进行服务的划分?怎么确定服务的粒度?有没有一些可以参考的业界通用规则?

实际上服务划分的本质是对系统进行架构设计,服务的划分粒度没有绝对的过大或过小之说,不同阶段的侧重点和思考的角度也不尽相同。创业初期的团队,过分的追求微服务,为了“微”而微,反而会导致业务逻辑过于分散,技术架构过于复杂,团队基础设施搭建能力弱,进而导致忽略了快速迭代交付产品的重要性,可能错失了市场机会。所以,关于服务的划分不是对错的选择题,而是需要综合考虑各种外界的因素,所作出的一个最适合的决策,这些外界因素通常包括业务、技术债、开发、运维、测试这

五个方面:




如果没有特别强烈的大规模水平扩展需求,拆分就没有必要,反而把问题搞复杂了。进行服务化的拆分时,通常会先按照业务子系统先进行一次划分,根据业务逻辑和数据的关系划分为若干个子系统,然后再考虑子系统内部是否可以再次进行拆分。至于拆分的基本原则,我推荐:




遗留系统应该如何改造

对旧系统进行改造,可以分为几个步骤:拆分前准备阶段,设计拆分改造方案,实施拆分计划。下面是我的一些经验之谈。

1)拆分之前先梳理系统关系和接口

其中准备阶段主要是梳理清楚了依赖关系和接口,就可以思考如何来拆,第一刀切在哪儿里,即能达到快速把一个复杂单体系统变成两个更小系统的目标,又能对系统的现有业务影响最小。要尽量避免构建出一个分布式的单体应用,一个包含了一大堆互相之间紧耦合的服务,却又必须部署在一起的所谓分布式系统。没分析清楚就强行拆,可能就一不小心剪断了大动脉,立马搞出来一个 A 类大故障,后患无穷。

2)不同阶段拆分要点不同,每个阶段的关注点要聚焦

拆分本身可以分成三个阶段,核心业务和非业务部分的拆分、核心业务的调整设计、核心业务内部的拆分。这三个阶段,第一阶段将核心业务瘦身,把非核心的部分切开,减少需要处理的系统大小;第二阶段。重新按照微服务设计核心业务部分;第三阶段把核心业务部分重构设计落地。拆分的方式也有三个:代码拆分、部署拆分、数据拆分。代码直接体现了依赖关系,拆完就可以单独打包部署。但是有时候,我们可以通过控制一些提供服务的开关,使用同一份代码和打包的程序,部署多组进程,每组提供不同的服务,这就是部署拆分,比如同一份代码,我们部署了 3 组机器,A 组 5 台提供订单服务,B 组 2 台提供用户服务,C 组 2 台提供任务调度处理任务。数据拆分最复杂,涉及到代码的调整,SQL 和事务的分析和重构,数据库表的拆分甚至数据迁移,数据结构的调整和数据迁移则一般意味着需要停机维护。这三个方式,可以在适当的条件下选择先做哪个操作合适。 另外,每个阶段需要聚焦到一两个具体的目标,否则目标太多反而很难把一件事儿做通透。例如某个系统的微服务拆分,制定了如下的几个目标:

  1. 性能指标(吞吐和延迟):核心交易吞吐提升一倍以上(TPS:1000->10000),A 业务延迟降低一半(Latency:250ms->125ms),B 业务延迟降低一半(Latency:70ms->35ms)。
  2. 稳定性指标(可用性,故障恢复时间):可用性>=99.99%,A 类故障恢复时间<=15 分钟,季度次数<=1 次。
  3. 质量指标:编写完善的产品需求文档、设计文档、部署运维文档,核心交易部分代码 90%以上单测覆盖率和 100%的自动化测试用例和场景覆盖,实现可持续的性能测试基准环境和长期持续性能优化机制。
  4. 扩展性指标:完成代码、部署、运行时和数据多个维度的合理拆分,对于核心系统重构后的各块业务和交易模块、以及对应的各个数据存储,都可以随时通过增加机器资源实现伸缩扩展。
  5. 可维护性指标:建立全面完善的监控指标、特别是全链路的实时性能指标数据,覆盖所有关键业务和状态,缩短监控报警响应处置时间,配合运维团队实现容量规划和管理,出现问题时可以在一分钟内拉起系统或者回滚到上一个可用版本(启动时间<=1 分钟)。
  6. 易用性指标,通过重构实现新的 API 接口既合理又简单,极大的满足各个层面用户的使用和需要,客户满意度持续上升。
  7. 业务支持指标:对于新的业务需求功能开发,在保障质量的前提下,开发效率提升一倍,开发资源和周期降低一半。
  8. 核心人员指标:培养 10 名以上熟悉核心交易业务和新系统的一线技术人员,形成结构合理的核心研发人才梯队。
结果可想而知了,目前太多了,反而没有目标。最后第一阶段只选择了稳定性作为最重要的指标,先稳住系统,然后再在后面的阶段里选择其他指标,逐步实现各个目标。

3)快速迭代,找到突破口,持续产出

大家都知道,敏捷开发之所以流行,就是因为小步快跑,快速迭代,实现对业务变化和新需求的第一时间响应,这对快速发展变化的外部市场,以及 KPI 压力非常大的业务部门非常重要。研发团队在系统改造过程也可以通过快速的阶段性产出,来证明团队的技术能力和推进水平,增进互相的背靠背信任关系,为长期的顺畅合作打下坚实基础。这方面,很多研发团队都想试图憋大招,搞个大项目,反而慢慢失去各个利益方的耐心,最终把合作关系搞僵,吃了大亏。

4)大胆假设,小心求证,稳步上线

凡事不破不立,拆分改造过程,我们每一次改动的地方,可能有多个不同的方案和路径,具体选择哪一个最合适,这需要我们放开思路,大胆假设,充分吸收各方面的意见和想法,然后小心谨慎的去测试,甚至在线上做验证,保障万无一失后,最后上线。

5)保障质量,不断重构和改善现有设计和代码

所有的事物都有产生,发展,衰退和消亡的过程。长期来看,软件系统的代码质量肯定是会一直下降的,就像是人的身体健康,到了一定的程度,就会难以为继,需要重构或者重做。而不断的重构,改善现有的设计集合代码,就像是一直在保养身体,可以减缓衰老,保证健康,增加寿命。

6)取得领导和业务方的支持,过程和决策透明化

拆分改造看起来,没有给系统带来明确的可见收益,比如没有明显改进了用户体验,也没有给系统新增了一个业务功能,但是却涉及到多方参与,付出劳动,这就必然会带来很大的阻力,怎么办呢?还是从《管理的常识》一书里,我看到了一个很有道理的话:”如果无法推动问题背后的人解决问题,那说明对问题挖掘的还不够深“。现代化的工作教会我们,双赢/多赢是协作的唯一办法,也是可以持续的办法。搞清楚怎么才能推动各个合作方的支持,怎么才能让领导同意,如果我们现在提的意见,他们不同意,那么他们关心的点是什么,怎么把他们关心的点,纳入到这个工作范围里来,从而实现大家可以达成一致来合作。同时需要注意的是,信息一定要透明,决策要公开,让大家都直接参与到这个过程,从而明确目标,一致前行。

总结成 48 字箴言的“微服务拆分核心价值观”:

  1. 功能剥离、数据解耦
  2. 自然演进、逐步拆分
  3. 小步快跑、快速迭代
  4. 灰度发布、谨慎试错
  5. 提质量线、还技术债
  6. 各方一致,过程透明
理想中的系统拆分改造效果(实际上一般最后都鸡飞狗跳):




关于微服务对性能的影响

大家可以先思考 2 个问题:延迟(latency)和吞吐量(throughout)有什么关系? 延迟是响应时间么?
先说一下延迟和响应时间,延迟是对于服务本身来说的,响应时间是相当于调用者来说的(更多的内容可以参考《数据密集型应用系统设计》一书):

理想状态下,延迟越低,吞吐越高,当然这是对单机单线程而言的,在分布式下就不成立了,举个反例:

比如从密云水库,拉一个水管到国贸,水流到国贸,需要 1 小时;如果再拉一个水管到顺义,20 分钟就可以。如果你在国贸用水龙头接水,你可以单位时间接到非常多的水,这个数量跟你在过国贸还是顺义,没有关系,只跟水库单位时间输入的水量/水压有关系。但是如果你在水管里放一个小球,它从密云到国贸的时间是到顺义的时间的三倍,这样对于到国贸的这个水管系统,延迟很高,但是系统的吞吐量跟到顺义的是一样的。
同理,如果一个单体系统,被拆分成了 10 个服务,假如一个业务处理流程要经过 5 个服务,这 5 个服务只要是每个吞吐量(TPS/QPS)不低于原先的单体,那么整个微服务系统的吞吐量是不变的。

相反地,我们通过服务变小,关系变简单,数据库简化,事务变小等等,如果 5 个系统的吞吐都比原来的系统打,那么改造后的系统,整体的吞吐也比之前要高。那么这个过程的副作用是什么呢?

简单的说,就是延迟变高了,原来都是本地调用,现在变成了 5 次远程调用,假设每次调用的网络延迟在 1-10 毫秒(物理机房+万兆网卡可以很低,云环境下比较高),那么延迟就会比之前增加增加 5-50 毫秒,而且前提是分布式下的请求,使用异步非阻塞的流式或消息处理方式,同步阻塞会更高,而且影响吞吐量。好在低延迟的系统要求比较少见,对于一般的业务系统来说,可以水平扩展的能力比延迟增加几毫秒要重要的多。

比如我们在淘宝或者京东,买个衣服,交易步骤的处理,在秒级都是可以接受的,如果是机票、酒店、电影票之类的,分钟级以上都是可以接受的。

再举一个现实的例子,某个公司从 2016 年起,就在做微服务改造,研发团队规模不大,业务发展很快,基础设施没有跟上,自动化测试、部署都没有。同时这个公司的主要核心业务是一个低延迟高并发的交易系统,微服务拆分导致系统的延迟进一步增大,客户满意度下降。很快研发团队就发现了拆分成了多个小系统以后,比单体更难以维护,继而采取了措施,把部分微服务进行合并,提高可维护性和控制延迟水平。
对于分布式微服务系统的低延迟设计,更多信息可以参考 Peter Lawyer 的博客:https://vanilla-java.github.io/

怎么考虑拆分后的数据一致性

微服务提倡每一个服务都使用自己的数据库存储,这就涉及到老系统的数据库拆分改造。一般的数据库拆分,我们可以把不同业务服务涉及的表拆分到不同的库中去,这样如果之前的事务中操作的两个表如果被拆分到了不同的数据库,就会涉及到分布式事务。下面先解释一下分布式事务。

我们知道 ACID(原子性 Atomicity、一致性 Consistency、隔离性 Isolation、持久性 Durability)定义了单个数据库操作的事务性,这样我们就能放心的使用数据库,而不用担心数据的一致性,操作的原子性等等。由于数据库同时可以并发的给多个应用、多个会话线程使用,这样就涉及到了锁,隔离级别和数据可见性等一系列工作,好在关系数据库都已经帮我们解决了这些问题。

但是在 SOA、分布式服务化和微服务架构的大背景下,数据拆分到多个不同的库已经是常态,这种改造或者设计中,同一个业务处理涉及到的关联数据生命周期可能要贯穿到多个不同的数据库,如果没有事务保证,那么数据的一致性或者正确性就会收到破坏,账就可能会错乱了,平台或者客户就会产生损失了。如何保证数据的事务性,则是一个非常有意思的话题。传统的数据库和消息系统一般都是支持 XA 分布式事务,通过一个 TM 事务管理器协调各个 RM 资管管理器,每个 RM 管理自己的本地事务,通过两阶段提交 2PC 来保障一致。

由于 CAP 的不可能三角约束下,我们大部分时候选择了从 ACID 到 BASE(Basically Available 基本可用, Soft-state 柔性状态, Eventually consistent 最终一致),这样分布式事务我们一般也从 XA 变成了 TCC(Try-Commit-Cancel),把分布式事务的控制权从底层资源层(比如数据库)挪到了业务实现层,从而通过释放数据库层的锁,来提升性能和灵活性。具体情况可以参阅下面的两个文章。

我的好朋友长源和张亮,也分别写过一个分布式事务的系列:




如果直接操作数据库或者支持 XA/JTA 的 MQ,可以使用 XA 事务。其他情况,可以使用开源的分布式事务中间件。开源的分布式事务中间件有 Apache ServiceComb Pack,Seata/Fescar,Apache ShardingSphere 等。

(个人微信号:Robynn-D , 欢迎交流)但是实际上,对大部分业务来说,性能远大于分布式事务带来的强一致性要求。更多时候,我们可以先牺牲掉强一致性,甚至是准实时一致性,先让多个不同节点的服务,都自己单独把业务执行掉。然后再通过定时任务去检查是否一致的状态,如果不一致,保证可以从某个存储拿到原来的数据,重新执行即可。这时候其实是做了补偿的操作,补偿会带来数据重复处理的情况,就是检查的时候没有执行,但是去补偿操作的时候,可能已经在执行了。

特别是我们使用异步的 MQ 之类的方式做业务处理和补偿,消息也可能由于 MQ 的机制而重复。这时候我们只需要加上业务处理的幂等操作即可,比如订单处理,我们可以使用 Redis 或者 RoaringBitmap,把最近的 10000 个订单 id 或者 1 小时以内的订单 id,都放进去,每次订单处理之前,先看一下 id 是不是已经在里面了,如果是说明重复了,就不处理。

总之,最好的处理办法就是直接牺牲掉强一致性和准实时一致性,不用事务,既简单,又快速。

系统和服务的高可用可伸缩如何实现




1) 扩展立方体

既然分布式的核心是水平扩展系统,那么我们先来看看“扩展立方体”,如上图所示,扩展有三个维度:

我们可以通过上面三个维度的扩展性,灵活的应用到自己的场景里去。高水平扩展性带来我们随时给系统增加机器资源的能力。假设 A 系统的可用性为 99%,忽略掉负载均衡器的可用性,那么每多一个 A 服务节点,就意味着我们的可用性再增加 2 个 9,2 个节点是 99.99%,3 个节点就是 99.9999%的可用性。(这也说明了为什么要拆分数据库,数据库跟系统是串行的,只使用一个数据库,就意味着数据库宕机导致所有的服务节点都不可用了。)

2) 无状态系统

同时为了做到尽量高的扩展性,我们需要尽量让每个服务是无状态的、不共享状态和数据,这样的话每个服务才能随时增加机器。

3) 版本机制与特性开关

当我们新上线一个功能,或者升级一个现有功能的时候,可能会产生不兼容,或者对客户有未预期的影响,这个时候考虑提供版本化的接口,或者使用特型开关,在一定的时间窗口内实现对客户的兼容和友好用户体验。

4) 容错机制

分布式环境下,我们默认上下游和基础设施都是不可靠的,那么怎么在不可靠的假设基础上实现可靠性?这就要求我们考虑容错性,实现面向负载和面向失败的设计,考虑系统的限流、服务熔断和降级、系统过载保护等。

5)尽早发现问题

程墨 Morgan 在 https://www.zhihu.com/question/21325941/answer/173370966 里提到的 case 一样,不要画蛇添足,做一些不必要的操作,“要让服务稳定而高可用,靠的可不是一台服务器,应该用多服务的方式来应对”,如果系统可能会发生问题,就让问题早点暴露,而不是让系统带病运行,最终问题大爆发,可能错过了最佳的处理时机。我也深有体会,特别是交易类的系统,如果带着一些不一致的 bug 运行一段时间,可能会让大量的交易账目出错,再来修补问题,更正数据,代价非常大,甚至要赔偿给用户上百万美元。

6)根因分析、积累故障处理经验

历史告诉我们,错误如果不彻底分析原因解决,一定会再次发生。所以,把所有犯过的错,出过的故障,积累起来,每次都做根因分析,一层层的复盘,找到本质原因,制定改进行动项,才能解决这一类问题。经过一段时间的积累,再分析这一段时间的故障情况,就知道我们的研发团队哪一块的能力有缺失,应该去增强和补充这一块的能力。通过积累处理经验,提升系统的可用性和稳定性。

上面总结了一些高可用可伸缩的要点,下面的小节会讲到自动伸缩扩容。

拆分过程的测试和部署如何处理

通过前面的分析,我们了解到测试、部署和运维,在微服务环境下会变得复杂。试想,原来只需要测试一个系统,现在要测试一堆系统,原来要发布一个应用,现在要发布一堆应用。原来线上排查问题,只需要从一个日志文件看日志信息,一个数据库找数据,现在都不知道去哪儿找数据,因为第一时间不知道业务处理在哪个环节出错了,需要先搞清楚一个跨多个系统的调用处理过程,在哪个环节出了错,如果是显式的错误,有日志还好点,要是没有报错,而是数据错了,那简直就是排查问题的噩梦。

特别是如果两个不同的服务系统,分别是两个小组维护,一有问题就可能会产生相互推诿扯皮,A 让 B 先去排查是不是 B 的问题,B 让 A 去排查是不是 A 的问题,出现两个和尚没水吃的尴尬境地。一个解决问题的办法就是,自动化,降低人为因素的影响,也消灭服务拆分带来的这种重复劳动的复杂性,提升测试、部署、运维效率。

1) 自动化测试

建立全功能覆盖的测试 case,并实现自动化,变更时全量自动回归。集成 Sonar 等工具,检查代码风格、单测覆盖率和成功率等,控制代码质量。我们一般要求核心业务代码,覆盖率 100%;重要业务代码,覆盖率 90%;一般的后端业务代码,覆盖率 80%;其他代码覆盖率 60%。遗留代码,维护时把本次修改设计到的代码,覆盖率提升到 60%。代码风格可以参考阿里巴巴或是 Google 的 Code Style 编码规范定制适合自己团队的标准。

2)自动化部署

借助与 Jenkins、Nexus、Ansible,Docker、K8S 等工具,实现多个应用的自动打包,编排,以及自动化部署,构建微服务项目的部署流水线。特别是基于 K8S,我们可以实现微服务的服务自愈和自动弹性伸缩,在服务失败后重新拉起,在负载高或者低时动态控制容器数量。

3)自动化运维

通过标准规范,配置管理工具,资源交付工具等手段的配合,逐步实现基础架构、应用、IT 服务和业务运营的自动化,实现日常运维处理和运维流程的自动化,降低风险、提高效率,促进组织能力和成熟度提升。

拆分后的运维和监控如何处理




监控与运维是生产环境运行系统的日常工作,就像是人体的免疫细胞一样,保障着整个系统的健康运行,业务的正常运转,下面我们从 5 个方面说明一些微服务下的健监控和运维工作要点。

1) 系统监控

系统监控是最基础的监控指标,是我们了解系统内部运行情况的直接手段。我们要对所有重要的状态进行度量和监控,全面实时的掌握系统健康状态。

2) 业务监控

业务监控意味着我们要从用户的角度看来待系统的监控指标,而不仅仅是技术角度,这样我们就能发现和分析业务指标的突然变化是什么原因造成的,跟系统本身有没有关系,有没有需要我们改进提升的地方,可以更好的支撑业务增长,影响和稳定业务指标。时常可能会出现,业务方说客户都在抱怨系统不稳定,卡了,延迟高了,但是我们从系统监控上看,系统的指标没有大的变化,那么一定是我们设定的监控指标不够,没有覆盖到业务的全流程。反过来,也说我们和业务方、和客户思考问题的角度有差异,为了更好的服务客户,我们需要调整自己的视角,从用户角度出发思考问题。

3) 容量规划

只了解系统的过去和现状是不够的,因为随时可能会有突发的流量袭来,导致系统被冲击,可能会超出系统处理能力导致延迟飙升,直至系统宕机崩溃。所以我们需要在平时做好容量规划,通过持续的压测,了解到系统的极限处理能力,针对瓶颈资源持续做优化,提升处理能力,做好容量预案,随时有激增流量的扩容方案,超出处理能力的限流和熔断、系统过载保护方案,保障系统的稳定运行。

4) 报警预警

做好了业务和系统指标监控,也做了容量规划,那么我们还需要通过这些指标和容量策略,在合适的时机对系统进行干预,把系统风险提前消灭掉。所以,我们需要根据经验定义预警报警的阈值,根据容量水位进行扩容缩容的后续处理动作。特别报警预警的实时性,是我们应对线上突发异常的一个重要指标,例如对于 web 和 app 用户,如果我们在系统突发异常的 10 秒内收到预警,然后又花了 20 秒把系统重启,恢复了服务能力,那么用户可能会觉得刚才 30 秒是不是网络卡了一下,不会产生大规模的客诉。相反地,假如我们 2 分钟收到报警,又花了 10 分钟才处理完,这时候基本上大批客户都会感知到了这次故障,称为一个有大量客诉的事故了。

5) 故障处理

从我们的实际经验来看,导致系统出现非预期的不可用性故障,主要有三类原因:

这三类问题的应对策略是分别如下

故障处理的第一原则是,先解决问题,然后再去考虑分析原因,复盘过程,总结经验教训,最后才是考虑要不要追责。特别强调的是,如果一个线上的发布或者变更操作,有可能造成客户的感知事件,最好就先跟客户进行一个可以预期造成业务影响的沟通,给客户同步一下操作的时间,目的,持续时间,可能造成的影响,让客户可以从容的安排和调整自己的业务,保证不受影响或者降低损失(如果停机会给客户造成损失的话)。如果技术团队对这个操作没有十足的把握,最好考虑在一个可接受的时间窗口内停机处理。对于发布造成的故障,我们一直有个说法:

如果发布可能导致宕机这件事是提前告知了客户,那么真的发生了宕机就是一个故事。相反,如果可能导致宕机这事儿没有提前告知客户,那么操作过程导致宕机,就是一个事故。

最佳实践的总结

林林总总说了这么多的微服务架构相关的知识也好,经验也罢,不一定适合每个希望做微服务系统的技术人员的实际需求。“道无常道,法无常法,君子审时度势,自可得而法”。实际项目里需要做哪些工作,采取哪些策略,先后运用哪些步骤,都需要因地制宜,借鉴各种“他山之石”,综合考虑。

微服务架构的最佳实践,其实就是把微服务架构的条条框框都思考一遍,这一条到底解决了什么问题,适用于什么场景,对我现在的工作有没有帮助,考虑清楚了以后彻底的忘掉这些“有为法”。然后用自己的思考成果去解决工作中遇到的各类问题,就算是真正学会了微服务。问题永远存在,解决掉遇到的问题,是推动技术发展,促进组织进步,提升个人技术能力的不二法门。

过去的几十年里,技术发展的越来越丰富,体系越来越庞大,业务系统越来越复杂,正像是《人月神话》中形容的那样:

“史前史中,没有别的场景比巨兽们在焦油坑中垂死挣扎的场面更令人震撼。上帝见证着恐龙、猛犸象、剑齿虎在焦油中挣扎。它们挣扎得越猛烈,焦油纠缠得就越紧,没有任何猛兽足够强壮或具有足够的技巧,能够挣脱束缚,它们最后都沉到了坑底。”



如何才能逃离“焦油坑”,目前看来使用“微服务架构”的指导思想,去解决复杂业务系统研发中的各类问题,是一个明确的解决办法。没有什么比去解决实际问题、让系统变好更重要。系统变好了,我们的技术也变好了,公司的业务也支持的更好了,业务好了则行业也发展的更好了,行业好了就会让整个国家和民族都更好了。所以,解决问题是关键。

万维钢老师在《精英日课》里说“这个世界上的问题,分为三类”:

第一类是目标明确、路径明确的,比如你上高中的时候,你知道把几门功课都学好,就能上个好大学,这就是一个单纯问题,但是可以看到,单纯问题也可能是非常难的。钱能直接解决的问题,一般也是这类问题。
第二类是两难问题,选择就意味着放弃,比如两个女孩喜欢你,一个有钱,一个漂亮,选了有钱的就不漂亮,选了漂亮的就没有钱。路径很明确,但你面临的痛苦不是因为问题很难,而是不管你怎么处理,都需要付出失去的代价,放弃潜在的另一些可能,会在将来造成实际的损失。你跟小张结了婚,10 年后天天吵架,后悔没有取晓丽,红玫瑰与白玫瑰。
第三类问题,我们叫棘手问题,这类问题,即可能没有特别明确的目标,而且也没有任何人能告诉你一个明确的路径,你现在做了什么努力,可能也短时间看不到效果,做好了不一定有很大成果,做不好肯定要背锅。大家可能都知道这个问题在,就是没有人去解决或者能去解决。最关键是,从外部的一些人看来,这些问题,很像是一个单纯问题。这特么就扯淡了。
而现在看来,微服务架构的这些知识和实践,包括最近几年大家在响应式微服务架构(Reactive MicroServices Architecture)、服务网格(Service Mesh)做得一些工作,恰恰就是给怎么去处理“复杂业务系统研发”这个棘手问题,带来了一个比较明确的路径,指引着大家逐渐的把这个棘手问题变成一个单纯问题,然后用各种新的思想和武器去解决它。

最后给大家分享一个今天看到的微服务和中台的段子:

Q:大师大师,微服务拆多了怎么办?
A:那就再合起来啊。
Q:那太没面子了啊。
A:你就说你已经跨越了微服务初级阶段,在做中台了。



(个人微信号:Robynn-D , 欢迎交流)

关键词:实践,深度,微服

74
73
25
news

版权所有© 亿企邦 1997-2025 保留一切法律许可权利。

为了最佳展示效果,本站不支持IE9及以下版本的浏览器,建议您使用谷歌Chrome浏览器。 点击下载Chrome浏览器
关闭