1. 程式人生 > >記一次返工

記一次返工

幸好 部署 系統數據 樸素 成了 忘記 小程序 目的 AC

記一次返工

作者:Grey

時間:2018-05-12

原文地址: http://www.cnblogs.com/greyzeng/p/9029530.html

說明

本周我經歷了參加工作以來,最大的一次返工,這一周都是茶飯不思的感覺,特此記錄一下,防止後續犯同樣的錯誤。

需求

有一個Web系統X,用戶可以通過這個系統查看自己的待辦信息,並且可以用於待辦的審批,還有一個我們做的手機應用Y,Y系統需要支持查看X系統的待辦信息並完成審批操作。

思路

剛拿到這個“粗粒度”需求的時候,我沒有進行足夠的需求分析,就開始“信心滿滿”梳理了一下自己的思路:

第一步:

X系統已有獲取待辦列表的接口,所以,獲取待辦列表這件事,我只需要調用X系統接口並轉換成Y系統需要的數據結構即可。

第二步:

我當時的方案是通過X系統提供的一個後門URL(這個URL可以直接打開某條待辦的詳情)然後用爬蟲抓取待辦詳情,詳情的抓取最初考慮的是用js找一堆頁面元素的值,因為詳情內容比較多,且分布在多個iframe和tab頁面中,所以要模擬瀏覽器跳轉到相應的iframe或者tab中抓取相應的詳情,這也為後續埋下了一個坑(因為要模擬瀏覽器操作,切換頁面等操作會導致抓取詳情時間會偏長,而Y系統獲取請求的時間只有10s,超過10s的就會顯示數據加載失敗了)。

第三步:
待辦詳情中有附件信息,我們無法通過抓取得到詳細的附件URL,所以需要在詳情頁面中,獲取相應的附件的ID,然後在X的服務器中部署一個我寫的小程序,這個小程序可以通過附件ID查詢X系統數據庫獲取附件的物理位置,然後拿到附件信息,並發請求到我們的文檔轉換系統中,可以將附件轉換成圖片,轉換成圖片最大地好處是可以在移動端很快速的預覽。

第四步:

第二步中的後門URL包含了審批信息輸入框和審批按鈕, 我只需要找到審批意見輸入框,用js腳本填入審批意見,找到審批按鈕,模擬瀏覽器點擊審批按鈕行為進行審批操作。稍微復雜一點地是要判斷點擊審批按鈕以後,是否需要選擇下一步地審批人,這一步也好解決,通過抓取詳情頁面中下拉選人框中的內容,就可以把下一步需要選的審批人也抓取出來並告知Y系統下一步需要支持選人的操作。

梳理完以後自己的思路以後,當時我是這樣的感覺,

技術分享圖片

然後就“信心滿滿”進行開發了。。。

第一次嘗試:JS抓取頁面信息

待辦詳情的抓取時間很不穩定,大概十次有6次會超時(超過10s),就算不我們有一個抓取的小程序,采用的是Phantom JS,我就用這個小程序進行詳情的抓取,邏輯大概是:

  1. 打開待辦URL,這個URL是後門URL,包含了用戶登錄的操作,所以是必須打開的一個頁面。
  2. 等待頁面完全打開,我是通過判斷頁面中某個元素是否存在來判斷這個頁面是否完全打開了,即如果元素存在,證明頁面已經完全渲染完畢,如果不存在則一直等待頁面渲染。
  3. 然後開始抓取詳情,這裏需要切換一個iframe和打開兩個tab頁面,分別從裏面獲取需要的數據。但是這個過程太長了,因為1和2已經耗費了大約3s多的時間,後面的切換iframe和切換tab,都要等頁面完全渲染完畢才能抓取信息,導致時間經常會超過10s,如果增加超時時間,體驗又會明顯變差,這顯然是不能接受的。

第二次嘗試:發Ajax請求獲取數據

面對超時問題,想到了另外一個解決方案,通過發ajax請求,直接獲取詳情數據,Chrome控制臺下看了一下待辦詳情頁面請求,大部分入參都可以通過正則查找當前頁面來獲得,這便減少了兩次切換頁面的操作,但是有幾個參數沒辦法在當前頁面獲取,還是需要至少切換一個tab來獲取。

所以,解決辦法就變成了:

  1. 同上
  2. 同上
  3. 然後開始抓取詳情,先在當前頁面獲取到能獲取到的所有需要發請求的入參值,然後用Js逐一發送請求獲取詳情。因為是逐一發ajax請求,要等待返回值操作再發下一個請求,雖然比第一次嘗試速度有所提升,但是速度還是不穩定,還是會出現超時現象。

第三次嘗試:Promise.all並發請求

第二次嘗試的瓶頸在於,串行發請求,速度還是有很大的限制,Js能否實現並發請求呢?說來慚愧,之前一直是做後端,用Java開發,連Js並發操作這件事,都不是特別清楚,所以就查了相關的資料,發現了Promise.all

將Ajax請求一個個封裝起來,諸如:

runAsync1()
runAsync2()
runAsync3()

等若幹個這樣的方法,
然後通過Promise.all來並發執行這些請求:

Promise
.all([runAsync1(), runAsync2(), runAsync3()])
.then(function(results){
    console.log(results);
});

用Promise.all重構完以後,我的心情是這樣的:

技術分享圖片

當我“戰戰兢兢”再一次嘗試獲取詳情的時候,速度是快了,但是,還是不穩定,還是會出現超時的現象(10次大概有5次,這個頻率也是無法被接受的)。

第四次嘗試:WebMagic並發請求

我堅信用JavaScript做這件事還有其他優化的方向,限於技術能力,我當時能想到的用JavaScript來處理的方式就這麽多,而且截至時間馬上要到了,我沒有時間再去探索了,所以我用了自己相對熟悉的WebMagic很快地重構了一版,並發抓取詳情,發現,速度快了,而且也穩定很多了,10次可能最多2次超時,後來仔細分析了一下超時的原因,ajax請求發送過去以後,對方有一兩個請求返回非常慢(30s以上),這件事我反饋給自己的領導,當時是沒有解決方案,對方系統目前不可能做優化。

好吧,就到這裏吧,可以開始流程測試了!

測試

我並沒有馬上提交測試人員進行測試,我想自己盡可能先走幾個流程,自己先測試整個流程是不是有問題,因為自己沒有用過X系統,所以找了相關的一位同事幫助我建了一些測試的流程,我走了幾個測試流程,發現,還蠻順利的,流程性的bug基本沒有,詳情的抓取內容也正常,我當時就恨不得馬上就可以上線:

技術分享圖片

正當我“信心滿滿”地準備交付的時候,心裏還是不自覺地警惕了一下,要不再測一個流程?這一測,心都拔涼了,發現了一個大bug,
原來待辦詳情沒有那麽簡單,待辦詳情裏面有一個列表,我一直覺得這個列表是作為詳情顯示的,沒有什麽特別的地方,
可是後來我發現,這個列表裏面的每一項,都可以單獨進行審批!這就意味著,原先的思路就是錯的!

原先的思路是:

點擊待辦->查看詳情->審批

而正確的思路是:

點擊待辦->查看這個待辦中的條目信息->點擊條目信息->查看條目信息->審批

此刻,我的心情是這樣的:

技術分享圖片

可是又有什麽辦法呢,硬著頭皮也要改!

第五次嘗試:從數據庫取數

上述bug帶來的問題不僅是架構的問題,還有性能,相當於每個條目都要切換一次頁面重新發送請求獲取詳情,
所以在待辦列表點擊進入條目的時候,要準備好每個條目需要的ajax請求參數,這會增加條目獲取的時間,用戶打開條目目錄這件事,就要花去相當於之前看待辦詳情的時間,然後點擊條目再看詳情這件事,也要花去同樣的時間,用戶體驗會極差,很大可能又會是超時。

最後,各方溝通,我們決定直接訪問X系統數據庫拿數據,因為是遺留系統,開發商已經不在了,運維人員也只有代碼並做一些簡單的維護,我們決定直接看X系統的源碼來找到相應的邏輯,並用sql來查詢並獲取X系統的數據。

幸好,我們找到了大部分SQL語句,SQL的參數,之前在查找Ajax請求的時候,已經找到大部分了,所以我在附件抓取程序中將待辦和條目詳情獲取的邏輯轉換成直接從數據庫查詢數據。在此,感謝MyBatis-Spring-Boot-Starter,@Annotation的方式簡直比之前的XML方式提升了太多的開發效率,我幾乎可以原封不動拷貝原系統的SQL來進行查詢,用法如下:

@Mapper
public interface MyMapper {

    @Select("select x as y from xxx where xx=#{p}")
    List<Map<String,Object>> findByState(@Param("p") String p);

}

這裏的

select x as y from xxx where xx=#{p}

就相當於是X系統原封不動的拿來的SQL語句,數據結構也來不及設計了,就用List<Map<String,Object>>把!

改成從數據庫拿數據以後,沒有了超時的問題了,重新測試了幾個流程,都正常了,終於可以舒一口氣了,但是心情卻很是復雜。

技術分享圖片

感悟

充分理解需求,應該在開發之前充分地理解需求,細化需求,由於需求理解不到位而返工開發的代價是巨大的。

在第三次嘗試中,有一個插曲,當時我面臨兩個方向的技術選擇,一個是使用自己相對不熟悉的JavaScript來做並發, 另一個是當時自己相對熟悉的Java中的爬蟲抓取工具WebMagic,也可以很好地實現並發操作。如果用WebMagic,或許並發和使用這塊,我會更加熟悉,之所以當時先選擇了JavaScript來做,是因為當時腦海中冒出了這樣的的想法:

自己的Js技術相對Java要弱一點,就要多鍛煉一下,所以就一直再探索用JavaScript可以有哪些優化的地方。畢竟自己的目標是希望可以做一個全棧工程師。

我似乎忘記了這是一個工作任務,完全把這個當成了自己學習和鍛煉的途徑,忽視了工作任務是有截至時間的。

這幾年,自己的學習方式大多是蜻蜓點水類型的,喜歡學習新技術,但是都不太深,後來,和一位老員工溝通了這件事,他告訴我一個很樸素的道理:

技術層出不窮,人的精力卻是有限的,語言只是一種工具,重要的是編程思想。

記一次返工