A寫了一個服務,給B調用java
A的這個服務針對一個key,在一次調用完成以前不能調用第二次,若是連續調用,第二次不會執行業務,而是直接返回系統繁忙,使用ConcurrentHashMap + Atomic的cas
控制。(不要吐槽咱們小公司小用戶量暫時沒有使用水平擴展的分佈式服務哦,這裏就考慮了單機線程安全)mysql
// {key -> 是否正在運算}
Map<Long, AtomicBoolean> map = new ConcurrentHashMap<>();
public * *(@RequestBody ..) {
//..省略校驗和獲取主鍵
// 可以保證單機線程安全
AtomicBoolean atomicBoolean = map.computeIfAbsent(key, (key)->new AtomicBoolean(false));
// 若是正在執行,直接返回一下子再來
if(!atomicBoolean.compareAndSet(false, true)){
return "系統繁忙";
}
try {
//..業務操做並返回
}finally {
atomicBoolean.set(false);
}
}
複製代碼
某日,調用方B看了日誌,有一個key調用這個服務後一直返回系統繁忙
,且沒再成功過,因而,開始了排查過程。react
首先,先看調用方B的日誌,找到了使用這個key第一次失敗調用的位置:sql
20-06-06 12:09:12 INFO ** - 開始調用某key
20-06-06 12:09:22 ERROR ** - 調用 fail :
java.net.http.HttpTimeoutException: request timed out
複製代碼
能夠看出使用了Http
調用,從開始調用到失敗日誌,有10s
時間,正是請求超時時間。shell
請求沒返回,以後調用過程一直返回系統繁忙,說明一直沒有執行到上面的釋放cas鎖
的塊:數據庫
try {
//..業務操做並返回
}finally {
// 沒有執行到
atomicBoolean.set(false);
}
複製代碼
基本猜到了問題,業務操做代碼中一直沒有執行完畢;安全
再去看了服務方A的日誌:bash
20-06-06 12:09:13.476 INFO ** - 結束
// 關鍵,有一個只有開始沒有結束
20-06-06 12:09:13.476 INFO ** - 開始
// 這個開始是其它key的
20-06-06 12:09:23.267 INFO ** - 開始
複製代碼
果真,在12:09:13
的一次調用只有開始,沒有結束。服務器
再去看了服務A的服務器進程線程運行情況:分佈式
top
top -Hp <pid>
複製代碼
發現有一個線程已經佔用了將近43分鐘
的CPU時間,而距離最後一次調用大約過去了一個小時時間,可見極可能是A的業務代碼裏面有死循環致使這個線程一直處於運行狀態。
由於這個線上只是用了jre
,沒有jdk
,沒有jstack
命令能夠查看詳細的線程信息,至於爲何沒有,得問A。
// 假想一下咱們執行了jstack 線程id查看到了確實這個線程一直處於運行狀態
jstack <pid>
複製代碼
看了看業務代碼,定位到了下面一個循環,A爲了分批保存大量數據,使用了一個while
循環,線下測試時沒有達到須要分批保存的閾值,而最後到了線上,須要分批保存,flg
沒有像預想的那樣退出循環,致使一直插入數據。
while (flg){
flg = false;
if(..){
testService.save(..);
if(..)
flg = true;
}
}
複製代碼
看到這裏,我猜測既然while循環中插入數據,那麼數據庫中這個表估計這一個小時已經被插入了許多重複數據了,可是取數據庫裏一刷新,居然沒有明顯新增:
猜測了一下,是由於事務一直在這裏循環,未提交,因此我去其它mysql
鏈接會話中固然不可見未提交事務中的記錄,可是爲了更加確認,決定去查如下數據庫表文件,發現這個只有八百多條記錄的表居然已經以下佔用了700M
左右的數據,因此,雖然事務未提交,可是它的表文件是在不停增長的。
並且,刷新以後發現,這個表文件的更改時間一直變化,至此證實了死循環問題代碼的存在性。
其實A的分批插入想法能夠比較可靠的使用reactor
中流的一個buffer
方法實現:
Flux.fromIterable(list)
.buffer(savePerNum)
.subscribe(testService::save);
複製代碼
避免了使用死循環,且可讀性更加的高。
發佈以後,問題解決。
ConcurrentHashMap + Atomic~ + cas
控制java
進程和線程信息證實猜測reactor
的buffer優雅完成小夥伴們有什麼建議和想法,歡迎討論啊~