系统的复杂性
我们团队负责的系统是分布式微服务部署架构,随着业务的不断发展壮大和多条线场景化的持续建设丰富,系统的业务逻辑越来越多,功能逻辑也越来越复杂。
系统早期单个应用的一个用户故事地图

系统交互


物理模型(库表)的复杂性

一个子系统的代码沉淀


在应用部署方面,目前现状我们的一个应用对应一个coding代码地址,部署以一个应用为单位发起部署申请,应用下有多个集群,集群下有多个分组,也区分灰度环境、正式线上环境。通过不同的部署编排,使用不同的代码版本部署不同的环境。
系统的复杂性来自多个方面:业务流程复杂性、架构复杂性、代码实现复杂性、物理模型(库表)的复杂性、监控运维的复杂性等。本文重点不是系统复杂性的治理,而是在现有基础上,如何低成本轻量级方式服务隔离,在大促为系统的稳定性中发挥作用。
一个Docker容器中部署的应用Java进程内,提供了各种各样的服务,以在库应用为例,包含了盘点、变更、补货、移库、盘盈亏、预包等相对独立的功能,每个功能又有自己的单据-任务-结果整套业务流程。既有RESTful服务,也有JSF服务,还有MQ消息处理,另外还有定时任务。这些资源虽有线程池隔离,但CPU、内存等资源仍是共享资源,在负载高的时候,比如CPU满载或内存OOM时,会造成服务卡顿,RT时间长,影响服务响应和功能使用。
方案
方案一:应用拆分
按业务域、技术域对进行拆分,比如在库应用按盘点、变更、移库、补货等拆分为单独的应用,不仅应用部署做了拆分,对应的数据库层面也按域进行拆分,盘点相关的表,例如盘点单主档、盘点单明细、盘点任务主档、盘点任务明细、盘点结果独立到单独的库中,可以按逻辑库独立,也可以独立到单独的数据库实例中,后者的隔离效果更好。在代码层面,可以将在库coding按域拆分出来单独的代码库,也可以不独立,保持共享代码库,只是在编译时按moudle进行按需集成,例如为盘点应用编译时,包含盘点moudle、公共module,其他不需要的moudle,比如变更module、补货module则不需要参与编译集成。
方案二:使用Hystrix进行服务隔离
Hystrix 主要实现的是进程内隔离,具体来说,它通过线程池隔离和信号量隔离两种机制,在单个应用进程内部对依赖服务的调用进行资源隔离和故障控制。
线程池隔离
Hystrix 为每个依赖服务分配独立的线程池,不同服务的调用请求在各自的线程池中执行,避免因某个服务故障或延迟耗尽整个应用的线程资源,这种隔离方式类似于“舱壁隔离”,将故障限制在特定范围内。
信号量隔离
通过控制并发请求的线程数(信号量阈值)实现隔离,适用于耗时短、并发量高的场景(如读缓存)。信号量隔离是同步阻塞方式,不涉及线程切换,开销较低。
方案三:轻量级进程间服务服务隔离
既不拆分应用,也不需要引入Sping Cloud Hystrix组件,不侵入业务代码,在部署层面实现服务隔离,属于应用内分组机器实例隔离,也是进程间服务隔离。数据库和代码库层面不需要隔离,仍采用共享模式。
以在库为例,为盘点、补货、变更等创建不同的业务分组,当然处于高可用考虑,会为盘点、补货、变更等每个业务分组,又会横跨多个机房分组,不如中云信机房分组、有孚机房分组。
本文探索实践的方案三示意图如下:

方案简单对比和选择
| 方案 | 隔离粒度 | 隔离效果 | 代码侵入性 | 实现成本 | 落地风险 | 落地速度 |
| 方案一:应用拆分 | 应用间隔离 | 高 | 高 | 高 | 高 | 慢 |
| 方案二:使用Hystrix进行服务隔离 | 线程间隔离 | 低 | 中 | 中 | 中 | 中 |
| 方案三:轻量级进程间服务服务隔离 | 进程间隔离 | 中 | 低 | 低 | 低 | 快 |
本文旨在探索一个轻量级的进程级服务隔离方法,短平快,易落地,见效快,可以在大促中快速发挥作用,保障系统的稳定性。
在方案选择上,本文选择方案三进行实操落地。选择方案三,是因为方案三很牛吗?不是的,相比之下方案一和方案二方案更为成熟,行业落地经验更为丰富。
之所以选择方案三,是在众多的因素考量中折中选择,在不同的场景下,采用合适的方案解决相应的痛点,够用 + 1,easy + 1。
方案二和三之间并无冲突,其实可以结合搭配使用。
实操
隔离部署分组
配置集合
通过配置集合,实现分组间共享配置,方便多分组管理。

跨机房多机房部署
通过多机房部署实现服务高可用。

隔离NP域名
按域隔离的RESTful,创建单独的NP域名。



NGINX拆分流量
拆分upstream,按照不同域RESTful方法的规则进行路由拆分配置。


JSF服务隔离
别名拆分,通过别名隔离服务,调用方无需改动。



随着服务隔离,同时兼顾机器资源利用率,拆分后的单域内机器数量少于拆分前机器数量,JSF业务线程池大小可适当调大,JSF的单机限流阈值也适当调大。
MQ消息队列隔离
在变更的yml中,只保留变更相关的TOPIC,其他置为NONE。

在盘点的yml中,只保留盘点相关的TOPIC,其他置为NONE。

其他分组按此调整配置。
落地效果
RESTFul服务
对应的logbook自然地按域拆分,方便查询定位流量机器。


JSF服务
通过隔离的JSF别名实现流量路由到的机器。

未来演进
目前,在应用稳定方面,探索并实践落地了一种轻量级进程间服务隔离单元化部署方法,在库和库存按业务域拆分服务部署单元化分组,在库按盘点、补货、变更、导出导出、通用服务部署,库存按库存查询、库容服务、高时效、worker服务等作为独立部署的部署单元,控制爆炸半径,每个部署单元都是双机房高可用,保障系统的稳定性。
未来,随着系统的长期发展,系统复杂性需按域合理拆分治理,业务单元化,服务单元化,系统演进与业务发展齐头并进,相互促进,使系统始终保持在健康的水位,可持续发展。





