後端思想篇:設計好介面的36個錦囊!

後端思想篇:設計好介面的36個錦囊!

作為後端開發,不管是什麼語言,Java、Go還是C++,其背後的後端思想都是類似的。

最後更新 2022/7/17 下午7:51
捡田螺的小男孩
預計閱讀 26 分鐘
分類
分享
標籤
Java Go 架構設計

前言

大家好,我是撿田螺的小男孩。作為後端開發,不管是什麼語言,JavaGo還是C++,其背後的後端思想都是類似的。後面打算出一個後端思想的技術專欄,主要包括後端的一些設計、或者後端規範相關的,希望對大家日常工作有幫助哈。

我們做後端開發工程師,主要工作就是:如何把一個介面設計好。所以,今天就給大家介紹,設計好介面的 36 個錦囊。本文就是後端思想專欄的第一篇哈。

1. 介面參數驗證

入參出參驗證是每個程式設計師必備的基本素養。你設計的介面,必須先驗證參數。比如入參是否允許為空,入參長度是否符合你的預期長度。這個要養成習慣哈,日常開發中,很多低級 bug 都是不驗證參數導致的。

比如你的資料庫表欄位設定為varchar(16),對方傳了一個 32 位的字串過來,如果你不驗證參數,插入資料庫直接異常了

出參也是,比如你定義的介面報文,參數是不為空的,但是你的介面返回參數,沒有做驗證,因為程式某些原因,直接返回別人一個null值。。。

2. 修改老介面時,注意介面的相容性

很多 bug 都是因為修改了對外舊介面,但是卻不做相容導致的。關鍵這個問題多數是比較嚴重的,可能直接導致系統發版失敗的。新手程式設計師很容易犯這個錯誤哦~

所以,如果你的需求是在原來介面上修改,尤其這個介面對外提供服務的話,一定要考慮介面相容。舉個例子吧,比如 dubbo 介面,原本是只接收 A,B 參數,現在你加了一個參數 C,就可以考慮這樣處理:

//老介面
void oldService(A,B){
  //相容新介面,傳個null代替C
  newService(A,B,null);
}

//新介面,暫時不能刪掉老介面,需要做相容。
void newService(A,B,C){
  ...
}

3. 設計介面時,充分考慮介面的可擴充性

要根據實際業務場景設計介面,充分考慮介面的可擴充性。

比如你接到一個需求:是用戶新增或者修改員工時,需要刷臉。那你是反手提供一個員工管理的提交刷臉資訊介面?還是先思考:提交刷臉是不是通用流程呢?比如轉帳或者一鍵提現需要接入刷臉的話,你是否需要重新實作一個介面呢?還是當前按業務類型劃分模組,複用這個介面就好,保留介面的可擴充性。

如果按模組劃分的話,未來如果其他場景比如一鍵提現接入刷臉的話,不用再搞一套新的介面,只需要新增列舉,然後複用刷臉通過流程介面,實現一鍵提現刷臉的差異化即可。

4.介面考慮是否需要防重處理

如果前端重複請求,你的邏輯如何處理?是不是考慮介面去重處理。

當然,如果是查詢類的請求,其實不用防重。如果是更新修改類的話,尤其金融轉帳類的,就要過濾重複請求了。簡單點,你可以使用 Redis 防重複請求,同樣的請求方,一定時間間隔內的相同請求,考慮是否過濾。當然,轉帳類介面,併發不高的話,推薦使用資料庫防重表,以唯一流水號作為主鍵或者唯一索引

5. 重點介面,考慮執行緒池隔離。

一些登入、轉帳交易、下單等重要介面,考慮執行緒池隔離哈。如果你所有業務都共用一個執行緒池,有些業務出 bug 導致執行緒池阻塞打滿的話,那就杯具了,所有業務都影響了。因此進行執行緒池隔離,重要業務分配多一點的核心執行緒,就更好保護重要業務。

6. 呼叫第三方介面要考慮異常和超時處理

如果你呼叫第三方介面,或者分散式遠端服務的話,需要考慮:

  • 異常處理

比如,你調別人的介面,如果異常了,怎麼處理,是重試還是當做失敗還是告警處理。

  • 介面超時

沒法預估對方介面一般多久返回,一般設定個超時斷開時間,以保護你的介面。之前見過一個生產問題,就是 http 呼叫不設定超時時間,最後響應方程式假死,請求一直佔著執行緒不釋放,拖垮執行緒池。

  • 重試次數

你的介面調失敗,需不需要重試?重試幾次?需要站在業務上角度思考這個問題

7. 介面實現考慮熔斷和降級

當前網際網路系統一般都是分散式部署的。而分散式系統中經常會出現某個基礎服務不可用,最終導致整個系統不可用的情況, 這種現象被稱為服務雪崩效應

比如分散式呼叫鏈路A->B->C....,下圖所示:

如果服務 C 出現問題,比如是因為慢 SQL 導致呼叫緩慢,那將導致 B 也會延遲,從而 A 也會延遲。堵住的 A 請求會消耗佔用系統的執行緒、IO 等資源。當請求 A 的服務越來越多,佔用計算機的資源也越來越多,最終會導致系統瓶頸出現,造成其他的請求同樣不可用,最後導致業務系統崩潰。

為了應對服務雪崩, 常見的做法是熔斷和降級。最簡單是加開關控制,當下游系統出問題時,開關降級,不再呼叫下游系統。還可以選用開源組件Hystrix

8. 日誌列印好,介面的關鍵程式碼,要有日誌保駕護航。

關鍵業務程式碼無論身處何地,都應該有足夠的日誌保駕護航。比如:你實現轉帳業務,轉個幾百萬,然後轉失敗了,接著客戶投訴,然後你還沒有列印到日誌,想想那種水深火熱的困境下,你卻毫無辦法。。。

那麼,你的轉帳業務都需要哪些日誌資訊呢?至少,方法呼叫前,入參需要列印吧,介面呼叫後,需要捕獲一下異常吧,同時列印異常相關日誌吧,如下:

public void transfer(TransferDTO transferDTO){
    log.info("invoke tranfer begin");
    //列印入參
    log.info("invoke tranfer,paramters:{}",transferDTO);
    try {
      res=  transferService.transfer(transferDTO);
    }catch(Exception e){
     log.error("transfer fail,account:{}",
     transferDTO.getAccount())
     log.error("transfer fail,exception:{}",e);
    }
    log.info("invoke tranfer end");
}

之前寫過一篇列印日誌的 15 個建議,大家可以看看哈:工作總結!日誌列印的 15 個建議

9. 介面的功能定義要具備單一性

單一性是指介面做的事情比較單一、專一。比如一個登入介面,它做的事情就只是驗證帳戶名密碼,然後返回登入成功以及userId即可。但是如果你為了減少介面交互,把一些註冊、一些配置查詢等全放到登入介面,就不太妥。

其實這也是微服務一些思想,介面的功能單一、明確。比如訂單服務、積分、商品資訊相關的介面都是劃分開的。將來拆分微服務的話,是不是就比較簡便啦。

10.介面有些場景,使用非同步更合理

舉個簡單的例子,比如你實現一個用戶註冊的介面。用戶註冊成功時,發個郵件或者簡訊去通知用戶。這個郵件或者發簡訊,就更適合非同步處理。因為總不能一個通知類的失敗,導致註冊失敗吧。

至於做非同步的方式,簡單的就是用執行緒池。還可以使用訊息佇列,就是用戶註冊成功後,生產者產生一個註冊成功的消息,消費者拉到註冊成功的消息,就發送通知。

不是所有的介面都適合設計為同步介面。比如你要做一個轉帳的功能,如果你是單筆的轉帳,你是可以把介面設計同步。用戶發起轉帳時,客戶端在靜靜等待轉帳結果就好。如果你是批量轉帳,一個批次一千筆,甚至一萬筆的,你則可以把介面設計為非同步。就是用戶發起批量轉帳時,持久化成功就先返回受理成功。然後用戶隔十分鐘或者十五分鐘等再來查轉帳結果就好。又或者,批量轉帳成功後,再回調上游系統。

11. 優化介面耗時,遠端序列考慮改平行呼叫

假設我們設計一個 APP 首頁的介面,它需要查用戶資訊、需要查 banner 資訊、需要查彈窗資訊等等。那你是一個一個介面序列調,還是平行呼叫呢?

如果是序列一個一個查,比如查用戶資訊 200ms,查 banner 資訊 100ms、查彈窗資訊 50ms,那一共就耗時350ms了,如果還查其他資訊,那耗時就更大了。這種場景是可以改為平行呼叫的。也就是說查用戶資訊、查 banner 資訊、查彈窗資訊,可以同時發起。

在 Java 中有個非同步程式設計利器:CompletableFuture,就可以很好實現這個功能。有興趣的小夥伴可以看我之前這個文章哈:CompletableFuture 詳解

12. 介面合併或者說考慮批量處理思想

資料庫操作或或者是遠端呼叫時,能批量操作就不要 for 迴圈呼叫。

一個簡單例子,我們平時一個列表明細資料插入資料庫時,不要在 for 迴圈一條一條插入,建議一個批次幾百條,進行批量插入。同理遠端呼叫也類似想法,比如你查詢行銷標籤是否命中,可以一個標籤一個標籤去查,也可以批量標籤去查,那批量進行,效率就更高嘛。

//反例
for(int i=0;i<n;i++){
  remoteSingleQuery(param)
}

//正例
remoteBatchQuery(param);

小夥伴們是否了解過kafka為什麼這麼快呢?其實其中一點原因,就是kafka 使用批量消息提升服務端處理能力。

13. 介面實現過程中,恰當使用快取

哪些場景適合使用快取?讀多寫少且資料時效要求越低的場景

快取用得好,可以承載更多的請求,提升查詢效率,減少資料庫的壓力。

比如一些平時變動很小或者說幾乎不會變的商品資訊,可以放到快取,請求過來時,先查詢快取,如果沒有再查資料庫,並且把資料庫的資料更新到快取。但是,使用快取增加了需要考慮這些點:快取和資料庫一致性如何保證、叢集、快取擊穿、快取雪崩、快取穿透等問題。

  • 保證資料庫和快取一致性:快取延時雙刪、刪除快取重試機制、讀取 biglog 非同步刪除快取
  • 快取擊穿:設定資料永不過期
  • 快取雪崩:Redis 叢集高可用、均勻設定過期時間
  • 快取穿透:介面層驗證、查詢為空設定個預設空值標記、布隆過濾器。

一般用Redis分散式快取,當然有些時候也可以考慮使用本地快取,如Guava Cache、Caffeine等。使用本地快取有些缺點,就是無法進行大資料儲存,並且應用程式的重啟,快取會失效。

14. 介面考慮熱點資料隔離性

瞬時間的高併發,可能會打垮你的系統。可以做一些熱點資料的隔離。比如業務隔離、系統隔離、用戶隔離、資料隔離等。

  • 業務隔離性,比如 12306 的分時段售票,將熱點資料分散處理,降低系統負載壓力。
  • 系統隔離:比如把系統分成了用戶、商品、社區三個板塊。這三個塊分別使用不同的域名、伺服器和資料庫,做到從接入層到應用層再到資料層三層完全隔離。
  • 用戶隔離:重點用戶請求到配置更好的機器。
  • 資料隔離:使用單獨的快取叢集或者資料庫服務熱點資料。

15. 可變參數配置化,比如紅包皮膚切換等

假如產品經理提了個紅包需求,聖誕節的時候,紅包皮膚為聖誕節相關的,春節的時候,為春節紅包皮膚等。

如果在程式碼寫死控制,可有類似以下程式碼:

if(duringChristmas){
   img = redPacketChristmasSkin;
}else if(duringSpringFestival){
   img =  redSpringFestivalSkin;
}

如果到了元宵節的時候,營運小姐姐突然又有想法,紅包皮膚換成燈籠相關的,這時候,是不是要去修改程式碼了,重新發布了?

從一開始介面設計時,可以實現一張紅包皮膚的配置表,將紅包皮膚做成配置化呢?更換紅包皮膚,只需修改一下表資料就好了。

當然,還有一些場景適合一些配置化的參數:一個分頁多少數量控制、某個搶紅包多久時間過期這些,都可以搞到參數配置化表裡面。這也是擴充性思想的一種體現

16.介面考慮冪等性

介面是需要考慮冪等性的,尤其搶紅包、轉帳這些重要介面。最直觀的業務場景,就是用戶連著點擊兩次,你的介面有沒有hold住。或者訊息佇列出現重複消費的情況,你的業務邏輯怎麼控制?

回憶下,什麼是冪等?

電腦科學中,冪等表示一次和多次請求某一個資源應該具有同樣的副作用,或者說,多次請求所產生的影響與一次請求執行的影響效果相同。

大家別搞混哈,防重和冪等設計其實是有區別的。防重主要為了避免產生重複資料,把重複請求攔截下來即可。而冪等設計除了攔截已經處理的請求,還要求每次相同的請求都回傳一樣的效果。不過呢,很多時候,它們的處理流程、方案是類似的哈。

介面冪等實現方案主要有 8 種:

  • select+insert+主鍵/唯一索引衝突
  • 直接 insert + 主鍵/唯一索引衝突
  • 狀態機冪等
  • 抽取防重表
  • token 令牌
  • 悲觀鎖
  • 樂觀鎖
  • 分散式鎖

大家可以看我這篇文章哈:聊聊冪等設計

17. 讀寫分離,優先考慮讀從庫,注意主從延遲問題

我們的資料庫都是叢集部署的,有主庫也有從庫,當前一般都是讀寫分離的。比如你寫入資料,肯定是寫入主庫,但是對於讀取即時性要求不高的資料,則優先考慮讀從庫,因為可以分擔主庫的壓力。

如果讀取從庫的話,需要考慮主從延遲的問題。

18.介面注意回傳的資料量,如果資料量大需要分頁

一個介面回傳報文,不應該包含過多的資料量。過多的資料量不僅處理複雜,並且資料量傳輸的壓力也非常大。因此數量實在是比較大,可以分頁回傳,如果是功能不相關的報文,那應該考慮介面拆分。

19. 好的介面實現,離不開 SQL 優化

我們做後端的,寫好一個介面,離不開 SQL 優化。

SQL 優化從這幾個維度思考:

  • explain 分析 SQL 查詢計畫(重點關注 type、extra、filtered 欄位)
  • show profile 分析,了解 SQL 執行的執行緒的狀態以及消耗的時間
  • 索引優化 (覆蓋索引、最左前綴原則、隱式轉換、order by 以及 group by 的優化、join 優化)
  • 大分頁問題優化(延遲關聯、記錄上一頁最大 ID)
  • 資料量太大(分庫分表、同步到 es,用 es 查詢)

20.程式碼鎖的粒度控制好

什麼是加鎖粒度呢?

其實就是就是你要鎖住的範圍是多大。比如你在家上衛生間,你只要鎖住衛生間就可以了吧,不需要將整個家都鎖起來不讓家人進門吧,衛生間就是你的加鎖粒度。

我們寫程式碼時,如果不涉及到共享資源,就沒有必要鎖住的。這就好像你上衛生間,不用把整個家都鎖住,鎖住衛生間門就可以了。

比如,在業務程式碼中,有一個 ArrayList 因為涉及到多執行緒操作,所以需要加鎖操作,假設剛好又有一段比較耗時的操作(程式碼中的slowNotShare方法)不涉及執行緒安全問題,你會如何加鎖呢?

反例:

//不涉及共享資源的慢方法
private void slowNotShare() {
    try {
        TimeUnit.MILLISECONDS.sleep(100);
    } catch (InterruptedException e) {
    }
}

//錯誤的加鎖方法
public int wrong() {
    long beginTime = System.currentTimeMillis();
    IntStream.rangeClosed(1, 10000).parallel().forEach(i -> {
        //加鎖粒度太粗了,slowNotShare其實不涉及共享資源
        synchronized (this) {
            slowNotShare();
            data.add(i);
        }
    });
    log.info("cosume time:{}", System.currentTimeMillis() - beginTime);
    return data.size();
}

正例:

public int right() {
    long beginTime = System.currentTimeMillis();
    IntStream.rangeClosed(1, 10000).parallel().forEach(i -> {
        slowNotShare();//可以不加鎖
        //只對List這部分加鎖
        synchronized (data) {
            data.add(i);
        }
    });
    log.info("cosume time:{}", System.currentTimeMillis() - beginTime);
    return data.size();
}

21.介面狀態和錯誤需要統一明確

提供必要的介面呼叫狀態資訊。比如你的一個轉帳介面呼叫是成功、失敗、處理中還是受理成功等,需要明確告訴客戶端。如果介面失敗,那麼具體失敗的原因是什麼。這些必要的資訊都必須要告訴給客戶端,因此需要定義明確的錯誤碼和對應的描述。同時,盡量對報錯資訊封裝一下,不要把後端的異常資訊完全拋出到客戶端。

22.介面要考慮異常處理

實現一個好的介面,離不開優雅的異常處理。對於異常處理,提十個小建議吧:

  • 盡量不要使用e.printStackTrace(),而是使用log列印。因為e.printStackTrace()語句可能會導致記憶體佔滿。
  • catch住異常時,建議列印出具體的exception,利於更好定位問題
  • 不要用一個Exception捕捉所有可能的異常
  • 記得使用finally關閉流資源或者直接使用 try-with-resource
  • 捕獲異常與拋出異常必須是完全匹配,或者捕獲異常是拋異常的父類
  • 捕獲到的異常,不能忽略它,至少打點日誌吧
  • 注意異常對你的程式碼層次結構的侵染
  • 自訂封裝異常,不要丟棄原始異常的資訊Throwable cause
  • 執行時異常RuntimeException ,不應該通過 catch 的方式來處理,而是先預檢查,比如:NullPointerException處理
  • 注意異常匹配的順序,優先捕獲具體的異常

小夥伴們有興趣可以看下我之前寫的這篇文章哈:Java 異常處理的十個建議

23. 優化程式邏輯

優化程式邏輯這塊還是挺重要的,也就是說,你實現的業務程式碼,如果是比較複雜的話,建議把註釋寫清楚。還有,程式碼邏輯盡量清晰,程式碼盡量高效。

比如,你要使用用戶資訊的屬性,你根據 session 已經獲取到userId了,然後就把用戶資訊從資料庫查詢出來,使用完後,後面可能又要用到用戶資訊的屬性,有些小夥伴沒想太多,反手就把userId再傳進去,再查一次資料庫。。。我在專案中,見過這種程式碼。。。直接把用戶物件傳下來不好嘛。。

反例偽程式碼:

public Response test(Session session){
    UserInfo user = UserDao.queryByUserId(session.getUserId());

    if(user==null){
       reutrn new Response();
    }

    return do(session.getUserId());
}

public Response do(String UserId){
  //多查了一次資料庫
  UserInfo user = UserDao.queryByUserId(session.getUserId());
  ......
  return new Response();
}

正例:

public Response test(Session session){
    UserInfo user = UserDao.queryByUserId(session.getUserId());

    if(user==null){
       reutrn new Response();
    }

    return do(session.getUserId());
}

//直接傳UserInfo物件過來即可,不用再多查一次資料庫
public Response do(UserInfo user){
  ......
  return new Response();
}

當然,這只是一些很小的一個例子,還有很多類似的例子,需要大家開發過程中,多點思考的哈。

24. 介面實現過程中,注意大檔案、大交易、大物件

  • 讀取大檔案時,不要Files.readAllBytes直接讀取到記憶體,這樣會 OOM 的,建議使用 BufferedReader 一行一行來。
  • 大交易可能導致死結、回滾時間長、主從延遲等問題,開發中盡量避免大交易。
  • 注意一些大物件的使用,因為大物件是直接進入老年代的,可能會觸發 fullGC

25. 你的介面,需要考慮限流

如果你的系統每秒扛住的請求是 1000,如果一秒鐘來了十萬請求呢?換個角度就是說,高併發的時候,流量洪峰來了,超過系統的承載能力,怎麼辦呢?

如果不採取措施,所有的請求打過來,系統 CPU、記憶體、Load 負載飆的很高,最後請求處理不過來,所有的請求無法正常回應。

針對這種場景,我們可以採用限流方案。就是為了保護系統,多餘的請求,直接丟棄。

限流定義:

在電腦網路中,限流就是控制網路介面發送或接收請求的速率,它可防止 DoS 攻擊和限制 Web 爬蟲。限流,也稱流量控制。是指系統在面臨高併發,或者大流量請求的情況下,限制新的請求對系統的訪問,從而保證系統的穩定性。

可以使用 Guava 的RateLimiter單機版限流,也可以使用Redis分散式限流,還可以使用阿里開源組件sentinel限流

大家可以看下我之前這篇文章哈:4 種經典限流演算法講解

26.程式碼實現時,注意執行時異常(比如空指針、下標越界等)

日常開發中,我們需要採取措施規避陣列邊界溢位,被零整除,空指標等執行時錯誤。類似程式碼比較常見:

String name = list.get(1).getName(); //list可能越界,因為不一定有2個元素哈

應該採取措施,預防一下陣列邊界溢位。正例如下:

if(CollectionsUtil.isNotEmpty(list)&& list.size()>1){
  String name = list.get(1).getName();
}

27.保證介面安全性

如果你的 API 介面對外提供,需要保證介面的安全性。保證介面的安全性有token 機制和介面簽章

token 機制身分驗證方案還比較簡單的,就是

  1. 客戶端發起請求,申請獲取 token。
  2. 服務端產生全域唯一的 token,保存到 redis 中(一般會設定一個過期時間),然後回傳給客戶端。
  3. 客戶端帶著 token,發起請求。
  4. 服務端去 redis 確認 token 是否存在,一般用 redis.del(token)的方式,如果存在會刪除成功,即處理業務邏輯,如果刪除失敗不處理業務邏輯,直接回傳結果。

介面簽章的方式,就是把介面請求相關資訊(請求報文,包括請求時間戳、版本號、appid 等),客戶端私鑰加簽,然後服務端用公鑰驗簽,驗證通過才認為是合法的、沒有被篡改過的請求。

有關於加簽驗簽的,大家可以看下我這篇文章哈:程式設計師必備基礎:加簽驗簽

除了加簽驗簽和 token 機制,介面報文一般是要加密的。當然,用 https 協定是會對報文加密的。如果是我們服務層的話,如何加解密呢?

可以參考 HTTPS 的原理,就是服務端把公鑰給客戶端,然後客戶端產生對稱金鑰,接著客戶端用服務端的公鑰加密對稱金鑰,再發到服務端,服務端用自己的私鑰解密,得到客戶端的對稱金鑰。這時候就可以愉快傳輸報文啦,客戶端用對稱金鑰加密請求報文,服務端用對應的對稱金鑰解密報文

有時候,介面的安全性,還包括手機號、身分證等資訊的去敏。就是說,用戶的隱私資料,不能隨便暴露

28.分散式交易,如何保證

分散式交易:就是指交易的參與者、支援交易的伺服器、資源伺服器以及交易管理器分別位於不同的分散式系統的不同節點之上。簡單來說,分散式交易指的就是分散式系統中的交易,它的存在就是為了保證不同資料庫節點的資料一致性。

分散式交易的幾種解決方案:

  • 2PC(二階段提交)方案、3PC
  • TCC(Try、Confirm、Cancel)
  • 本地訊息表
  • 最大努力通知
  • seata

大家可以看下這篇文章哈:看一遍就理解:分散式交易詳解

29. 交易失效的一些經典場景

我們的介面開發過程中,經常需要使用到交易。所以需要避開交易失效的一些經典場景。

  • 方法的存取權限必須是 public,其他 private 等權限,交易失效
  • 方法被定義成了 final 的,這樣會導致交易失效。
  • 在同一個類中的方法直接內部呼叫,會導致交易失效。
  • 一個方法如果沒交給 spring 管理,就不會產生 spring 交易。
  • 多執行緒呼叫,兩個方法不在同一個執行緒中,獲取到的資料庫連線不一樣的。
  • 表的儲存引擎不支援交易
  • 如果自己 try...catch 誤吞了異常,交易失效。
  • 錯誤的傳播特性

推薦大家看下這篇文章:聊聊 spring 交易失效的 12 種場景,太坑了

30. 掌握常用的設計模式

把程式碼寫好,還是需要熟練常用的設計模式,比如策略模式、工廠模式、模板方法模式、觀察者模式等等。設計模式,是程式碼設計經驗的總結。使用設計模式可以可重用程式碼、讓程式碼更容易被他人理解、保證程式碼可靠性。

我之前寫過一篇總結工作中常用設計模式的文章,寫得挺不錯的,大家可以看下:實戰!工作中常用到哪些設計模式

31. 寫程式碼時,考慮線性安全問題

高併發情況下,HashMap可能會出現無窮迴圈。因為它是非線性安全的,可以考慮使用ConcurrentHashMap。所以這個也盡量養成習慣,不要上來反手就是一個new HashMap();

  • Hashmap、Arraylist、LinkedList、TreeMap 等都是線性不安全的;
  • Vector、Hashtable、ConcurrentHashMap 等都是線性安全的

32.介面定義清晰易懂,命名規範。

我們寫程式碼,不僅僅是為了實現目前的功能,也要有利於後面的維護。說到維護,程式碼不僅僅是寫給自己看的,也是給別人看的。所以介面定義要清晰易懂,命名規範。

33. 介面的版本控制

介面要做好版本控制。就是說,請求基礎報文,應該包含version介面版本號欄位,方便未來做介面相容。其實這個點也算介面擴充性的一個體現點吧。

比如客戶端 APP 某個功能最佳化了,新舊版本會共存,這時候我們的version版本號就派上用場了,對version做升級,做好版本控制。

34. 注意程式碼規範問題

注意一些常見的程式碼壞味道:

  • 大量重複程式碼(抽共用方法,設計模式)
  • 方法參數過多(可封裝成一個 DTO 物件)
  • 方法過長(抽小函數)
  • 判斷條件太多(最佳化 if...else)
  • 不處理沒用的程式碼
  • 不注重程式碼格式
  • 避免過度設計

程式碼的壞味道,這裡我都寫到啦:25 種程式碼壞味道總結+最佳化範例

35.保證介面正確性,其實就是保證更少的 bug

保證介面的正確性,換個角度講,就是保證更少的 bug,甚至是沒有 bug。所以介面開發完後,一般需要開發自測一下。然後的話,介面的正確還體現在,多執行緒併發的時候,保證資料的正確性, 等等。比如你做一筆轉帳交易,扣減餘額的時候,可以透過 CAS 樂觀鎖的方式保證餘額扣減正確吧。

如果你實現秒殺介面,得防止超賣問題吧。你可以使用 Redis 分散式鎖防止超賣問題。使用 Redis 分散式鎖,有幾個注意要點,大家可以看下我之前這篇文章哈:七種方案!探討 Redis 分散式鎖的正確使用姿勢

36.學會溝通,跟前端溝通,跟產品溝通

我把這一點放到最後,學會溝通是非常非常重要的。比如你開發定義介面時,一定不能上來就自己埋頭把介面定義完了,需要跟客戶端先對齊介面。遇到一些難點時,跟技術 leader 對齊方案。實現需求的過程中,有什麼問題,及時跟產品溝通。

總之就是,開發介面過程中,一定要溝通好~

最後(求關注,別白嫖我)

如果這篇文章對您有所幫助,或者有所啟發的話,求一鍵三連:按讚、轉發、在看,您的支持是我堅持寫作最大的動力。

繼續探索

延伸閱讀

更多文章
同分類 / 同標籤 2022/3/17

工作6年,失業19天

我是一名Java程式設計師,在北京工作了6年,此篇文章記錄了我2019年11月,在這個寒冷的冬天被裁員之後,心態變化及重新找工作的心路歷程。

繼續閱讀