物联网项目架构演进——MinIO本地存储迁移实战
IoT存储模块技术演进:从"三选一"到MinIO统一架构
本文面向进阶开发人员,用通俗易懂的方式介绍常见项目中实现文件存储架构的升级之路。
一、为什么需要文件存储?
想象一下,我们的物联网平台每天会产生大量文件:
- 🖼️ 设备截图:设备上报的故障图片
- 🗺️ 区域地图:园区、厂区的平面图
- 📱 设备图标:不同设备类型的显示图标
- 📹 视频片段:监控摄像头的录像
这些文件不能直接存放在数据库里(数据库擅长存表格数据),需要专门的文件存储服务来管理。
二、项目曾使用的"三选一"文件存储架构
在项目迭代初期,为了满足不同场景的需求,我们同时实现了三种文件存储方案:
| 方案 | 特点 | 适用场景 |
|---|---|---|
| FastDFS | 开源的分布式文件系统 | 早期生产环境 |
| 阿里云OSS | 阿里云提供的云存储服务 | 需要弹性扩展时 |
| MinIO | 轻量级对象存储,兼容S3协议 | 本地开发和测试 |
问题来了:三种方案并存有多麻烦?
- 代码臃肿:每种方案都有一套独立的代码,维护三套代码很费力
- 配置混乱:不同环境要用不同配置,容易出错
- 逻辑重复:三个方案的上传、下载逻辑高度相似,但各自实现
- OSS安全性:虽然实现了OSS,但由于是云端存储,需考虑项目中涉及文件的保密性
另外由于项目多次迭代,出现旧版文件上传逻辑与新版并存,使得storage模块膨胀
三、为什么最终选择MinIO?
经过评估,我们决定统一使用MinIO,原因很简单:
1. 轻量级,部署简单
MinIO可以像普通程序一样运行,不需要复杂的集群配置。一台服务器就能搞定,开发环境甚至可以用Docker一键启动。
2. 完全开源
不像阿里云OSS需要花钱,MinIO完全免费开源,适合中小项目使用。
3. 兼容S3协议
S3是亚马逊推出的对象存储标准协议,很多云厂商都支持。这意味着未来如果需要迁移到云存储,可以无缝切换。
4. 功能足够用
对于我们的物联网场景(图片、小视频),MinIO提供的功能完全够用:
- 文件上传/下载
- 自动创建存储桶
- 生成访问链接
5. 服务器迁移便利,配置灵活(最主要)
- MinIO 的所有连接信息都集中在配置文件中,与业务代码解耦。当项目服务器进行内外网隔离、MinIO 服务 IP发生变化或整体迁移时,只需修改
application.yml中的minio.endpoint和minio.domain,无需改动任何 Java源码。
重启服务后,所有文件的上传、下载和预览链接都会自动指向新地址,实现零代码迁移。 - 同样,MinIO的访问密钥(accessKey/secretKey)也通过配置管理,当安全策略调整时仅需更新密钥,整个过程对业务无侵入,大幅降低运维成本和停机风险。
四、改造后的架构:简单就是美
改造前的混乱架构
外部调用者(管理后台、设备服务等)
↓
FileUploadUtils(统一入口,但要判断用哪个方案)
├─ 判断1:是MinIO吗?→ MinioServiceImpl
├─ 判断2:是OSS吗?→ OssServiceImpl
└─ 默认:FastDFS → FastDFSUtils
改造后的清晰架构
外部调用者
↓
FileUploadUtils(统一入口,直接调用)
↓
IStorageService(接口,定义了上传下载方法)
↓
MinioStorageServiceImpl(唯一实现,所有文件都走这里)
↓
MinioClient(MinIO官方SDK)
核心变化:去掉了所有判断逻辑,所有文件操作统一走MinIO一条路。
五、文件是怎么存的?—— object-key命名规范
您可能会好奇,这么多文件怎么管理才不会乱?由此我设计了一套"文件路径"规则:
按业务类型分类存储
| 文件类型 | 存储路径示例 | 说明 |
|---|---|---|
| 区域图 | zone/1/yyyyMMddHHmmssSSS.jpg |
区域1的图片 |
| 设备类别图标 | insType/1/icon/yyyyMMddHHmmssSSS.jpg |
设备类型1的图标 |
| 设备图片 | device/123/yyyyMMddHHmmssSSS.jpg |
设备123某天的截图 |
| 公共上传 | public/yyyyMMddHHmmssSSS.png |
通用文件 |
为什么这样命名?
- 一目了然:看到路径就知道是什么文件、属于谁
- 便于管理:按业务分类,可以方便地查找和删除
- 避免重名:文件名用时间戳(精确到毫秒),几乎不可能重复
六、内外网地址分离:一个实用的小技巧
在实际部署中,我们遇到一个问题:
- MinIO服务在内网运行(比如
http://192.168.1.100:9000) - 前端需要从外网访问这些文件
如果直接把内网地址返回给前端,用户肯定访问不了。
解决方案:配置两个地址
minio:
endpoint: http://192.168.1.100:9000 # 内部访问地址
domain: http://www.example.com:9000 # 外部访问地址
这样:
- 程序内部用
endpoint连接MinIO - 返回给前端的URL用
domain
即使MinIO服务迁移了,只要更新 domain 配置,数据库里的旧URL依然有效。
七、改造带来的好处
1. 代码量大幅减少
- 删除了 20个文件(三套方案的legacy代码)
- 核心工具类从 244行精简到144行(去掉了路由判断逻辑)
2. 配置更简单
以前需要配置 fileService(选哪个方案)、fdfs.*(FastDFS配置)、spring.cloud.alicloud.*(OSS配置),现在只需要配置 minio.* 相关参数。
3. 外部调用方零改动
所有使用文件上传功能的模块(管理后台、设备服务、通信服务)都不需要修改代码,因为我们保留了原来的API接口。
八、给初级开发者的建议
1. 接口先行,实现后置
先定义好接口(如 IStorageService),再写具体实现。这样未来如果需要换存储方案,只需要写一个新的实现类即可。
2. 保持API兼容性
在重构时,尽量保留原来的接口方法签名,这样调用方不需要修改代码。
3. 分阶段实施
不要一次性把所有代码都改了,可以分阶段:
- 第一阶段:实现新的统一方案
- 第二阶段:清理旧代码
- 第三阶段:验证测试
4. 配置解耦很重要
把连接地址和对外地址分开配置,可以避免很多麻烦。
总结与优化
虽然现在已经很简洁了,但我们还有一些改进计划:
- 文件自动过期:设备上报的图片可能只需要保留30天,可以配置自动删除
- 图片加速:对接CDN,让图片加载更快
- 分类统计:通过标签(Tag)功能,统计不同类型文件的使用情况
这次改造的核心思想就是:化繁为简。
从三套方案并存,到单一MinIO实现;从混乱的配置,到统一的参数;从重复的代码,到清晰的架构。
对于初级开发者来说,最重要的启示是:好的架构不是功能越多越好,而是越简单越好。简单意味着容易理解、容易维护、不容易出错。
更多推荐


所有评论(0)