偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

我已經(jīng)受夠了“系統(tǒng)異?!保?/h1>

系統(tǒng) 其他OS
給系統(tǒng)異常后面帶了個 flag 標識,當出現(xiàn)問題時,根據(jù)標識就能快速定位日志來排查問題了,對于有完善日志系統(tǒng)(如 ELK)的項目來說已經(jīng)大大改善了程序員們的生存狀況。

作為用戶,你有沒有這樣的經(jīng)驗:用個軟件,隔三岔五彈個框:系統(tǒng)異常!

作為程序員,你有沒有這樣的經(jīng)驗:

運營同學又屁顛屁顛跑來求助:“用戶不能下單了!”

“報什么錯?”

“系統(tǒng)異常!”

無論作為用戶還是程序員,一見到“系統(tǒng)異常”四個大字,我整個人都不好了。

它除了告訴我系統(tǒng)出問題了,沒有任何有價值的信息。

這往往是程序員一天苦逼生活的開始。

我們獲取不到任何有價值的信息,只能到處抓蝦。

先看看系統(tǒng)負載,嗯,沒問題。

再看看錯誤日志,一大堆日志滾來滾去,也看不出所以然。

于是我們不得不求助運營同學:“去要一下用戶手機號或者賬號,手機型號、版本,最好能錄個頻!”

等了半天,運營妹妹終于搞來了這些信息,于是我們又一頓各種查日志,然后盯著代碼一行一行找,最終發(fā)現(xiàn)了 bug 所在。

為何會有“系統(tǒng)異?!保?/h2>

喜歡將對外錯誤信息一股腦寫成“系統(tǒng)異?!钡模话闾幱谝韵聨追N原因:

  1. 剛?cè)胄械男“?,尚未深入體驗程序員的苦難生活。
  2. “敏感信息”信徒,對他們來說,任何系統(tǒng)錯誤信息都屬于敏感信息,需要“包裝”一下。
  3. 高敏行業(yè),公司強制要求。

我見過一些系統(tǒng)是這樣處理的:

class BaseController {
    errorHandler(err) {
        this.response.sendJSON({code: 500, message: '系統(tǒng)異常'})
    }
}

意思是,該系統(tǒng)的所有 throws 都被轉(zhuǎn)成“系統(tǒng)異?!?!

關(guān)鍵還連個日志都不記錄!

后續(xù)的開發(fā)人員為了方便定位錯誤,便在業(yè)務(wù)層代碼里面各種 log,業(yè)務(wù)代碼慘不忍睹。

“系統(tǒng)異?!眰兊母倪M

上面那種極端的代碼是比較少見的,一般遇到更多的是這樣:

class BaseController {
    errorHandler(err) {
        // 生成異常標識并記錄日志
        let flag = random()
        log(err, flag)
        this.response.sendJSON({"code": 500, "message": `系統(tǒng)異常(${flag})`})
    }
}

給系統(tǒng)異常后面帶了個 flag 標識,當出現(xiàn)問題時,根據(jù)標識就能快速定位日志來排查問題了,對于有完善日志系統(tǒng)(如 ELK)的項目來說已經(jīng)大大改善了程序員們的生存狀況。

但上面的代碼有什么問題呢?

試想某支付邏輯有如下代碼:

if (balance < amount) {
    throw new NotEnoughException('卡余額不足')
}

余額不足,很常見的場景,但用戶看到的是這樣的提示:“系統(tǒng)異常(1877618)”。

此時,我不知道用戶和程序員有沒有崩潰,至少你的老板是崩潰的。

“錯誤碼”們橫空出現(xiàn)

“系統(tǒng)異常”們搞出的事情令人猿共憤,如今這些信徒已經(jīng)不多了,要么迫于壓力改邪歸正了,要么被主管開除殆盡了。

如今,你更可能遇到的是這樣的代碼:

配置文件:

// 全局:定義統(tǒng)一的錯誤碼和錯誤文字
const OK = 200
const SYS_ERR = 500
const NOT_FOUND = 404
const NOT_ENOUGH = 405

const map = {
    200: "OK",
    500: "系統(tǒng)錯誤",
    404: "未找到資源",
    405: "余額不足",
}

// 錯誤碼轉(zhuǎn)文字
function error(code) {
    return map[code]
}

業(yè)務(wù)層代碼:

if (balance < amount) {
    // 該自定義異常類僅允許傳入錯誤碼,內(nèi)部根據(jù) error() 函數(shù)轉(zhuǎn)文字
    throw new MyException(NOT_ENOUGH)
}

控制器:

class BaseController {
    errorHandler(err) {
        log(err)
        this.response.sendJSON({"code": err.code, "message": err.message})
        // 或者:this.response.sendJSON({"code": err.code, "message": error(err.code)})
    }
}

這種錯誤處理原則是通過錯誤碼統(tǒng)一整個項目的 code 和 message,開發(fā)人員不能在程序中自己定義錯誤描述。

我稱這類程序員為”錯誤碼“信徒。

“錯誤碼”們主要的擔心是:如果讓開發(fā)人員自己在代碼里面定義錯誤描述,會導致“哈莫雷特”問題,即每個人的描述可能都不一樣,而且有可能會導致敏感信息泄露。

相對于“系統(tǒng)異?!眰儯板e誤碼”們已經(jīng)有了長足的進步,大家終于知道系統(tǒng)發(fā)生了什么樣的錯誤,老板們也不用擔心因客戶卡余額不足導致的“系統(tǒng)異?!痹伊似放菩蜗罅恕?/p>

從此人猿共歡了!

從此人猿共歡了?

用戶購買 500 元商品時提示“卡余額不足”,但更好的提示應(yīng)該是“卡余額不足,當前可用余額 420.00”。

當根據(jù) userId 查不到用戶信息時,應(yīng)該提示“用戶不存在”,但不能保證開發(fā)人員因不想定義新 code 而直接使用 404(未找到資源)。

錯誤碼機制的問題是其文字提示過于籠統(tǒng),導致在某些錯誤場景下丟失重要價值信息(進而導致問題排查上的困難,問題遲遲得不到解決),另一些場景下則帶來不好的用戶體驗。

對于開發(fā)人員來說,它會帶來兩種效果:一些開發(fā)人員不想新定義一大堆錯誤碼,于是將就著使用現(xiàn)有的錯誤碼,導致錯誤提示不倫不類;另外一些開發(fā)人員則傾向于定義大量的錯誤碼,幾乎每處異常都定義一個新錯誤碼(理由是每處異常文字提示都不一樣),最終導致錯誤碼失控。

“錯誤碼”們的改進

改進其實很簡單,就是允許異常類傳入自定義描述:

// 增加了可選參數(shù) message,允許傳入自定義描述
class MyException(code, message = '') {
    ...
}

期望程序中有如下調(diào)用:

if (balance < amount) {
    throw new MyException(NOT_ENOUGH, '卡余額不足,當前可用余額' + balance)
}

但你會驚奇地發(fā)現(xiàn),大部分地方仍舊是這樣調(diào)的:

if (balance < amount) {
    throw new MyException(NOT_ENOUGH)
}

“錯誤碼”們忽略了很重要的心理學上的問題。

人都是有惰性的,如果你提供了偷懶的途徑,他沒有理由不偷懶。

反“錯誤碼”們:追求自由

和“系統(tǒng)異?!眰円约啊板e誤碼”們力求嚴格限制系統(tǒng)輸出不同,“自由派”追求極致的自由,code 和 message 都不用約束,開發(fā)人員想怎么寫就怎么寫。

所以你可能在多個地方看到“卡余額不足”的錯誤,但每個的錯誤碼都不同(可能是不同的人寫的,也可能是同一個開發(fā)人員在不同時期寫的,甚至是同一個人在同一天寫的,寫的時候完全看心情)。

自由派的做法對于錯誤提示是有好處的,開發(fā)人員可以盡情地定制個性化的提示內(nèi)容,當系統(tǒng)出現(xiàn)異常時能根據(jù)現(xiàn)場提示很快定位錯誤所在。不過由于錯誤碼是隨性寫的,對于依賴錯誤碼的調(diào)用方(系統(tǒng))并不友好。一些系統(tǒng)需要依據(jù) API 返回的錯誤碼做一些特殊邏輯處理,當調(diào)用方認為 405 表示余額不足,然而過幾天又來個 503 的余額不足時,調(diào)用方程序員的內(nèi)心肯定是崩潰的。

中庸之道

本人的異常處理原則是:強制固定 code、自定義 message。

要想設(shè)計出“人猿共歡”的異常處理機制,必須先搞清楚誰需要用到這些信息。

異常信息的第一使用者是人,這里包括使用者(用戶)和異常處理者(運營人員、程序員)。

細分一下,異常又分為業(yè)務(wù)異常和系統(tǒng) bug。

業(yè)務(wù)異常是指業(yè)務(wù)流程中的異常場景,如支付時卡余額不足導致無法支付、用券時發(fā)現(xiàn)券不符合使用條件、用戶執(zhí)行了某個未授權(quán)的操作等。這類異常的觸發(fā)者是用戶自己(而不是系統(tǒng)),信息受眾是用戶。所以業(yè)務(wù)異常的信息提示必須注重用戶體驗,優(yōu)秀的提示文字至少要做到以下幾點:

  1. 尊重用戶,不要讓用戶感覺受到冒犯或戲謔(請慎用自認為很“幽默”的話語);
  2. 清晰,應(yīng)包含觸發(fā)異常的關(guān)鍵信息(如當余額不足時應(yīng)提示當前余額是多少);
  3. 具備指引性,用戶看了之后清楚該怎么做;

第二類異常是系統(tǒng) bug,如接口超時、非預(yù)期參數(shù)導致程序崩潰、代碼邏輯 bug 等。該類異常的觸發(fā)者是系統(tǒng)(或者說開發(fā)系統(tǒng)的程序員),信息受眾是程序員。所以 bug 類型異常的信息提示必須對程序員友好,讓程序員看到錯誤提示后能夠快速定位到問題的原因、代碼所在的位置。

我們說異常,一般就是指 bug 型異常,這類異常占程序員的精力也是最多的,也最值得優(yōu)化處理機制。

bug 型異常具有如下特征:

  1. 不可控性。沒有程序員會主動去寫 bug,但沒有哪個系統(tǒng)完全沒有 bug。我們無法預(yù)知 bug 到底來自哪里、會有什么樣的提示信息;
  2. 定位困難。當系統(tǒng)提示“余額不足”時,我們很快知道是用戶卡沒錢了,但當系統(tǒng)提示“參數(shù)類型錯誤”時,我們往往只能一臉懵逼;
  3. 可能涉及敏感信息。如 SQL 操作錯誤時可能會將整個 SQL 語句暴露給外界;

因而優(yōu)秀的 bug 型異常處理機制應(yīng)做到:

  1. 提示信息對程序員友好;
  2. 記錄函數(shù)調(diào)用棧信息;
  3. 脫敏;

提示信息對程序員友好,可能意味著對用戶并不友好,一些程序員正是據(jù)此以“用戶體驗”之名將 bug 提示信息轉(zhuǎn)換成了“對用戶友好”的提示文案,結(jié)果是所有人看了都云里霧里。

我的觀點是:bug 型異常壓根不用考慮用戶體驗。

為啥?

因為系統(tǒng)出 bug 本身已經(jīng)是非常糟糕的用戶體驗了,用戶不會因諸如“哎呀,系統(tǒng)開小差了”之類的廢話就變得好受些,用戶真正關(guān)心的是盡快能正常下單。

此時的當務(wù)之急是快速修復(fù) bug,所以提示文案的定位功能就非常重要,一段純技術(shù)性的文字,對于用戶來說可能是天書,但對于程序員很實用。

然而,這不意味著給到用戶端的錯誤提示就可以為所欲為。如果我們?yōu)榱朔奖愣ㄎ槐銓⒄麄€程序調(diào)用棧 alert 出來,雖然可能并不會進一步拉低用戶體驗,但至少給人的感覺是不專業(yè),而且過多的信息也意味著很容易暴露敏感信息(如程序路徑、軟件版本、SQL 語句),如果對方是個黑客,你只能自祈多福了。

另外要注重脫敏。大部分框架在數(shù)據(jù)庫操作失敗時,其 message 信息中都會包含諸如 SQL 語句之類的敏感信息,這類信息不可暴露到外面。

綜上,我們可以采取文案+日志的策略,文案中包含關(guān)鍵信息,日志中包含詳細信息(包括調(diào)用棧信息)。

大部分的 DB 庫拋出的異常都有共同基類(如 DBException),我們可以針對這類異常做脫敏處理。

這也告訴我們另一件事:當我們自己開發(fā)公共庫時,最好為該庫定義一個統(tǒng)一基類異常,這樣當使用者想要特殊處理該庫拋出的所有異常時不至于狗咬刺猬無處下牙了。

另外,有些團隊并不想記錄業(yè)務(wù)型異常的調(diào)用棧信息(“卡余額不足”時,調(diào)用棧信息并無多大意義)。我們可以在框架層面定義個業(yè)務(wù)異常基類:BusinessException,異常處理時不記錄該類型的調(diào)用棧信息。

異常信息的另一個使用者是系統(tǒng)。包括其他服務(wù)、前端 js 腳本等。

我見過類似這樣的代碼:

try {
    ...
} catch (e) {
    switch (e.message) {
        case'用戶不存在':
            ...
        case ...
    }
}

如果某個后端程序員哪天心血來潮將“用戶不存在”改成“用戶信息不存在”,系統(tǒng)就崩了。

寫出如此脆弱系統(tǒng)的程序員應(yīng)該被釘?shù)?1024 號恥辱柱上!

不過,在釘釘子之前,我們應(yīng)該傾聽一下他那痛苦的心聲:接口返回的錯誤碼實在是雜亂無章,光“用戶不存在”的錯誤碼就有八個,說不定未來還會增加。為“系統(tǒng)穩(wěn)定性”考慮,最終選擇匹配 message。

好吧,應(yīng)該將后端程序員一起釘上去!

系統(tǒng)只會,也只應(yīng)該關(guān)注錯誤碼。所以和 message 的隨意性不同,code 應(yīng)具備相當?shù)姆€(wěn)定性。

同一個系統(tǒng),如果 406 表示“用戶不存在”,就絕不應(yīng)該再用其他值(如 604)表示相同的含義。

另外,“code 面向系統(tǒng)”這一特點也要求 code 定義的是某一類異常(而不是某一個異常)。例如“訂單創(chuàng)建失敗”是一類異常,在業(yè)務(wù)代碼中針對不同的失敗原因有不同的 message,但其 code 都是一樣的。

然而人類對數(shù)字并不敏感,要不同的程序員都保證寫 throw new Exception('用戶不存在', 406)(而不是寫throw new Exception('用戶不存在', 604))是不可能的。

所以需要將數(shù)字文本化,也就是定義錯誤碼常量:

const USER_NOT_EXISTS = 406

代碼中只能使用錯誤碼常量:

throw new Exception('用戶不存在', USER_NOT_EXISTS)

禁止使用字面量。

不過上面這段 throw 并不理想,首先默認類型 Exception 并不具備業(yè)務(wù)語義,另外開發(fā)人員如果硬是用數(shù)字字面量誰也沒辦法。更可取的方式是針對每種類型異常定義單獨的異常類,該異常類僅允許傳入 message,類內(nèi)部自行綁定 code:

// 用戶不存在
class UserNotExistsException extends Exception { 
    constructor(message) {
        super(message)
        
        this.code = ErrCode.USER_NOT_EXISTS
    }
}

使用:

if (!User.find(uid)) {
    // 此寫法更具表達性,而且開發(fā)人員無需關(guān)注錯誤碼
    throw new UserNotExistsException(`用戶不存在(uid:${uid})`)
}

異常捕獲機制代碼示例

先總結(jié)一下中庸主義的異常捕獲機制特點:

  1. 強制開發(fā)人員自己編寫異常描述文案;
  2. 整個項目強制使用統(tǒng)一的錯誤碼定義;
  3. 為業(yè)務(wù)型異常定義單獨的基類;
  4. 關(guān)鍵信息脫敏處理;

統(tǒng)一錯誤碼定義:

const OK = 200
const SYS_ERR = 500
const NOT_FOUND = 404
const NOT_ENOUGH = 405
const USER_NOT_EXISTS = 406
...

業(yè)務(wù)異?;悾?/p>

class BussinessException extends Exception {
    ...
}

異常類定義:

class UserNotExistsException extends BussinessException {
    constructor(message) {
        super(message)
        
        this.code = ErrCode.USER_NOT_EXISTS
    }
}

...

業(yè)務(wù)層使用:

...
if (!User.find(uid)) {
    throw new UserNotExistsException(`用戶不存在(uid:${uid})`)
}
...

控制器基類捕獲異常

class BaseController {
    ...
    
    errorHandler(err) {
        // 是否業(yè)務(wù)型異常
        const isBussError = err instanceof BussinessException
        // 是否數(shù)據(jù)庫異常
        const isDBError = err instanceof DBException
        // 生成用于跟蹤異常日志的隨機串
        const flag = isBussError ? '' : random()
        
        let message = err.message
        if (isDBError) {
            // 數(shù)據(jù)庫異常,脫敏
            message = `數(shù)據(jù)異常(flag:${flag})`
        } elseif (!isBussError) {
            // 非業(yè)務(wù)型異常記錄 flag 標識
            message += `(flag:${flag})`
        }
        
        // 記錄日志(日志要記錄原始的 message)
        log(err.message, isBussError ? '' : err.stackTrace(), flag)
        
        // 返回給調(diào)用端
        this.response.sendJSON({"code": err.code, "message": message})
    }
    
    function log(message, stackTrace, flag) {
        ...
    }
    ...
}

基于約定的異常處理機制

即便框架層提供了完善的異常處理機制,你還是無法阻止開發(fā)人員寫這樣的代碼:

if (!User.find(uid)) {
    throw new Exception(’系統(tǒng)異?!? 500)
}

一行代碼就給你打回原形!

所以異常處理機制是基于約定的(團隊公約)。

責任編輯:武曉燕 來源: 編碼胡同
相關(guān)推薦

2024-06-11 00:00:01

系統(tǒng)技術(shù)代碼

2025-10-26 14:50:25

AI人工智能ChatGPT

2011-10-25 09:24:08

2023-08-29 06:50:01

Javamaven

2018-02-23 09:55:12

程序員壓迫Python

2025-01-22 07:00:00

C++11構(gòu)造函數(shù)C++

2011-11-08 11:22:35

技術(shù)周刊

2025-10-27 08:46:00

2015-08-10 10:26:08

2020-05-22 15:16:45

遠程工作辦公互聯(lián)網(wǎng)

2013-03-08 09:54:25

2021-07-01 05:17:52

Windows 11操作系統(tǒng)微軟

2014-03-06 09:23:19

Git服務(wù)器Github

2012-11-12 12:03:26

臺式機Mac聯(lián)想

2020-07-06 14:40:28

攜號轉(zhuǎn)網(wǎng)運營商服務(wù)

2021-03-03 14:55:10

開發(fā)MySQL代碼

2021-03-19 08:54:02

芯片Morpheus漏洞

2012-05-04 13:09:46

IBM大型機云計算

2018-03-08 07:03:35

2015-11-17 09:47:32

代碼寫下去
點贊
收藏

51CTO技術(shù)棧公眾號