1.4 技术栈对齐:Spring Boot 与云产品

技术栈选型最怕两件事:一是版本之间打架,二是自建中间件无人维护。我们团队吃过第一个亏:Spring Boot 用 3.x,结果公司内部的二方库还没支持 Jakarta 包名,编译期一堆错误。第二个亏也吃过:自己搭的 Redis 哨兵集群半夜主从切换失败,值班同学修到凌晨三点。所以最后核心中间件全部上云产品,自己只管业务代码。

场景:技术栈要回答的四个问题

订单中心的技术栈要回答四个问题:Web 框架选什么版本、数据访问层怎么组织、缓存与锁用啥、日志怎么收。每个问题都有「自建还是云产品」的子选择。我们的原则是:跟业务无关的基础设施一律用云产品,出了问题有 SLA 兜底;跟业务强相关的代码一律自己写,不依赖外部封装。

方案:版本对应表

Spring Boot 版本决定了配套生态的版本,选错一处全盘返工。这是我们对齐后的组合:

组件 版本 用途 选型理由
Spring Boot 2.7.x Web 框架 JDK 8 团队友好,生态最稳
MyBatis 3.5.x 数据访问 SQL 可控,分区表好优化
云数据库 MySQL 8.0 订单与库存库 分区表与窗口函数
云数据库 Redis 6.x 分布式锁与幂等 免运维主从
日志服务 CLS — 链路日志 采集与检索一体

为什么是 Spring Boot 2.7 而不是 3.x?团队 JDK 还在 8,且订单中心的依赖里 MyBatis 生态、连接池监控插件对 Jakarta 的支持当时还不完整。2.7 是 2.x 的末班车,能拿到安全补丁,升级路径也留好了。版本决策要把这些权衡写进文档,不然半年后没人记得为什么。

配置:pom 与数据源

依赖只列订单中心真正用到的核心三件,其他按需添加:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>2.3.x</version>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

数据源池由 Spring Boot 默认的 HikariCP 提供,无需额外引入依赖。配置走 application.yml,云数据库的连接串全部从环境变量注入,不进代码仓库。

数据源池有几个参数必须显式配,默认值不适合订单场景:

参数 值 理由
maximum-pool-size 20 单机过大会压垮 MySQL
connection-timeout 3000ms 下单链路对 Connection 敏感
idle-timeout 600000ms 与云数据库空闲连接回收策略对齐
max-lifetime 1800000ms 避免用到已断开的连接

踩坑:版本与云产品的三个坑

第一个坑是 MySQL 驱动版本。云数据库 MySQL 8.0 需要驱动 8.0.x,但项目里残留的 5.1 驱动在连接时会协商失败,报 SSL 相关错误。升级驱动后还要在 URL 上加 allowPublicKeyRetrieval=true 与 useSSL=true,否则登录云库会拒绝。

第二个坑是时区。CVM 与云数据库默认时区不一致,订单的创建时间出现 8 小时偏差。处理方式是服务端统一 serverTimezone=Asia/Shanghai,同时数据库也设成东八区。这个坑在做 对账作业 的日期切分时再次出现,批处理按自然日切数据,时区一错整天的数据都错位。

第三个坑是 Redis 客户端连接超时。云数据库 Redis 的内网地址首次解析有延迟,Lettuce 的默认超时 60 秒掩盖了连接问题,表现为启动慢而不是报错。把超时调到 3 秒后,故障立刻可观测。

另外提一个经验,云产品的 SDK 与 Spring Boot 的集成最好自己封一层薄适配,不要让业务代码直接调 SDK。订单中心只依赖薄接口,将来从云数据库迁到自建库的成本就低很多,这点在 腾讯云架构实践 里也有相通的讨论。

进阶实战:依赖治理的三条线

技术栈对齐之后,依赖治理同样重要。我们对所有二方与三方依赖画了三条线。第一条线是锁版本:所有依赖版本统一在父 pom 里声明,子模块不许私自升版本,防止同一项目出现三个版本的 httpclient。第二条线是依赖扫描:CI 里跑漏洞扫描,高危依赖必须在三天内升级,中等风险进排期。第三条线是白名单:新增中间件依赖必须过架构评审,不能谁开源用谁。

依赖类型 管理策略 更新频率
框架类(Spring Boot) 锁小版本 跟随安全补丁
数据库驱动 严格对齐 DB 版本 不主动升
工具类(fastjson 等) 优先换安全替代 立即处理
云产品 SDK 薄封装 + 接口隔离 按需

fastjson 值得单独一说。历史项目里有用 fastjson 做序列化,爆出反序列化漏洞之后,我们一次性替换成 Jackson,并配置全局禁止开启动态类型特性。替换的代价是几处兼容性处理,换来的是不再被漏洞清单追着跑。凡是带「动态类型」能力的序列化库,在订单这种对外暴露接口的系统里都值得评估。

速查清单

技术栈对齐的四条规矩:

规矩 说明
版本决策留档 选型理由写进文档
依赖锁版本 父 pom 统一声明
配置走环境变量 连接串不进代码库
SDK 薄封装 业务代码只依赖接口

小结

技术栈对齐的本质是降低「协作摩擦」:版本之间不冲突,云产品之间能联动,团队能 hold 住。记录选型的理由比选型本身更重要。到这里第一篇结束,下一章进入真正的设计:订单表与状态机。

常见误解:技术栈选型的三个陷阱

第一个陷阱是「追新」。Spring Boot 3 出来的时候有同事跃跃欲试,评估之后发现配套生态(监控、连接池增强、部分二方库)半年内跟不上,升级收益为零、风险翻倍。技术栈选型的首要原则是「优先稳定」:团队的成熟度比框架的先进性重要十倍。我们形成了一个惯例:主框架只在年度技术规划里评估升级,日常只跟安全补丁。

陷阱 现实
追新版本 生态跟不上更伤
云产品绑定焦虑 接口薄封装即可缓解
配置即代码 配置走环境变量

第二个陷阱是「云产品绑定恐惧」。很多人担心用云数据库就被绑架,其实用标准 MySQL 协议的云产品,迁移成本主要是数据搬迁而非代码重写。真正要警惕的是用了大量私有 API 的场景,所以我们只在必要的增强能力上用 SDK,核心访问走标准协议。薄封装那层换实现也方便,这种克制换来了迁移自由度。

第三个陷阱是「配置即代码的滥用」。有人喜欢把所有配置写死在代码里认为可靠,有人则全部塞进配置中心认为灵活。我们的界限是:与部署环境无关的放配置中心(限流阈值、超时时间),与实例相关的放环境变量(数据库地址、Redis 地址),绝密信息只放密钥管理。分类清晰,问题排查时也知道去哪看。

团队协作:技术栈的知识共享

技术栈定下来后,团队对每项选型写了「一页文档」,半页说选了什么、半页说为什么。新人入职第一周读完十页一页文档,技术背景就补齐了。比起厚重的架构 wiki,一页文档的优势是大家都真读、也真更新。

文档 保存形式 更新频率
技术选型一页纸 wiki 版本变更时
云产品对接手册 wiki 接口变更时
故障复盘 工单系统 每次故障后
上线检查表 发布平台 每季度

另一个有效实践是「云产品双周分享」。团队轮流讲一个云产品的使用经验:Redis 的高可用切换、MySQL 的参数调优、CLS 的检索语法。分享逼着每个人把「能用」升级为「会用」,半年下来团队的云产品知识明显拉平。工具再好,最终用它的还是人。

延伸阅读