七个关键问题的应对策略
1.如何合理拆分微服务
当一个系统服务化的时候,就会面临一个问题:如何进行服务的划分?怎么确定服务的粒度?有没有一些可以参考的业界通用规则?
实际上服务划分的本质是对系统进行架构设计,服务的划分粒度没有绝对的过大或过小之说,不同阶段的侧重点和思考的角度也不尽相同。
创业初期的团队,过分的追求微服务,为了“微”而微,反而会导致业务逻辑过于分散,技术架构过于复杂,团队基础设施搭建能力弱,进而导致忽略了快速迭代交付产品的重要性,可能错失了市场机会。所以,关于服务的划分不是对错的选择题,而是需要综合考虑各种外界的因素,所作出的一个最适合的决策,这些外界因素通常包括业务、技术债、开发、运维、测试这
五个方面:
- 业务所处领域的市场性质:对市场比较敏感的项目,创业初期粒度应该尽量划分的粗一些,先提供充足的弹药去占领市场,然后再去考虑对系统进行重构和优化;
- 与原有系统之间的关系:对于历史遗留的系统,需要做好新旧系统之间的边界划分,避免过于激进、过大幅度的改造,应该采取小步快跑的方式,有节奏的对老系统进行服务化改造;
- 开发团队的成熟度:服务化带来的技术风险应该提前进行评估,要考虑团队的承受度,用合适的人做适合的事,考虑团队需要有包括敏捷,包括 Devops,包括基础设施,运维和测试的自动化等基础能力;
- 基础设施的搭建能力:在进行细粒度的服务划分时,要考虑团队是否有足够的能力来支撑大量服务实例运行的运维复杂度,是否可以做好分布式的日志追踪和服务的监控;
- 测试团队的测试执行效率:过于细粒度的服务划分,如果测试团队不能通过自动化测试、自动回归、压力测试、极限测试等手段来提高测试执行效率,必然会带来测试工作量的大幅度上升,进而影响整个项目的上线周期;
如果没有特别强烈的大规模水平扩展需求,拆分就没有必要,反而把问题搞复杂了。进行服务化的拆分时,通常会先按照业务子系统先进行一次划分,根据业务逻辑和数据的关系划分为若干个子系统,然后再考虑子系统内部是否可以再次进行拆分。至于拆分的基本原则,我推荐:
- 高内聚低耦合:这个已经提了很多了,简单说一下,就是要把强相关的部分,总是会一起改动的部分,聚合到一起,相关性不大的部分拆开,可以参考 DDD 中的一些办法。
- 粗粒度服务:服务的粒度要稍微的抽象和粗粒度一些,因为服务是基于业务场景的抽象和设计,不能做成是直接把数据库的增删改查暴露出来成接口和方法,而是应该隐藏这些细节,考虑清楚从业务和客户角度来看,哪些步骤和过程,是必须封装起来的,细节隐藏掉,然后对外提供的就是粗粒度的服务,而在单体系统的时候,我们可以直接调用这些细节,无需过多考虑。
2.遗留系统应该如何改造
对旧系统进行改造,可以分为几个步骤:拆分前准备阶段,设计拆分改造方案,实施拆分计划。下面是我的一些经验之谈。
1)拆分之前先梳理系统关系和接口
其中准备阶段主要是梳理清楚了依赖关系和接口,就可以思考如何来拆,第一刀切在哪儿里,即能达到快速把一个复杂单体系统变成两个更小系统的目标,又能对系统的现有业务影响最小。要尽量避免构建出一个分布式的单体应用,一个包含了一大堆互相之间紧耦合的服务,却又必须部署在一起的所谓分布式系统。没分析清楚就强行拆,可能就一不小心剪断了大动脉,立马搞出来一个 A 类大故障,后患无穷。
2)不同阶段拆分要点不同,每个阶段的关注点要聚焦
拆分本身可以分成三个阶段,核心业务和非业务部分的拆分、核心业务的调整设计、核心业务内部的拆分。
第一阶段将核心业务瘦身,把非核心的部分切开,减少需要处理的系统大小;
第二阶段。重新按照微服务设计核心业务部分;
第三阶段把核心业务部分重构设计落地。
拆分的方式也有三个:代码拆分、部署拆分、数据拆分。
代码直接体现了依赖关系,拆完就可以单独打包部署。但是有时候,我们可以通过控制一些提供服务的开关,使用同一份代码和打包的程序,部署多组进程,每组提供不同的服务,这就是部署拆分,比如同一份代码,我们部署了 3 组机器,A 组 5 台提供订单服务,B 组 2 台提供用户服务,C 组 2 台提供任务调度处理任务。数据拆分最复杂,涉及到代码的调整,SQL 和事务的分析和重构,数据库表的拆分甚至数据迁移,数据结构的调整和数据迁移则一般意味着需要停机维护。这三个方式,可以在适当的条件下选择先做哪个操作合适。
另外,每个阶段需要聚焦到一两个具体的目标,否则目标太多反而很难把一件事儿做通透。例如某个系统的微服务拆分,制定了如下的几个目标:
- 性能指标(吞吐和延迟):核心交易吞吐提升一倍以上(TPS:1000->10000),A 业务延迟降低一半(Latency:250ms->125ms),B 业务延迟降低一半(Latency:70ms->35ms)。
- 稳定性指标(可用性,故障恢复时间):可用性>=99.99%,A 类故障恢复时间<=15 分钟,季度次数<=1 次。
- 质量指标:编写完善的产品需求文档、设计文档、部署运维文档,核心交易部分代码 90%以上单测覆盖率和 100%的自动化测试用例和场景覆盖,实现可持续的性能测试基准环境和长期持续性能优化机制。
- 扩展性指标:完成代码、部署、运行时和数据多个维度的合理拆分,对于核心系统重构后的各块业务和交易模块、以及对应的各个数据存储,都可以随时通过增加机器资源实现伸缩扩展。
- 可维护性指标:建立全面完善的监控指标、特别是全链路的实时性能指标数据,覆盖所有关键业务和状态,缩短监控报警响应处置时间,配合运维团队实现容量规划和管理,出现问题时可以在一分钟内拉起系统或者回滚到上一个可用版本(启动时间<=1 分钟)。
- 易用性指标,通过重构实现新的 API 接口既合理又简单,极大的满足各个层面用户的使用和需要,客户满意度持续上升。
- 业务支持指标:对于新的业务需求功能开发,在保障质量的前提下,开发效率提升一倍,开发资源和周期降低一半。
- 核心人员指标:培养 10 名以上熟悉核心交易业务和新系统的一线技术人员,形成结构合理的核心研发人才梯队。
结果可想而知了,目前太多了,反而没有目标。最后第一阶段只选择了稳定性作为最重要的指标,先稳住系统,然后再在后面的阶段里选择其他指标,逐步实现各个目标。
3)快速迭代,找到突破口,持续产出
大家都知道,敏捷开发之所以流行,就是因为小步快跑,快速迭代,实现对业务变化和新需求的第一时间响应,这对快速发展变化的外部市场,以及 KPI 压力非常大的业务部门非常重要。研发团队在系统改造过程也可以通过快速的阶段性产出,来证明团队的技术能力和推进水平,增进互相的背靠背信任关系,为长期的顺畅合作打下坚实基础。这方面,很多研发团队都想试图憋大招,搞个大项目,反而慢慢失去各个利益方的耐心,最终把合作关系搞僵,吃了大亏。
4)大胆假设,小心求证,稳步上线
凡事不破不立,拆分改造过程,我们每一次改动的地方,可能有多个不同的方案和路径,具体选择哪一个最合适,这需要我们放开思路,大胆假设,充分吸收各方面的意见和想法,然后小心谨慎的去测试,甚至在线上做验证,保障万无一失后,最后上线。
5)保障质量,不断重构和改善现有设计和代码
所有的事物都有产生,发展,衰退和消亡的过程。长期来看,软件系统的代码质量肯定是会一直下降的,就像是人的身体健康,到了一定的程度,就会难以为继,需要重构或者重做。而不断的重构,改善现有的设计集合代码,就像是一直在保养身体,可以减缓衰老,保证健康,增加寿命。
6)取得领导和业务方的支持,过程和决策透明化
拆分改造看起来,没有给系统带来明确的可见收益,比如没有明显改进了用户体验,也没有给系统新增了一个业务功能,但是却涉及到多方参与,付出劳动,这就必然会带来很大的阻力,怎么办呢?
还是从《管理的常识》一书里,我看到了一个很有道理的话:”如果无法推动问题背后的人解决问题,那说明对问题挖掘的还不够深“。现代化的工作教会我们,双赢/多赢是协作的唯一办法,也是可以持续的办法。
搞清楚怎么才能推动各个合作方的支持,怎么才能让领导同意,如果我们现在提的意见,他们不同意,那么他们关心的点是什么,怎么把他们关心的点,纳入到这个工作范围里来,从而实现大家可以达成一致来合作。同时需要注意的是,信息一定要透明,决策要公开,让大家都直接参与到这个过程,从而明确目标,一致前行。
总结成 48 字箴言的“微服务拆分核心价值观”:
- 功能剥离、数据解耦
- 自然演进、逐步拆分
- 小步快跑、快速迭代
- 灰度发布、谨慎试错
- 提质量线、还技术债
- 各方一致,过程透明
原创文章,作者:奋斗,如若转载,请注明出处:https://blog.ytso.com/309675.html