一個被自己坑的線上事故
前言
前段時間,我們線上系統(tǒng)出現(xiàn)了一個事故:用戶創(chuàng)建了商品,在商城的商品列表頁看不到,也搜索不到。、
這個問題持續(xù)了大概半個小時,最后發(fā)現(xiàn)竟然是我的鍋。
這個事情怎么說呢,完全是我自己把自己坑了。到底怎么回事呢?
1. 從需求說起
1.1 背景
由于我們這個迭代是個大版本,上線的日子要臨近了。這次上線需要運營配合提供很多商品屬性的數(shù)據(jù),他們需要把第三方的屬性和我方系統(tǒng)的屬性,在excel表格中匹配起來。
原本是這樣規(guī)劃的:由運營同學(xué)在excel表格中手動對應(yīng)雙方屬性的映射關(guān)系。
但后來,他們覺得屬性太多了,如果他們?nèi)斯ぴ趀xcel表格中對應(yīng)屬性映射關(guān)系,可能時間上有點來不及。
于是,他們在某次會議上,特意給我提了需求,希望我可以通過程序幫他們在excel中,把雙方的屬性值映射上。
有一個要求就是要:快。
因為其他同事,還要基于這份excel數(shù)據(jù),做一些后續(xù)處理。
1.2 原始需求
剛開始開會時,運營說的需求是:他們提供一個excel表格,里面有分類和屬性字段,然后讓我在程序中全匹配,把能夠匹配上的屬性編號和屬性,在excel的另外兩列中返回給他們。
然后,他們根據(jù)這份excel數(shù)據(jù),把匹配不上的(即另外兩列為空)數(shù)據(jù),在我們系統(tǒng)中手動錄入,這樣最終都能匹配上。
1.3 加戲了
本來我覺得這個需求挺簡單的。
但后來,運營加戲了(加新需求)。
其實,剛開始運營沒說完。
后來發(fā)現(xiàn),他們要接入兩個廠商。
而且運營提供的兩個廠商的excel表格中字段的格式不一樣,沒法用一套程序搞定。
而且,我們發(fā)現(xiàn)有部分屬性中包含了一個區(qū)間范圍,跟我們系統(tǒng)的數(shù)據(jù)肯定是對應(yīng)不上的,必須要拆分屬性后再匹配。
很顯然,運營是不愿意做這種手動拆分的工作的,這事他們想我?guī)退麄冇贸绦蛱幚恚@就給我增加了不少工作量。
此外,廠商1還有一個特殊需求:運營手動把excel中的部分?jǐn)?shù)據(jù)剔除掉,然后基于這份新數(shù)據(jù)重新匹配一份新excel數(shù)據(jù)。
2. 最快的方案
了解運營的需求之后,我簡單的分析了一下。按需求的優(yōu)先級,排了一個順序:
- 導(dǎo)出廠商1的屬性數(shù)據(jù)。
- 導(dǎo)出廠商2的屬性數(shù)據(jù)。
- 給廠商1導(dǎo)一份特殊的屬性數(shù)據(jù)。
- 導(dǎo)出區(qū)間范圍能匹配上的數(shù)據(jù)。
如果這些需求都寫程序處理,可能要寫4個程序,而且還需要花時間部署代碼,我怕時間上來不及。
于是我想了一個快速處理需求1、2、3的辦法即:直接通過sql語句查詢出所需數(shù)據(jù)。
不過這套方案的前提是:需要把excel中的數(shù)據(jù)導(dǎo)入到生產(chǎn)環(huán)境中。
為了保險起見,我先把excel中的數(shù)據(jù)導(dǎo)入dev環(huán)境中。等我寫好sql,測試好數(shù)據(jù)之后,再導(dǎo)入生產(chǎn)環(huán)境。
使用數(shù)據(jù)庫管理工具:Navicat Premium的Import Wizard功能,可以輕松將excel表格中的數(shù)據(jù)直接導(dǎo)入一張新表中。
它里面可以指定excel的sheet對于哪張表,指定excel中的列對應(yīng)表中哪些列。
由于這些需求都是新表,無需特別指定,我就按默認(rèn)的表名和字段名導(dǎo)入數(shù)據(jù)了。
但有個問題就是:表名和字段名都是中文的,因為excel中的sheet名和sheet中的字段名都是中文的。
其實,我當(dāng)時已經(jīng)發(fā)現(xiàn)了這個問題。
但當(dāng)時又想了想,表中字段比較多,要一一改成英文的,光起名字要花些時間。這些字段最終還是要轉(zhuǎn)換成運營可以看得懂的中文的字段名,這樣轉(zhuǎn)來轉(zhuǎn)去有點畫蛇添足,浪費時間。
而且這張表導(dǎo)入生產(chǎn)環(huán)境之后,是一張臨時表,用完了就會被刪除的,影響不大。
此外,這張表是新加的,如果沒有程序使用的話,應(yīng)該是不會有問題的。
所以,當(dāng)時沒多想,就找人把數(shù)據(jù)導(dǎo)入生產(chǎn)環(huán)境了。
導(dǎo)出數(shù)據(jù)的方法很簡單:
使用Navicat Premium的Dump SQL File中的Structure + Data即可。
這樣該數(shù)據(jù)庫工具,就會把相關(guān)表的建表的create語句,和插入數(shù)據(jù)的insert語句,導(dǎo)出到一個.sql后綴的文件中。
有個小問題就是:每條數(shù)據(jù)會生成一個insert語句,如果太多了放到生成環(huán)境執(zhí)行,執(zhí)行效率會比較低。
這時可以將insert腳本復(fù)制到idea或者其他的工具中打開,然后全文替換一下,去掉多余的insert,拼接成一個insert語句。
然后再用在線的sql壓縮工具,比如:https://tool.lu/sql,壓縮一下去掉多余的空格。
這樣插入數(shù)據(jù)的sql放到生產(chǎn)環(huán)境執(zhí)行,效率要快很多。
運營提供的excel表格中的數(shù)據(jù),被導(dǎo)入生產(chǎn)環(huán)境之后。按計劃,通過一條sql語句,把運營所需要的結(jié)果直接查詢出來,然后把結(jié)果復(fù)制到excel表格中。(注意:如果查詢結(jié)果的數(shù)據(jù)太多,不建議這么玩)。
按上面的做法,我很快完成了需求:1、2、3,并且把運營所需要的數(shù)據(jù)及時給他們了。
3. 一個插曲
原本按計劃,導(dǎo)完數(shù)據(jù)之后,生產(chǎn)環(huán)境中臨時表是要刪除的。
但出現(xiàn)了一個小插曲,運營給我提了一個臨時需求:需要重新導(dǎo)一份廠商2的數(shù)據(jù)給他們。
他們已經(jīng)按照表格中的內(nèi)容,把需要添加的屬性已經(jīng)添加到系統(tǒng)中了。需要我們重新導(dǎo)一份數(shù)據(jù),確認(rèn)一下,現(xiàn)在是否所有數(shù)據(jù)都能匹配上。
此時,我當(dāng)時在慶幸幸好數(shù)據(jù)沒刪。
運營的這個臨時需求,在線上執(zhí)行相同的sql很快就把數(shù)據(jù)導(dǎo)出來了。
1分鐘實現(xiàn)需求,當(dāng)時那叫一個:爽。
而且我觀察了一下,系統(tǒng)沒有出現(xiàn)異樣。
給運營把數(shù)據(jù)導(dǎo)完之后,我就忙其他事情去了,把刪除數(shù)據(jù)這個事情給忘了。
4. 線上出現(xiàn)問題
第二天上午,領(lǐng)導(dǎo)把我叫過去說:canal服務(wù)掛了。
我們分析canal了異常日志后發(fā)現(xiàn),這個問題是由于canal訂閱者,讀取中文的表名時,出現(xiàn)了亂碼,沒有成功讀取到。該程序直接拋了異常,導(dǎo)致canal訂閱者不能正常工作了。
這個問題對用戶的影響是:用戶創(chuàng)建了商品,在商城的商品列表頁看不到,也搜索不到,有用戶投訴到運營那邊了。
我當(dāng)時的第一反應(yīng)是:這也能掛?
我當(dāng)時不知道下游的業(yè)務(wù)系統(tǒng),通過canal監(jiān)聽了我這邊的整個數(shù)據(jù)庫。
話不多說,先把問題解決了吧。
我們當(dāng)時為了快速解決問題,先把中文的臨時表都刪了,然后把canal重啟一下。
果然,這個辦法是有效的。
canal監(jiān)聽者立馬恢復(fù)正常了。
當(dāng)天,沒有再出現(xiàn)過問題。
第二天,領(lǐng)導(dǎo)把我叫過去說:canal服務(wù)又掛了。
我當(dāng)時一臉懵逼。我什么都沒干呀。
Canal解析數(shù)據(jù)報錯:column size is not match for table xxxx 8 vs 9。
后來,我們經(jīng)過分析之后發(fā)現(xiàn),canal有一份緩存,如果canal出現(xiàn)異常,可能跟數(shù)據(jù)庫真實的情況不一致。
然后,我們把canal的meta.dat刪除了,然后重啟服務(wù),果然恢復(fù)正常了。
后面也一直沒出現(xiàn)過問題。
5. 確定需求4的方案
前面說過運營總共提了4個需求,我通過前面的騷操作,完成了3個需求。
但第4個需求,里面還有點特殊要求,通過sql腳本不容易搞定,只能硬著頭皮寫java程序了。
運營的需求是把他們提供的excel表格中的數(shù)據(jù)導(dǎo)入系統(tǒng),然后由系統(tǒng)匹配某個區(qū)間范圍內(nèi)的數(shù)據(jù),把結(jié)果寫入excel的另外兩列中,最后返回該excel文件。
拿到這個需求我腦子里想了三個方案:
- 寫一個可執(zhí)行的springboot工具項目,直接放到線上環(huán)境中執(zhí)行jar包。
- 使用一個job處理,本身已經(jīng)有xxl-job了,接入非常方便。
- 寫一個api接口。
最終我選擇了第3個方案。
為什么?
其實這3個方案代碼的工作量差不多,但前面兩個方案需要先上傳excel到應(yīng)用服務(wù)器,或者到OSS等文件服務(wù)器。
而如果運營需要導(dǎo)多次數(shù)據(jù),每次都需要上傳一次excel,不僅浪費服務(wù)器資源,而且比較費時,還麻煩。
如果用api接口的話,可以直接使用postman遠程調(diào)用,直接上傳文件,通過輸入流的形式讀取數(shù)據(jù),不保存到服務(wù)器。然后處理完數(shù)據(jù),在將excel內(nèi)容以輸出流的形式返回給我們下載即可。
使用postman調(diào)用遠程接口時,入?yún)⑦x擇form-data格式,key那里輸入File,然后在右側(cè)下拉列表中選擇File,就會出現(xiàn)Select Files按鈕。
通過該按鈕,就能選擇我們需要上傳的excel文件。
如果想調(diào)用接口后直接下載excel文件:
在postman中可以選擇Send and Download按鈕,即可下載文件。
注意,在圖片中的請求api接口地址是localhost,我只是舉了個例子,實際情況中是接口的域名。
此時,有些小伙伴可能會問題:這個接口不需要登錄就能訪問?
答:確實不需要登錄,我在網(wǎng)關(guān)層放開了該接口的訪問權(quán)限。
那不是有安全問題?
答:為了解決接口安全問題,也避免發(fā)版影響正常用戶的使用。我的想法是基于master分支新拉一個分支:hotfix,而pre環(huán)境(預(yù)生產(chǎn)環(huán)境,能訪問生產(chǎn)環(huán)境的數(shù)據(jù)庫)部署hotfix分支的代碼。
還有一個非常關(guān)鍵,而且我們一直在用的策略是:訪問pre的所有接口都必須使用指定的代理。
公司外面的人肯定是不知道這個代理的存在的,換句話說,只有我們公司內(nèi)部的人才能訪問pre環(huán)境的接口。
因此,新加的excel處理接口是非常安全的,而且該接口只部署pre環(huán)境,對正常用戶不會造成影響
這個方案看似挺完美的。
然后三下五除二,我把代碼寫完,本地測試通過了,準(zhǔn)備發(fā)到pre環(huán)境導(dǎo)數(shù)據(jù)。
6. jar包沖突
該功能部署pre環(huán)境其實非常簡單,只需要部署hotfix分支的代碼即可。
代碼部署好之后,我準(zhǔn)備開始訪問接口。
先在postman的這個地方配置pre的代理。
代碼部署好之后,就能通過上一節(jié)中介紹的內(nèi)容上傳excel文件,然后下載結(jié)果excel文件了。
但我第一次調(diào)用接口時,沒有返回想要的數(shù)據(jù)。從應(yīng)用服務(wù)器的日志中看到,該接口報錯了。
報的竟然是某個類找不到。。。。
我這次為了快速導(dǎo)入和導(dǎo)出excel文件,選擇了阿里的easyexcel工具類。
本地開發(fā)環(huán)境,我確認(rèn)過,那個類是有的。而且我這個功能是可以正常運行的,我都導(dǎo)出數(shù)據(jù)了。
但pre環(huán)境卻報了類找不到。
我猜可能是有jar包版本不兼容。
于是,調(diào)整了一下pom文件中引入的jar包的版本,之后,重新部署pre環(huán)境。
還真是這個原因,這一次接口能正常訪問,能夠返回數(shù)據(jù)了。
我心里暗自竊喜。
后來,把運營所需要的excel文件及時發(fā)給他們了。
7. pre環(huán)境網(wǎng)絡(luò)異常
又過了兩天,需求4有點調(diào)整。我把代碼改了,還是那個hotfix分支,找人重新部署了pre環(huán)境。
打算用之前相同的方法導(dǎo)數(shù)據(jù)的。
但馬上被啪啪打臉。
用postman請求該接口很久都不返回,我知道肯定是出了什么幺蛾子。
查了一下pre環(huán)境應(yīng)用服務(wù)器的日志,竟然沒有查到請求該數(shù)據(jù)處理接口的記錄。
接著,我查了一下pre環(huán)境應(yīng)用網(wǎng)關(guān)層的日志,竟然也沒有記錄。
不對呀。
然后又查了一下生產(chǎn)環(huán)境應(yīng)用網(wǎng)關(guān)層的日志,原來是請求到生產(chǎn)環(huán)境了。
不是配置了代理嗎?
為什么會訪問到生產(chǎn)環(huán)境?
我?guī)е@兩個問題咨詢了一下公司的IT部門同事。他們追查了一下原因,發(fā)現(xiàn)原來網(wǎng)絡(luò)帶寬被打滿,導(dǎo)致pre環(huán)境的代理出問題了。
經(jīng)過一段時間之后,pre環(huán)境的代理恢復(fù)正常了。
其實,pre環(huán)境代理出問題后,我們也嘗試了一下登錄到遠程服務(wù)器上,執(zhí)行相關(guān)curl命令,直接調(diào)用服務(wù)器的本地接口。最后,發(fā)現(xiàn)用這種方式不太好下載文件。
8. 部署錯分支了
pre環(huán)境代理恢復(fù)之后,我滿懷希望去用postman請求數(shù)據(jù)處理接口導(dǎo)數(shù)據(jù)。
但我發(fā)現(xiàn)導(dǎo)出的數(shù)據(jù)不對。
導(dǎo)出的excel文件根本打不開。
我打開excel文件看數(shù)據(jù)內(nèi)容時,提示excel文件格式不對,或者已經(jīng)被損壞了。
然后,我趕緊看應(yīng)用服務(wù)器的日志,有請求記錄,但是沒有返回記錄,從這個日志中看不出問題。
當(dāng)時我靈機一動:既然保存成.xlsx后綴的excel文件打不開,如果把文件后綴改成.csv格式呢?
于是,我把導(dǎo)出的excel文件后綴改成了.csv格式,果然可以打開文件。
文件內(nèi)容中提示404。
這時我就明白了,可能是pre環(huán)境的接口沒發(fā)成功,被其他分支的代碼沖掉了。
然后,跟部署代碼的同學(xué)溝通之后,他當(dāng)時操作失誤,部署的master分支的代碼,果然把hotfix的代碼沖掉了。
后來,他重新部署了hotfix的代碼,我順利的把數(shù)據(jù)導(dǎo)給運營了。
至此,這4個需求順利完成了。
總結(jié)
這次給運營導(dǎo)數(shù)據(jù),是一次比較難得的經(jīng)歷,遇到了很多問題,值得總結(jié)一下。
當(dāng)然這其中有一部分是自己給自己挖的坑,也有一部分是被其他人坑了。
不要怕踩坑,其實踩坑,也是成長的機會,我通過這次經(jīng)歷也收獲了不少寶貴的經(jīng)驗。
生產(chǎn)環(huán)境的表名或字段名,一定不能用中文的。不要抱僥幸心里,說不定哪天就出問題了。
生產(chǎn)環(huán)境創(chuàng)建的臨時表,用完之后,一定要記得及時清理。
使用canal時,最好別全庫監(jiān)聽。用到什么表,就監(jiān)聽什么表,避免出現(xiàn)一些意外事故。
版本不兼容,會導(dǎo)致類找不到問題。
如果使用了代理,要考慮代理出現(xiàn)問題的情況。
代碼發(fā)版之后,一定要再三確認(rèn)分支是否正確。
刪除meta.dat文件,重新canal服務(wù),可以解決canal的很多問題。
postman真的非常強大,建議大家都好好用一下。
把多條insert語句合成一條執(zhí)行,效率更高??梢允褂胔ttps://tool.lu/sql,這里在線工具,壓縮一下sql去掉多余的空格。
excel導(dǎo)入和導(dǎo)出用阿里的easyexcel工具,真的非常方便。
還有挺多收獲的,這里就不一一列舉了。
希望你看了我的文章,自己也會有點收獲,能從我的經(jīng)歷中,學(xué)到一點點東西,我就已經(jīng)心滿意足了。