記錄一次生產發布事件——(簡單的非空驗證也能引發大問題)


文章導航-readme

歡樂小插曲

​ 時間定格在周四——常規發布日下午三點,研發任務早已完成、測試也無大礙。這時,辦公室外走廊里傳來了一陣急促的報警聲——“此大樓發生緊急情況,請各單位抓緊撤離”。像往常一樣刷刷博客園,坐等發布生產的我聞聽此聲,虎軀一震正准備要跑路,轉頭看向周圍的同事。大都很淡定的坐在工位上各自擼碼。於是收回我那一只已經邁向過道的腿,佯裝淡定的坐下,悄悄的問了問隔壁的老大哥:老哥,怎么沒人跑路?“兄弟,習慣就好,八成又是大樓搞消防演習”老哥扶了扶眼鏡,繼續擼碼。果不其然,半個小時后收到了公司的馬后炮郵件——“近期大樓消防演習,請大家聽到警報聲后無需驚訝,各自工作即可......”

事件經過

​ 下午四點,發布生產g環境(生產環境m為正式環境,g為內測環境)。這時測試有人提出“服務器忙”。聽到這里我趕緊翻了翻內測日志,發現了最熟悉的老朋友——未將對象引用設置到對象的實例。問題出現在如下代碼上。(我下面附上了偽代碼,大家可以看看下面的代碼有沒有問題,當時我看了很久才發現問題所在)

//獲取賬戶信息
var accountInfo=GetAccountInfo();
if(accountInfo==null){
    return "xxx";
}
//判斷賬戶信息中是否包含xx——問題點就出現在下面這行代碼(未將對象引用設置到對象的實例)
//其中Test字段為此次發布新增字段——線上版本的AccountInfo中並不存在此字段
bool isTest=accountInfo.Test.Contains("A");
//獲取賬戶信息方法
public AccountInfo GetAccountInfo(){
    //先讀取Cache
    AccountInfo info=cache.GetCache();
    if(info!=null)
    {
        return info;             
    }   
    info=SOAService.GetInfo();
    if(info!=null){
        cache.SetCache(info);
        return info;
    }
    return null;
}
//賬戶信息類
public class SOAService()
{
    //從遠程服務獲取賬戶信息
    public static AccountInfo GetInfo()
    {  
       //獲取一個服務Client
       SOAClient client=SOAClient.GetClient();
       //獲取賬戶信息
       Account account= client.GetAccountInfo();     
       if(account!=null){
           AccountInfo info=new AccountInfo();
           info.xx=account.xx;
           //Test為null處理
           info.Test=account.Test??string.empty;
           return info;
       }
       return null;
    }
}

//賬戶信息實體類
public class AccountInfo{
    
    private string name;   
    //...此處省略n個字段
    
    //此處新增字段test
    private string test;
    
    //屬性Name
    public string Name{
        get { return this.name; }
        set { this.name=value; }
    }
     //...此處省略n個屬性
    //屬性Test
    public string Test{
        get { return this.test; }
        set { this.test=value; }
    }
}

​ 在代碼中觀察許久仍沒有發現問題。這時測試一句話提醒了我,“我看m環境沒有問題”。靈光一閃,原來測試先從m環境登錄,瀏覽了一圈頁面后,已經緩存了AccountInfo,但是m環境此時是沒有新增字段Test的,此時切換到g環境(我們的m環境,g環境對應緩存數據都是一樣的,區別僅僅是應用服務器不同),獲取賬戶信息時會直接從Cache中讀出來,然后accountInfo.Test在用之前並沒有判空,所以...未將對象引用設置到對象的實例。於是乎得意的跟測試說,你登錄后別再m環境操作,直接切到g環境,就可以了,等發m不會有問題的。果不其然,測試按我說的做了不再報錯。

​ 如果你以為事情就這么結束,那就錯了。請原諒我那豬油蒙了心的傻叉操作。不久,g環境驗證無誤,開始往m環境發布。起初未見異常,當發了集群大概三分之一節點的時候,大量異常突然襲來,瞬間監控開始報警。一看日志滿屏的老朋友。緊急關頭得虧腦子反應快,緊急回滾代碼。靜下心來腦子一想,生產用戶本身處於登錄狀態,有使用緩存。剛剛出現問題沒去處理,真是悔之晚矣!於是緊急修復。增加使用前判空,問題終於解決。

事件教訓

  • 對於程序中大量使用緩存的系統,開發時一定要考慮好緩存。(這個系統一些不合理的緩存設計坑的我苦不堪言)
  • 對於測試中的每一個問題都要認真對待
  • 紙上得來終覺淺——背的滾瓜爛熟的,緩存穿透、雪崩,緩存更新、程序非空驗證。實戰起來還是不夠用
  • 敬畏每一次生產環境的發布


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM