記一次redis命令異常:參數截斷
- 2019 年 12 月 10 日
- 筆記
# 問題背景
最近項目在使用linux平台c++做開發,redis用到了hiredis庫。項目中用到redis list結構作為隊列,生產者和消費者模式解耦非同步任務:
生產者:
1. 將業務pb結構序列化為字元串 pbstr
2. 將字元串通過 rpush list-queue pbstr
消費者:
1. 從list-queue獲取任務:lpop list-queue 獲得字元串 pbstr
2. 將pbstr反向序列化為pb結構,執行業務邏輯
遇到問題:
消費者在步驟2中,獲取到的pbstr反序列化為pb結構失敗了!!!導致消費者後續的業務邏輯無法處理。
# 排查思路
1. 懷疑序列化問題,單獨從業務層面對pb結構進行序列pbstr,然後在將pbstr反向序列化為pb結構,沒有遇到問題,排除pb的問題。
2. 懷疑redis隊列除了問題。有一下幾個排查思路:
a. 系統多執行緒,比較難調試。
b. strace 對進程進行跟蹤,比較容易,本文採用這種方法。
工具:strace -p [pid] -s 1024 -o s.out
# 發現問題
圖1是pb轉為一個pbstr字元串:m_msgBody, 可見序列化後的長度是1029

圖2是執行的redis命令,這裡說一下redis命令的協議格式:
*[命令行參數個數]rn$[參數1長度]rn[參數1字元串]rn$[參數2長度]rn[參數2字元串]rn
例如:
RPUSH mylist Lippman
redis網路傳輸的命令傳如下:
"*3rn$5rnRPUSHrn$6rnmylistrn$7rnLippmanrn"

從圖2看出,我們的1029長度的消息,莫名其妙變為了97!!!
# 問題解決
結合程式碼層面的命令行拼接方式是基於字元串的fmt方式,懷疑是業務pb本身某些欄位含有 , 導致序列化後的字元串被截斷了。

做個c預研字元串fmt遇到/0的實驗:實驗可以驗證,
字元串 s = 「abcdedn xxxxxxxxxxxxx」
s.length=21
s.size=21。因為C++類中的字元串長度是記錄buffer使用的實際位元組長度。
strlen(s.c_str())=7。 因為C語言以 作為字元串結束符。
字元串通過printf("%s", s.c_str) 結果只列印了 abcdedn。因為遇到 被截斷了

## hiredis的兩種命令行形式
方式1:redisvFormatCommand
從如下程式碼可看出,字元串的結束判定是
“`
int redisvFormatCommand(char
**target,
const
char
*format, va_list ap)
{
-
const
char
*c = format;
-
...
-
while(*c !=
'