淺談API錯誤碼設計
痛點
你是否曾遇到過以下問題?
1.API錯誤碼形同虛設,無法提供有效幫助?
2.API錯誤碼文檔晦澀難懂,別說其他團隊,連團隊內同事都看不明白?
3.API錯誤碼定義混亂,缺乏一致性?
4.鏈路上的錯誤碼信息無法正確傳遞?
API設計的重要性
在軟件架構中,API(應用程序編程接口)和數據庫(DB)的設計至關重要,因為它們分別代表了系統的外部交互界面和內部數據存儲機制。良好的設計不僅能夠提高系統的穩定性、可擴展性和可維護性,而且在未來進行代碼重構或系統升級時,也能大大減少對上游服務和數據遷移的影響。
圖片
1)API設計的重要性
1.抽象層次:API作為系統與外界交互的接口,提供了一層抽象,隱藏了底層的業務邏輯和實現細節。這意味著,只要API的接口保持不變,系統內部的實現可以自由變化而不影響外部調用者。
2.穩定性與兼容性:良好設計的API應該考慮到向后兼容性,即使在系統升級或重構時,也能保證對現有客戶端的支持。這減少了上游服務調整的需要,使得系統的迭代更加平滑。
2)數據庫設計的重要性
1.擴展性和可維護性:隨著系統的發展,數據量會增加,業務需求也會變化。一個設計良好的數據庫能夠更容易地進行擴展和維護,比如通過合理的索引設計、分表分庫等策略來提高性能。
2.數據遷移的便利性:在系統升級或重構過程中,可能需要進行數據遷移。如果數據庫設計考慮了未來可能的變化,那么數據遷移的工作會相對容易和安全。合理的數據版本控制和遷移腳本也是重要的一環。
總之,API和底層數據庫的設計是軟件架構中的關鍵部分,它們的良好設計是確保系統長期健康發展的基石。通過投入足夠的時間和資源來設計和實現高質量的API和數據庫,可以在系統的整個生命周期中節省大量的時間和成本。
在API接口設計中,定義接口錯誤碼是至關重要的一環。本文將重點討論API錯誤碼的設計,而不涉及API的其他方面。
什么是錯誤碼
根據亞馬遜官方文檔的定義,錯誤碼是通過對錯誤進行抽象,幫助用戶或開發者識別和理解異常性質的代號。錯誤碼與具體錯誤不是一對一的關系,而是錯誤類型的一種抽象表示。盡管錯誤碼在系統中只是一個小模塊,但它是不可或缺的。
錯誤消息應該幫助用戶輕松并快速地理解并解決API 錯誤,以下是一些設計原則:
1. 不要假設用戶非常了解你的API。用戶可能是客戶端開發者、運維人員、IT人員或者APP的普通用戶。
2. 不要假設用戶了解服務實現的細節或熟悉錯誤上下文(例如日志分析)。
3. 如果可能,應構建錯誤消息,以便技術用戶(但不一定是API的開發人員)可以對錯誤進行響應并更正。
4. 保持錯誤信息簡短。如果需要,提供鏈接以便用戶能夠提出問題或獲取更多信息。
錯誤碼的設計應當直接解答核心問題:錯誤碼的設計首要明確指出哪里出了問題(系統),其次細化到系統內部具體的模塊或功能點。這種精細化的信息有助于快速定位和解決問題。
錯誤碼設計原則
錯誤碼的設計原則:快速追蹤溯源、簡單、描述清晰(客戶視角)。
1.快速追蹤溯源:一個好的錯誤碼應該使開發者和運維人員能夠迅速識別錯誤的根源(比如APP看到用戶提示錯誤信息,見名思意即可快速定位是ABCDEF哪個系統的什么問題),避免在不同系統或文檔之間來回查找。
2.簡單清晰易記:有效的錯誤碼設計需要考慮其可讀性和可比性,錯誤碼應該有一個清晰的結構和邏輯,方便在代碼中進行比較和處理。
3.信息描述清晰:很多錯誤碼message描述連團隊內同事都看不懂,何況調用你API的開發人員呢,所以要把message描述的清晰明了。
這些設計原則有助于構建一個高效、可靠且易于使用的錯誤碼。通過遵循這些原則,可以確保錯誤碼在實際應用中發揮出最大的價值。
API--錯誤碼設計
在服務器接口設計中,定義接口錯誤碼是至關重要的一環。通常,服務端會設定一系列的錯誤碼,以便于指導接口調用者或用戶采取恰當的操作。這些錯誤碼可能涉及諸如參數非法、配置出錯、內部異常等多種情況,為用戶提供明確的反饋。
1)Response響應
在Response響應方面,我們的 API 將返回一個結構化的 JSON 對象,該對象包含以下三個關鍵屬性:
1.code(狀態碼): 這是一個標準化的數字代碼,用于表明請求的處理結果。每個狀態碼都對應一個特定的情況,使得客戶端可以快速識別請求的狀態。
2.message(狀態碼描述): 該字段提供了一個簡短且清晰的描述,解釋了狀態碼的含義。這將幫助客戶端了解請求成功與否,如果出現問題,它將提供足夠的信息以指導進一步的行動。
3.data(響應數據): 這是實際的響應內容,包含了請求成功時所需的數據。這個數據對象的結構將根據具體的 API 調用而有所不同,但它總是以一種易于客戶端解析和使用的格式提供。
字段 | 類型 | 描述 |
code | String | 業務狀態碼 |
message | String | 錯誤碼描述,需要描述清晰明了 |
data | Object | 封裝對象 |
案例如下:
public class PromiseResponse<T> {
/**
* 錯誤編碼
*/
private String code;
/**
* 提示信息
*/
private String message;
/**
* 業務數據
*/
private T data
{
"code": 200,
"message": "成功",
"data": {
"transferTime": "Mon Jul 29 00:01:00 CST 2024",
"endOrderTime": "Mon Jul 29 18:00:00 CST 2024",
"outStoreTime": "Tue Jul 30 00:00:00 CST 2024",
"jitEndDate": "Tue Jul 30 00:00:00 CST 2024",
"storeDeliveryHandoverTime": "Tue Jul 30 01:00:00 CST 2024",
"deliveryTime": "Thu Aug 01 22:00:00 CST 2024",
"routeProductionResult": {
"ruleType": null,
"ruleName": null
},
"promiseControlResult": {
"controlResultCode": 2,
"suspendReasonCode": 0,
"suspendReason": null,
"abnormalLink": 0,
"abnormalLinkName": null,
"abnormalReason": null
}
}
}
當發生可以重試的錯誤碼時客戶端應該以指數級增長的間隔來重試請求。除非文檔中進行了說明。對于其他錯誤,重試操作可能并不可行,請先確保請求是冪等的并查看錯誤消息以獲得指引。
2)錯誤傳播
如果 API 服務依賴于其他服務,則不應盲目地將這些服務中的錯誤傳播給客戶端。翻譯錯誤時,有如下建議:
- 錯誤碼轉換,比如下游返回錯誤碼A,需要轉換你對外的錯誤碼B
- 錯誤碼描述可追加下游錯誤碼描述信息,讓鏈路錯誤碼描述清晰可見
- 最終對用戶肯定是需要隱藏下游實現細節和機密信息,讓用戶體驗良好的錯誤提示信息
3)?不合理案例
?不合理的API錯誤碼,不合理地方如下:
- 錯誤碼無規則:比較混亂。比如錯誤碼一會1,一會-50,-1等,沒有規則
- 錯誤碼過細:錯誤碼定義過細過多、過度隨意,將會導致調用方對錯誤處理的邏輯復雜,無法很好的對錯誤碼進行轉義或收斂。比如入參錯誤定義了N個錯誤碼,其實定義一個入參錯誤碼即可,錯誤碼描述可以描述具體的錯誤碼信息。
- 錯誤碼描述不清晰:一堆英文,他人看不懂
圖片
4)?行業案例
業界錯誤碼的規范很多,但是這些規范各不相同,甚至很多點相悖(比如Google緊密依賴于HTTP狀態碼)
4.1 京東云錯誤碼
圖片
圖片
4.2 谷歌 API 錯誤碼定義
谷歌API的錯誤碼設計緊密依賴于HTTP狀態碼,采用全數字的錯誤碼定義方式。然而,這種設計缺乏明確的錯誤分類體系,導致其快速識別和自解釋能力相對較弱。
圖片
錯誤碼傳遞
1)現在場景鏈路錯誤碼信息
現有應用基本都是沒有錯誤碼傳遞功能,比如下圖 Y應用出現故障,需要排查對應的依賴服務ABC,服務B返回的是服務B自定義的錯誤碼B,但通過B是無法快速定位故障應用是C2。服務B經過各種排查,最終定位是服務B2的問題,服務B2通過錯誤碼B2也無法快速定位是故障應用C2,繼續每個應用排查,最終定位是服務C2錯誤。
圖片
2)錯誤碼傳遞(轉換)
接收下層模塊發來的錯誤碼A,錯誤碼A是當下層模塊有故障分支時生成的;然后將自身生成的錯誤碼B和所接收到的錯誤碼A合成錯誤碼AB,將錯誤碼AB傳遞給上層模塊。
接收單元,用于接收下層模塊發來的錯誤碼A,錯誤碼A是當下層模塊有故障分支時生成的;
合成單元,用于將自身生成的錯誤碼B和所接收到的錯誤碼A合成錯誤碼AB;
發送單元,用于將所述錯誤碼AB傳遞給上層模塊。
錯誤碼在傳遞的過程中攜帶各層模塊的故障分支信息,這樣,根據錯誤碼就可以確定錯誤碼的傳遞路徑,以便精確的定位故障錯誤原因,提高可維護性。
團隊日常咨詢案例
?錯誤碼設計---未傳播錯誤碼
案例:冷鏈外單無妥投時間,目前鏈路是A---->B---->C系統。但錯誤碼是各自封裝,沒有把根本原因傳播出去,而是各自加工,導致最終看到的原因跟真實的原因千差萬別。導致整個鏈路牽扯 業務方--->A研發---->B研發---->C研發---->C業務同事 總共5個環節,如下圖:
圖片
具體咨詢鏈路如下 :
- 業務聯系A系統開發,告知系統異常提示如下
- 聯系B系統小蜜,小蜜根據訂單查詢調用純配接口出入參查詢日志:
- B系統聯系C系統研發,周知出入參
- C同事聯系 C業務,告知路線沒配,信息如下:{"code":0,"data":[],"message":"派送范圍維護->未查找到配置數據XXXXX","tid":44845519852540539}
現狀:整個鏈路牽扯ABCD系統研發業務,溝通成本大(技術&業務),效率低
改進方案:?錯誤碼信息--傳播錯誤碼信息
推動錯誤碼封裝,錯誤信息傳遞功能,讓最終A系統用戶清晰明了根本原因(D系統)是什么。減少ABCD中間過程,A直接聯系D,直接可見即所得。
- 如果api在翻譯錯誤時,需要把底層根本原因返回上去,比如上面案例,把沒有妥投日期的根本原因【派送范圍維護->未查找到配置數據XXXX","tid":44845519852540539】周知
改造后鏈路 A業務方---->C業務同事 總共2個環節(改造前5個環節),因為界面提示錯誤信息,所見即所得,減少了中間環節。提升了業務效率,減少了研發內部中間環節的排查成本。
【探討】全鏈路錯誤碼系統設計
前面介紹了API的錯誤碼設計及錯誤碼傳遞,本章節探討全鏈路錯誤碼如何串聯起來,不一定對,只是個人的思考,并且實踐起來也是比較困難的不太現實
1)痛點:全鏈路排查問題慢
在京東復雜的系統架構中,故障診斷往往像是在迷宮中尋路。想象一下,一個由多達20+個相互依賴的系統組成的服務鏈路,從入口到底層的第N個服務,每個系統都是潛在的故障點。當前,一旦系統出現問題,都是上游拉群,定位問題拉下游N個系統,線上語音討論是哪個出現的問題故障導致的,整個流程可能需要耗費長達1-2個小時甚至更長時間才能追蹤到問題實際出現在M系統上。這個過程不僅耗時,而且效率低下。
現狀故障處理流程:
圖片
現有系統交互圖如下:
圖片
在這種復雜系統架構中,目前故障診斷的技術面臨多個挑戰和缺點,主要包括:
- 時間消耗長、效率低下:當系統出現問題時,故障診斷需要逐個檢查各個系統,需要長時間才能定位到問題所在的系統,這直接影響了故障恢復時間和系統的整體可用性。
- 復雜性管理不足:在多個相互依賴的系統中,即使是小問題也可能迅速演變成復雜問題,現有的技術似乎沒有很好地管理這種復雜性。
- 信息孤島:系統間可能存在信息隔離,導致故障信息不能快速傳遞,增加了診斷時間。
- 依賴專業知識:可能過度依賴工程師的專業知識和經驗進行故障排查,這不僅效率低,而且不利于知識傳承和團隊協作。
- 缺乏自動化:聽起來這個過程很大程度上依賴于手動操作,缺乏自動化工具來快速識別和定位大概是哪個系統的問題。
API中如何設計一個號的錯誤碼,鏈路調用錯誤碼如何傳遞,線上故障如何通過錯誤碼快速定位鏈路某個系統或者某個功能問題點
2)設計思想
如下圖:如果服務C2有故障,則通過全鏈路traceId可快速查看Y的故障對應的錯誤碼,根據錯誤碼定位是因為C2應用故障導致的。
圖片
日志錯誤碼架構思路如下:
參考【京東基礎技術統一平臺XXX系統】--》【業務監控】每個應用無需接入錯誤碼Agent,錯誤碼Agent其實就是日志采集。跟業務監控一樣,你只需要把你應用的業務指標錯誤信息打印到日志中即可,比如服務ABCDEF鏈路都打印日志,相關的應用logbook接入TOPIC-1,則通過TOPIC-1可拿到整個鏈路traceId以及每個應用的錯誤碼信息(如果有)。
日志格式:traceId|應用錯誤碼|錯誤碼信息描述
1677474.49460.17235995037011944.3460091.193151.9140|WMS_10001|調用Promise(系統B)系統異常,缺少預計妥投時間
1677474.49460.17235995037011944.3460091.193151.9140|PROMIES_10002|Promise調用路由系統(系統C)異常,缺少預計妥投時間
1677474.49460.17235995037011944.3460091.193151.9140|ROUTE_20003|派送范圍維護->未查找到配置數據,請排查派送范圍維護,派送地址:四川-達州市-宣漢縣-龍泉土家族鄉,產品:生鮮特殊次晨,生鮮特惠次晨,生鮮標快
這樣通traceId(17235995037011944.3460091.193151.9140)可拿到鏈路中的AB(B1,B2,B3)C系統的錯誤碼,其中B1/B2/B3等系統無錯誤,則可不打錯誤碼。
具體系統設計圖如下:
圖片
- 在應用程序中,使用日志打印功能記錄接口對應的錯誤碼及其詳細信息,確保每次接口調用出現錯誤時都能捕獲到相關數據。
- 錯誤碼Agent會從日志中收集這些錯誤碼信息,并將其發送到消息隊列中,以便后續的處理和分析。
- 消費消息隊列中的錯誤碼信息,進行深入的數據分析,包括但不限于錯誤碼的頻率、分布和趨勢等。
- 根據traceId,追蹤并收集整個鏈路的數據,并將其存儲到MySQL或Elasticsearch等數據庫中,以便進行全鏈路的錯誤碼信息查詢和分析。
- 在應用程序的界面上,用戶可以通過輸入對應的traceId來查詢整個鏈路的錯誤碼信息,方便快速定位問題。
- 系統會自動對錯誤碼信息進行實時的數據分析和趨勢預測,幫助團隊及時發現和解決潛在的故障。
- 如果錯誤碼信息被識別為異常或高優先級事件,系統將自動觸發報警服務,通過多種方式(如咚咚、電子郵件、京Me等)通知相關干系人。對于緊急情況,系統還可以通過電話等方式直接聯系一線人員,確保問題得到及時處理。
3)挑戰性:鏈路改造范圍廣
全鏈路錯誤碼系統設計需要對現有系統進行大范圍改造,確保每個系統都能記錄和傳遞錯誤碼。這不僅涉及大量的開發工作,還需要跨團隊的協調和配合。此外,系統之間的日志格式和錯誤碼標準需要統一,確保數據的可追溯性和一致性。
總結
設計一個好的API錯誤碼是一個具有挑戰性的任務,但它對于提高系統的可維護性和用戶體驗至關重要。本文拋磚引玉,希望提供的思路和實踐能夠幫助你在設計API錯誤碼時更加得心應手。
如本文里面信息有誤請指正,如有更好的知識點,歡迎評論交流完善補充。謝謝!
參考內容
1、京東云錯誤碼:https://docs.jdcloud.com/cn/face-compare/api/error-code
2、Google錯誤碼:https://cloud.google.com/apis/design/errors?hl=zh-cn