先分清楚狀態、命令與事實
典型的裝置影子包含 desired 與 reported:desired 表示平台希望裝置最終達到的狀態,reported 則是裝置實際觀察並回報的狀態。兩者的差異可形成 delta,讓裝置在連線恢復後繼續同步。這個模型很適合燈號模式、採樣頻率、溫控設定或功能開關等「可持續存在」的設定,但不應把每一個操作都塞進 desired。
例如「將採樣間隔改為 30 秒」是一項狀態;「立即重新開機」則是一次性命令。若把重新開機寫成 desired,裝置可能在每次重連時再次執行。一次性操作應使用具唯一識別碼、到期時間與執行結果的命令通道;影子只保留重開後仍應成立的設定。工程上最先要決定的不是 JSON 欄位,而是每個欄位由誰擁有、是否持久,以及重送是否安全。
- 平台擁有:政策型設定,例如回報頻率、允許的功能或維護模式。
- 裝置擁有:感測值、韌體版本、訊號品質、硬體能力與故障狀態。
- 共同協調:平台可提出目標,但裝置必須依能力、安全條件與現場限制決定是否接受。
- 命令事件:校正、重啟、擷取診斷資料等一次性工作,應與持久狀態分離。
用版本與條件更新處理非同步
IoT 通訊通常不是同步交易。裝置可能離線數小時、訊息可能重送或亂序,管理介面也可能同時被使用者、排程與 ERP 流程修改。因此每次影子更新都應帶有單調遞增的版本或修訂號,並在平台端採用條件更新。呼叫端以自己讀到的版本提交變更;若版本已被其他流程推進,就重新讀取、合併並決定是否重試,而不是直接覆蓋。
裝置收到 desired 後,應先比較版本與本機已處理版本,再驗證欄位、能力與安全條件。執行成功後,以同一個目標版本更新 reported;若無法執行,則回報結構化原因,例如不支援、參數超界、前置條件未滿足或暫時故障。不要為了讓 delta 消失,就把 requested value 原樣複製到 reported。reported 必須描述實際生效的值,否則平台只會得到看似一致、實際錯誤的狀態。
合併策略也要按欄位定義。單純的 last-write-wins 容易實作,卻可能讓延遲訊息覆蓋較新的安全設定。對高風險欄位,可要求來源優先序、人工核准或 compare-and-set;對可交換的集合則可做欄位級合併。若現場旋鈕與雲端平台都能控制同一項設定,必須明確規定權責:現場優先、雲端優先,或現場變更只在一段租約期間有效。
把同步設計成可重複的收斂迴圈
可靠的裝置端流程通常是 reconciliation loop,而不是「收到訊息就立刻改值」。裝置先取得最新影子,比對 desired、reported 與本機實際狀態,產生需要執行的最小變更,再依相依順序套用。每一步都應具冪等性:同一版本即使被重送,也不應造成重複重啟、重複寫入憑證或反覆切換輸出。
建議為同步狀態建立明確生命週期,例如 pending、applying、applied、rejected 與 failed,並記錄目標版本、最後嘗試時間及錯誤碼。暫時性錯誤可使用有上限的指數退避;永久性錯誤則停止盲目重試,等待設定被修正。多欄位變更若有相依性,例如先下載憑證再切換端點,應包成具版本的設定組,先完整驗證與暫存,再原子切換;否則斷電可能留下半套新設定。
離線重連時,裝置應先讀取最新快照,而不是依序重播所有中間狀態。若平台在離線期間把模式從 A 改成 B、再改成 C,裝置通常只需收斂到 C。真正需要逐筆保留的業務操作應放在命令佇列或事件日誌中,並配置到期時間、去重識別碼與確認機制。
可觀測性與測試決定系統是否可維運
監控不能只看 MQTT 是否連線。更有用的訊號包括 desired 與 reported 不一致的持續時間、等待中的版本、拒絕原因、重試次數、裝置最後回報時間,以及影子更新來源。管理介面應把「平台已接受設定」和「裝置已套用設定」分開呈現,避免操作人員把 API 成功回應誤認為現場設備已完成變更。
測試時要主動製造不理想條件:在套用中途斷電、重送相同版本、讓舊訊息晚到、同時從兩個後台修改、送出裝置不支援的值,以及讓韌體升級後影子結構改版。影子 schema 應有明確版本,新增欄位時保留向後相容;移除或改名則要有遷移期。若不同硬體型號能力不一,可由裝置回報 capabilities,平台據此驗證 desired,避免下發必然失敗的設定。
實務上,我們會先建立欄位權責表、命令與狀態分類,以及失敗語意,再決定雲端服務與通訊協定。當 LINE、ERP、CRM 或維運後台都可能改變裝置設定時,這層整合規則尤其重要;必要時可由熟悉雲端與現場系統的整合團隊協助把權限、版本與稽核流程一起落地。