系統(tǒng)設(shè)計基礎(chǔ)9:API的設(shè)計

本文討論一下什么是API,以及如何進(jìn)行設(shè)計出好的API。

API的概念

API:Application Programmable Interface,我們通常理解的接口。

它是一個方法,可以提供給使用方進(jìn)行遠(yuǎn)程訪問。它與系統(tǒng)內(nèi)部的具體實(shí)現(xiàn)無關(guān),只定義了和外部使用方的交互方式,他們需要提供什么,方法可以返回什么,因此API描述的可以通過這個接口做什么事情。

public List<Admin> getAdmins(String groupId);

上面是一個API的定義,API接收一個groupId并返回所有的管理員,這里getAdmin為API的名字,groupId為傳入?yún)?shù),List<Admin>為返回值。

API設(shè)計的Checklist

  • Where:API應(yīng)該在哪里定義。在微服務(wù)中,存在一個和Group相關(guān)的服務(wù),那么上面通過GroupID獲取所有管理員的API應(yīng)該在Group服務(wù)中實(shí)現(xiàn)。
  • What:API的功能是什么。API的名字要可以明確的表述出API能夠?qū)崿F(xiàn)的功能。
  • How:API如何調(diào)用。API的參數(shù)設(shè)計,定義了在調(diào)用API時需要傳入的參數(shù)。
  • Result:API返回什么。API在執(zhí)行結(jié)束后,返回給客戶端的結(jié)果。

API設(shè)計的常見錯誤

  • 命名問題:例如上面的API,返回的是屬于某組的管理員,那它的名字就不應(yīng)該是getAdmins,而應(yīng)該是類似于getAdminsBelongToGroup,能夠準(zhǔn)確表達(dá)它的含義。
  • 參數(shù)定義問題:假如請求方想判斷一個用戶列表是否全部為該組的管理員,那應(yīng)該再設(shè)計一個API類似:
public boolean checkAdmins(String groupId, List<String> userId);

這里要強(qiáng)調(diào)的是,要根據(jù)API的功能進(jìn)行參數(shù)的設(shè)計,不要傳入額外不必要的參數(shù)。

  • 參數(shù)類型的定義:定義準(zhǔn)確的參數(shù)數(shù)據(jù)類型,如果調(diào)用者傳入了錯誤的參數(shù)類型,直接返回調(diào)用失敗。
  • 盡可能多攜帶有用信息:假設(shè)getAdmins操作需要對傳入的groupId進(jìn)行額外的,比如鑒權(quán)或者之類的判斷,而這些判斷在調(diào)用getAdmins之前已經(jīng)獲得。那么是可以將這部分信息通過參數(shù)傳給getAdmins,以減少重復(fù)的調(diào)用。
  • 返回內(nèi)容定義問題:有種設(shè)計方式是將所有信息全部提供給調(diào)用者,可以不關(guān)心調(diào)用者使用哪些信息,從安全和網(wǎng)絡(luò)性能的角度考慮,這種方式是不推薦的,應(yīng)該按照調(diào)用者的需求設(shè)計返回內(nèi)容的格式。

POST和GET請求

仍然以獲取某個組的所有管理員為例,如果設(shè)計成POST請求,那么API為:

* POST:https://www.meazza.tk/chat_messaging/getAdmins,
* Request: 
{
    "groupId": "123"
}
* Response:
{
    "admins": [
        {
            "id": 11111,
            "name": "meazza"
        }
    ]
}

該P(yáng)OST請求也可以修改為將action放在Request中:

* POST:https://www.meazza.tk/chat_messaging,
* Request: 
{
    "groupId": "123",
    "action": "getAdmins"
}
* Response:
{
    "admins": [
        {
            "id": 11111,
            "name": "meazza"
        }
    ]
}

如果設(shè)計成GET請求,不需要發(fā)送payload,設(shè)計的API如下:

* GET:https://www.meazza.tk/chat_messaging/admins?groupId=123
* Response:
{
    "admins": [
        {
            "id": 11111,
            "name": "meazza"
        }
    ]
}

避免副作用

假設(shè)有一個API,是將一組用戶設(shè)置為該組的管理員,方法定義如下:

public void setAdmins(List<Admin> admins, String groupId);

現(xiàn)在的問題是,如果admins中包含了非該組的成員,該如何處理?一種方式是API的方法邏輯中進(jìn)行判斷,如果發(fā)生這種情況返回請求失??;另一種方式是將admins中不是該組成員的,添加到該組中,并設(shè)置成管理員。因此這個API的行為是不確定的,可能并不是一個好的API設(shè)計。

如果采用第二種處理方式,那么這個API的帶有副作用的,也就是將一些用戶添加到了該組中。因此這里有兩個API的設(shè)計原則:

  • 獨(dú)立性:比如在API中完成了多種操作,并通過一些flag決定執(zhí)行哪些操作的話,這個API是很奇怪的,此時應(yīng)該拆成多個API。
  • 原子性:比如setAdmins的實(shí)現(xiàn),每次都要先清除group的管理員,如果某次請求失敗,可能導(dǎo)致后續(xù)的請求無法執(zhí)行,因此要確保每次API的操作都是相同的,不能存在中間狀態(tài)。

解決返回數(shù)據(jù)量過大

如果一個API返回的數(shù)據(jù)量比較多,有兩種方式可以進(jìn)行優(yōu)化:

  • 分頁(Pagination):比如一個返回200個用戶的API,可以每次返回10個,并標(biāo)記當(dāng)前頁數(shù)和總頁數(shù)等信息。
  • 分段(Fragment):分拆這個API的返回信息,并使用多個API實(shí)現(xiàn),某個API返回其中的一部分信息,客戶端可以按順序進(jìn)行依次請求。

數(shù)據(jù)一致性和可用性

最后討論一些API在實(shí)際使用中的一些問題:

  • 數(shù)據(jù)一致性:假如API是從數(shù)據(jù)庫中查詢數(shù)據(jù),如果在API查詢后,數(shù)據(jù)庫中添加了新的數(shù)據(jù),可能會導(dǎo)致出現(xiàn)數(shù)據(jù)不一致的情況,如果要解決不一致的問題,就會導(dǎo)致API的性能下降。因此要結(jié)合應(yīng)用場景考慮,如果是不需要保證數(shù)據(jù)嚴(yán)格一致的場景,比如查詢一個帖子的所有評論,是可以接受不一致的情況的。
  • API的降級:如果API的返回內(nèi)容過大導(dǎo)致有可能崩潰,此時應(yīng)該保證API可以返回最重要的信息,而丟棄一部分信息,使得系統(tǒng)不會徹底崩潰。

小結(jié)

本節(jié)介紹了關(guān)于API設(shè)計的方方面面,在保證API設(shè)計的基本規(guī)范的前提下,在設(shè)計時還要考慮實(shí)際的應(yīng)用場景,結(jié)合實(shí)際情況設(shè)計出最合適系統(tǒng)的API。

歡迎大家訂閱專題,其中包含了系統(tǒng)設(shè)計基礎(chǔ)系列的全部文章:System Design

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容