自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

受夠了“系統(tǒng)異?!保?/h1>

系統(tǒng) 其他OS
技術(shù)負(fù)責(zé)人必須為所有成員提供系統(tǒng)培訓(xùn),并公開建立團(tuán)隊(duì)代碼標(biāo)準(zhǔn)。他們應(yīng)堅(jiān)決拒絕不符合標(biāo)準(zhǔn)的 pull 請(qǐng)求,并與那些屢教不改的人進(jìn)行“黑房對(duì)話”。

作為用戶,你是否有過這樣的經(jīng)歷:使用軟件時(shí)偶爾彈出一個(gè)消息,顯示“系統(tǒng)異常!”?

作為程序員,你是否有過這樣的經(jīng)歷:

運(yùn)維同事跑來求助:“用戶不能下單了!”

“顯示什么錯(cuò)誤?”

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

無論是作為用戶還是程序員,當(dāng)看到這四個(gè)字“系統(tǒng)異?!睍r(shí),我都感到不安。

它只告訴我系統(tǒng)有問題,卻沒有提供任何有價(jià)值的信息。

這通常標(biāo)志著程序員另一個(gè)痛苦日子的開始。

我們無法獲取任何有價(jià)值的信息,只能盲目地到處查找。

首先,我們檢查系統(tǒng)負(fù)載。嗯,沒問題。

然后,我們查看錯(cuò)誤日志。成堆的日志滾動(dòng)不斷,但似乎沒有任何意義。

于是,我們不得不向運(yùn)維同事求助:“能否幫我們獲取用戶的電話號(hào)碼或賬號(hào)信息?另外,他們的手機(jī)型號(hào)和版本也會(huì)有幫助!如果可以,請(qǐng)錄制一個(gè)視頻!”

等待了好像一個(gè)世紀(jì),運(yùn)維同事終于收集到了這些信息。然后我們花費(fèi)數(shù)小時(shí)查看各種日志,仔細(xì)審查每一行代碼,最終找到了錯(cuò)誤所在。

為什么會(huì)有“系統(tǒng)異常”?

那些喜歡將所有外部錯(cuò)誤信息寫成“系統(tǒng)異?!钡娜送ǔS幸韵聨追N原因:

  1. 剛進(jìn)入行業(yè)的新手,還沒有經(jīng)歷過程序員的辛苦。
  2. 相信“敏感信息”,對(duì)他們來說,任何系統(tǒng)錯(cuò)誤信息都是敏感的,必須“包裝”起來。
  3. 公司所在的行業(yè)敏感,強(qiáng)制要求如此處理。

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

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

這意味著這個(gè)系統(tǒng)的所有拋出的錯(cuò)誤都會(huì)轉(zhuǎn)換為“系統(tǒng)異?!?!

而最糟糕的是,甚至沒有記錄任何日志!

為了方便后續(xù)開發(fā)人員定位錯(cuò)誤,各種日志被添加到業(yè)務(wù)層代碼中,使得業(yè)務(wù)代碼不堪重負(fù)。

“系統(tǒng)異?!睈酆谜叩母倪M(jìn)措施

上述極端代碼相對(duì)較少見。通常,我們會(huì)遇到這樣的情況:

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

在系統(tǒng)異常后添加一個(gè)標(biāo)識(shí)符。當(dāng)出現(xiàn)問題時(shí),可以根據(jù)這個(gè)標(biāo)識(shí)符快速定位和排查日志。對(duì)于擁有完善日志系統(tǒng)的項(xiàng)目(如 ELK),大大提高了程序員的生存狀態(tài)。

但是,上述代碼有什么問題?

假設(shè)某個(gè)支付邏輯有如下代碼:

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

余額不足是一個(gè)非常常見的場景,但用戶看到的提示是:“系統(tǒng)異常(1877618)”。

此時(shí),我不知道用戶和程序員是否崩潰了,但至少你的老板是崩潰的。

“系統(tǒng)異?!钡慕K結(jié):錯(cuò)誤代碼的出現(xiàn)

“系統(tǒng)異常”引發(fā)的事情讓人憤怒。如今,已經(jīng)沒有多少信徒了。要么他們?cè)趬毫ο赂淖兞俗龇?,要么已?jīng)被主管完全開除了。

現(xiàn)在,你更有可能遇到這樣的代碼:

配置文件:

// 全局:定義統(tǒng)一的錯(cuò)誤代碼和錯(cuò)誤消息。
const OK = 200
const SYS_ERR = 500
const NOT_FOUND = 404
const NOT_ENOUGH = 405

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

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

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

if (balance < amount) {
    // 此自定義異常類只允許傳遞錯(cuò)誤代碼,并在內(nèi)部使用 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)})
    }
}

這種錯(cuò)誤處理原則是通過錯(cuò)誤代碼統(tǒng)一項(xiàng)目的代碼和消息。開發(fā)人員不能在程序中定義自己的錯(cuò)誤描述。

我稱這些程序員為“錯(cuò)誤代碼”信徒。

“錯(cuò)誤代碼”組的主要擔(dān)憂是,如果允許開發(fā)人員在代碼中定義錯(cuò)誤描述,可能會(huì)導(dǎo)致“哈姆雷特”問題,即每個(gè)人的描述可能不同,還可能導(dǎo)致敏感信息泄漏。

相比“系統(tǒng)異?!?,“錯(cuò)誤代碼”組取得了顯著進(jìn)步。大家終于知道系統(tǒng)中發(fā)生了什么錯(cuò)誤,老板們也不再擔(dān)心因?yàn)榭蛻艨ㄓ囝~不足導(dǎo)致的“系統(tǒng)異常”損害品牌形象。

當(dāng)用戶購買500元商品時(shí),收到的提示是“余額不足”,而更好的提示應(yīng)該是“余額不足,目前可用余額:420.00”。

當(dāng)根據(jù) userId 找不到用戶信息時(shí),應(yīng)該顯示“用戶不存在”的提示,但開發(fā)人員不應(yīng)僅因?yàn)椴幌攵x新代碼而直接使用 404(資源未找到)。

錯(cuò)誤代碼的問題

錯(cuò)誤代碼的問題在于它們的文本提示過于模糊,導(dǎo)致某些錯(cuò)誤場景中丟失了重要的有價(jià)值信息(這導(dǎo)致未解決問題的排查困難),同時(shí)在其他場景中導(dǎo)致用戶體驗(yàn)不佳。

對(duì)于開發(fā)人員來說,它帶來了兩種影響:一些開發(fā)人員不愿定義大量新錯(cuò)誤代碼,所以他們湊合使用現(xiàn)有代碼,導(dǎo)致錯(cuò)誤提示不一致;其他開發(fā)人員傾向于為幾乎每個(gè)異常定義大量錯(cuò)誤代碼(認(rèn)為每個(gè)異常的文本提示不同),最終導(dǎo)致錯(cuò)誤代碼的失控增長。

改進(jìn)錯(cuò)誤代碼

改進(jìn)非常簡單,只需允許異常類傳入自定義描述。

// 添加可選參數(shù) "message",以允許自定義描述輸入。
class MyException {
    constructor(code, message = '') {
        this.code = code
        this.message = message
    }
}

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

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

追求自由:反“錯(cuò)誤代碼”

與“系統(tǒng)異常”和“錯(cuò)誤代碼”努力嚴(yán)格限制系統(tǒng)輸出不同,自由派追求終極自由,對(duì)代碼和消息沒有任何限制。開發(fā)人員可以隨意編寫它們。

你可能會(huì)在多個(gè)地方看到“余額不足”的錯(cuò)誤,但每個(gè)錯(cuò)誤代碼都不同(可能由不同的人編寫,甚至是同一個(gè)開發(fā)人員在不同時(shí)間或同一天心情不同)。

自由派的方法對(duì)于錯(cuò)誤提示有其好處。開發(fā)人員可以自由定制個(gè)性化的提示內(nèi)容,當(dāng)系統(tǒng)遇到異常時(shí)可以快速定位錯(cuò)誤。然而,由于錯(cuò)誤代碼隨意編寫,對(duì)于依賴這些錯(cuò)誤代碼的調(diào)用方(系統(tǒng))不友好。一些系統(tǒng)需要根據(jù) API 返回的錯(cuò)誤代碼執(zhí)行特殊邏輯。當(dāng)調(diào)用方認(rèn)為 405 代表余額不足,但幾天后遇到 503 也表示余額不足時(shí),程序員的心肯定會(huì)崩潰。

中庸之道

我對(duì)異常處理的原則是:強(qiáng)制使用固定代碼和自定義消息。

要設(shè)計(jì)一個(gè)讓用戶和程序員都開心的異常處理機(jī)制,首先要了解誰需要使用這些信息。

異常信息的首要用戶是人,包括用戶(客戶)和異常處理者(運(yùn)維人員、程序員)。

進(jìn)一步細(xì)分,異??梢苑譃闃I(yè)務(wù)異常和系統(tǒng)錯(cuò)誤。

業(yè)務(wù)異常是指業(yè)務(wù)流程中的異常場景,例如支付時(shí)卡余額不足導(dǎo)致支付失敗,使用優(yōu)惠券時(shí)發(fā)現(xiàn)不符合使用條件,以及用戶進(jìn)行未授權(quán)操作。這類異常的觸發(fā)是用戶自己(而非系統(tǒng)),信息的受眾是用戶。因此,業(yè)務(wù)異常的信息提示必須關(guān)注用戶體驗(yàn)。優(yōu)秀的提示文本至少應(yīng)達(dá)到以下幾點(diǎn):

  1. 尊重用戶,避免讓用戶感到被冒犯或嘲笑(請(qǐng)謹(jǐn)慎使用你認(rèn)為“幽默”的詞語);
  2. 清晰,并包含觸發(fā)異常的關(guān)鍵信息(如余額不足時(shí)提示當(dāng)前余額);
  3. 引導(dǎo)用戶,讓他們知道看完提示后該做什么;

第二類異常是系統(tǒng)錯(cuò)誤,例如接口超時(shí),意外參數(shù)導(dǎo)致的程序崩潰,代碼邏輯錯(cuò)誤等。這類異常的觸發(fā)是系統(tǒng)(或開發(fā)系統(tǒng)的程序員),信息的受眾是程序員。因此,錯(cuò)誤消息對(duì)錯(cuò)誤類型異常必須對(duì)程序員友好,允許他們快速識(shí)別問題的原因并定位代碼中的位置。

我們通常談?wù)摰漠惓J侵稿e(cuò)誤類型異常。這類異常消耗了程序員的大部分精力,也值得優(yōu)化處理機(jī)制。

錯(cuò)誤類型異常具有以下特點(diǎn):

  • 不可預(yù)測(cè)性:沒有程序員會(huì)主動(dòng)寫錯(cuò)誤,但沒有系統(tǒng)是完全沒有錯(cuò)誤的。我們無法預(yù)測(cè)錯(cuò)誤來自哪里或會(huì)產(chǎn)生什么樣的錯(cuò)誤信息。
  • 難以定位:當(dāng)系統(tǒng)提示“余額不足”時(shí),我們很快知道這是用戶的卡沒有錢。然而,當(dāng)系統(tǒng)提示“參數(shù)類型錯(cuò)誤”時(shí),我們通常感到困惑。
  • 可能涉及敏感信息:例如,當(dāng)出現(xiàn) SQL 操作錯(cuò)誤時(shí),可能會(huì)將整個(gè) SQL 語句暴露給外部。

綜合考慮

錯(cuò)誤提示對(duì)程序員友好,這可能意味著對(duì)用戶不友好。一些程序員利用這一點(diǎn),以“用戶體驗(yàn)”的名義將錯(cuò)誤提示信息轉(zhuǎn)換為“用戶友好”的提示。結(jié)果是每個(gè)人都感到困惑。

我的觀點(diǎn)是,錯(cuò)誤類型異常根本不需要考慮用戶體驗(yàn)。

為什么?

因?yàn)橄到y(tǒng)出現(xiàn)錯(cuò)誤本身已經(jīng)是一個(gè)糟糕的用戶體驗(yàn)。用戶不會(huì)因?yàn)橄瘛癘ops,系統(tǒng)出錯(cuò)了”這樣的詞句而感覺好一點(diǎn)。用戶真正關(guān)心的是能盡快正常下單。

此時(shí)的當(dāng)務(wù)之急是快速修復(fù)錯(cuò)誤,因此提示文本的定位功能變得非常重要。純技術(shù)性的文本對(duì)用戶可能是胡言亂語,但對(duì)程序員來說非常有用。

然而,這并不意味著可以隨意給用戶錯(cuò)誤提示。如果為了便于定位而提示整個(gè)程序調(diào)用棧,雖然這可能不會(huì)進(jìn)一步降低用戶體驗(yàn),但會(huì)給人一種不專業(yè)的印象,過多的信息也意味著容易暴露敏感信息(如程序路徑、軟件版本、SQL 語句)。如果對(duì)方是黑客,你只能祈禱好運(yùn)。

此外,應(yīng)注意脫敏。在大多數(shù)框架中,當(dāng)數(shù)據(jù)庫操作失敗時(shí),它們的消息信息通常包含敏感信息,如 SQL 語句。這種信息不應(yīng)暴露在外。

因此,我們可以采用文本 + 日志結(jié)合的策略,在文本中包含關(guān)鍵信息,在日志中記錄詳細(xì)信息(包括調(diào)用棧)。

這也告訴我們另一件事:當(dāng)我們自己開發(fā)公共庫時(shí),最好為該庫定義統(tǒng)一的基類異常。這樣,想要以特殊方式處理該庫拋出的所有異常的用戶就不會(huì)手足無措。

此外,有些團(tuán)隊(duì)不想記錄業(yè)務(wù)異常的調(diào)用棧信息(“余額不足”的調(diào)用棧信息沒有太大意義)。我們可以在框架級(jí)別定義業(yè)務(wù)異常的基類:BusinessException,并在處理異常時(shí)不記錄此類異常的調(diào)用棧信息。

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

我見過類似的代碼:

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

如果有一天后端程序員心血來潮,將“用戶不存在”改為“用戶信息不存在”,系統(tǒng)就會(huì)崩潰。

創(chuàng)建這樣脆弱系統(tǒng)的程序員應(yīng)被釘在第1024柱羞恥柱上!

然而,在釘他們之前,我們應(yīng)該聽聽他們痛苦的呼聲:接口返回的錯(cuò)誤代碼混亂,已經(jīng)有八個(gè)不同的錯(cuò)誤代碼表示“用戶不存在”,將來也可能會(huì)更多。為了“系統(tǒng)穩(wěn)定”,最終決定基于消息進(jìn)行匹配。

好吧,那讓我們一起釘所有后端程序員!

系統(tǒng)只應(yīng)關(guān)注錯(cuò)誤代碼,而不是其他內(nèi)容。與消息可以自由變化不同,錯(cuò)誤代碼應(yīng)具有相當(dāng)?shù)姆€(wěn)定性。

在同一系統(tǒng)中,如果 406 表示“用戶不存在”,則不應(yīng)使用其他值(如 604)表示相同含義。

此外,代碼面向系統(tǒng)的特性還要求代碼定義一類異常(而不僅僅是一種異常)。例如,“訂單創(chuàng)建失敗”是一類異常,不同的失敗原因在業(yè)務(wù)代碼中有不同的消息,但共享相同的代碼。

然而,人類對(duì)數(shù)字不敏感。每個(gè)程序員都不能確保寫 throw new Exception('用戶不存在', 406) 而不是 throw new Exception('用戶不存在', 604)。

因此,有必要通過定義常量錯(cuò)誤代碼將數(shù)值轉(zhuǎn)換為文本:

const USER_NOT_EXISTS = 406

代碼中只能使用錯(cuò)誤代碼常量。

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

禁止使用文字常量。

然而,上述 throw 語句并不理想。首先,默認(rèn)的 Exception 類型沒有業(yè)務(wù)語義。其次,如果開發(fā)人員堅(jiān)持使用數(shù)字常量,誰也無法阻止。更好的方法是為每種異常類型定義單獨(dú)的異常類,只允許傳遞消息,并在內(nèi)部綁定代碼。

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

使用:

if (!User.find(uid)) {
    // 這種寫法更有表現(xiàn)力,開發(fā)人員不需要關(guān)注錯(cuò)誤代碼
    throw new UserNotExistsException(`用戶不存在(uid:${uid})`)
}

異常處理機(jī)制的示例

首先,總結(jié)中庸之道的異常處理機(jī)制的特點(diǎn):

  • 強(qiáng)制開發(fā)人員編寫異常描述文本;
  • 整個(gè)項(xiàng)目要求使用統(tǒng)一的錯(cuò)誤代碼定義;
  • 為業(yè)務(wù)異常定義單獨(dú)的基類;
  • 對(duì)敏感信息進(jìn)行脫敏處理;

錯(cuò)誤代碼的統(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})`)
}

基礎(chǔ)控制器捕獲異常:

class BaseController {
    ...
    
    errorHandler(err) {
        // 是否是業(yè)務(wù)異常
        const isBussError = err instanceof BussinessException
        // 是否是數(shù)據(jù)庫異常
        const isDBError = err instanceof DBException
        // 生成用于跟蹤異常日志的隨機(jī)字符串
        const flag = isBussError ? '' : random()
        
        let message = err.message
        if (isDBError) {
            // 數(shù)據(jù)庫異常,脫敏處理
            message = `數(shù)據(jù)異常(flag:${flag})`
        } else if (!isBussError) {
            // 非業(yè)務(wù)異常記錄標(biāo)識(shí)符
            message += `(flag:${flag})`
        }
        
         // 記錄錯(cuò)誤(日志應(yīng)記錄原始消息)
         log(err.message, isBussError ? '' : err.stackTrace(), flag)
         
         // 返回給調(diào)用方
        this.response.sendJSON({"code": err.code, "message": message})
    }
    
    function log(message, stackTrace, flag) {
        ...
    }
    ...
}

約定機(jī)制

即使框架提供了全面的異常處理機(jī)制,你仍然無法阻止開發(fā)人員編寫這樣的代碼:

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

一行代碼將使你回到原點(diǎn)!

因此,異常處理機(jī)制是基于約定的(團(tuán)隊(duì)約定)。

技術(shù)負(fù)責(zé)人必須為所有成員提供系統(tǒng)培訓(xùn),并公開建立團(tuán)隊(duì)代碼標(biāo)準(zhǔn)。他們應(yīng)堅(jiān)決拒絕不符合標(biāo)準(zhǔn)的 pull 請(qǐng)求,并與那些屢教不改的人進(jìn)行“黑房對(duì)話”。

責(zé)任編輯:武曉燕 來源: 大遷世界
相關(guān)推薦

2023-04-26 08:55:30

2018-02-23 09:55:12

程序員壓迫Python

2025-01-22 07:00:00

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

2015-08-10 10:26:08

2011-10-25 09:24:08

2020-05-22 15:16:45

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

2011-11-08 11:22:35

技術(shù)周刊

2021-07-01 05:17:52

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

2023-08-29 06:50:01

Javamaven

2014-03-06 09:23:19

Git服務(wù)器Github

2012-11-12 12:03:26

臺(tái)式機(jī)Mac聯(lián)想

2013-03-08 09:54:25

2020-07-06 14:40:28

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

2021-03-03 14:55:10

開發(fā)MySQL代碼

2017-05-08 11:01:54

交通馬斯克地下隧道

2019-10-09 10:06:48

容器監(jiān)控軟件

2020-07-06 08:06:00

Java模塊系統(tǒng)

2012-11-05 13:59:12

WebFdSafeJS

2013-05-06 17:08:00

Linux操作系統(tǒng)異常處理

2020-04-10 12:38:01

Chrome瀏覽器谷歌
點(diǎn)贊
收藏

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