關於錯誤碼的那點事 - 知乎 https://zhuanlan.zhihu.com/p/411726319
關於錯誤碼的那點事
一、什么是錯誤碼
錯誤碼一般情況分為對外錯誤碼,系統內部錯誤碼。
對外錯誤碼常應用在一些開放接口,比如http接口,rpc接口等,通過錯誤碼的形式給予上游更加友好的錯誤提示以及錯誤描述。
系統內部錯誤碼,存在於關系緊密的微服務之間、或者程序的上下游中。因為某種業務錯誤、或者系統不可用造成的錯誤,開發人員可以根據錯誤碼、錯誤信息進行具體定位;或者根據上游根據錯誤碼做業務邏輯判斷,從而保證整體流程完整性。
總而言之,錯誤碼的作用: 指出錯誤的原因,快速定位問題,指導上游系統做出正確的業務判斷,引導用戶進行正確的操作。因此構建一個通用且架構清晰的錯誤碼體系是一件很有必要的事情。
那么怎么定義一種對外對內都友好的錯誤碼呢?至今業內也並沒有一個比較好的方案或者規范。這里結合新老支付系統融合,逐步進行摸索。
二、支付系統錯誤碼現狀
由於歷史原因,公司內部目前有兩套運行中的錢包系統。為了提高錢包系統可用性以及性能,對兩套錢包系統進行遷移整合升級優化。但遷移合並兩套錢包系統的途中,發現兩套錢包系統、以及之前現存的已經優化過的錯誤碼之前存在沖突、類型不一致、定義混亂、隨意性高等問題。

由於其一錢包系統之前由其他團隊開發維護,后期也對其進行重構過,這造成了新錢包系統的錯誤碼定義規則存在兩套;從代碼角度來看,新老錢包系統在設計之初的時候,對錯誤碼的定義都各自定義了一個比較合適的規范,但是在后期開發維護中,越來越少的開發人員去遵循規范定義錯誤碼,最終造成了由錯誤描述決定錯誤碼的現象。而目前新系統中錯誤碼,在設計之初,並未考慮到未來整合帶來的錯誤碼沖突等問題,造成了部分錯誤碼重合,語義大相徑庭的問題,所以重新定義錯誤碼規范也是迫在眉睫。
錢包系統錯誤碼現狀:
錢包一錯誤碼定義: 6位錯誤碼。 首位表示錯誤類型,區分系統級別、校驗、rpc服務調用錯誤。 這種設計方式,第一位是明確的,后面5位都是預留的錯誤碼,長度足夠,滿足后續錯誤碼的增加;缺點錯誤碼對應的類型太少,會出現相同語義的錯誤碼,對應不同的首位數字。后重構版本的錯誤碼修改了原有的錯誤碼位數,修改后7位。造成了該系統的接口層,6位錯誤碼,7位錯誤碼混亂,並且錯誤碼沒有分類,全部順序后排。
錢包二錯誤碼定義: 提供錯誤碼工具類,規定了大多數錢包常用的錯誤碼;然后以此為基礎,進行增加。優點: 錯誤碼分類清晰,結構明了,缺點: 不容易根據錯誤碼定位具體錯誤。
簡單來說,目前錢包系統錯誤碼存在以下問題
1. 錯誤碼字段類型定義不一致。有的定義數值類型,有的定義字符串類型
2.錯誤碼重合問題。相同的錯誤碼,在不同系統中有着不同的語義

三、調研業內接口定義
鑒於當前支付系統錯誤碼的痛點,如錯誤碼應該用數值類型還是字符串類型,長度命名格式是怎樣的等問題,調研了多家大廠的API規范以及接口定義,來探索適用於支付系統的錯誤碼規范。
- 微信支付
a. 參考微信支付v2接口
微信支付v2接口: 協議:http, content-type: text/xml
參考鏈接: https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_1
原格式是xml,為了更加直觀,這里先加工為json格式 { "return_code":"SUCCESS", // SUCCESS/FAIL 此字段是通信標識,非交易標識,交易是否成功需要查看result_code來判斷 "return_msg":"OK", "result_code":"SUCCESS", // SUCCESS/FAIL 標識業務成功失敗 "err_code":"SYSTEMERROR", // 當result_code為FAIL的時候,該值返回業務錯誤碼 "err_code_des":"系統錯誤" }
微信支付錯誤碼結構為三級結構:
一級、公共錯誤碼(網關層) 。該層僅返回通信成功失敗信息
二級、業務錯誤碼 (總): 表示該業務是否處理成功
三級、具體業務錯誤碼
業務錯誤碼舉例:
error_code | err_code_des |
---|---|
NOAUTH | 商戶無此接口權限 |
INVALID_REQUEST | 參數錯誤 |
NOTENOUGH | 余額不足 |
b.參考微信支付v3接口
微信支付v3接口: 協議:http, content-type: application/json
參考鏈接: https://pay.weixin.qq.com/wiki/doc/apiv3/Share/error_code.shtml
err_code | http_code | err_msg |
---|---|---|
USERPAYING | 202 | 用戶正在付款中 |
OUT_TRADE_NO_USED | 403 | 商戶訂單號重復 |
ORDERNOTEXIST | 404 | 訂單不存在 |
根據微信v3的接口文檔可知,微信支付返回的業務錯誤的同時,會返回一個與之對應的httpcode。當http_code的狀態碼在[200,300)之間,認為該請求是有合法的返回的;當大於300時,判斷接口返回一定是有異常錯誤的。微信支付V3 sdk封裝了http_code與err_code的相關處理。
對外暴露http接口,在拋出業務錯誤的同時,也要拋出相同語義的httpcode,這就需要開發人員明確httpcode語義。 這樣的劣勢在於學習成本較高,依賴開發人員對httpcode的熟練程度,可能會出現 httpcode語義與業務錯誤語義不一致的現象。
2.支付寶錯誤碼定義
參考鏈接: https://opendocs.alipay.com/open/common/105806
不同業務的業務錯誤碼: 舉一個接口的例子 https://opendocs.alipay.com/apis#%E4%B8%9A%E5%8A%A1%E9%94%99%E8%AF%AF%E7%A0%81
sub_code、sub_msg這兩個參數標識支付寶返回的業務錯誤碼、業務錯誤信息;
{ "code":"",//網關返回碼 "msg":"",//網關返回碼 "sub_code":"ACQ.INVALID_PARAMETER", "sub_msg":"參數無效" }
支付寶的錯誤碼定義 與 微信支付的v2接口定義風格比較相像。
支付寶錯誤碼結構分為兩級: 一級: 網關, 二級: 業務錯誤碼。
請求支付寶接口,先通過支付寶網關系統,網關系統進行驗簽、加解密、流控等功能,如果網關校驗出錯,則拋出公共錯誤碼。網關校驗成功之后,交給下游業務系統,sub_code都是語義明確的錯誤碼,以及錯誤描述。
3. google Api規范
參考鏈接: https://www.bookstack.cn/read/API-design-guide/API-design-guide-07-%E9%94%99%E8%AF%AF.md
google的錯誤碼定義中,將返回碼的結構定義為
message Status { // A simple error code that can be easily handled by the client. The // actual error code is defined by `google.rpc.Code`. int32 code = 1; // A developer-facing human-readable error message in English. It should // both explain the error and offer an actionable resolution to it. string message = 2; // Additional error information that the client code can use to handle // the error, such as retry delay or a help link. repeated google.protobuf.Any details = 3; }
其中 code: 是錯誤碼,message: 是具體的錯誤信息,detail是根據這個錯誤,推薦調用方采取怎樣的措施。
google對於錯誤碼的定義,是比較簡潔的。一個大類分配一個code;並不會因為多個相似的錯誤類型提供多個code。
google規范里details信息: 表示該錯誤的具體原因,定義參考: https://github.com/googleapis/googleapis/blob/master/google/rpc/error_details.proto
detail里面定義了 retryable信息 可以表示該錯誤碼返回是可以進行重試的,並且給出推薦的重試延遲時間、QuotaFailure信息表示配額出錯、限流超限等;badRequest可以詳細的給出為什么會報這種錯誤 等等開發人員可以根據這樣詳細的錯誤返回碼作出正確的反應。
4. 微博 規范
參考api: https://open.weibo.com/wiki/Error_code
{ "request" : "/statuses/home_timeline.json", "error_code" : "20502", "error" : "Need you follow uid." }
20502 其中 錯誤碼的組成: 1位 錯誤級別編號(系統、服務) + 2位服務模塊(比如 網關、微博、評價、私信 比較像服務標識) + 2位 錯誤代碼(自定義的錯誤編碼)
2 | 05 | 02 |
---|---|---|
服務級錯誤(1為系統級錯誤) | 服務模塊代碼 | 具體錯誤代碼 |
微博的錯誤碼有明確的結構語義。將服務系統標識,顯示在錯誤碼中。這個操作與支付寶錯誤碼構成有一點相像。第一位 標識服務級、系統級的字段,不確定是否是表示該錯誤是有網關拋出、還是說明確幾種系統級別的錯誤,按分類拋出。
5.阿里巴巴的JAVA技術手冊
阿里巴巴規范
第一點: 說明了錯誤碼的特點: 要簡單明了;
第二點: 錯誤碼最好定義為string字符串類型: 來源 + 錯誤編號 (這樣,錯誤碼的數值可以攜帶更多的信息)
第三點: 避免隨意添加錯誤碼、避免直接暴露錯誤碼給到用戶側
綜上所述, 通過調研的這幾家的對外文檔來看,錯誤碼的定義業內並沒有一個統一的規范。但是大體的設計思路如下:
a. 錯誤碼類型為字符串類型
b. 系統如果由網關→ 內部服務構成,則錯誤碼分為兩級
c. 錯誤碼可以標識出拋出錯誤的來源服務。
d. 錯誤碼可以抽象出來兩種 公共錯誤碼、業務錯誤碼
四、思考與結論
結合上述調研的行業錯誤碼定義,以及當前錢包系統現狀。由於支付系統處於整體業務流程的最基礎層,提供支付、付款等RPC能力,不存在直接對外暴露http接口的可能。所以微信支付、支付寶支付的三層錯誤碼結構並不適用於錢包系統。
借鑒上述調研的api,錯誤碼定義成字符串類型,更適用於業務場景,方便於后期的業務擴展。而google規范中對於錯誤碼場景定義的統一規范,在一定程度上又降低了開發人員隨意定義新錯誤碼的可能。所以在error_code的場景定義上參考google-api規范
錯誤碼error_code: 字符串. 前N位為當前業務所屬領域標識,優勢: 易於區分其他業務錯誤碼,如果后期由於業務擴展、或者業務縮小帶來的服務拆分合並,錯誤碼仍然可以保持當前的設置。
提取公共常用的錯誤:
參考google規范進行歸集: https://github.com/googleapis/googleapis/blob/master/google/rpc/code.proto
RPC接口常用錯誤 | error_code |
---|---|
內部錯誤 | 115 |
該請求不支持 | 112 |
狀態錯誤 | 110 |
請求頻繁 | 109 |
沒有權限類錯誤 | 108 |
權限校驗錯誤 | 107 |
已存在類錯誤 | 106 |
不存在類錯誤 | 105 |
超時類錯誤 | 104 |
參數錯誤 | 103 |
未知錯誤 (比如調用下游接口出錯,可以拋這個異常) |
101 |
其他業務錯誤碼,可以使用200~999錯誤段進行自定義設置;但是如果有錯誤語義命中上述錯誤,則需要優先選擇上述錯誤碼。
五、RPC錯誤碼結構定義
exception RpcError{ 1:required string err_code; 2:required string err_desc; }
- 錯誤碼定義:
構成: 業務+錯誤碼類型+自定義業務編碼; 其中自定義業務編碼是系統自定義的。
標識處理業務 | 錯誤碼類型(3) | 自定義業務編碼(2) |
---|
參數校驗失敗:
{
"err_code":"WORDER.10501",
"err_desc":"交易不存在"
}
WORDER:表示當前錯誤發生時,所處理的業務標識
105: 不存在
01: 交易不存在
02: 用戶不存在
03: 訂單不存在
...
2. err_desc: 錯誤信息
錯誤信息 開發人員可以快速定位問題。
3. 錯誤要拋出來
接口如果處理出錯了,包裝好合適的錯誤碼以及錯誤描述,將該錯誤throw出,而不是將錯誤包在接口返回參數中。由於公司使用的是thrift協議-http,並且監控告警強依賴於httpCode,將錯誤直接拋出去,可以使監控更加有效的監控RPC接口,避免處理出錯,但是返回httpcode是200的場景。

六、網關類型的httpcode設計
- httpcode基礎
錯誤碼 | 代表含義 |
---|---|
2xx | 成功 |
3xx | 重定向 |
4xx | 客戶端原因引起的錯誤 |
5xx | 服務器原因引起的錯誤 |
2. http服務返回
{ "code":"0", // 成功:0 失敗:返回對應錯誤碼 "message":"", "data":{ //接口實際處理結果 }, "pagination":{ "is_end":false, "is_first":true, "offset":20, "limit":20, "total":1000 } }
3 錯誤信息轉換
http接口,如果接口返回成功。則httpcode錯誤碼返回200;如果失敗,可以按需返回上述httpcode。
http接口一般分為兩種,第一種: 內部http接口; 第二種,與前端進行交互。內部http接口,錯誤碼可以參照rpc接口;外部接口,避免將內部錯誤碼外露出去,對用戶展示的錯誤描述,最好可以在http層進行轉換,不要將內部錯誤描述直接暴露出去。
七、展望
在系統遷移、重構、優化的時候,經常會遇到由於原有系統設計不合理,后續趕時間堆需求,造成的系統日益難以維護的問題。本次主要針對遷移過程中,遇到的錯誤碼定義混亂這一問題,提出調研、以及自己的思考。目標能夠統一錯誤碼格式的規范,以及rpc、http類型接口的錯誤定義規范。在后續系統遷移過程中,使用規范的錯誤定義,降低上游系統理解錯誤的復雜度,並且在一定程度上可以降低運維效率。
在后續的規划中,針對規范的錯誤碼使用,可以有更多的技術設想,比如錯誤集成SDK,內部包含錯誤碼定義,以及錯誤的打點上報,錯誤監控大盤等。