一年前一個偶然的機會參與了公司的一個重點項目,須要長時間出差,開發團隊規模在20人左右,並且時間緊迫。在異地,少了公司技術團隊的支持,遠程溝通不方便,不少事情都顯得比較困難,碰到問題每每須要本身摸索,本身解決。有句話說,一個開發團隊有的時候就像一臺發動機,只要啓動起來,就能有成果和產出。但若是方向存在誤差,發動機越跑越遠,可能收不住腳,最終會致使項目失控。很慶幸,咱們這個項目順利上線,順利完成電商大促,以及最後順利交付。但中間的存在一些風險點以及經驗教訓仍是值得拿出來總結一下。web
一般開發經理或架構師會早於開發人員介入項目,瞭解項目的需求,系統分析,作相關的技術選型,制定開發計劃與開發規範。redis
在制定技術規格說明階段,開發經理或架構師要協調起全部的開發人員,指定相關的技術規範與開發人員保持溝通,讓開發人員理解他們負責的模塊或者子系統,確保開發人員可以按照架構意圖實現各項功能。數據庫
1 基本編碼規範緩存
這個基本上每一個公司都有一份這樣的文檔(若是沒有你基本上能夠考慮job-hopping),這個文檔通常跟項目無關,好比命名規範,註釋規範,SQL規範等等。另外,要統一jdk,包括本地開發環境、服務器環境;定好項目名,包名,數據庫名,表名,以及是否每一個表須要通用字段(如version樂觀鎖版本號)等等。服務器
這裏重點強調2個地方,工程規範和包名目錄規範。數據結構
工程規範:要明肯定義每一個工程模塊的邊界,尤爲是在分佈式系統中,這一點顯得尤其重要,開發人員要對框架的層次結構很是理解,好比何時該定義DTO,何時該定義Domain。這個若是沒有搞清楚,在項目的整個生命週期裏面,項目的體積愈來愈龐大,這個問題一旦暴露出來簡直就是災難。多線程
包名目錄規範:包目錄規範要儘量從頂層開始,開發人員的包目錄權限要儘量低,簡單的說就是儘量讓開發人員少去建包,這一點很像日本的外包項目,目的就是統一規範。另一點,要避免兩個工程模塊出現相同的包路徑,這樣在引用的時候極有可能出現衝突。架構
2 定義好組件的邊界和職責框架
系統分解以後,要定義好組件(子系統或者模塊)的邊界和職責,這個是項目初期開發人員最關心的問題,若是這個沒有定義清楚,後面系統就要面臨重構的危險。異步
好比基礎數據,並非全部基礎資料、配置信息都放到基礎數據中,只有跨系統、跨服務、模塊的基礎資料、配置信息才屬於基礎數據管理範疇;另外基礎數據服務的接口須要具有必定的通用性,儘量減小針對某個系統、服務、模塊開放特殊接口;而且不容許基礎數據服務依賴上層服務。
3 項目版本定義
項目初期,除了一些基礎的模塊,好比工具類等公共模塊可能被打包成Release版本,其餘的通常都是SNAPSHOT版本,當項目陸陸續續上線以後,好比分佈式系統中RPC調用要給客戶端系統提供jar包引用,版本控制的做用就會凸顯出來。這裏推薦一批文章《語義化版本2.0.0》,是關於語義化的版本規範,這個規範是由 Gravatars 創辦者兼 GitHub 共同創辦者 Tom Preston-Werner 所創建。
4 svn代碼管理與發佈規範
根據項目的開發模型,定好代碼的主幹(trunk),分支(branches),基線(tags)的關係,以及相關環境的發佈流程規範。
5 自動構建
在分佈式系統開發中,沒有自動構建簡直不敢想象,每每一個項目有不少個子項目部署在幾十臺甚至幾百臺服務器上。若是沒有自動構建,開發人員有可能要花費大量的時間在服務打包發佈上,一旦須要立刻發佈一個測試bug,都須要很長一段時間,這會另整個團隊精疲力竭。
6 代碼日誌規範
項目的日誌在生產環境上禁止將日誌輸出到Console中。項目代碼中都須要經過Logger對象來輸出日誌和異常,禁止使用system.out.println等方法進行輸出,禁止使用e.printStackTrace來輸出異常。日誌須要經過日期、大小兩個維度來分割文件,避免日誌文件過大沒法打開。生產環境的日誌級別禁止開啓DEBUG,若是排查確實須要則能夠針對特定類打開而不能將整個環境設置爲DEBUG,可能會形成系統由於文件鎖卡死。
7 統一異常處理
分佈式業務處理系統,系統中存在大量的跨服務調用,而且須要對不一樣類型的異常作不一樣級別的處理,處理的方式須要隨着系統的不斷擴展而適應不一樣類型的異常處理,而且作到跨服務的異常統必定義和快速定位跨服務的異常發生的源頭和緣由。通常經過定義全系通通一的異常編碼,並定義其產生的緣由,並須要達到識別系統的目的。
8 提供批量更新方法
批量更新必須使用提供的統一批量更新方法,對性能有很大的提高,若是有很是特殊的場景需求沒法使用則必需要通過評審,不然這有可能成爲一個性能瓶頸。
9 儘早提供基礎服務
如短信服務,郵件服務,這些基礎服務要優先安排開發,由於幾乎每一個模塊都有可能涉及到。
10 規範多線程寫法
若是系統中有用到多線程,不要隨意開闢線程,儘量使用統一的線程池,並封裝公共的調用方法以及返回結果。
11 規範事務的處理
11.1 避免產生一個較長時間鎖定多行數據的事務,儘量將事務拆解或改成異步。
11.2 分佈式事務
在項目儘量不經過數據庫層面的分佈式事務來實現數據的一致性而是經過異步、補償、冪等等方式來實現數據的最終一致性。
l 冪等性:
任何對外提供的服務入口方法都必須實現冪等性,重複使用一樣的參數調用同一方法時總能得到一樣的結果,而不會形成重複的處理。
查詢服務:查詢自己就是冪等的,因此不須要作額外的處理。
新增服務:經過惟一性約束來控制數據的惟一性避免重複寫入數據
更新服務:經過樂觀鎖來控制數據的冪等性
刪除服務:刪除自己也是冪等的,因此不須要作額外的處理
l 異步消息處理:
跨系統進行事務處理時儘量的使用異步消息處理來進行,先將數據保存爲一箇中間狀態,並將消息寫入MQ,由下游系統訂閱處理。下游系統處理完以後再將結果反饋給上游更新狀態。MQ中寫入的數據正常狀況下不要使用完整數據,會形成MQ的IO壓力很高並且數據多是已通過期的,而且會將隊列變成專用而不是通用,可能沒法被其餘服務訂閱。
l 補償
MQ處理時可能存在消息丟失、故障等問題丟失數據致使流程中斷,因此須要補償措施來保障流程不會長時間中斷。由一個分佈式事務的最上游系統提供一個定時補償措施檢測長時間未完成事務的流程,並從新觸發這個流程(從新寫入MQ)。
12 肯定基礎數據緩存方案
緩存每每被忽視,等到項目後期碰到性能瓶頸的時候才發現須要重構,那個時候的代價是很大的,因此須要儘早的定好緩存方案,是用ehcache? guava? redis?這個要肯定下來並讓開發人員實施下去。
13 規範緩存環境使用
memcached/redis緩存環境規劃,要定義好數據結構使用規範,好比不能讓全部人都用key/value這種方式來存儲,最終致使緩存環境髒亂。
14 分佈式文件存儲
儘早肯定是在物理上作共享磁盤?仍是使用分佈式的文件系統?
15 單元測試
編寫單元測試要成爲一種習慣,另外,單元測試應該是沒有反作用的。定義良好的單元測試在運行屢次的狀況下,若是沒有其餘條件發生變化,那麼每一次都應該產生徹底相同的結果。好比說,在數據庫中一般會插入一些假數據,而後在測試中驗證這些數據,這種方式的測試不可靠,由於數據庫可能發生變化。能夠在單元測試過程當中使用內存數據庫或者每次單元測試的數據都是自動生產最後自動刪除的。
許多同事怕寫單元測試的一個主要緣由就是依賴太多(遠程服務調用、redis、webservice等),若是一個服務由於種種緣由掛掉了,那麼這個測試就會失敗。要解決這個痛點,能夠引入mock對象能夠知足這些條件的需求,而Mockito正是這樣的一個框架,用Mockito來模擬相關行爲,不用費力去準備各類依賴環境,這時只需專一於業務邏輯便可。