第4章系统架构与设计决策4.1 整体架构概览4.1.1 系统层次架构┌─────────────────────────────────────────────────────────────┐ │ 应用层 (API Layer) │ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ │ │ HTTP Server │ │ Agent │ │ Simulator │ │ │ └──────────────┘ └──────────────┘ └──────────────┘ │ └─────────────────────────────────────────────────────────────┘ ↓ ┌─────────────────────────────────────────────────────────────┐ │ 业务逻辑层 (Business Layer) │ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │ │ MQTT │ │ Auth │ │ Alert │ │ Sync │ │ │ │ Client │ │ Manager │ │ Notifier │ │ Manager │ │ │ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │ │ Monitor │ │ Retention│ │Resource │ │ Analyzer │ │ │ │ │ │ Manager │ │ Monitor │ │ │ │ │ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │ └─────────────────────────────────────────────────────────────┘ ↓ ┌─────────────────────────────────────────────────────────────┐ │ 数据层 (Data Layer) │ │ ┌──────────────────────────────────────────────────────┐ │ │ │ Queue (本地磁盘队列) │ │ │ └──────────────────────────────────────────────────────┘ │ │ ┌──────────────────────────────────────────────────────┐ │ │ │ Database (sfsDb/LevelDB) │ │ │ └──────────────────────────────────────────────────────┘ │ └─────────────────────────────────────────────────────────────┘ ↓ ┌─────────────────────────────────────────────────────────────┐ │ 外部系统 (External Systems) │ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ │ │ EdgeX Foundry│ │ MQTT Broker│ │ 监控系统 │ │ │ └──────────────┘ └──────────────┘ └──────────────┘ │ └─────────────────────────────────────────────────────────────┘4.1.2 核心模块依赖关系// main.go 中的初始化顺序展示了模块依赖关系funcmain(){// 1. 配置层无依赖appConfig,errconfig.Load()// 2. 监控层依赖配置monitorInstancemonitor.NewMonitor()// 3. 告警层依赖配置和监控alertNotifieralert.NewNotifier(appConfig)monitorInstance.SetNotifier(alertNotifier)// 4. 数据层依赖配置database.Init(appConfig.DBPath,...)// 5. 队列层无特殊依赖dataQueue,errqueue.NewQueue(./data_queue)// 6. MQTT 层依赖配置、队列、监控、分析器mqttClient,errmqtt.NewClient(appConfig,dataQueue,monitorInstance,analyzerInstance)// 7. HTTP 服务层依赖数据层、配置、监控等serverInstance:server.NewServer(database.Table,appConfig,...)}4.2 设计决策详解4.2.1 嵌入式数据库 vs 独立数据库决策使用嵌入式数据库sfsDb/LevelDB理由部署简单无需额外部署数据库服务边缘友好资源占用低适合边缘设备性能优异本地访问无网络开销离线可用不依赖网络连接权衡维度嵌入式数据库独立数据库部署复杂度低高资源占用低高并发能力有限强分布式不支持支持适用场景边缘设备、单机应用服务器、分布式系统4.2.2 MQTT 作为数据传输协议决策使用 MQTT 协议接收 EdgeX 数据理由轻量级协议开销小适合带宽受限环境发布订阅解耦数据生产者和消费者EdgeX 原生支持EdgeX Foundry 默认使用 MQTT可靠性支持 QoS 0/1/2保证消息送达MQTT QoS 选择// mqtt/client.go 中的 QoS 设置constDefaultQoS1// 至少送达一次// QoS 0最多一次可能丢失// QoS 1至少一次保证送达可能重复// QoS 2恰好一次保证不重不丢开销最大4.2.3 本地磁盘队列作为缓冲层决策实现本地磁盘队列理由数据可靠性数据库写入失败时不丢失数据削峰填谷平滑突发流量离线缓冲网络中断时缓存数据自动重试后台自动处理队列数据实现// queue/queue.go:32-47typeQueuestruct{queueDirstringmutex sync.Mutex}funcNewQueue(queueDirstring)(*Queue,error){iferr:os.MkdirAll(queueDir,0755);err!nil{returnnil,fmt.Errorf(failed to create queue directory: %v,err)}returnamp;Queue{queueDir:queueDir,},nil}4.2.4 组合主键索引设计决策使用 (deviceName timestamp) 作为组合主键理由查询模式匹配常见查询是按设备和时间范围数据局部性同一设备的数据物理存储在一起高效范围查询利用组合主键前缀优化设计示例// database/database.go:112-123primaryKey,err:engine.DefaultPrimaryKeyNew(pk)iferr!nil{returnfmt.Errorf(failed to create primary key: %v,err)}primaryKey.AddFields(deviceName,timestamp)iferr:Table.CreateIndex(primaryKey);err!nil{// 忽略索引已存在的错误iferr.Error()!index already exists{returnfmt.Errorf(failed to create primary key index: %v,err)}}4.2.5 中间件模式设计决策使用中间件模式处理 HTTP 请求理由关注点分离认证、日志、限流等独立处理可组合性灵活组合不同中间件可维护性每个中间件职责单一实现示例// server/server.go:82-98funcDeviceNameMiddleware(next http.HandlerFunc)http.HandlerFunc{returnfunc(w http.ResponseWriter,r*http.Request){deviceName:r.URL.Query().Get(deviceName)ifdeviceName!{formattedDeviceName:common.FormatDeviceName(deviceName)url:*r.URL q:url.Query()q.Set(deviceName,formattedDeviceName)url.RawQueryq.Encode()*r.URLurl}next(w,r)}}// 中间件组合使用http.HandleFunc(/api/readings,auth.AuthMiddleware(DeviceNameMiddleware(s.handleQueryReadings)))4.3 错误处理策略4.3.1 分级错误处理// 1. 致命错误程序无法继续运行iferrdatabase.Init(appConfig.DBPath,...);err!nil{log.Fatalf(Failed to initialize database: %v,err)}// 2. 严重错误记录日志继续运行iferr:alertNotifier.Start();err!nil{log.Printf(Failed to start alert notifier: %v,err)}// 3. 可恢复错误重试或降级funcBatchInsertWithRetry(...)error{fori:0;ilt;maxRetries;i{_,err:tbl.BatchInsertNoInc(records)iferrnil{returnnil}time.Sleep(retryInterval)}returnfmt.Errorf(failed after %d attempts,maxRetries)}4.3.2 优雅关闭// main.go:166-205quit:make(chanos.Signal,1)signal.Notify(quit,syscall.SIGINT,syscall.SIGTERM)lt;-quit log.Println(Shutting down adapter...)// 按依赖逆序停止ifagentInstance!nil{agentInstance.Stop()}ifretentionManager!nil{retentionManager.Stop()}ifalertNotifier!nil{alertNotifier.Stop()}time.Sleep(5*time.Second)log.Println(Adapter exited)4.4 配置管理设计4.4.1 多来源配置支持// 配置优先级从低到高// 1. 默认值// 2. 配置文件 (config.json)// 3. 环境变量// 4. 命令行参数// config/config.gotypeConfigstruct{DBPathstringjson:db_pathDBUseEncryptionbooljson:db_use_encryptionDBEncryptionKeystringjson:db_encryption_keyMQTTBrokerstringjson:mqtt_brokerHTTPPortstringjson:http_port// ...}4.4.2 配置热更新// server/server.go:1119-1185func(s*Server)handleUpdateConfig(w http.ResponseWriter,r*http.Request){varnewConfig config.Configiferr:json.NewDecoder(r.Body).Decode(amp;newConfig);err!nil{w.WriteHeader(http.StatusBadRequest)json.NewEncoder(w).Encode(map[string]string{error:Invalid request body})return}configManager:config.GetConfigManager()iferr:configManager.UpdateConfig(amp;newConfig);err!nil{w.WriteHeader(http.StatusInternalServerError)json.NewEncoder(w).Encode(map[string]string{error:err.Error()})return}}4.5 实战练习练习 4.1架构设计评审分析当前架构的优缺点提出改进建议。练习 4.2设计决策模拟假设有一个新的需求分析并做出设计决策。练习 4.3错误处理改进为某个模块设计更完善的错误处理策略。4.6 本章小结本章深入探讨了 sfsEdgeStore 的系统架构和设计决策整体架构的层次划分关键设计决策的理由和权衡错误处理策略的设计配置管理的实现优雅关闭机制理解这些设计决策将帮助你更好地扩展和维护系统。本书版本1.0.0最后更新2026-03-08sfsEdgeStore- 让边缘数据存储更简单技术栈- Go语言、sfsDb与EdgeX Foundry。纯golang工业物联网边缘计算技术栈项目地址GitHubGitCode 镜像GitCode