天天看點

關于錯誤碼的那點事

作者:閃念基因

一、什麼是錯誤碼

錯誤碼一般情況分為對外錯誤碼,系統内部錯誤碼。

關于錯誤碼的那點事

對外錯誤碼常應用在一些開放接口,比如http接口,rpc接口等,通過錯誤碼的形式給予上遊更加友好的錯誤提示以及錯誤描述。

系統内部錯誤碼,存在于關系緊密的微服務之間、或者程式的上下遊中。因為某種業務錯誤、或者系統不可用造成的錯誤,開發人員可以根據錯誤碼、錯誤資訊進行具體定位;或者根據上遊根據錯誤碼做業務邏輯判斷,進而保證整體流程完整性。

總而言之,錯誤碼的作用: 指出錯誤的原因,快速定位問題,指導上遊系統做出正确的業務判斷,引導使用者進行正确的操作。是以建構一個通用且架構清晰的錯誤碼體系是一件很有必要的事情。

那麼怎麼定義一種對外對内都友好的錯誤碼呢?至今業内也并沒有一個比較好的方案或者規範。這裡結合新老支付系統融合,逐漸進行摸索。

二、支付系統錯誤碼現狀

由于曆史原因,公司内部目前有兩套運作中的錢包系統。為了提高錢包系統可用性以及性能,對兩套錢包系統進行遷移整合更新優化。但遷移合并兩套錢包系統的途中,發現兩套錢包系統、以及之前現存的已經優化過的錯誤碼之前存在沖突、類型不一緻、定義混亂、随意性高等問題。

關于錯誤碼的那點事

由于其一錢包系統之前由其他團隊開發維護,後期也對其進行重構過,這造成了新錢包系統的錯誤碼定義規則存在兩套;從代碼角度來看,新老錢包系統在設計之初的時候,對錯誤碼的定義都各自定義了一個比較合适的規範,但是在後期開發維護中,越來越少的開發人員去遵循規範定義錯誤碼,最終造成了由錯誤描述決定錯誤碼的現象。而目前新系統中錯誤碼,在設計之初,并未考慮到未來整合帶來的錯誤碼沖突等問題,造成了部分錯誤碼重合,語義大相徑庭的問題,是以重新定義錯誤碼規範也是迫在眉睫。

錢包系統錯誤碼現狀:

錢包一錯誤碼定義: 6位錯誤碼。 首位表示錯誤類型,區分系統級别、校驗、rpc服務調用錯誤。 這種設計方式,第一位是明确的,後面5位都是預留的錯誤碼,長度足夠,滿足後續錯誤碼的增加;缺點錯誤碼對應的類型太少,會出現相同語義的錯誤碼,對應不同的首位數字。後重構版本的錯誤碼修改了原有的錯誤碼位數,修改後7位。造成了該系統的接口層,6位錯誤碼,7位錯誤碼混亂,并且錯誤碼沒有分類,全部順序後排。

錢包二錯誤碼定義: 提供錯誤碼工具類,規定了大多數錢包常用的錯誤碼;然後以此為基礎,進行增加。優點: 錯誤碼分類清晰,結構明了,缺點: 不容易根據錯誤碼定位具體錯誤。

簡單來說,目前錢包系統錯誤碼存在以下問題

1. 錯誤碼字段類型定義不一緻。有的定義數值類型,有的定義字元串類型

2.錯誤碼重合問題。相同的錯誤碼,在不同系統中有着不同的語義

關于錯誤碼的那點事

三、調研業内接口定義

鑒于目前支付系統錯誤碼的痛點,如錯誤碼應該用數值類型還是字元串類型,長度命名格式是怎樣的等問題,調研了多家大廠的API規範以及接口定義,來探索适用于支付系統的錯誤碼規範。

  1. 微信支付

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

參考連結:

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規範

參考連結:

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資訊: 表示該錯誤的具體原因,定義參考:

detail裡面定義了 retryable資訊 可以表示該錯誤碼傳回是可以進行重試的,并且給出推薦的重試延遲時間、QuotaFailure資訊表示配額出錯、限流超限等;badRequest可以詳細的給出為什麼會報這種錯誤 等等開發人員可以根據這樣詳細的錯誤傳回碼作出正确的反應。

4. 微網誌 規範

參考api:

{
    "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;
}           
  1. 錯誤碼定義:

構成: 業務+錯誤碼類型+自定義業務編碼; 其中自定義業務編碼是系統自定義的。

辨別處理業務 錯誤碼類型(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設計

  1. 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,内部包含錯誤碼定義,以及錯誤的打點上報,錯誤監控大盤等。

作者:任我去留

出處:https://zhuanlan.zhihu.com/p/411726319