首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >数字乡村项目做信创改造:MQ、缓存、注册中心替换踩坑记录

数字乡村项目做信创改造:MQ、缓存、注册中心替换踩坑记录

原创
作者头像
小小码农爱奋斗
发布2026-08-18 12:31:39
发布2026-08-18 12:31:39
1210
举报

去年接了一个县级数字乡村项目,甲方在合同附件里塞了一份信创清单,要求操作系统、数据库、中间件、CPU 架构全部换成国产方案。我们交付的是万村乐数字乡村系统的全开源版,前后端代码都在手上,理论上可以随便改。真动手才发现,替换中间件这件事,难的不是换依赖,是换完之后那些藏在细节里的行为差异。

原始技术栈是这样:JDK 1.8、MySQL 8.0.28、SpringBoot 2.5.4、MybatisPlus 3.5.1、Sa-Token 1.30.0、Hutool 5.7.22、Druid 1.2.8、Knife4j 2.0.9、EasyTrans 2.0.3,Java 版采用微服务架构做前后端分离。前端是 Vue 3.2.31、Ant-Design-Vue 3.2.10、Vite 2.8.6、Axios 0.24.0,村民端由 UNI-APP 打包成小程序和 App。

先摸清楚哪些是真依赖

替换之前我干的头一件事是把所有中间件调用点梳理一遍,用一个简单的脚本扫描依赖树。

扫出来的结果比想象中干净。系统对中间件的依赖点集中在四处:会话与验证码缓存走 Redis,短信与消息推送的异步化走 MQ,微服务之间的服务发现走注册中心,数据持久化走 MySQL 8.0.28 加 Druid 1.2.8 连接池。文件存储那块反而不用改,系统本身就支持本地、阿里云、腾讯云三种方式配置切换,信创项目直接选本地存储挂在国产分布式存储上就行。

真正让人头疼的是隐式依赖。Sa-Token 1.30.0 的分布式会话默认用 Redis 做存储,序列化方式是 JDK 原生序列化。如果替换成协议兼容但序列化行为有差异的产品,登录态会在切换瞬间全部失效。这类问题不看源码是发现不了的。

数据库:达梦是最省事的一环

MySQL 8.0.28 换达梦 DM8 是整个改造里阻力最小的。万村乐的可视化大数据平台在数据源管理上原生支持 MySQL、达梦、MongoDB、PostgreSQL、Oracle、SQLite、MsSQL,通过 SQL 语句直接拉取数据,大屏那侧几乎零改动。业务库这边需要处理的是方言和 SQL 兼容。

踩到的具体问题有三个。达梦默认大小写敏感,MySQL 建库时习惯写小写表名,迁过去之后所有 Mapper 里的 SQL 都报表不存在,解决办法是初始化实例时把CASE_SENSITIVE参数设成 0。GROUP_CONCAT在达梦里叫WM_CONCAT,系统里统计村民种养信息、家庭成员列表的几处聚合查询要改写。MySQL 8.0.28 的 JSON 列在达梦里对应CLOB加JSON_VALUE函数,低代码表单存 payload 那张表需要单独适配一版 Mapper。

字段类型上还有一个坑。达梦的VARCHAR长度按字节算,MySQL 8 的 utf8mb4 按字符算。村民姓名、村庄简介这类中文字段,如果直接照搬长度定义,插入时会报超长。我们的做法是迁移脚本里把所有VARCHAR(n)统一放大到VARCHAR(n*3),粗暴但有效。

缓存:协议兼容不等于行为兼容

Redis 这一环甲方给了两个选项,东方通 TongRDS 或者用开源方案自建。我们选了协议兼容的国产发行版,客户端依然用 Lettuce,改动量小。但真跑起来暴露了两个差异。

一个是SCAN命令的游标返回顺序不完全一致。系统里有一段清理过期验证码的定时任务,用SCAN遍历sms:code:*前缀,原来的实现假设游标单调递增到 0 就结束,换了实现之后偶发死循环。改成用 Spring Data Redis 的ScanOptions加最大迭代次数兜底之后正常。

另一个是 Lua 脚本的执行超时阈值不同。分布式锁那段用的是经典的SET NX PX加 Lua 解锁,脚本本身没问题,但国产实现的默认lua-time-limit更保守。高峰期批量推送通知公告时,村民数量上万,一次推送要拿几十次锁,偶尔触发脚本超时告警。把锁的粒度从"全村一把锁"改成"按村民 ID 取模分 16 把锁"之后,单次脚本执行时间从 40 毫秒左右降到 5 毫秒以内。

消息队列:把重试语义想清楚再换

系统里用 MQ 的场景有三类:短信验证码下发对接阿里云短信接口,通知公告的批量推送,物流状态回调对接快递 100。这三类对可靠性的要求完全不同,替换时不能一刀切。

短信下发是"宁可重发也不能漏发",因为村民收不到验证码就登录不了。通知公告是"宁可漏发也不能重发",同一条三务公开的公告推两遍,村民会以为村里出了什么事。物流回调是幂等的,重复几次无所谓。

我们把消息按语义分成三个 Topic,重试策略分别配置。国产 MQ 产品在死信队列的行为上和 RocketMQ 有差异,有的产品死信消息不会自动创建对应的 Topic,需要提前建好,否则消息直接丢弃。这个坑是压测时用一批故意抛异常的消息试出来的,如果不测,上线之后就是静默丢消息。

幂等键的过期时间设成 7 天,是根据阅读情况统计的业务窗口定的。系统会记录一条公告的推送人数、推送户数、已读人数、已读户数和占比,这份统计一般在发布后一周内看,超过这个窗口重复推送的概率可以忽略。

注册中心和国密改造

注册中心这块反而没什么波折。微服务数量不多,服务发现的行为在各家实现之间差异有限,把地址配置改掉、健康检查间隔从默认值调到 5 秒,跑起来就稳定了。需要注意的是优雅下线,SpringBoot 2.5.4 的spring.lifecycle.timeout-per-shutdown-phase默认 30 秒,国产注册中心的实例摘除延迟如果超过这个值,滚动发布时会出现短暂的 502。我们把摘除延迟压到 3 秒,同时在下线钩子里主动发注销请求。

国密这块是意外之喜。万村乐前端本身就依赖 sm-crypto 0.3.11,后端依赖 Smcrypto 0.3.2,也就是说 SM2、SM3、SM4 的能力代码里已经有了,只是原来只用在密码传输环节。信创验收要求敏感数据落库加密,我们直接复用这套库,把村民身份证号、手机号、家庭收入这几个字段改成 SM4 加密存储。

加密之后查询会受影响。手机号原来支持模糊搜索,加密之后没法做 LIKE。折中方案是额外存一列手机号后四位的 SM3 摘要,搜索时先按摘要过滤再解密比对,村民管理列表页的检索体验基本保住了。

一些实际结论

整个改造周期两个多月,其中真正写代码的时间不到三周,剩下的都花在验证和回归上。几点体会分享给同行。

替换顺序建议是数据库排前面,缓存和 MQ 排中间,注册中心排后面。数据库的改动辐射面广,越早暴露越好。注册中心的改动最独立,放最后做也不影响别的模块。

压测要用带异常的流量,不能只跑正常路径。死信 Topic 不存在、Lua 脚本超时、连接池耗尽这些问题,正常流量压不出来。我们的做法是在压测脚本里注入 5% 的故障请求,专门看异常路径的表现。

保留回退能力。数据库驱动、缓存客户端、MQ 客户端我们都做成了配置开关,一份代码可以在原方案和国产方案之间切换。验收结束之后这套开关没删,后来另一个项目要求换回 MySQL,改一行配置就搞定了。

信创改造本身没有多少技术含量,考验的是对系统内部依赖关系的掌握程度。源码在手是前提,如果拿到的是加密版本,很多适配根本无从下手。这也是我们在政务项目上倾向选开源交付版本的原因。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档