一次日常需求處理帶給我的思考
需求背景
團隊項目原來使用的云存儲中間件已經(jīng)下線了,由于歷史原因未能及時將其全部遷移到新的云存儲平臺,進而導致部分功能在使用時出現(xiàn)問題。比如在某些需要上傳并存放文件的場景下,會導致上傳失敗,影響正常的業(yè)務邏輯;在某些需要下載文件的場景下無法找到正確的路徑,從而無法下載相關業(yè)務數(shù)據(jù)。這個問題之所以被發(fā)覺是因為前臺客訴反饋過來:某個菜單里的導出按鈕在點擊后沒有反應。
排查思路
收到反饋后,我的思路大概是這樣子的:
首先是先評估下這個問題的影響面,我的思路也比較簡單:這個問題持續(xù)了多久了?影響到了多少用戶/操作的觸發(fā)呢?處于業(yè)務中的那一步?
還好云存儲中間件這個系統(tǒng)是面向內(nèi)部的,因此待導出的數(shù)據(jù)并不多。并且因為它是獨立于業(yè)務主流程的,是需要單獨手動觸發(fā)的一個非必然操作,因此并未阻礙主流程的執(zhí)行,影響范圍較小。
在確定了大致的影響范圍之后,就要進入排查環(huán)節(jié)。
排查環(huán)節(jié)可以從日志、執(zhí)行流程、業(yè)務邏輯以及第三方依賴這幾個方面入手(受個人的思維局限性影響,大家有建議請指正),套上這次具體的場景,大概就是以下幾個問題。
- 服務器日志中有無錯誤信息?
- 提交后的表單是否通過了驗證邏輯并進入下載處理流程?
- 下載處理流程是怎么樣的?
- 中間是否有第三方依賴?以及第三方依賴狀態(tài)是否正常?
我們把這些問題解了,大概率也就找到了這個bug藏在了哪里。
工作過程
找到問題在哪后,我先去看了后臺被觸發(fā)的方法,以及方法內(nèi)打印的日志內(nèi)容:??save to *** failed, service not found?
?,然后順藤摸瓜找到了問題的原因:調(diào)用了某個已經(jīng)下線的云存儲中間件。導出操作的時序圖簡化版如下:
導出操作時序圖
在查看項目代碼的時候,我發(fā)現(xiàn)之前有前輩做過相關工作的遷移,并且封裝了幾個上傳到新的云存儲平臺的方法,大概是這個按鈕不常被點擊,歷史遷移的時候也就給他漏了。于是結(jié)合老的文件導出的處理流程,我在已有方法的基礎上新增了可以接收 File 類型的方法,文件存放的位置則沿用了之前前輩所使用的 bucket 和父目錄。(bucket: 存放文件的某個桶,可以將不同的 bucket 理解為不同的存儲空間)
新增完方法之后,為了簡化該方法在使用時的成本,就在方法體內(nèi)部根據(jù) 用戶ID 和 UUID 自動生成了父目錄下的文件子路徑,從而將其封裝為了僅需要一個 File 類型參數(shù)即可上傳文件的方法。
相比于其他上傳方法在調(diào)用前需要定義好幾個方法參數(shù)來說,這里只需要一行代碼直接 save 就行了。
這樣乍一看感覺沒什么問題,甚至在提交CR的時候我都覺得這個方法很不錯,用起來可以說是干凈又衛(wèi)生。既然這樣,我為什么還要寫這篇文章呢?
問題就在于,它可能有點“干凈”但并不“衛(wèi)生”!
“干凈”在于之前說的調(diào)用的時候只需要傳入 File對象即可,對已有代碼塊只需要改動一行就成。那為什么說它不“衛(wèi)生”呢?
思考總結(jié)
在代碼上線前的CR環(huán)節(jié),師兄從代碼的安全性、通用性、維護性等幾個角度進行了評估,發(fā)現(xiàn)存在很多問題。
從 安全性 角度來講:不安全。本文之前有提到 “沿用了之前前輩的 bucket”,這點就是個容易被忽略的點,自己在使用這個 bucket 的時候并沒有去查看這個 bucket 中文件的訪問權限問題,而是直接默認“這個項目中用到 oss 存儲的地方都是這個 bucket,應該只申請了這一個 bucket ”,那我直接用也是 ok 的。
這其實是一個十分危險的想法,只把目光放到了項目上,而并未下沉到業(yè)務角度。在 CR 的過程中我們發(fā)現(xiàn),之前使用這個 bucket 存放內(nèi)容,是因為對應的業(yè)務方法和存放的數(shù)據(jù)都是可以對外的。而本次需求對應的業(yè)務方法,則并不對外,而是針對公司小二的,那再用這個就不合適了。
從 通用性 角度來講:不通用。設計初衷是這個方法可以直接傳入對象就能存儲,不需要再寫幾行代碼來定義對應的 bucket 和文件子路徑;但是其實在我們舍棄掉參數(shù)入口的時候,也關掉了靈活性和通用性的大門。這樣的設計意味著如果用戶使用這個方法,那么文件的存儲配置就必須得用你這個方法里的了,那萬一人家想改改配置呢?這種情況下就造成了使用者很大的不便。
從 整體維護性 角度來講:目錄不易維護。前邊有提到,子目錄的路徑是在方法體內(nèi)部由登錄用戶的ID和 UUID 生成的。這樣的目錄劃分方式其實并不合理,因為我們存放的是文件,而在維護存儲的時候,我們可能會時不時整理/清理目錄與文件。
而這個整理/清理的維度通常是從業(yè)務角度出發(fā)的,并非用戶角度,比方說,我們要清理某月以前的歷史退款信息。一看目錄,看到的都是一堆用戶的id,這個時候負責處理的同學估計就是一臉懵了。相比較而言,文件目錄首級以業(yè)務進行分類,下一級以用戶信息或者日期信息再分類就會稍好點。那要怎么實現(xiàn)呢?答案就在 “通用性” 那一條:增加方法入?yún)?,把路徑定義的權限給具體的業(yè)務方法,雖然業(yè)務方法可能要多寫兩行代碼,但是最終的效果是好的。
舉個例子
以上就是這次需求處理的整個流程了,綜上,給自己最大的收獲就是:
- 當某個需求中使用到了自己之前沒用過的依賴/中間件的時候,要先去了解這個中間件的特性,以及在這個中間件中自己團隊已有的配置信息及其所代表的含義,不要盲目根據(jù)看到的代碼去YY;
- 對于一個方法的設計,不要去盲目的追求簡潔或者通用,要以實際業(yè)務出發(fā)去思考如何設計,否則可能會適得其反;
- 任何時候安全性一定要納入需求處理時的考慮范疇,千萬不要覺得一個功能很隱蔽就不用著重考慮它的安全性,沒有人能確定遲發(fā)性的影響會“送”我們一個多大的故障。
作為新入行的新人,我的經(jīng)驗和思考有很大的局限性,歡迎各位前輩指正文中內(nèi)容的不足或給出建議,不勝感激。