時間定格在週四——常規發佈日下午三點,研發任務早已完成、測試也無大礙。這時,辦公室外走廊裏傳來了一陣急促的報警聲——「此大樓發生緊急狀況,請各單位抓緊撤離」。像往常同樣刷刷博客園,坐等發佈生產的我聞聽此聲,虎軀一震正準備要跑路,轉頭看向周圍的同事。大都很淡定的坐在工位上各自擼碼。因而收回我那一隻已經邁向過道的腿,佯裝淡定的坐下,悄悄的問了問隔壁的老大哥:老哥,怎麼沒人跑路?「兄弟,習慣就好,八成又是大樓搞消防演習」老哥扶了扶眼鏡,繼續擼碼。果不其然,半個小時後收到了公司的馬後炮郵件——「近期大樓消防演習,請你們聽到警報聲後無需驚訝,各自工做便可......」html
下午四點,發佈生產g環境(生產環境m爲正式環境,g爲內測環境)。這時測試有人提出「服務器忙」。聽到這裏我趕忙翻了翻內測日誌,發現了最熟悉的老朋友——未將對象引用設置到對象的實例。問題出如今以下代碼上。(我下面附上了僞代碼,你們能夠看看下面的代碼有沒有問題,當時我看了好久才發現問題所在)c#
//獲取帳戶信息 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環境發佈。起初未見異常,當發了集羣大概三分之一節點的時候,大量異常忽然襲來,瞬間監控開始報警。一看日誌滿屏的老朋友。緊急關頭得虧腦子反應快,緊急回滾代碼。靜下心來腦子一想,生產用戶自己處於登陸狀態,有使用緩存。剛剛出現問題沒去處理,真是悔之晚矣!因而緊急修復。增長使用前判空,問題終於解決。服務器