探讨篇(一):服务粒度的艺术 - 简化架构与避免服务泛滥
一、背景
上周小组有个需求上线牵扯9个应用(小组目前维护了26个服务,由于团队系统业务属性特征基于高可用、高性能原则拆分,有些是合理的,有些不是很合理的),同时上周OpsReview的一个微服务滥用典范案例(Promise服务A调用服务B,服务B只是读个配置数据返回,无具体业务逻辑),OpsReview会上也看到过其他微服务泛滥等案例,这些事情引发了我对服务泛滥和服务粒度的深入思考,接下来在稳定性的前提下,基于成本收益维度思考,小组哪些应用(先从2级开始)应该合并治理。
在当今软件开发的舞台上,微服务架构因其所灵活性以及独立部署等优势而广受推崇。然而,随着服务的过度细分,我们面临着服务数量激增所带来的管理复杂性和分布式通用问题挑战性等。
本文通过Promise后端现有服务探讨服务粒度的合理划分与有效合并的关键因素。本文的观点源自我在学习与实践过程中的思考,尚处于不断探索和验证的阶段。希望能“抛砖引玉”,引发大家的思考与讨论,让我们共同进步,从而优化我们的系统设计。
目前团队服务泛滥的影响如下: 1、维护成本增加:服务数量的增加直接导致了维护工作量的增加。这不仅包括服务器维护,还包括代码维护依赖管理,比如Promise时效内核升级、中间件JSF等版本升级、安全漏洞工单log4j版本改造等都需求部署上线N个(最多26)应用。当服务数量过多时,即使是小的变更也可能需要协调多个服务,从而增加了工作量。 2、团队协作难度:在服务泛滥的环境中,团队成员可能需要跨多个服务进行协作,比如本次需求上线牵扯9个应用。这不仅需要更多的沟通和协调,而且还需要确保所有相关人员对服务的改动和更新都有清晰的认识。这种跨服务的依赖关系可能会导致团队间的沟通混乱和效率低下。 3、资源利用效率低:服务泛滥可能导致资源分配不均,一些服务可能过度使用资源,而其他服务则可能闲置。这种不平衡的资源分配会导致整体成本的上升。
名词解释: 模块化:是指将一个复杂的系统分解成若干个相互独立且可集成的部分,这样可以简化系统的设计、开发和管理。模块化的目的是通过创建高内聚、低耦合的模块来提高软件的可维护性、可复用性和可扩展性。在模块化的过程中,每个模块都是一个独立的单元,可以单独开发、测试和优化,同时通过定义良好的接口与其他模块通信。 粒度:在软件架构中,粒度通常用来描述组件或模块的大小和层次。较小的粒度意味着系统更加灵活,因为它允许更精细的控制,但可能会带来性能问题,如增加硬件资源和通信开销。较大的粒度则可能提高效率,但会牺牲一定的灵活性。
二、Promise服务粒度问题
在微服务架构的设计理念中,最大陷阱之一在于,并非所有构成程序的组成部分都必须被设计为微服务。
系统交互图
简单描述下各应用作用:
对应用户在京东APP购买商品流程如下:
那么问题来了
接下来针对这个问题,进行分析。
三、服务粒度
模块化关心系统分解成单独的部分。而粒度则处理这些单独部分的大小。粒度才是分布式各种挑战的问题关键。那如何来度量粒度大小呢?看里面java文件数、class类数量、代码行数?其实并不是的,而是看服务的职责,服务范围包含哪些,另外一个指标是看服务对外提供的公共API接口或者MQ的数量。虽然这2个指标也是存在变化和主观因素,但这应该是目前最接近客观度量和评估服务粒度的方法。
如下图:当业务复杂度达到一定程度后,微服务架构消耗的成本才会体现优势,并不是所有的场景都适合采用微服务架构,服务的划分应逐步进行,持续演进。
现在很多拆分粗粒度可能是主观意识或者直觉,缺乏理论。我们需要使用【拆分因素】和【合并因素】来客观综合分析利弊,从而形成拆还是不拆服务的充分合理解释。
1、拆分因素
粒度拆分因素解决的是什么时候应该将服务拆分为更小部分。为服务拆分为更小的部分提供了指导和依据。其中粒度拆分因素的主要因素如下:
1.1、服务职责&业务边界
服务职责是拆分服务考虑的第一因素,确保服务与业务能力对应,领域治理,职责单一,避免单个服务承担过多不相关的功能。需要从两个维度来考虑。
比如上面说的Promise时效B应用、C产能应用、D消息全程跟踪、持久化应用 这4个应用内聚性紧密型不大,产能域和时效域拆分。同理订单时效E-mysql持久化服务和E-es持久化服务,这2个应用都是数据持久化,功能是一样,即把订单时效数据保存起来。内聚性相对较强,入口也都是消费对应MQ。强内聚意味着数据持久化这个服务是单一目的性的,这2个应用不应该拆分。
1.2、容错性&稳定性&高可用
比如案例中的物流配送时效和产能服务通常是关键服务,它们的可用性直接影响用户体验。拆分这些服务时,要特别考虑如何设计系统的高可用性和容错机制。这也是为什么上面为什么要通过MQ异步方式解耦把时效核心服务和 全程跟踪数据持久化应用拆分,优先保障订单库存生产(物流配送),再来处理全程跟踪话术(用户体验)和数据持久化数据分析。因为数据持久化mysql耗时并且可能故障概率大。
如果mysql服务和es服务容错性不一样,比如es老是崩溃,不稳定,进而影响了MYSQL服务,导致业务影响,则可拆分为2个服务。
1.3、性能&吞吐量
比如持久化mysql服务100W/M 和持久化es服务80W/M 是一样的吞吐量。大促期间es消费相对较慢,但性能在可接受范围。比如自营订单1W/S TP99位20MS,外单100/S TP99 为3000MS可能更适合拆分
1.4、可扩展性
1.5、团队大小和稳定性
从拆分因素来看上面的promise服务粒度应用的划分是否妥当? A: 对于B时效服务、C产能服务以及D消息服务,其应用拆分是根据业务的独特需求来定制的。特别是在大型促销活动期间,订单的实时生产、仓库产能实时管理情况是至关重要的。因此,我们基于时效性和产能管理的需求进行了服务划分。此外,通过MQ(消息队列)机制,我们实现了业务特征的解耦,使D消息服务能够消费相应的MQ,并处理相关的业务逻辑,例如提供用户的全程跟踪体验和订单时效性的缓存。为了确保服务的稳定性和高可用性,我们特意将持久化应用独立出来,从而定义了清晰的职责边界。例如,在进行MySQL数据库的主从切换时,我们只需暂停持久化应用,这样就不会影响到订单的时效处理和用户的全程跟踪逻辑。
2、合并因素
上面的拆分因素为何时将服务分解为更小的部分提供指导和依据,那粒度合并因素则相反,为服务重新重合在一起提供指导和参考依据。粒度集成的几个主要因素如下:
2.1、服务原子性
服务的原子性是指服务操作应该是一个不可分割的整体,要么全部成功,要么全部失败。这有助于保证数据的一致性和系统的可靠性。
2.2、成本效益
拆分服务可能会增加运维成本,因此需要进行成本效益分析,确保服务的粒度调整能够带来正面的经济效益。
2.3、网络开销
2.4、共享代码
2.5、数据关系
2.6、代码变更&部署频率
比如上面的【订单时效E-mysql持久化】服务和【E-es持久化】服务变更频率是一样的,时效增加一个字段,用于下游考核业绩,es同样也需要用于日常排查。
2.7、测试和监控
从集成因素来看上面的promise服务粒度问题
Q: 关于持久化,目前包括两个应用:E-mysql和E-es。是将它们设计为一个服务更合理,还是作为两个独立的服务? A: 综合前述的分析,将这两个应用设计成一个服务显得更为合理。这样的设计可以充分利用两者之间的协同效应,同时简化系统架构,提高维护效率。通过统一的服务接口,我们可以更加灵活地管理数据持久化的过程,无论是对MySQL还是Elasticsearch的操作,都可以确保一致性和高效性。这种一体化的服务设计,既可以减少系统复杂性,也便于在需要时对服务进行扩展和优化,但需要做好es容错性,即es有问题不能影响mysql业务。
四、总结
在微服务架构的世界里,服务粒度的艺术不仅仅是技术上的划分,更是对业务理解的体现,对系统复杂性的把控,对团队协作和效率的考量。我们不能忽视服务粒度选择对系统性能、可维护性和可扩展性的深远影响。正如我们通过Promise系统的探索所见(Promise业务属性是下单前商详结算黄金链路、下单后订单控制节奏,一切的拆分原则优先考虑高可用、高并发出发),恰当的服务粒度能够带来清晰的职责边界,提升系统的响应性和可靠性,同时降低维护的复杂性和成本。
在决定服务如何拆分或合并时,我们不仅要考虑技术因素,还要考虑团队结构、业务需求和未来的可扩展性。这需要我们深入理解业务逻辑,预见潜在的变化,并且勇于对现有架构进行调整。服务粒度的选择不是一成不变的,而是随着业务的发展和技术的进步而不断演化的。
我们不要简单地追求服务数量的增加,而是追求每个服务能够在正确的时间做正确的事情。通过精心设计服务粒度,构建出既强大又灵活的系统,这些系统能够支持我们的业务成长,应对未来的挑战。
如果您的团队面临服务泛滥,您会如何权衡服务的粒度呢?欢迎大家指正和完善,谢谢!
本文的观点源自我在学习与实践过程中的思考,尚处于不断探索和验证的阶段。希望能“抛砖引玉”,引发大家的思考与讨论,让我们共同进步,从而优化我们的系统设计。
附:
1、软件架构第一定律:软件架构中的一切都是在权衡
作者:京东物流 冯志文
来源:京东云开发者社区