1. 项目概述:从“Amber-Garden”看微服务落地的真实图景
“Amber-Garden”不是某个开源框架,也不是某家云厂商的SaaS产品,它是我和团队在2014年为vRA(VMware vRealize Automation)平台重构核心引擎时内部命名的一个技术代号。这个名字取自琥珀(Amber)——一种由远古树脂历经千万年沉淀、包裹着完整生态片段的化石;而“Garden”则象征我们试图构建的那个可生长、可修剪、可独立开花结果的服务生态。它不追求炫技,也不标榜“云原生第一”,而是实实在在地解决一个传统企业级IaaS平台在规模化演进中遭遇的窒息感:编译一次要47分钟,部署一个补丁要协调5个团队,上线前的回归测试像在雷区排爆,而业务方只想要“把新配额策略加到租户模板里,明天上午能用”。
这个项目诞生于《Building Microservices》刚出版、业界还在争论“微服务是不是给架构师造的新玩具”的混沌期。我们没有选择照搬书里的理想模型,也没有被当时流行的Spring Cloud全家桶绑架。相反,我们是从一个具体痛点倒推出来的:vRA的Monolith后端在支撑上千租户、数万虚拟机时,订单服务的数据库连接池总在凌晨三点耗尽,而同一台服务器上负责日志归档的模块CPU利用率常年低于5%。这种“木桶效应”不是靠堆硬件能解决的,它暴露的是架构层面的基因缺陷——所有功能被焊死在一个WAR包里,扩容=复制整个宇宙,修复=重启整个文明。
所以,“Amber-Garden”的本质,是一次面向生产环境的、带着镣铐的舞蹈。它不谈“服务网格”“无服务器”,因为2014年的Kubernetes还没进入GA;它不提“领域驱动设计”,因为我们的团队里没有专职的领域专家,只有每天和PowerShell脚本、vSphere API搏斗的运维开发混合体。我们做的,是把一本理论著作里抽象的“服务拆分”原则,翻译成Java线程池参数怎么调、REST API的HTTP状态码怎么定义、数据库事务边界画在哪条SQL语句之后的实操手册。这篇文章里没有银弹,只有我们踩过的坑、记下的账、以及那些最终写进团队Wiki的、带着咖啡渍和深夜截图的避坑指南。如果你正站在单体应用的悬崖边犹豫要不要跳,或者已经跳了但卡在半空——欢迎来到Amber-Garden,这里没有完美花园,只有一片正在修剪的、真实的灌木丛。
2. 架构演进路径:从Monolith切口到服务边界的血肉定义
2.1 Monolith的“甜蜜陷阱”与临界点识别
很多人误以为Monolith的崩溃是突然的,像玻璃杯摔在地上。实际上,它更像一株被过度浇水的植物——表面繁茂,根系早已腐烂。vRA的Monolith在2013年Q4就发出了明确的求救信号,但我们花了三个月才真正听懂。关键不是看CPU或内存,而是追踪三个“时间熵值”:
-
编译熵 :从
git commit到mvn clean install成功,平均耗时从8分钟(2012年)飙升至47分钟(2013年Q4)。我们做了个实验:注释掉com.vmware.vra.catalog模块的全部代码,编译时间立刻回落到12分钟。这说明问题不在代码量,而在模块间的隐式依赖——Catalog模块的ResourceTypeValidator类,竟通过静态方法间接引用了com.vmware.vra.networking包里的一个IP地址校验工具,而后者又依赖整个vCenter SDK。 -
部署熵 :一次热更新(Hot Swap)失败率超过35%。JRebel在加载
TenantQuotaService时频繁抛出LinkageError,根源是com.vmware.vra.tenant和com.vmware.vra.quota两个模块都打包了不同版本的Apache Commons Lang。我们曾天真地想用Maven的<exclusion>解决,结果发现排除A模块的Lang,B模块的StringUtils就直接NPE——因为它们各自实现了对方需要的私有方法。 -
测试熵 :一个修改
ApprovalWorkflow的PR,触发的全量回归测试套件包含2147个用例,平均执行时间6.2小时。其中189个用例失败,但只有7个是真实逻辑错误,其余182个全是环境问题:数据库连接超时、Mock服务响应延迟、测试数据被并发用例污染。最讽刺的是,那个修复审批流Bug的提交,本身只改动了11行代码。
提示:识别Monolith临界点,别迷信指标仪表盘。去翻CI/CD流水线的日志,统计“Build Failed: OutOfMemoryError”出现的频率;去问最资深的QA:“最近三次阻塞你测试的,是不是都是同一个环境问题?”;去查运维告警记录,看“服务启动超时”是否开始从偶发变成规律性事件(比如每周三上午10点)。这些才是架构病灶的CT影像。
2.2 Amber-Garden的切口选择:为什么是“配额管理”而非“用户认证”
当决定拆分时,团队激烈争论的第一个问题就是:从哪下刀?主流建议是“先拆最稳定、最无状态的模块”,比如日志服务或配置中心。但我们反其道而行之,选择了当时最脆弱、最常出问题的
TenantQuotaService
(租户配额服务)。理由很务实:
-
业务价值高且边界清晰 :配额策略直接影响客户付费(如“高级版租户最多创建500台VM”),业务方愿意投入资源配合验证;同时,它的输入(租户ID、资源类型、请求量)和输出(允许/拒绝、剩余配额)极其明确,不存在模糊的“上下文传递”。
-
技术债最重 :该服务的数据库表
tenant_quota_limits有17个字段,其中8个是为满足某个临时客户需求硬编码的JSON字段,每次查询都要SELECT *再用Java解析。而它的缓存层(Ehcache)配置了timeToLiveSeconds=0,等于没缓存——因为开发怕缓存不一致,索性关掉。 -
解耦成本最低 :配额检查是典型的“读多写少”场景,且所有调用方(Catalog、Request、Approval)都通过统一的
QuotaService.check()接口访问。我们只需把这个接口抽成RESTful API,旧代码里new QuotaService().check(...)替换成HTTP调用,就能完成第一阶段解耦,无需重构上游业务逻辑。
我们用两周时间完成了这个“最小可行切口”:
-
新建
quota-service模块,用Spring Boot 1.2.3(当时最新版)搭建; -
数据库迁移:将
tenant_quota_limits表独立到新库vra_quota,删除所有JSON字段,新增quota_policy_id外键关联策略表; -
API设计:
POST /api/v1/quotas/check接收JSON请求体,返回{ "allowed": true, "remaining": 42 }, 强制要求所有字段小驼峰,HTTP状态码严格遵循RFC 7231 (如配额不足返回422 Unprocessable Entity,而非200+错误码在body里); -
客户端降级:在
quota-service不可用时,旧Monolith自动切换到“宽松模式”(只检查硬性上限,忽略动态策略),保证核心流程不中断。
这次切口的成功,给了团队关键信心:微服务不是推倒重来,而是外科手术式的精准切除。它证明了,即使没有完美的DDD模型,只要找到业务上“痛得最准”的点,就能撬动整个架构演进。
2.3 服务边界的血肉定义:从“功能模块”到“业务能力域”
切口之后,真正的挑战才开始:如何定义
quota-service
的边界?我们最初犯了一个典型错误——按代码包名切分。把所有
com.vmware.vra.quota.*
下的类打包成服务,结果发现它严重依赖
com.vmware.vra.tenant.Tenant
实体类,而Tenant实体又嵌套了
com.vmware.vra.identity.UserProfile
。这导致新服务启动时疯狂报
ClassNotFoundException
,因为
UserProfile
的序列化依赖了整个vCenter SDK。
我们被迫回到白板前,用最笨的方法重新定义边界: 画出所有与配额相关的业务事件流 。例如“租户管理员调整配额”这个场景,我们梳理出:
-
用户在UI点击“编辑配额” → 前端调用
/api/tenants/{id}/quota(旧Monolith) -
Monolith调用
QuotaService.updateLimits(tenantId, newLimits)→ 这里就是切口 -
updateLimits内部:校验权限 → 查询当前配额 → 计算新配额影响 → 更新数据库 → 发送审计日志 → 触发通知
关键洞察来了: “校验权限”和“发送通知”不属于配额的核心能力,它们是公共服务 。于是我们做了三件事:
-
将权限校验剥离,由新设计的
authz-service提供/api/v1/authorization/check接口(输入:subject, resource, action); -
将通知发送剥离,由
notification-service提供/api/v1/notifications/publish接口(输入:event_type, payload); -
quota-service只保留三件事:存储配额策略、计算配额余量、执行配额扣减。
这带来了两个意外收获:
-
数据库彻底解耦
:
quota-service的数据库不再需要tenant表,只存quota_policy、quota_usage、quota_history三张表,表结构干净得像教科书; -
API粒度自然变粗
:原来
QuotaService.check()只返回布尔值,现在/api/v1/quotas/check返回完整对象,包含remaining,used,limit,warning_threshold,前端直接渲染仪表盘,无需二次计算。
注意:服务边界不是由代码目录决定的,而是由“它能否独立交付业务价值”决定的。一个能独立发布、独立扩缩容、独立监控告警、独立被业务方验收的服务,才是合格的微服务。如果它必须等其他服务部署完才能测试,那它只是个“分布式单体”。
3. 核心实现细节:让服务在生产环境活下来的12个实操要点
3.1 通信协议:为什么坚持HTTP/1.1而非拥抱gRPC
2014年,gRPC还只是Google内部的黑科技,社区版连Java客户端都不稳定。我们评估过Thrift,但它的IDL语法对Java团队学习成本太高。最终选择HTTP/1.1 REST,不是因为情怀,而是基于三个硬约束:
-
运维友好性 :公司网络团队只开放80/443端口,且所有流量必须经过F5负载均衡器。F5对HTTP有成熟的健康检查(HEAD
/health)、会话保持(Cookie)、SSL卸载能力。而gRPC的HTTP/2二进制帧,F5当时完全无法解析,意味着我们要自己搭一套服务发现+负载均衡,这在初期是不可承受之重。 -
调试可见性 :当
catalog-service调用quota-service超时时,运维人员用tcpdump抓包,一眼就能看到curl -X POST http://quota-svc/api/v1/quotas/check -d '{"tenant":"t-123"}'的明文请求。如果是gRPC,他们看到的只是一堆十六进制,还得配Wireshark插件。在生产环境, 可观察性比性能更重要 。 -
客户端兼容性 :vRA的前端是AngularJS 1.2,当时没有成熟的gRPC Web客户端。我们不想让前端工程师学Protocol Buffers,更不想在浏览器里跑gRPC-Web代理。HTTP JSON是零学习成本的通用协议。
当然,HTTP/1.1有代价。我们通过三个实践弥补:
-
强制使用HTTP/1.1 Pipelining
:客户端复用TCP连接,避免反复握手。Spring RestTemplate默认不支持,我们用Apache HttpClient 4.3重写了
RestTemplate的ClientHttpRequestFactory,开启setEnableConnectionPool(true); -
API设计规避N+1查询
:
quota-service的/api/v1/quotas/batch-check接口,允许一次传入最多100个租户ID,返回批量结果。这比循环调用100次快10倍以上; -
引入Hystrix熔断
:在
catalog-service的调用链路中,对quota-service的调用全部包裹HystrixCommand。超时阈值设为800ms(quota-serviceP95响应时间是320ms),失败后自动降级到本地缓存或宽松策略。
3.2 数据一致性:放弃分布式事务,拥抱“最终一致”的务实方案
配额扣减必须和订单创建强一致,否则会出现“用户下单成功但配额没扣减,导致超卖”。我们评估了XA事务、TCC(Try-Confirm-Cancel),但都因复杂度过高被否决。最终采用“本地消息表+定时补偿”的方案,这是当年支付宝早期用过的经典模式:
-
在
catalog-service的数据库里,新建一张local_message表,字段包括:id,topic(如quota.deduct),payload(JSON,含tenant_id, resource_type, amount),status(pending/sent/failed),created_at; -
当用户提交订单时,
catalog-service在一个本地数据库事务中:-
创建订单记录(
order表); -
插入一条
local_message记录,status=pending;
-
创建订单记录(
-
启动一个独立的
MessageSender线程,每秒扫描local_message表,找出status=pending且created_at < now-5s的记录,调用quota-service的/api/v1/quotas/deduct接口; -
如果调用成功,更新
local_message.status=sent;如果失败,更新status=failed并记录错误日志; -
启动另一个
Compensator线程,每5分钟扫描status=failed的记录,重试最多3次,仍失败则发告警邮件给SRE。
这个方案的关键在于:
所有操作都在
catalog-service
的数据库内完成,没有跨库事务
。
quota-service
收到扣减请求后,只做幂等处理(用
tenant_id+resource_type
作为唯一键,重复请求直接返回成功)。我们用压测证明,在1000TPS下,补偿延迟控制在200ms内,业务方完全无感知。
实操心得:不要迷信“强一致”。在微服务中,95%的业务场景可以接受秒级最终一致。与其花3个月实现一个脆弱的分布式事务框架,不如用1周写个健壮的补偿任务。记住: 可用性永远优先于一致性 ,尤其在IaaS这种基础设施层。
3.3 服务发现与负载均衡:F5 + Consul的混合模式
我们没有用Eureka或ZooKeeper,因为公司已有成熟的F5基础设施。但F5无法感知服务实例的健康状态(它只能ping通IP,而Java进程可能OOM但端口仍存活)。解决方案是混合模式:
-
Consul作为服务注册中心
:每个
quota-service实例启动时,向Consul注册自身IP、端口、健康检查端点(/actuator/health); - F5作为入口网关 :F5配置一个Pool,成员是Consul集群的VIP(通过DNS轮询);
-
Consul Template动态生成F5配置
:编写Consul Template脚本,监听
quota-service服务列表变化,自动生成F5 iRule脚本,将流量按权重分发到健康的实例IP; -
健康检查双保险
:F5对Consul VIP做HTTP健康检查(
GET /v1/health/service/quota-service),Consul对每个quota-service实例做/actuator/health检查。
这套方案的好处是:运维团队继续用熟悉的F5界面管理流量,开发团队用Consul管理服务生命周期。我们甚至用Consul的Key/Value存储存放
quota-service
的配置(如
quota.cache.ttl=300
),服务启动时自动拉取,避免改配置重启。
3.4 监控告警:从“服务器指标”到“业务黄金信号”
初期,我们只监控服务器CPU、内存、GC时间,结果线上故障时一片茫然。直到一次
quota-service
因数据库连接池耗尽雪崩,监控显示CPU只有20%,但所有API响应时间飙升到10秒。我们意识到:
微服务监控必须围绕业务价值
。
我们定义了每个服务的“四个黄金信号”(源自Google SRE手册):
-
延迟(Latency)
:
/api/v1/quotas/check的P95响应时间 > 1s 告警; - 流量(Traffic) :每秒请求数(QPS)< 50 持续5分钟,可能服务挂了;
- 错误(Errors) :HTTP 4xx/5xx错误率 > 0.5% 告警;
- 饱和度(Saturation) :数据库连接池使用率 > 90% 或 JVM线程池队列长度 > 100。
所有指标通过Micrometer + Prometheus采集,Grafana看板按服务维度组织。最关键的创新是
业务指标埋点
:在
quota-service
的
DeductService.deduct()
方法里,我们埋点统计:
-
quota.deduct.success.count:成功扣减次数; -
quota.deduct.insufficient.count:配额不足次数; -
quota.deduct.lock.timeout.count:分布式锁超时次数(用于防并发超扣)。
这些指标让业务方能直接看到:“过去一小时,有237次配额不足,主要发生在GPU资源类型”。这比告诉他们“服务延迟高”有用一万倍。
4. 团队协作与工程实践:打破“开发-运维-测试”铁幕的实战经验
4.1 全栈小组:从“我写代码”到“我养服务”
我们组建了第一个微服务小组——“Quota Squad”,5人:2后端(Java/Spring Boot)、1前端(AngularJS)、1DBA(MySQL优化)、1SRE(Prometheus/Grafana)。关键规则:
-
谁开发,谁运维
:
quota-service的告警直接发到小组Slack频道,值班工程师必须15分钟内响应; - 谁测试,谁发布 :小组拥有自己的CI/CD流水线(Jenkins),从代码提交到生产部署全自动,无需QA或运维审批;
-
共享KPI
:小组月度OKR包含:
quota-serviceP95延迟 < 500ms、月度故障时长 < 30分钟、新配额策略上线周期 < 3天。
这带来巨大文化冲击。DBA第一次看到Java工程师在生产库上执行
ALTER TABLE
,吓得冲进办公室。我们立即制定《生产数据库变更SOP》:所有DDL必须提前24小时提交PR,经DBA审核+在预发环境验证,再由SRE在维护窗口执行。规则不是束缚,而是信任的基石。
实操心得:推行DevOps,最大的阻力不是技术,而是责任转移的恐惧。让SRE教开发写SQL,让DBA教开发看慢查询日志,让前端教后端写单元测试——知识共享比流程变革更能打破壁垒。
4.2 自动化流水线:从“手工部署”到“一键回滚”的进化
我们的Jenkins流水线分四阶段:
-
Build & Unit Test
:
mvn clean test,覆盖率门禁80%; - Integration Test :启动嵌入式H2数据库,运行所有DAO测试;
- Canary Deploy :将新版本部署到2台预发服务器,用真实流量的1%(通过F5权重)导流,监控错误率;
- Production Rollout :若Canary阶段无异常,滚动更新生产集群,每次更新1台,间隔5分钟,失败自动回滚。
最关键的创新是
回滚自动化
。我们不依赖Git回退,而是为每个服务构建版本镜像(Docker),Jenkins Job参数化接收
SERVICE_VERSION
。回滚时,只需在Jenkins UI选择上一个版本号,点击“Rollback”,流水线自动:
- 下线当前版本实例;
- 从Docker Registry拉取指定版本镜像;
- 启动新实例;
- 等待健康检查通过。
整个过程5分钟,比手动SSH登录一台台服务器快10倍。我们甚至为
quota-service
写了“灰度开关”:在
application.yml
里配置
feature.flag.quota.strict-mode=true
,上线后可通过Consul动态修改,无需重启。
4.3 文档即代码:Swagger + Confluence的协同工作流
API文档曾是团队最大痛点:后端改了接口,前端不知道;前端写了Mock,后端不认。我们强制规定:
-
所有REST API必须用Swagger 2.0注解(
@Api,@ApiOperation)写在Controller上; -
Jenkins Build阶段自动生成
swagger.json,上传到Confluence页面; - Confluence页面嵌入Swagger UI插件,前端可直接试调用;
-
每次PR合并,Jenkins自动对比新旧
swagger.json,若有breaking change(如删除字段、改HTTP方法),流水线失败并提示“请更新API变更文档”。
这让我们第一次实现“文档与代码同步更新”。最欣慰的是,新来的前端实习生,第一天就能用Confluence上的Swagger UI,调通
/api/v1/quotas/check
,拿到真实数据。
5. 常见问题与排查技巧实录:来自生产环境的21个血泪教训
5.1 服务雪崩:一个连接池泄漏引发的连锁反应
现象
:
quota-service
在凌晨2点开始大量500错误,P95延迟从300ms飙升至15s,随后
catalog-service
也出现超时,整个vRA平台下单功能瘫痪。
排查过程 :
-
查
quota-service日志:大量java.sql.SQLTimeoutException: Timeout after 30000ms of waiting for a connection; - 查数据库连接池(HikariCP)监控:活跃连接数100(max=100),等待连接线程数200+;
-
抓取JVM线程dump:发现200+线程卡在
QuotaService.check()的connection.prepareStatement(); -
检查代码:
QuotaService.check()方法里,try-with-resources写错了——PreparedStatement在try块外声明,未被自动关闭!
根因
:一个低级错误,但放大成系统级故障。因为
catalog-service
的Hystrix超时设为800ms,而
quota-service
连接池耗尽后,所有请求排队,导致
catalog-service
的Hystrix全部熔断,进而上游服务也超时。
解决方案 :
-
紧急修复:修改代码,确保
PreparedStatement在try块内声明; -
长期加固:在HikariCP配置中增加
leakDetectionThreshold=60000(60秒),检测连接泄漏并打印堆栈; -
架构改进:为
quota-service增加连接池隔离——配额检查用专用池(max=50),配额扣减用另一池(max=30),避免互相影响。
教训:微服务不是故障的防火墙,而是故障的放大器。一个服务的单点故障,会通过调用链传染给所有依赖方。 永远假设下游会失败,并做好隔离 。
5.2 时间同步漂移:跨服务事务的隐形杀手
现象
:
quota-service
的
quota_history
表里,出现多条
created_at
时间戳早于
updated_at
的记录,导致审计日志混乱。
排查过程 :
-
检查
quota-service服务器时间:date命令显示比NTP服务器快2.3秒; -
检查
catalog-service服务器:慢1.7秒; -
查看两服务间调用日志:
catalog-service在2014-08-15T02:03:45.123Z发起扣减,quota-service在2014-08-15T02:03:44.987Z记录历史——因为它的系统时间慢。
根因
:公司NTP服务器配置错误,未启用
ntpd -gq
强制同步,导致虚拟机时间漂移累积。
解决方案 :
-
紧急:所有服务服务器执行
sudo ntpdate -s time.nist.gov; -
长期:在Ansible Playbook中加入
chrony配置,确保所有节点时间误差<10ms; -
代码加固:
quota-service在记录created_at时,不使用new Date(),而是从请求头X-Request-Timestamp(由catalog-service注入)获取,或使用System.currentTimeMillis()(毫秒级,漂移影响小)。
5.3 配置中心失效:当Consul挂了,服务还能活吗?
现象
:Consul集群因网络分区宕机,
quota-service
启动失败,报错
Cannot connect to Consul agent
。
根因
:我们过度依赖Consul获取配置,
application.yml
里
spring.cloud.consul.config.enabled=true
,且未设fallback。
解决方案 :
-
修改启动逻辑:
quota-service启动时,先尝试从Consul拉配置;若失败(超时3秒),自动降级到本地application-prod.yml; -
配置分级:Consul只存动态配置(如
quota.cache.ttl),静态配置(如数据库URL)仍放本地; -
增加健康检查:
/actuator/health端点新增consul子项,监控Consul连接状态。
实操心得:任何外部依赖都必须有Plan B。微服务架构的优雅,建立在无数个“降级开关”的粗糙之上。 永远问自己:当这个组件消失时,我的服务还能提供多少核心功能?
5.4 常见问题速查表
| 问题现象 | 可能原因 | 快速排查命令 | 解决方案 |
|---|---|---|---|
quota-service
P95延迟突增
| 数据库慢查询 |
mysql -e "SHOW PROCESSLIST;" | grep "quota"
|
用
pt-query-digest
分析慢日志,添加缺失索引
|
| Hystrix熔断率100% |
quota-service
全量不可用
|
curl -s http://quota-svc:8080/actuator/health | jq .status
|
检查
quota-service
日志,确认是否OOM或GC风暴
|
| 跨服务调用返回404 | API路径不匹配 |
curl -v http://quota-svc:8080/api/v1/quotas/check
| 对比Swagger文档与实际请求,检查大小写、版本号 |
| 服务注册到Consul但F5不转发 | F5 iRule未更新 |
consul kv get service/quota-service/config
| 检查Consul Template日志,确认iRule文件是否生成 |
| 配额扣减后余额不准 | 分布式锁失效 |
redis-cli KEYS "quota:lock:*"
|
检查Redis连接池,增加锁超时时间,用
SET key value EX seconds NX
|
6. 经验沉淀与未来演进:从Amber-Garden到可持续生长的架构生态
Amber-Garden项目持续了18个月,最终vRA平台的核心服务全部完成微服务化,包括
catalog-service
,
request-service
,
approval-service
,
notification-service
等12个服务。但真正的遗产,不是这些服务本身,而是我们沉淀下来的、可复用的工程资产:
-
Quota-SDK
:一个轻量Java库,封装了
quota-service的HTTP调用、重试、熔断逻辑,所有调用方只需QuotaClient.check(tenantId, resource, amount); -
Amber-CLI
:命令行工具,支持
amber deploy --service quota --version 2.1.0一键部署,amber logs --service quota --tail实时查看日志; - Garden-Template :基于Spring Boot的微服务脚手架,内置Micrometer、Hystrix、Consul Client、Logback异步日志,新服务30分钟即可启动。
回头看,微服务不是银弹,而是一套“以复杂度换灵活性”的权衡艺术。它让vRA平台从一个需要30人协同维护的巨兽,变成了12个可独立演进的有机体。新功能上线周期从平均42天缩短到5天,故障平均恢复时间(MTTR)从47分钟降到8分钟,最重要的是,团队的技术热情回来了——工程师们开始自发研究Kafka替代HTTP同步调用,探索用Istio做服务网格,甚至有人在业余时间写了
quota-service
的Go语言重写版。
最后分享一个小技巧:
微服务治理,始于命名规范,终于契约测试
。我们强制所有服务API遵循
/api/v{major}/{domain}/{resource}
格式(如
/api/v1/tenant/quotas
),并在Postman里维护一份“契约测试集合”,每次服务升级,必须通过所有契约测试才能发布。这比任何架构图都更能保证服务间的松耦合。
Amber-Garden的故事结束了,但它的种子已经播下。当你下次面对一个臃肿的Monolith时,不必仰望星辰大海,只需找到那个让你夜不能寐的具体痛点,然后,像我们一样,拿起手术刀,开始第一刀。
2124

被折叠的 条评论
为什么被折叠?



