簡使用pop,不能保證最少消費一次,比如pop超時可能中途丟失,或者消費者處理過程中異常而未能處理完。
解決此問題有多種方法:
1) 方法一:使用rpoplpush替代pop
這種方法相當於建立了一個回滾,由於操作是在redis端完成的,可保證數據不會丟,當消費者完成業務邏輯后,再清掉lpush的另一隊列,這步有點類似於事務的commit提交。如果在處理過程中消費者異常重啟,則在重啟時先檢查lpush的隊列,完成處理后再清lpush的隊列。
2) 方法二:使用lrange+ltrim替代pop
消費時先lrange拿到數據,處理完后再使用ltrim清掉已處理的數據(這步也類似於事務的commit提交)。
不過這里存在一個缺陷:同時只能有一個消費者,不能多線程多進程和多機器同時處理同一個隊列,因此消費者存在單點問題,基於Zookeeper等來解決單點有些重,而且在切換會有些延遲,存在短暫停頓問題。
3) 方法三:使用eval(lua)+lrange+ltrim替代pop
站在redis端,list可看作是一個本地隊列,借助eval+lua,可實際這個轉換。既然是本地隊列,則可輕松的實現多消費者並發消費(本質是串型的),通過lua取list中的數據,並維護list數據的移動。
第一步是lua方式取得數據,並更新消費偏移;第二步是lua方式提交更新(也類似於事務的commit提交)。
上述三種方法,方法二缺陷明顯。而方法一如果消費者在提交前掛掉,另lpush隊列中的數據需要另外處理。方法三相對好維護,即使消費者在提交前掛掉,另外的消費在提交時可以幫助解決消費位置移動(實際是調用ltrim)問題。
當然由於redis的主節點和復制節點間是異步復制,該機制存在的丟數據問題無法通過上述三個方法來解決。