SpringCloud Alibaba 实战 - 前京东金融架构师 - 拉勾教育
前文我们探讨了什么是微服务,尽管微服务架构有着高度独立的软件模块、单一的业务职责、可灵活调整的技术栈等优点,但也不能忽视它所带来的弊端,本节我们将针对引入微服务架构后带来的新问题展开讨论,并分享一些我在微服务实践中的心得。
微服务架构的新挑战
在著名软件著作《人月神话》中提到,软件世界没有 “银弹”,这句话当然适用于架构领域,随着从单体架构过渡到微服务架构,因为将原有系统打散,给系统增加了许多不稳定因素。
单体架构向微服务架构转变
下面我从网络、性能、运维成本、组织架构与集成测试五个方面分别进行阐述。
第一点,跨进程通信带来的新问题。 以往单体应用是在单机中进行进程内通信,通信稳定性相当好。但是打散为分布式系统后,变为进程间通信,往往这个过程还伴随着跨设备的网络访问,架构师在设计时必须考虑上下游系统因为网络因素无法通信的情况,要假设网络是不可靠的,并设计微服务在网络异常时也能进行符合预期的异常处理。以支付模块为例,用户支付成功后系统自动调用短信服务向用户手机发送 “订单支付成功” 的消息,此时架构师就必须假设短信服务在服务或者网络不可用时不会影响到订单业务的正常执行。
微服务间跨进程 RESTful 调用
第二点,较高的响应延迟。 相比传统单体架构进程内通信,跨进程、跨网络的微服务通信在网络传输与消息序列化带来的延迟是不可被忽略的,尤其是在五个以上微服务间消息调用时,网络延迟对于实时系统的影响是很大的。早些年我和军事院校合作了一个雷达仿真训练的系统,因为要模拟 “导弹打飞机” 的场景,在计算飞行轨道时 1 毫秒的响应增加都可以会影响到最终的结果,显然这类系统采用分布式设计就不再合适。
雷达指挥训练系统流程
第三点,运维成本会直线上升。 早期单体应用因为结构简单,规模也较小,发版时通常面对几台服务器部署几个 Jar/War 文件就可以了。同时,应用的交付周期也是以周甚至月为单位,此时硬件设备成本与运维人员技术要求都比较低,采用手动部署即可满足要求。而对于微服务架构而言,每一个服务都是可独立运行、独立部署、独立维护的业务单元,再加上互联网时代用户需求的不断变化以及市场的不稳定因素,运维人员每天面对成百上千台服务器发布几十次已是家常便饭,传统手动部署显然已经无法满足互联网的快速变化。
京东 JDOS 自动化运维架构图
第四点,组织架构层面的调整。 微服务不但是一种架构风格,同样也是一种软件组织模型,以往软件公司会以职能划分研发、测试、运维部门进行独立管理考核,而在微服务的实施过程中,是以业务模块进行团队划分,每一个团队是内聚的,要求可以独立完成从调研到发版的全流程,尽量减少对外界的依赖。如何将传统的职能团队调整为按业务划分的研发团队,同样是对管理者的巨大挑战,要知道人的思想比架构更难改变。
独立的全生命周期研发团队
第五点,服务间的集成测试变得举步维艰。 传统单体架构集成测试是将不同的模块按业务流程进行组合,在进程内验证每一种可能性下其模块间协作是否符合预期即可。但对于微服务而言,系统被拆解为很多独立运行的单元,服务间采用接口进行网络通信。要获取准确的测试结果,必须搭建完整的微服务环境,光这一项就需要花费大量的人力物力。同时,因为微服务是跨网络通信,网络延迟、超时、带宽、数据量等因素都将影响最终结果,测试结果易产生偏差。
微服务最佳实践
刚刚我们总结了引入微服务架构的一些新挑战,下面我将结合自己多年的微服务落地经验,总结出五点微服务架构最佳实践,希望能对你日后的工作提供帮助。
第一点,微服务的划分原则。 将已有系统拆分为多个微服务,本就没有统一的标准。举个例子,一个初创电商公司,要开发一套电商系统,将 “促销活动” 单独剥离出来作为 “促销服务” 是没有问题的。但是如果在 “淘宝”“京东” 这种体量的电商平台,“促销服务”就显得粒度太粗了。可以继续拆解为 “价格服务”“优惠券服务”“京豆服务” 等更细粒度的小服务,每个服务有专门团队负责维护。
京东商城的微服务业务划分
因此,在微服务拆分过程中,我们通常会从业务场景、团队能力、组织架构等多种因素综合考虑,这特别考验架构师的业务能力。一般来说,我们总结出几点通用原则:
- 单一职责原则。 每一个微服务只做好一件事,体现出 “高内聚、低耦合”,尽量减少对外界环境的依赖。比如,在公司创业之初,完全可将订单与仓储服务进行合并。因为订单与仓储在业务与数据上紧密相关,如果强行拆分会导致出现跨进程通信带来的数据一致性难题。随着业务的发展,仓储的业务职责扩展,派生出许多与订单无紧密联系的功能,到时再将其剥离形成独立的 “仓储服务”。
- 服务依赖原则。 避免服务间的循环引用,在设计时就要对服务进行分级,区分核心服务与非核心服务。比如订单服务与短信服务,显然短信服务是非核心服务,服务间调用要遵循 “核心服务” 到“非核心服务”的方向,不允许出现反向调用。同时,对于核心服务要做好保护,避免非核心服务出现问题影响核心服务的正常运行。
- Two Pizza Team 原则。 就是说让团队保持在两个比萨能让队员吃饱的小规模的概念。团队要小到让每个成员都能做出显著的贡献,并且相互依赖,有共同目标,以及统一的成功标准。一个微服务团队应涵盖从设计到发布运维的完整生命周期,使团队内部便可以解决大部分任务,从人数上 4~6 人是比较理想的规模。
Two Pizza Team
第二点,为每一个微服务模块明确使命。 这里推荐一套标准的微服务叙述模板,集中体现 “只做好一件事” 的原则。
模板
XX 微服务用来
在出现痛点场景的情况下
解决现有的 XX 问题
从而达到了 XXX 的效果
体现了微服务的价值
示例
商品检索微服务用来
在商品数据全量多维度组合查询的情况下
解决了 MySQL 数据库全表扫描查询慢的问题
从而让查询响应降低到 50ms 以下
有效提升了用户体验
通过这种描述,服务的职责与边界就十分明确,团队便以此为目标确认职责。在实施过程中因为我们是以解决问题为目标,切分时可能会比较细碎。经过漫长时间沉淀,系统中出现了类似于 “商品检索服务”“订单检索服务”“商铺检索服务” 等多个小服务,这时可以对这些服务形成聚合生成新的“通用检索服务”,以此来控制微服务的整体规模。反之,对于庞大的服务,可以考虑拆分为多个小服务进行细粒度的管理。总之,拆与合是伴随着公司业务的演进而变化的,一切以解决问题为准。
第三点,微服务确保独立的数据存储。 数据是任何系统最重要的资产。以往单体应用通常会选择 MySQL 这种关系型数据库作为数据的唯一存储,这样做的好处是涉及多表操作时,利用数据库自带的事务机制便可最大程度保证数据完整性。但这样做却存在诸多问题,以下图为例,不同的微服务对数据存储的需求也是不同的,订单服务需要 MySQL 数据保存订单与订单明细;新闻服务需要 Elasticsearch 提供全文检索支持;朋友圈需要图数据库表达现实世界人际关系;文件存储服务则需要分布式文件系统。如果将所有数据都揉在 MySQL 中使用会变得十分蹩脚,好的做法是为每一个微服务提供符合自身业务特性的数据库。
独立的数据存储
但理想很丰满现实很骨感,在分库后涉及跨库操作会变的难以处理。比如,订单依赖会员数据,原本单库处理时一条 SQL 语句便可实现。
SELECT order.* , member.* FROM order,member WHERE order.member_id = member.member_id
但在微服务架构下,因为数据库绝不允许其他团队访问,关联查询只能变为 API 调用形式,程序实现层面比单库复杂不少。
通过 RESTful 通信实现数据关联
与之类似,如果涉及多表写入时一致性问题更复杂。
BEGIN ;
写入表A;
写入表B;
COMMIT;
在拆分为服务后,数据被分散到多库,为保证异构多库的数据一致性是所有分布式应用的巨大挑战,至今没有完美的解决方案,这块内容我在后面课程单独做一个专题进行讲解。
第四点,服务间通信优先采用聚合器模式。在微服务间通信时存在两种消息传递模式:链式模式与聚合器模式。下图所展示的是链式模式,请求按业务流程在各个服务间流转,最终处理完成返回客户端。
链式模式
因为请求是按业务流程传递,很容易能被开发人员理解,链式模式成为最常用的服务间通信模式。但链式模式采用串联模式,调用的整体成功率等于单个服务成功率的乘积,假设每个服务可靠性为 90%,一个业务在 4 个服务执行后的最终成功率只有 90%90%90%*90%≈66%,有将近一半的请求会处理失败,这是无法接受的。此外,链式模式因默认采用同步方式传输,在服务处理完成前应用会一直处于阻塞状态,当调用链较长时,系统整体性能会严重下滑。
聚合器模式则是通过服务作为入口,组装其他服务的调用。以下图为例,因为 “订单流程服务” 是将其他服务进行聚合操作,所以称其为聚合器模式。以 “订单流程服务” 为例,将 “订单”“支付”“库存” 服务进行聚合,一个服务实现了下单、支付、减库存的完整流程。
聚合器模式
采用聚合器模式后,业务流程与编排集中在 “订单流程服务” 中,可对整体业务进行有效编排,支付与扣库存可以并行调用,可以有效提高系统的性能。
第五点,不要强行 “微服务” 化。 在以前公司任职时因为业务需要,要开发一个工单系统让售后人员在线为客户提供售后服务,但当时因为公司产品已经非常成熟,每天产生的工单只有几十笔,如果做成单体应用配合 Nginx 反向代理,从存储到应用便能满足需求。此任务分配给一位 “年轻” 的架构师,可能是为了证明实力,他采用了全套微服务技术来“炫技”,并在会议上侃侃而谈。结果老板反问他“你这么设计有必要么,为一个工单系统投入超过 10 台服务器成本你考虑了吗?” ,当时那场景别提多尴尬了。其实在我看来,微服务也不过是一种方案,没必要盲从。它也没有违背架构的基本规律:架构是解决当前需求和痛点而演进的。在满足需要的前提下,选择合适的而不是选择最好的,合理降低成本才是好架构师该考虑的事情。
以上微服务的经验都是我在实际工作中总结归纳出来的,如有不足的地方欢迎同学们在评论中给予补充。
微服务架构的适用场景
本节最后一部分,我总结了适合微服务使用场景,首先咱们梳理下适合做微服务的场景:
- 新规划的大型业务系统, 这肯定是最适合引入微服务架构的情况了, 微服务强调 “高内聚,低耦合”,每一个团队负责一个服务,这就意味着从根本上和传统的整体性应用有本质不同,从规划阶段采用微服务架构是再好不过的。
- 敏捷的小团队系统,公司在大型项目微服务实践前,往往这类边缘化的小项目会起到 “试验田” 的作用, 引入快速迭代、持续交付等模式,积累适合本公司特点的微服务实践经验,再将这些经验扩大到其他大型项目中。
- 历史的大型留存业务系统,之前多年我一直在金融软件领域工作,在银行内部许多系统已经使用超过 10 年时间,成百上千个模块错综复杂维护愈发困难,无论架构、框架乃至技术人员都需要更新迭代,但都不可能一次大动手术,这时微服务的 “微” 就体现出来,重构时可以将某一个部分剥离为微服务独立运行,确保无误后再继续剥离出下一个服务,通过抽丝剥茧一般的剥离,逐步将原有大系统剥离为若干子服务,虽然过程十分痛苦,但这是必须做的事情。
下面咱们再来列举几个不适合引入微服务的场景:
- 微型项目,前面提到的工单系统,系统压力很小,需求变化也不大,利用单体架构便可以很好解决,使用分布式架构反而增加了架构复杂度,让系统更容易不稳定。
- 对数据响应要求高的系统,就像前面文中提到的 “导弹打飞机” 的科研项目,对实时性要求极高,那显然是不合适的。
小结与预告
从本课时开始,我们针对引入微服务架构后带来的诸多新问题进行了分析,之后给出了多条微服务架构最佳实践,最后总结了微服务架构的使用场景。
这里给你留一道思考题:微服务架构作为一种设计风格,必须要依托具体的技术与开发框架才可以实施落地,那你能列举出具体包含哪些技术吗?
在接下来的课程中,我们将 Spring Cloud 与 Spring Cloud Alibaba 微服务生态进行分解,了解每一个技术组件的职能与用法。