問題描述
這兩天本來忙着新功能開發,結果之前的一個項目最近要上了,然后又在測試,然后就喜提bug一枚(not mine),看bug描述,很簡單,而且本地環境也重現了,只要輸入2000個英文字符就可以復現。
核心就是:
當任務描述輸入最大字符2000時,報未知異常
問題定位
這種字符數限制的,一般就是數據庫的字段長度短了,於是我直接找到ssh,找到對應的日志看了一下,果然是這個問題。
com.mysql.jdbc.MysqlDataTruncation: Data truncation: Data too long for column 'feedback_content' at row 1
at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:3931)
at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:3869)
at com.mysql.jdbc.MysqlIO.sendCommand(MysqlIO.java:2524)
at com.mysql.jdbc.MysqlIO.sqlQueryDirect(MysqlIO.java:2675)
at com.mysql.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:2465)
還能說啥,直接上去改唄,於是連上數據庫,打開表,查看表結構,發現好像沒啥問題:

這不就是設置的2000嗎,難道后面的字符集有問題嗎,因為表是用的utf8mb4,難道有影響?然后改成了下面這樣:
`feedback_content` varchar(2000) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '反饋內容',
結果並沒有用,果然不應該懷疑這個的,utf8 和 utf8mb4,差距也沒那么大,這里又沒有特殊字符。
另外就是,中途也試了直接用工具改表,好像沒啥問題。
中途甚至懷疑連錯庫了,用命令查看了java進程(開發環境,java進程和數據庫在同一台機器)的連接,確認庫沒連錯。
netstat -ntp|grep 1279

然后我嘗試着用1999、1998字符去驗證,還是有問題,想着哥見過的bug多了,還能栽在這兒嗎,實在不行就開idea,本地debug一下吧。
這樣想着,到了午飯時間,就去吃飯了。
再次定位
吃完飯后,中午並沒有看bug,不過有個大概思路准備試一下。
思路就是,服務器上用tcpdump抓應用程序和mysql之間的包,然后看看情況,看看到底是哪里有問題吧。
然后下午空了一會,就在服務器上開了抓包:
tcpdump -i lo tcp port 3306 -Ann
- -i 抓環回網卡,因為我java服務和mysql在一台上
- tcp port 3306 這個是捕獲表達式,意思是,抓的包,其端口(不管是source還是destination,其端口需要是3306)
- -A Print each packet (minus its link level header) in ASCII. Handy for capturing web pages. 意為直接asicc打印包的內容,因為mysql不是完全看不懂的二進制協議,所以可以直接打印
- -nn Don't convert protocol and port numbers etc. to names either. 端口顯示數字就好,不用把知名端口轉換為可讀的文本
開了抓包后,然后馬上去操作了一把,然后這邊馬上看到,包已經打印出來了(包如果很多,記得趕快ctrl c中斷抓包):

可以看到,確實是mysql 服務器報錯了,和我們客戶端沒半毛錢關系。
但是,還是很奇怪,不知道為啥報錯。
於是,我重新抓了一次包。
tcpdump -i lo tcp port 3306 -w test.pcap
這里沒指定-Ann了,因為我們這次是要保存下來,到test.pcap這個文件。
然后再去測了一下,然后回來ctrl c,可以看到如下信息:

(這里僅供演示,沒再去測試了,我把下午抓的包給大家看)
wireshark分析
用wireshark打開該test.pcap,可以看到:

可以發現,wireshark直接內置了mysql的解析協議,很方便,我們這里隱去了一些表信息。大家學着用一下,很簡單。
但是現在還沒看出來問題,我仔細觀察了一下,

上圖中,我發現,每一列后,都會跟個逗號來分隔;但是,我們反饋內容那一列,前面怎么那么多個點呢?
然后基本可以肯定,是程序在里面加料了,然后去看了代碼邏輯,果然,程序里,對前端傳過來的內容,前面還拼了一些東西進去,然后基本就這樣找到問題了。
實際上,我們也可以這樣看,對着這個包,點右鍵,follow stream,就會把這個socket 四元組的通信都過濾出來。

注意,上圖是asicc顯示的,我們切換為utf8看一下,就很明顯了。

總結
問題本身很簡單,因為慣性思維,我也沒有第一時間去看業務邏輯,本能地開始懷疑mysql那邊的問題去了,其實還是應該先看看業務。
另外,tcpdump、wireshark這類神器,大家一定要掌握,縮小問題邊界、不同服務間問題甩鍋啥的,就靠它了。
對了,這里分享下最近畫的JVM腦圖一張,有用的話,大家給腦圖點個贊哈。
https://www.processon.com/view/link/5f0ad405f346fb3fdc66a153
