我做資源服務一年半的經驗總結
前言:從去年3月份入職到現在剛好一年半,在這一年半的時間裡一直負責部門的資源服務開發與搭建,由於公司戰略的調整我負責的這個服務需要交接到別的部門。因為在負責服務的一年半中遇到太多太多坑,也受到太多的批評和質疑,不過很幸運自己堅挺下來了,服務日訪問量也由剛接手時候的8千變成2.0億,業務方也擴大近一倍,今天我在這裡把所有的經驗與大家分享,希望大家以後在做服務時候少走彎路。
一:基礎服務應該考慮的事項
1、基礎服務的定義
當接手一個新服務的時候我們就必須搞明白下面2件事:
- 這個服務的定位
- 這個服務承擔的職責
我在工作中有次領導突然問我你能告訴我資源服務是什麼嗎,當時我有點暈圈,半天沒有回答出來,因為我一直在根據業務方需求或者pm來開發自己玩去處於一個被動的角色,所以那次對話給我帶來的震驚挺大的。從那以後我就經常想我的服務是什麼,後來就明白了它就是一個基於資源的庫存系統,既然是庫存系統那麼它應該具有哪些能力呢,無非就是新增、消耗、返點、凍結、解凍、轉賬等功能,當我們明白這些功能後我們就清楚哪些是我們開發所承擔的能力,哪些不屬於我們承擔的能力,以至於後期我對我的服務進行大量解耦(比如一個使用者想獲取資源還進行判斷是不是會員,其實他並不屬於我們服務職責,那麼類似這樣的業務需求我們就應該果斷拋給業務方)
2、基礎服務的文件
我在做服務的初期並不存在文件,但是慢慢發現文件在整個服務中也處於一個非常重要的角色,因為新每個業務方需要接入服務的時候你不能每次都直介面述,那樣會耽誤太多精力,而且很多接入服務出現的問題沒有文件的記錄也可能會丟失,這樣一來新接入的業務方可能會出現同樣的問題,為我們開發帶來大大的不便(文件最好是準備2份,一份用wiki寫接入文件,另一個是介面文件)。下面總結下wiki文件主要的內容
- 服務專業名字解釋
- 服務負責人以及聯絡方式
- 服務配置說明(如版本號、沙箱ip等)
- 服務介面說明
- 服務呼叫demo
- 常見問題說明
3、服務的效能
效能是每個服務都需要關注的點,但是我們在設計服務初期就應該有一個性能指標,比如我們操作介面平均響應時長控制在30ms以內,查詢介面響應時長控制在10ms以內(這些指標可以根據業務方的需要來,比如一個下單功能整體要求是500ms可能分到我們提供的介面只有50ms),因為任何效能都是有一定標準的我們就圍繞這個標準去打造,效能優化指標可以參考我以前的一篇文章( ofollow,noindex">https://www.cnblogs.com/LipeiNet/p/6379579.html )
4、服務的高可用
服務的高可用可以採取負載均衡來保證一臺機器宕機從而不影響整體,目前我們並沒有採用技術方面來保證高可用
4、服務的併發
併發基本大多服務都會面臨,一旦服務出現併發很可能造成了資料安全性出現問題,比如我們服務A使用者請求資源然後進行消耗,同一時間A使用者繼續請求資源進行消耗,那麼導致的後果就是A消耗了2次資源但是隻扣一次點,這樣一來服務就是出現問題,解決這個問題通常我們採用鎖,鎖主要分2種樂觀鎖和悲觀鎖。
悲觀鎖:認為所有的請求都會發生併發,所以悲觀鎖會對每一個請求加鎖,單點常用的悲觀鎖有synchronized和lock,具體這兩種鎖區別大家可以自己百度,當然另外如果服務是分散式的,那麼就需要採用分散式鎖,常用的分散式鎖有基於redis和 Zookeeper來實現的,我們這邊採用的是基於redis鎖,因為對效能要求比較高
樂觀鎖:認為所有的請求都不會發送併發,一般常用cas鎖來解決,就是先比後更的模式。如對資料加入版本號,獲取資料時候記錄版本號,如果更新時的版本號和獲取時不一致則丟擲異常
5、服務的一致性
服務一致性我在這裡來說明2種情況,一種是服務本身資料的一致性,另外是和呼叫方保持一致性.
呼叫方保持一致性:
我們服務提供冪等,當呼叫方出現超時或異常在次呼叫服務我們將會返回訂單重複提交,呼叫方可以根據返回值來判斷這個操作對自己業務而言是否是正確的.冪等我們服務採用的是永久性冪等,這樣做法其實有缺陷,因為量級太大會佔用很大的資料庫資源,所以可以優化成對於新增採用永久性冪等,而一些消耗採用非永久性冪等,這樣一來就可以對冪等資料做歸檔,就會減少資料庫資源的利用。
服務本身一致性:
這個就是我們常說的一致性,我們現在並沒有完全解決這個問題,目前我對於跨單消耗我採用的是佔位思想進行解決,也就是先會更新資源然後出現異常進行返點。並沒有採用最終一致性來解決,因為最終一致性會出現資料被多扣,比如我得資源第一次扣除失敗然後放入佇列,在佇列進行扣除,但是恰恰新的請求過來同樣會獲取這條資源進行扣除,這樣一來就出現資料安全問題,目前我們採用的是三次重試,所以嚴格來講我們服務的一致性並沒有完全解決.關於一致性問題在以後的文章會繼續和大家聊。
6、服務的接入方
當業務方需要呼叫我們服務的時候自己一定慎重搞不好就掉坑裡了,下面我說2個例子
第一個例子:
有一個業務方告訴我們他們要做活動需要每天新增資源,然後我就問了他每天的量級,經過評估後我發現量級沒太大問題,但是當我要審批的時候發現有新的問題,因為活動期間新增資源會出現大量零碎的訂單,如果過期時間過久那麼可能資料庫每種資源達到幾百或者上千條,那麼帶來的後果就是我們在查詢或者消耗時候會非常慢,而且以前因為查詢過多訂單導致服務超時問題,所以後續我們就這問題溝通,他們要過期時間設定1個月,然後我在做壓力測試確認無誤後才審批。
第二個例子:
我們提供定製化介面給業務方,這個給我們帶來了特別多痛,我們提供一個消耗介面,但是這個業務方需要消耗詳情,我們同樣提供了,這樣就導致他的消耗和別人不一樣,做技術升級的時候還需要單獨考慮這個介面,因為我的忽略導致幾次沒有考慮清楚都出現線上事故,後來我們去問他們要消耗詳情幹嘛,他說他業務其實不用需要把這些消耗詳情存入他們的庫,我們聽後覺得特別無語,這樣的資料直接走離線資料即可,這也是早期沒聊明白需求導致一個毒瘤介面。
綜上總結:
1、業務方的需求我們必須聊清楚,明白我們服務應該提供的能力,對於不合理的需求直接拒絕
2、拒絕提供定製化的介面,如果某個業務方需求影響整體同樣我們必須拒絕或者找到其他相容方案
3、對於每次業務方的接入必須郵件寫的非常明白包括背景、量級、QPS等
二:基礎服務過程中case彙總
1、資料遷移和資料轉換引發事故
關於資料遷移和轉換引起的事故請參考這篇文章 https://www.cnblogs.com/LipeiNet/p/7809567.html
2、線上操作資料庫表結構引發的事故
關於這個事故請檢視這篇文章 https://www.cnblogs.com/LipeiNet/p/9182454.html
3、重構引發的線上事故
重構我們一共出過兩次事故,一個程式碼整合,一個是刪除已廢棄邏輯.
程式碼整合帶來的問題:
背景:
剛進入公司的時候我看到專案中有大量程式碼重複,就想著重構,把相同的程式碼用函式的方式合併,最後經過審批後也這麼幹了,但是上線以後一線反饋自己新增的資源無法查詢,最後排查日誌中發現在重構程式碼中有一處程式碼的資源訂單的開始生效日期是當前日期並不採用使用者傳入的日期。
刪除已廢棄邏輯帶有的問題:
背景:
由於我們服務的和外部服務解耦其中的一個數據庫欄位被廢棄,所以我們在程式碼中去除掉這個欄位的邏輯資訊,上線後業務方反饋資源消耗異常,排查後我們發現返回實體中去除欄位應該是0但是我們去掉邏輯後變成null,這樣一來別的業務方判斷空指標異常
綜上總結:
1、專案重構前一定另外拉分支,修改專案的版本號,便於回滾
2、嚴格測試,用測試賬號測試線上介面保留返回值,然後同樣的操作用於被重構後的介面,比對連個返回值是否相同,如果不同,那麼就需要弄明白是否對線上業務造成影響
4、慢查詢引發的線上事故
一次我上線了一個查詢介面,但是不久被反饋線上服務部分出現超時,經過排查後發現sql的欄位沒有加索引。
總結:當我們上線新的服務時候必須經過沙箱嚴格測試包括效能指標。
5、內外部實體未解耦引發的線上事故
背景:
我們在新開發一個功能的時候在資料庫中增加了一個int型別的欄位,而這個欄位主要用於服務內部,並不會對外進行提供,但是在更新庫存的時候並沒有分離內外部實體,然後在更新的時候講這個欄位值覆蓋成為0導致了線上的bug。
修復過程:
內外部實體徹底解耦,內部介面不採用任何外部介面進行傳輸
綜上總結:
設計階段:
設計階段明確內外部實體的職責,內外部實體不必保持一致,外部實體主要傳輸業務方需要的欄位(具有業務含義)而內部實體需要和資料庫欄位保持一致。
升級階段:
升級內部邏輯:
有時候因為某些原因我們需要進對內部服務升級,這個時候我們一定要注意是否在內部服務中採用了外部實體進行傳輸,如果有進行外部實體進行傳輸的就一定注意更改內部實體影響業務方,如果時間准許應該進行內外部實體進行解耦
某個業務方需求:
如果某一個業務方有特殊需求涉及屬性的更改,這個時候考慮這個需求是否合理,是否應該我們服務提供的能力,如果可以通過配置化規則來解決,如果解決不了然後升級服務版本,對返回值進行升級,但是內部一定要注意不要影響其他業務方呼叫
所有業務方需求:
更新pom版本升級服務,統計所有的呼叫方,讓呼叫方進行升級
6、持久層捕獲異常引發的線上事故
背景:
持久層捕獲異常並沒有丟擲
綜上總結:
持久層中儘量不進行異常捕獲,如果需要捕獲介面中必須要有返回值
7、列舉使用不當引發的線上事故
詳情請檢視 https://www.cnblogs.com/LipeiNet/p/9487900.html
8、限流把控不嚴引起業務方資源無法新增
背景:某個業務方用MQ非同步呼叫我們的服務,但是他們是刷資料所以短時間內流量特別大,因為我們服務的限流這樣一來就出現大量的資料新增異常。
問題分析:主要出現這樣的問題原因有下面2個
1、業務方定時處理資料的時候沒能周知我們
2、我們並沒有把流量進行削峰
綜上總結:
1、在服務文件上特別加上業務方定時跑資料時候提前郵件周知最大QPS,資料的量級以及操作時間
1、採用流量削峰,避免短時間的流量過來造成業務異常,主要做法將短時間多出的流量儲存在MQ端,如果出現一定量級採用報警