前言
作為一個開發(fā)唱较,肯定會遇到線上問題扎唾,遇到線上故障快速的定位解決,是開發(fā)者一項必備的能力南缓。工作中可能會遇到各種故障胸遇,這邊主要記錄下之前遇到的一個線上問題
問題描述
早上到辦公室打開釘釘,發(fā)現(xiàn)線上應用的一個實例重啟了…..
臥槽感覺情況不妙!!!
迅速打開sls看下線上日志情況汉形,一看果然有問題
oom了….
自己登錄系統(tǒng)大致瀏覽了下纸镊,系統(tǒng)比較正常,應該是重啟之后概疆,使用過程中沒發(fā)生異常情況逗威,感覺應該是偶發(fā)情況,準備看下代碼慢慢定位下問題岔冀,再解決凯旭。
就在這時,系統(tǒng)的用戶找過來了
于此同時發(fā)現(xiàn),公司內(nèi)部運維系統(tǒng)消息推送群罐呼,出現(xiàn)一大波線上實例重啟的日志
感覺這個問題很嚴重呀…...
問題分析
再次到sls看下鞠柄,竟然沒有發(fā)現(xiàn)異常日志,這是怎么回事呢弄贿?
找運維問下吧
運維給出的回復是OOM了春锋,但是我這邊最近幾次oom的日志,我這邊不確定和上面的是不是同一個問題(上一次有明確的日志差凹,這幾次實例重啟沒有日志呀)
隨后運維給出了線上服務的cpu期奔、內(nèi)存使用情況
OOMKilled 是因為實例內(nèi)存超過了K8S給它分配的最高允許內(nèi)存,所以K8S就把它oomkill掉
但是我還是懷疑和上面是同一個問題危尿,我結合上面日志描述的異衬琶龋看了下代碼,然后我找到上面報錯日志指向的接口谊娇,一看真的是要罵娘(當然自己代碼寫的也很爛)肺孤,這代碼寫的呀…...
這種代碼頻繁訪問接口,線程創(chuàng)建的多了济欢,肯定會有很大的內(nèi)存占用赠堵,K8s檢測到了那就把實例殺掉了呀
用過CompletableFuture的同學就知道,默認情況下 CompletableFuture 會使用公共的 ForkJoinPool 線程池法褥,這個線程池默認創(chuàng)建的線程數(shù)是 CPU 的核數(shù)(也可以通過 JVM option:-Djava.util.concurrent.ForkJoinPool.common.parallelism 來設置 ForkJoinPool 線程池的線程數(shù))茫叭。如果所有 CompletableFuture 共享一個線程池,那么一旦有任務執(zhí)行一些很慢的 I/O 操作半等,就會導致線程池中所有線程都阻塞在 I/O 操作上揍愁。這樣等有大請求量過來,處理邏輯又很復雜杀饵,很多線程都在等待執(zhí)行莽囤,慢慢拖垮了服務器。
這個異常問題本質原因是我們創(chuàng)建了太多的線程切距,而能創(chuàng)建的線程數(shù)是有限制的朽缎,導致了異常的發(fā)生。能創(chuàng)建的線程數(shù)的具體計算公式如下
(MaxProcessMemory - JVMMemory - ReservedOsMemory) / (ThreadStackSize) = Number of threads
- MaxProcessMemory 指的是一個進程的最大內(nèi)存
- JVMMemory JVM內(nèi)存
- ReservedOsMemory 保留的操作系統(tǒng)內(nèi)存
- ThreadStackSize 線程棧的大小
在java語言里谜悟, 當你創(chuàng)建一個線程的時候话肖,虛擬機會在JVM內(nèi)存創(chuàng)建一個Thread對象同時創(chuàng)建一個操作系統(tǒng)線程,而這個系統(tǒng)線程的內(nèi)存用的不是JVMMemory赌躺,而是系統(tǒng)中剩下的內(nèi)存(MaxProcessMemory - JVMMemory - ReservedOsMemory)狼牺。由公式得出結論:你給JVM內(nèi)存越多,那么你能創(chuàng)建的線程越少礼患,越容易發(fā)生 java.lang.OutOfMemoryError: unable to create new native thread
問題復現(xiàn)
那么問題基本可以確定了是钥,根據(jù)接口找到對應的頁面掠归,然后在預發(fā)環(huán)境頻繁瀏覽那個頁面,然后我讓運維到宿主機和容器看下具體的情況(看看這線程數(shù)量漲的…..)悄泥,果然又發(fā)現(xiàn)預發(fā)的實例也重啟了虏冻!
問題解決
問題定位了清楚就好解決了呀
剛才的代碼對應的地方改成自定義線程池就好了
//可以指定線程池
static CompletableFuture<Void>
runAsync(Runnable runnable, Executor executor)
改完上線,線上立馬穩(wěn)了…...
問題總結
- 線程屬于寶貴資源弹囚,使用的時候盡量使用線程池管理厨相,在使用線程池的時候,要注意盡量使用自定義線程池鸥鹉,明確線程池中的各個參數(shù)
- codereview很重要呀!!!
碼字不易蛮穿,覺得還不錯可以點個贊
原文地址