動態

詳情 返回 返回

處理一個Redis數據庫切換邏輯缺陷導致的問題 - 動態 詳情

問題背景

最近在做瓶體回收設備改成沒有顯示大屏, 設備與服務器通過MQTT協議進行通信的方式, 服務器後台訂閲了所有設備的發佈頻道"device/+/publish", 這樣後台就能收到給所有設備發佈的消息, 進而進行邏輯處理.

問題表現

小程序用户掃碼設備上的二維碼與設備綁定後, 後台會更新redis中對應設備的緩存信息設置is_bind=1,uid=綁定用户id, 標記當前設備已經與某個用户綁定了, 避免別的用户再次綁定該設備,瓶體的投遞流程大致如下:
掃瓶身條碼-->放置到傳送帶-->傳送-->掉落

瓶子投遞過程的日誌log記錄

日誌記錄的投遞流程如下:

[2025-08-30 18:33:26] [stream.INFO] 接收到頻道消息1756550006.7742 {"topic":"device/CC:64:1A:9A:E6:2B/publish","message":"{\"device_sn\":\"CC:64:1A:9A:E6:2B\",\"time\":1756550006,\"event\":\"scan_barcode\",\"barcode\":\"6954767410173\"}"} 
[2025-08-30 18:33:28] [stream.INFO] 接收到頻道消息1756550008.2799 {"topic":"device/CC:64:1A:9A:E6:2B/publish","message":"{\"member_id\":3039,\"device_sn\":\"CC:64:1A:9A:E6:2B\",\"bind_code\":\"2f29adef47812dd655de25f600ec065a\",\"material_id\":75,\"time\":1756550007,\"event\":\"material_placed\",\"material_name\":\"可口可樂經典美味300毫升\",\"weight_can_use\":true}"} 
[2025-08-30 18:33:29] [stream.INFO] 接收到頻道消息1756550009.9827 {"topic":"device/CC:64:1A:9A:E6:2B/publish","message":"{\"member_id\":3039,\"device_sn\":\"CC:64:1A:9A:E6:2B\",\"bind_code\":\"2f29adef47812dd655de25f600ec065a\",\"weight\":24,\"material_id\":75,\"event\":\"weighing_complete\",\"material_name\":\"可口可樂經典美味300毫升\"}"} 
[2025-08-30 18:33:29] [stream.INFO] 接收到頻道消息1756550009.9851 {"topic":"device/CC:64:1A:9A:E6:2B/publish","message":"{\"member_id\":3039,\"device_sn\":\"CC:64:1A:9A:E6:2B\",\"bind_code\":\"2f29adef47812dd655de25f600ec065a\",\"belt_flag\":true,\"current_state\":\"TRANSPORTING\",\"material_id\":75,\"time\":1756550009,\"event\":\"material_transporting\",\"material_name\":\"可口可樂經典美味300毫升\"}"} 
[2025-08-30 18:33:30] [stream.INFO] 接收到頻道消息1756550010.9869 {"topic":"device/CC:64:1A:9A:E6:2B/publish","message":"{\"member_id\":3039,\"device_sn\":\"CC:64:1A:9A:E6:2B\",\"bind_code\":\"2f29adef47812dd655de25f600ec065a\",\"current_state\":\"TRANSPORTING\",\"material_id\":75,\"time\":1756550010,\"event\":\"material_dropped\",\"material_name\":\"可口可樂經典美味300毫升\"}"} 
[2025-08-30 18:34:30] [stream.INFO] 接收到頻道消息1756550070.9917 {"topic":"device/CC:64:1A:9A:E6:2B/publish","message":"{\"device_sn\":\"CC:64:1A:9A:E6:2B\",\"bind_code\":\"2f29adef47812dd655de25f600ec065a\",\"description\":\"倒計時結束自動關門\",\"time\":1756550070,\"event\":\"device_close_door\"}"} 
  • 處理正常的地方
    一個瓶子投遞完成後, 我看了下後台數據庫 對應訂單記錄也生成了, 用户的積分也增加了
  • 出問題的地方
    但是再次投遞的時候提示設備已經綁定,查看reidis中對應設備信息如下所示:
    image.png

這個就很奇怪了, 正常情況下不應該出現上面的問題,因為後台在處理"device_close_door"事件的時候就會更新redis中對應設備的綁定狀態(is_bind=0), 設備主動關門事件後台處理邏輯如下所示:

case 'device_close_door': //設備要求關閉事件
  $bindInfo = DeviceBindModel::where('device_sn', $deviceSn)
    ->where('status', 1)->find();
  if ($bindInfo) {
    $bindInfo->save(['status' => 3, 'unbind_time' => time()]);
    RedisService::pushWebSocketMessage(1, $bindInfo['user_id'], [
        'type' => 'delivery_info', //投遞流程
        //投遞步驟step:1=綁定開門,10=掃瓶子條碼,20=放置,30=傳送,40=掉落,50=設備關門(投遞結束)
        'step' => 50,
        'code' => 1, //1成功, 0異常
        'msg' => '投遞結束,設備要求關門',
    ]);
  }
  //解除設備綁定
  DeviceModel::userDeviceRelation($deviceSn, 0, 3);
  return [
    'need_publish' => true, //是否需要發佈給前台信息
    'data' => [
        'msg' => '設備要求關門',
        'type' => 'device_close_door_response',
        'event' => 'close_door', //推送關門事件
    ],
  ];

問題排查過程

剛開始錯誤處理過程

  1. 剛開始發現這個問題的時候我以為是redis出問題了, 之前設備信息保存的時候是所有設備的信息都保存在'mqtt:device:info:hash' 這個key中 裏面每個鍵名就是device_sn, 減值是對應設備信息的json字符串
  2. 發現問題後我就想是不是redis異常了, 就把設備信息的保存格式給修改了, 每個設備信息單獨存儲,key是"mqtt:device:info:設備編碼", 對應的鍵名是各個信息屬性,鍵值就是對應屬性的值, 費半天勁兒改過後還是不行
  3. 後來又想既然myslq相關的修改都成功了,要不就改成mysql來存儲設備信息不用redis了
  4. 後來想是不是MqttListen.php這個監聽程序是不是有問題, 試來試去還是不行
  5. 最後又想要不要把邏輯改掉, 都改成EMQX消息事件回調的方式, 不再用thinkphp自定義指令, 進程監聽的方式

從週五發現這個問題,週六排查一天, 週日也沒閒着最後發現都沒有解決問題 , 一直糾結於在同一個程序中其它步驟都執行成功了, 為啥就修改redis中信息這塊兒沒成功

最終問題原因

週一早上最終發現原因是由於Redis數據庫切換邏輯缺陷造成的上述問題.
image.png

驗證
redis切換到15庫查看有沒有對應的key, 發現15庫中還真有那些本來應該只存在於0庫中的key
image.png

原來是在生成訂單的時候要給前台用户推送websocket信息, 記錄用户對應的socket信息記錄存儲在redis的15庫中, 後續更新設備信息的時候直接獲取redis實例(用的單例模式)更新設備信息, 其實也是在15庫中操作的, 並沒有在原來的0庫中修改, 所以最終的過程就是, 只要生成了訂單redis用的庫就變成15了, 後續的操作也是在15庫中, 而綁定用户的時候我查的是0庫中的設備信息,這就是導致上述問題的真正原因.

user avatar motianlun_5d0766992e67a 頭像 shoushoudeqie 頭像 icollection 頭像 banni99 頭像
點贊 4 用戶, 點贊了這篇動態!
點贊

Add a new 評論

Some HTML is okay.