聊一聊系統重構

2023-03-20 09:02:35

打破常規,重立新規;


01


開始想聊這個話題的時候,我是打算放棄的;因為這個話題涉及範圍之廣,內容之多,讓我犯怵;

近幾年,待過兩家公司;一家經歷過重構,另一家也打算重構......

其實要下定決心,推翻重來,是一個很有勇氣的決定;

歸根結底,不到萬不得已,誰想這麼玩,誰願意花費大精力去做這些髒活、累活;

所以究其原因,也只能說是一種綜合因素吧,就像古話說的,天時、地利、人和;

至於為什麼這是個很有勇氣的決定,因為做重構這事的團隊風險極高;上至業務高層,下至底層碼農,都有可能一個不小心就被刀;

我曾經待過的一個團隊,經歷過一次重構,不過那是一次失敗的經歷;

開發預估了兩個多月的時間,業務大佬決定,期間直接對新需求停滯;但是,最終上到生產的系統,老問題還在,然後又多了很多的新問題;

再後來啊,業務大佬和技術主管都被刀了;然後系統回退成老版本,重新承接新需求,並且開發時間被嚴重壓縮,導致技術部門沒日沒夜的加班;

下面,我大致做了歸檔和整理,從這八個方面重新聊一聊系統重構;


02



【重構原因】
其實上面也提到過,這是一個綜合因素,可能是各種各樣的因素疊加,造成一個不得不重構的結果;

下面,聊一聊我所碰到過的因素;要是沒聊到的地方,也歡迎大家補充;

第一個,可以歸結為系統的穩定性或可用性:不管吹得怎麼天花亂墜,你的系統總要可用吧,所以說可用和穩定是一個系統的最基本要求。開發一個需求還要全服務停用嗎?業務量增長,系統還能抗下所有嗎?那麼問題來了,怎麼保證可用和穩定?

首先,如果你是單服務系統,那麼為了保障可用和穩定,需要對服務進行擴充套件,加入更多的機器來分擔系統的效能壓力,也就是常說的「服務叢集」;

但是,往往我們的系統並不是單服務的,也就是我們常說的分散式微服務;這種情況下,就不得不扯到分散式的三大法寶:限流、熔斷、快取。相信要實現這三個法寶,有很多成熟的框架和工具等,這裡就不過多闡述了;

第二個,開發規範問題:這也考驗了團隊leader對底下成員的約束力,以及是否存在程式碼review的環節;

假設一個團隊,leader確實沒有對成員強制約束開發規範,更別提程式碼的review了;最終導致的問題就是,各寫各的,每個人都有自己的一套開發習慣和開發規範;最終就是一個大寫的「亂」;

這樣日積月累,一代目埋坑,一代目撤,二代目上,二代目撤...... 系統不知道經了多少開發人員的手,最終,新入職的冤種某天需要排查一個產線問題,彷彿誤入了程式碼的迷宮,深陷其中,無法自拔;

第三個,推翻原先核心業務,重立新規:這個要解釋起來比較麻煩,也碰到會比較少,然而,我恰恰就遇到了;

某天,業務大佬宣佈,目前做的這套業務規則要被廢棄了,他們重新擬定了一套全新的規則,意味著先前不知道幾手的程式碼已經失去意義了,幾乎90%都需要重新寫;

都到這一步了,那就大刀闊斧的改革吧;出事了,emmm,業務的鍋;


03



【重構會議】
既然已經確定要進行專案的重構,那麼拉成員例會是必不可少的,不然就會一片迷茫,不知道乾點啥;

第一次會議,由技術老大牽頭,叫上業務負責人,技術主管,開發全員,測試,產品,運維;

一般,第一次這種全體會議是沒有結果,但是又非常必要的;因為上級領導需要知曉整個重構涉及到的點,影響的方面,開發的時間,以及整體的技術方案,參與的人員,還有需要的資源等等;

第一次會議消化完後,很快就是第二次會議;這個會議也是由技術老大牽頭,參會的主要是技術主管和參與開發的人員和運維;主要是對系統重構進行總體的技術選型,討論用到的框架,元件,實現的方案,部署方案等等;

這個會議持續的時間會比較久,大家搬好小板凳,準備打持久戰,可能從上班開始,到下班點了都還不能結束;當然,還是會有中場休息時間的;基本可以各抒己見,提出自己的意見和看法;主要是圍繞做什麼?怎麼做?做多久?這三個疑問點討論;

接下來,就是由技術主管組織的技術部內部會議了;由於之前已經確定好了統一的技術棧和實現方案,這一次討論都是在此基礎上展開的;

當然這一次會議可能需要更久的時間,因為涉及到是微服務的話,就避免不了一個問題:「服務拆分」;

大家都知道,微服務是需要根據業務去進行拆分的;那麼問題來了,業務的劃分界限是什麼?為什麼A業務部分要包含A1,A2,A3;其實這裡就需要大家達成一個統一的認知,也是這次會議要達成的最終目的;

這次會議最後,服務已經劃分完畢,但是缺少去維護這些服務的人;那就需要對開發人員進行分組,每組有對應的負責人,看人員情況而定(如果就兩三個,甚至更少的開發,那就沒必要分組什麼的了,就是冤種,活都得你幹),每組對N個服務的開發和維護;

分組過後,組長會進行簡短的組內會議,這次其實就是派活會議,認領各自的工作;也會明確一點,大家要幹些什麼?誰幹哪些?每個人幹完都需要多久的時間?

最後,會整理一份排期計劃,上面每個人的名字赫然在列,當然還有任務列表,工時,等等;

一切準備工作就緒,最後就是開幹了,當然後面開發過程中還有一些不明確的點,也會臨時組織一些會議,讓大家達到統一的認知;


04



【並行需求】
在系統重構的開發過程中,因為佔用的週期會很長,投入的資源會比較多,所以難免還會遇到這樣一個問題;

一天產品經理急衝衝的跑過來說,現在有一些需求很著急,需要將重構停一停,先把這些需求排期上線;至於為什麼會這麼著急,咱也不知道,咱也不敢問呀;

這時候就會面臨一個噁心的局面,這次開發的需求,肯定需要在原先的服務中進行開發,那麼重構的服務中,是不是還需要再寫一遍?

王德發!說歸說,罵歸罵,辦法總比困難多!這時候有一種方式,那就是派出個談判專家,以三寸不爛之舌,動之以情,曉之以理;多說說技術難點、痛點,佔用的資源,實現的難度等等,去打動他,說服他,讓這次需求排在重構後面;

如果你成功了,那麼恭喜你,這次重構任務,大家幫你分擔一半

什麼?產品經理不同意;那就沒有辦法,只能在改動老服務的基礎上,記錄調整的點,最終在寫新服務業務程式碼的時候,將改動的點一併加上去;

最後在提測的時候,通知測試同學,這些需求相關的地方,在重構的新服務上線之前都需要重新走一遍流程;

當然,還有一種情況,就是像我之前說的,需求即伴隨著重構;這種需求也肯定是大版本,開發週期較長;這種情況是最理想化的,只需要按部就班的進行開發即可;


05



【技術選型】
這個話題,在上面的會議模組已經提到過了,這裡再來詳細的聊一聊;

當然,這次也僅限於比較流行的分散式微服務的技術選型;單服務的話,額,暫且不說吧;

其實也沒什麼好爭議的,對於Java系統而言,無非就是spring全家桶無腦往上堆,伺服器資源拉滿;就像買汽車一樣,越豪華的車,堆的設定越多,價格越貴,但是舒適性和操控性也就越好;

但是還需要考慮一些因素:成本、產品開源與否、技術社群活躍度、產品的成熟度、結合實際情況等等;

成本:時間成本、學習成本、金錢成本;

事情是人做的,程式碼也是人敲的;需要考慮團隊成員擅長哪些技術棧,如果一門技術只有少部分人會或者都不會,那就需要結合開發時間,去斟酌需不需要用它,即便是熱門的技術;

當然啦,如果老闆願意出錢,直接買各種付費的技術就行,我們只需要搭好基礎的框架,然後往裡面填充業務程式碼即可;如果不行,那就老老實實看看免費版,出了啥問題,只能自己排查去解決;

產品開源與否:說到這個,開源是關鍵,最香的詞「開源免費」;

就像上面說的,一旦遇到了什麼問題,其實有必要去看這門技術的原始碼去解決的;比如典型的SpringBoot自定義starter,需要定義「META-INF/spring.factories」檔案,將檔案中設定的型別資訊載入到 Spring 容器;

一旦你閉源,那怎麼玩?我怎麼知道你內部怎麼去實現的?只能看官方檔案,看你自己吹什麼什麼功能;

技術社群活躍度:這個因素其實和產品開源可以歸為一類,都講的是一旦這個框架或產品出了問題,我們就可以去它的官方社群上找類似的解決方案;如果一個產品連社群都沒,或者活躍度很低,那也就意味著你只能靠自己去解決了,那花的時間成本可就......

產品的成熟度:我們選用一款合適的產品,首先它得符合我們主要的需求,而後它才有其他額外的功能,這當然是最好的;因為一旦咱有一些特殊的業務場景,恰巧這些額外功能正好符合你這個場景,豈不美哉;這樣就不必要接入額外的產品或元件了;

比如Redis,大家都不陌生;相信這個工具大家最多的是用它做快取,它確實也是一款優秀的快取工具;

但是,我之前在做OA軟體時,碰到這麼一個需求;使用者申請一個會議,並且指定了參會人員,當在會議開始前15分鐘,前5分鐘,分別對參會人員傳送一個提醒;

典型的釋出、訂閱的模式,然後正好Redis就有現成的功能,當時就是用了Redis的這個功能;

但Redis適合小型應用,如果是大型架構,相信還是會使用rabbitMQ或者kafka等更專業的MQ佇列軟體

結合實際情況:說到這個,有句老話說:「只有適合自己的才是最好」;在考慮需要用到哪項技術時,最理想的情況那就是直接付費購買,出啥問題,都是有技術支援滴,我出錢了,你得為我解決,但是,成本有限啊;

當然,有些人會說,技術選型這玩意,那是技術主管或者技術總監他們決定的,我們小碼農只管擰好螺絲就行;是的,往往很多時候就是這樣;但是,你不去大膽說出你的想法和意見,你怎麼知道他們不會聽取呢?格局開啟,往上一層考慮就對了;


06



【服務劃分】
這塊內容其實可聊的沒多少,但是,也是最難的一塊;

首先,劃分;怎麼分?按什麼標準去分?分多少服務合適?分好之後怎麼去管理?安排那些人去管理?等等一堆問題;

我也只能淺談下我之前做過的一次劃分經歷,因為每個公司遇到業務和場景是不同的,最終得到的結果也是不同的;而且,服務並不是一次劃分就能結束,它是一個持續不斷的過程;

當時我們是全部參與這次重構的開發一起拉會議的,在此之前,已經確定好了技術棧,就是熱門的springcloud全家桶;技術主管先是淺談一波他自己的想法,然後讓大家在他劃分服務的基礎上,提出自己的看法,並且每個人都要說,因為不可能想法都是一致的;

我記得比較清楚的是,他劃分中,有個序列號的服務,就是這個服務專門去生成序列號;後面幾個人都點名這個服務也包括我;其實當時想的是,我們的系統業務裡沒那麼大,能省一個服務,也是省一點資源;最終大家統一共識,將這種場景寫一個工具類去處理;

最終討論後,達到的共識,也只是一個初步的共識;隨著業務需求的增長,一部分業務服務必將變得臃腫,那時候第二波拆分也就隨之到來了;


07



【開發規範】
上面提到,造成系統重構的因素有很多,其中一個因素就是程式碼混亂,大家各有各的習慣和規範;

其實不難發現問題了,並不是每個人不知道開發規範,而是大家沒有統一的一套規範,大家只是沒有達成共識;

那就來解決這個問題,制定一套統一的標準,讓大家都沒有異議的一套規範;

這時候就不得不提到阿里的規範,現在廣為流傳的《阿里巴巴Java開發手冊》,甚至基於這套規範,在IDEA上還有專門的檢查外掛可以安裝;

當然,這樣做確實省事,省去了leader對程式碼的review時間,包括我之前待過的一家公司,也確實就是這麼做的;

其實仔細用過幾次這款外掛就會發現,有些檢測其實大可不必,但是你又不得不去遵守它的規則,這時候反而徒增工作量;

遇到這種情況就需要看上級了,可以把這個疑問點反饋給他,或者反饋給身邊其他的小夥伴,起碼讓他們GET到你要表達的點,引起大家的共鳴;相信越來越多的人反饋這些問題,上級不會坐視不管的;那麼恭喜你,又去掉一塊大家都認為不合適的規範,讓你開發不繞彎路;

總之,總結起來就是一句話:沒有標準,大家統一認可的就是標準犯規;


08



【資料、介面遷移】
談到這個,除非開發的是0-1的專案,否則在平時開發中,或多或少都是會遇到的,而非重構會單獨遇到的問題;

而我們在入職一家新公司,或者長期在一家公司幹活,遇到0-1的專案几乎沒有;如果有,那麼你的運氣很好;

就拿我上面的經歷來說,業務推翻了之前的規則,重新制定了新的業務規則,那就不得不引出一個問題,之前的老資料怎麼辦?

這個時候就需要跟業務確認,這批老資料是否可以按照某種預設的規則,做統一的處理;或者說,直接廢棄不管;一般來說,按前者處理的方案居多;

考慮到重構,往往你的表也是重新設計過的,也有可能是老表和新表公用,一般來說後者的情況居多;

其實,說白了就是在老表裡面去修改資料,在新表裡面去新增資料,而我們要考慮的是這批資料怎麼去調整比較合適;

按我們之前的處理方式,如果涉及到的表,欄位等較少,儲存結構也頗為簡單,是可以直接用SQL指令碼去解決的;另外,還需要考慮如果表資料較多,SQL執行快慢的問題;

還有一種情況,要是涉及到的場景很多,處理的表也居多,可以考慮寫程式去解決;比如,寫個任務,去手動執行這個任務,處理掉這批資料;當然,也得考慮資料量的問題,如果資料量大,用多執行緒跑批,或者多服務跑批等方式;

下面,來談談重構中的介面遷移;

要保證一個原則,就是對介面的出入參保持不動;不然,前端直接飛過來跟你聊人生;

但是,老服務的介面很多怎麼辦?還能怎麼辦,分工處理;

上面提到過,我們是分組的方式,每個組負責一部分服務,分工比較明確,所以相對來說,也比較快;

大家整理檔案,老介面的URL是什麼,新介面的URL是什麼,出參,入參能不變就不變;如果真的需要調整的,需要給出合理的解釋,大家一致通過後,然後自行跟前端去battle;

有些老服務還會執行一段時間,所以並不是所有介面都會涉及到遷移,這種介面,那就相安無事;

在遷移的過程中,肯定也會遇到各種問題;比如舉個簡單的例子,之前老服務的分頁外掛使用的是PageHelper;而在新服務中,由於用上了Mybatis Plus框架,那自然也就用它框架自帶的分頁外掛了;這個時候就涉及到修改,你只能自己內部消化了;

最後,當你覺得一切介面都遷移完畢的時候,別忘記對自己遷移的介面,進行一次自測;不要怕麻煩,這個自測對於後面的聯調和提測流程,都可以免去很多不必要的問題,也可以大大提高團隊的效率;


09



【實現方案】
最後還有一點,業務場景的實現方案;這個聽著有點抽象,其實在上面說的會議中,也會涉及;

不要以為這只是技術的決定,其實產品的參與也是很有必要的;你品,你細品;

我拿一個經典場景舉例,A賬戶往B賬戶打錢,然後系統是微服務,很熟悉吧;

沒錯,就是分散式事務的一個經典場景,要保證資料的一致性,A被扣錢的同時,B要加上相應的錢;

分散式事務我不多說,相信很多同學隨便一查,各種五花八門的方案;

但是這裡我只講一點,達到資料一致的時間節點;你是要什麼時候保證這個資料達到一致,也就是說,強一致性還是最終一致性;

要求強一致必然是犧牲掉一部分效能的,要求最終一致性就有可能在一段時間內產生資料不一致的問題;

這個時候,就需要拉上產品經理,跟他闡述其中的緣由,說明技術方案上無法規避的點,讓他做取捨;

有的時候,好的設計往往能規避技術上的難點,也可以節省開發的成本和時間;

所以,在討論這塊內容的時候,必須要有產品經理在場,提出設計上不合理之處;我們不能只侷限於研究技術問題,其實換個角度,或者你質疑的點,就不存在了;

記得實習的時候,公司的老闆講過一句話「人人都是產品經理」,現在回過頭一想,還真是那麼回事;

大膽的提出自己的意見,做一個有自己主見的小碼農;


10



對於系統重構,我就以一句話結尾吧,這也是參照了一位大佬的話,「對公司現有的資源和戰略妥協,在技術棧和方案之間取捨」。

程式設計檔案:
https://gitee.com/cicadasmile/butte-java-note

應用倉庫:
https://gitee.com/cicadasmile/butte-flyer-parent