事件經過
下午四點,釋出生產g環境(生產環境m為正式環境,g為內測環境)。這時測試有人提出“伺服器忙”。聽到這裡我趕緊翻了翻內測日誌,發現了最熟悉的老朋友——未將物件取用設定到物件的實體。問題出現在如下程式碼上。(我下麵附上了偽程式碼,大家可以看看下麵的程式碼有沒有問題,當時我看了很久才發現問題所在)
var accountInfo=GetAccountInfo();
if(accountInfo==null){
return "xxx";
}
bool isTest=accountInfo.Test.Contains("A");
public AccountInfo GetAccountInfo(){
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()
{
SOAClient client=SOAClient.GetClient();
Account account= client.GetAccountInfo();
if(account!=null){
AccountInfo info=new AccountInfo();
info.xx=account.xx;
info.Test=account.Test??string.empty;
return info;
}
return null;
}
}
public class AccountInfo{
private string name;
private string test;
public string Name{
get { return this.name; }
set { this.name=value; }
}
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環境釋出。起初未見異常,當發了叢集大概三分之一節點的時候,大量異常突然襲來,瞬間監控開始報警。一看日誌滿屏的老朋友。緊急關頭得虧腦子反應快,緊急回滾程式碼。靜下心來腦子一想,生產使用者本身處於登入狀態,有使用快取。剛剛出現問題沒去處理,真是悔之晚矣!於是緊急修複。增加使用前判空,問題終於解決。
事件教訓
- 對於程式中大量使用快取的系統,開發時一定要考慮好快取。(這個系統一些不合理的快取設計坑的我苦不堪言)
- 對於測試中的每一個問題都要認真對待
- 紙上得來終覺淺——背的滾瓜爛熟的,快取穿透、雪崩,快取更新、程式非空驗證。實戰起來還是不夠用
- 敬畏每一次生產環境的釋出
朋友會在“發現-看一看”看到你“在看”的內容