背景
最近接到平臺研發(fā)同事反饋說inpass的/user/varify接口慢,經(jīng)過溝通后開始排查了,對方給了cat監(jiān)控的歷史,每天大概有個100~200個樣子,說是驗證超過1s,我這邊監(jiān)控平臺除了這個接口其他接口都監(jiān)控到了,并無異常,因為沒有接調(diào)用鏈,架構(gòu)部也無法給出準(zhǔn)確答復(fù),由于歷史原因接入調(diào)用鏈的時機(jī)并不成熟所以也不好說什么,自己通過access日志排查,發(fā)現(xiàn)也沒有多慢,因為這個接口是高頻接口,本能上告訴對方不會有多慢,但是對方說人家倆接口也是高頻接口,,所以我這邊也只能說繼續(xù)跟進(jìn)了。
說明
1.inpass:內(nèi)部passport的簡單實現(xiàn),登錄一個系統(tǒng)后,登錄其他系統(tǒng)不需要再次登錄;
2./user/varify:該接口的作用是驗證cookie和ticket的有效性,也就是說相當(dāng)于一個攔截器,在業(yè)務(wù)系統(tǒng)的每個http請求之后,后端的攔截器會調(diào)用這個接口進(jìn)行登錄狀態(tài)的驗證。所以調(diào)用量會非常多。
正好,最近有個inpass單設(shè)備登錄的需求,因此進(jìn)行了排查
1.首先在接口開始的地方獲取當(dāng)前時間戳,然后在返回的地方獲取當(dāng)前時間戳,然后取時間差,就是毫秒數(shù)了。有4個地方進(jìn)行了返回,因此在4個地方都打印了執(zhí)行的時間差。
2.經(jīng)過測試環(huán)境的日志打印發(fā)現(xiàn),確實有些有問題,時不時的有100 ~ 600ms之間的調(diào)用量,但是90%的情況下是10 ~ 30ms之內(nèi)返回驗證成功。
查代碼

上圖代碼中的邏輯是登錄成功之后,調(diào)用服務(wù)填充信息,并返回,然后打印執(zhí)行時間。
重點就在畫紅框的地方。
下面跟蹤分析一下代碼邏輯

再進(jìn)上圖中的RPC方法里(在另外一個工程里),由于邏輯有點多,不貼代碼了,下面使用偽代碼說明一下調(diào)用情況
User user = userDB.getByid(id);
if(user != null){
userB = user.copy();//屬性復(fù)制
staffRedis = staffservice.getredis();
if(staffRedis == null){
Staff staff = staffDB.getById(id)
if(staff != null){
staffB = staff.copy();
if(staffB.deptid !=null){
Depart depart = departservice.getredis(staffB.deptid);
if(depart == null){
depart = departDB.getById(id)
while(depart.superid != 0){
depart = departDB.getById(depart.superid)
}
departB = depart.copy();
departB.putredis();
}
}
User userC = userDB.getByStaff(id);
staffB.setuser(userC.copy());
staffB.putredis();
}
}else{
Depart depart = departservice.getredis(staffB.deptid);
if(depart == null){
depart = departDB.getById(id)
while(depart.superid != 0){
depart = departDB.getById(depart.superid)
}
departB = depart.copy();
departB.putredis();
}
}
}
當(dāng)然,通過上面代碼我們可以知道,為啥很少的情況下會出現(xiàn)100~600之間的調(diào)用返回了,同時,由于有多次屬性copy,多次redis調(diào)用,多次db調(diào)用從而導(dǎo)致接口抖動,后面再看,登錄成功或者驗證登錄狀態(tài)成功之后我們需要返回哪些信息
userId,userName,staffId,nickName,handphone,jobNumber,email,departmentId,departmentName
那么分析到這里該怎么優(yōu)化呢
- 重寫后端getById邏輯
- 提供新接口獲取以上字段數(shù)據(jù)
- 提供新接口獲取以上字段數(shù)據(jù)并實時緩存,保證緩存永久有效
三種思路
- 接口可能被別的地方調(diào)用,重寫影響穩(wěn)定性
- 按字段邏輯需要取三張表的數(shù)據(jù)(user,staff,depart),由于是高頻訪問接口(DB+Redis)的普通訪問邏輯同樣存在多次調(diào)用的情況,連表查+Redis的方案也不行,高頻訪問的話,總會有redis key失效的時候,由于底層DB被多個上層應(yīng)用共享,導(dǎo)致Redis key失效的情況很多,不可能讓其他服務(wù)專門清這些redis,所以也不完美
- 提供新接口的時候做如下操作
- 調(diào)用RCP服務(wù)直接根據(jù)userID取Redis
- 使用定時任務(wù)線程從User表中獲取所有在職的員工的賬號,然后取所有關(guān)聯(lián)表的staff,department表中的相關(guān)字段的數(shù)據(jù)
- 然后根據(jù)userId遍歷Redis中的相關(guān)key,進(jìn)行更新
- 最后可以設(shè)置userID_redis的失效時間是2小時,定時任務(wù)的執(zhí)行頻率是30分鐘一次,即可滿足Redis數(shù)據(jù)永不失效,同時提供近實時的數(shù)據(jù)
看數(shù)據(jù)量:user:33370,staff:26437,department:2456,雖然不是很多,由于是高頻訪問(qps200左右),且其他有很多應(yīng)用系統(tǒng)也是直連這個庫,所以有必要采用第三種方案優(yōu)化
總結(jié)
由于很長時間沒有反饋說這個系統(tǒng)有問題了,畢竟這個系統(tǒng)是個只負(fù)責(zé)登錄的系統(tǒng),同時肩負(fù)著全公司近200多個業(yè)務(wù)系統(tǒng)的使用和驗證,因此非常有必要進(jìn)行排查以及代碼優(yōu)化。幸好有同事反饋,不然還不知道背后的調(diào)用過程如此繁瑣,影響接入方。