1. 程式人生 > >一場HttpClient呼叫未關閉流引發的問題

一場HttpClient呼叫未關閉流引發的問題

最近生產環境出現了一個問題,就是Job服務日誌好端端的不列印日誌了,服務也沒有掛, 現在將此次問題解決過程記錄下來~


問題描述


生產環境有一臺Job伺服器,是專門用來跑所有定時任務的,然後有一天發現定時任務好像沒有執行,所以上Job伺服器檢視日誌,結果發現的情況是:
最後列印的是昨天晚上九點半的,到我看的時候就一直沒有日誌,沒有日誌就沒有執行Job;當時為了快速解決問題就重啟了伺服器,Job就正常執行了;後來第二天上去看的時候居然又停止了, 但是Job的JVM是還在的,並沒有掛掉,就是沒有日誌,看起來就是被阻塞了一樣;
這個時候又把生成環境重啟讓他先正常執行,然後立馬去測試環境看看能不能重現, 結果測試環境的日誌也是一樣

問題程式碼

//允許非同步執行  Schedule
@EnableAsync
@Component
public class TestSchedule {
    private static final Logger LOGGER = LoggerFactory.getLogger(LotterySchedule.class);

    // 使用執行緒池myAsync來執行這個Job
    @Async("myAsync")
    @Scheduled(cron = "0/1 * * * * ?")
    public void testDoGet(){
        LOGGER.
info("\ntestDoGet:"+Thread.currentThread()); //業務程式碼:裡面呼叫了 String json = HttpUtil.doGet(url);來呼叫第三方介面 HttpUtil.doGet("www.baidu.com") } //這裡沒有用非同步執行,單執行緒執行 @Scheduled(cron = "0/1 * * * * ?") public void testPrint(){ LOGGER.info("\ntestPrint:"+Thread.currentThread()); } }

然後看看執行緒池的程式碼

@Configuration
@EnableAsync
public class ExecutorConfig {

    /** Set the ThreadPoolExecutor's core pool size. */
    private int corePoolSize = 10;
    /** Set the ThreadPoolExecutor's maximum pool size. */
    private int maxPoolSize = 25;
    /** Set the capacity for the ThreadPoolExecutor's BlockingQueue. */
    private int queueCapacity = 10;


    @Bean
    public Executor myAsync() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(corePoolSize);
        executor.setMaxPoolSize(maxPoolSize);
        executor.setQueueCapacity(queueCapacity);
        executor.setThreadNamePrefix("MyJobExecutor-");

        // rejection-policy:當pool已經達到max size的時候,如何處理新任務
        // CALLER_RUNS:不在新執行緒中執行任務,而是有呼叫者所在的執行緒來執行
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        executor.initialize();
        return executor;
    }
}

1.檢視JVM情況


Linux使用jstat命令檢視jvm的GC情況

jps 查詢Jvm程序號
查詢Jvm jstat -gc 21738 5000
發現Jvm好像沒有出現頻繁GC,GC處理異常的情況,而且Jvm啟動也配置了:+HeapDumpOnOutOfMemoryError;但是沒有看到記憶體溢位的Dump檔案;排除 Jvm異常的情況

2.檢視執行緒棧分析

jps 查詢Jvm程序號
jstack -l 22741 查詢執行緒棧資訊

"MyJobExecutor-2" #25 prio=5 os_prio=31 tid=0x00007fc7f7374800 nid=0xa203 waiting on condition [0x0000700004def000]
   java.lang.Thread.State: WAITING (parking)
        at sun.misc.Unsafe.park(Native Method)
        - parking to wait for  <0x0000000742196c58> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
        at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)
        at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2039)
        at org.apache.http.pool.AbstractConnPool.getPoolEntryBlocking(AbstractConnPool.java:380)
        at org.apache.http.pool.AbstractConnPool.access$200(AbstractConnPool.java:69)
        at org.apache.http.pool.AbstractConnPool$2.get(AbstractConnPool.java:246)
        - locked <0x00000007969b6910> (a org.apache.http.pool.AbstractConnPool$2)
        at org.apache.http.pool.AbstractConnPool$2.get(AbstractConnPool.java:193)
        at org.apache.http.impl.conn.PoolingHttpClientConnectionManager.leaseConnection(PoolingHttpClientConnectionManager.java:303)
        at org.apache.http.impl.conn.PoolingHttpClientConnectionManager$1.get(PoolingHttpClientConnectionManager.java:279)
        at org.apache.http.impl.execchain.MainClientExec.execute(MainClientExec.java:191)
        at org.apache.http.impl.execchain.ProtocolExec.execute(ProtocolExec.java:185)
        at org.apache.http.impl.execchain.RetryExec.execute(RetryExec.java:89)
        at org.apache.http.impl.execchain.RedirectExec.execute(RedirectExec.java:110)
        at org.apache.http.impl.client.InternalHttpClient.doExecute(InternalHttpClient.java:185)
        at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:83)
        at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:108)
        at com.xxx.util.HttpUtil.doGet(HttpUtil.java:97)
        at com.xxx.util.HttpUtil.doGet(HttpUtil.java:70)
		省略
        at 
       at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204)
        at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:746)
        at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163)
        at org.springframework.transaction.interceptor.TransactionInterceptor$$Lambda$392/226041624.proceedWithInvocation(Unknown Source)
        at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:294)
        at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:98)
        at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:185)
        at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:688)
        at com.bfc.service.impl.LotteryHbkuai3MainServiceImpl$$EnhancerBySpringCGLIB$$4d3de61.publishPreByJob(<generated>)
        at com.bfc.job.LotterySchedule.publishByJob(LotterySchedule.java:45)
        at com.bfc.job.LotterySchedule$$FastClassBySpringCGLIB$$75ac0373.invoke(<generated>)
        at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204)
        at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:746)
        at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163)
        at org.springframework.aop.interceptor.AsyncExecutionInterceptor.lambda$invoke$0(AsyncExecutionInterceptor.java:115)
        at org.springframework.aop.interceptor.AsyncExecutionInterceptor$$Lambda$391/524684837.call(Unknown Source)
        at java.util.concurrent.FutureTask.run(FutureTask.java:266)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
        at java.lang.Thread.run(Thread.java:745)

   Locked ownable synchronizers:
        - <0x0000000741db91a0> (a java.util.concurrent.ThreadPoolExecutor$Worker)

"MyJobExecutor-1" #24 prio=5 os_prio=31 tid=0x00007fc7f5ff7800 nid=0xa403 waiting on condition [0x0000700004cec000]
   java.lang.Thread.State: WAITING (parking)
        at sun.misc.Unsafe.park(Native Method)
        - parking to wait for  <0x0000000742196c58> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
        at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)
        at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2039)
        at org.apache.http.pool.AbstractConnPool.getPoolEntryBlocking(AbstractConnPool.java:380)
        at org.apache.http.pool.AbstractConnPool.access$200(AbstractConnPool.java:69)
        at org.apache.http.pool.AbstractConnPool$2.get(AbstractConnPool.java:246)
        - locked <0x00000007968318d8> (a org.apache.http.pool.AbstractConnPool$2)
        at org.apache.http.pool.AbstractConnPool$2.get(AbstractConnPool.java:193)
        at org.apache.http.impl.conn.PoolingHttpClientConnectionManager.leaseConnection(PoolingHttpClientConnectionManager.java:303)
        at org.apache.http.impl.conn.PoolingHttpClientConnectionManager$1.get(PoolingHttpClientConnectionManager.java:279)
        at org.apache.http.impl.execchain.MainClientExec.execute(MainClientExec.java:191)
        at org.apache.http.impl.execchain.ProtocolExec.execute(ProtocolExec.java:185)
        at org.apache.http.impl.execchain.RetryExec.execute(RetryExec.java:89)
        at org.apache.http.impl.execchain.RedirectExec.execute(RedirectExec.java:110)
        at org.apache.http.impl.client.InternalHttpClient.doExecute(InternalHttpClient.java:185)
        at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:83)
        at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:108)
        at com.xxx.util.HttpUtil.doGet(HttpUtil.java:97)
        at com.xxx.util.HttpUtil.doGet(HttpUtil.java:70)
        at 
        省略
        at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204)
        at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:746)
        at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163)
        at org.springframework.transaction.interceptor.TransactionInterceptor$$Lambda$392/226041624.proceedWithInvocation(Unknown Source)
        at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:294)
        at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:98)
        at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:185)
        at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:688)
        at com.bfc.service.impl.LotteryHbkuai3MainServiceImpl$$EnhancerBySpringCGLIB$$4d3de61.publishPreByJob(<generated>)
        at com.bfc.job.LotterySchedule.publishByJob(LotterySchedule.java:45)
        at com.bfc.job.LotterySchedule$$FastClassBySpringCGLIB$$75ac0373.invoke(<generated>)
        at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204)
        at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:746)
        at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163)
        at org.springframework.aop.interceptor.AsyncExecutionInterceptor.lambda$invoke$0(AsyncExecutionInterceptor.java:115)
        at org.springframework.aop.interceptor.AsyncExecutionInterceptor$$Lambda$391/524684837.call(Unknown Source)
        at java.util.concurrent.FutureTask.run(FutureTask.java:266)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
        at java.lang.Thread.run(Thread.java:745)

   Locked ownable synchronizers:
        - <0x0000000741da4a28> (a java.util.concurrent.ThreadPoolExecutor$Worker)


分析問題

通過棧資訊可以發現,基本上所有的執行緒都被阻塞了,都在wait;
重點看 MyJobExecutor-" 開頭的執行緒都在wait同一個lock,並且程式碼發生的地方是 HttpUtil.doGet 方法;
這麼看來好像是HttpUtil.doGet 發生了阻塞;然後分析問題程式碼,結果發現這個程式碼是這樣的

  private static PoolingHttpClientConnectionManager connMgr;
    private static RequestConfig requestConfig;
    private static final int MAX_TIMEOUT = 7000;
    private static CloseableHttpClient httpClient ;
    static {
        // 設定連線池
        connMgr = new PoolingHttpClientConnectionManager();
        // 設定連線池大小
        connMgr.setMaxTotal(8);
        connMgr.setDefaultMaxPerRoute(connMgr.getMaxTotal());
        RequestConfig.Builder configBuilder = RequestConfig.custom();
        // 設定連線超時
        configBuilder.setConnectTimeout(MAX_TIMEOUT);
        // 設定讀取超時
        configBuilder.setSocketTimeout(MAX_TIMEOUT);
        // 設定從連線池獲取連線例項的超時
        configBuilder.setConnectionRequestTimeout(MAX_TIMEOUT);
        // 在提交請求之前 測試連線是否可用
        configBuilder.setStaleConnectionCheckEnabled(true);
        requestConfig = configBuilder.build();
        httpClient = HttpClients.custom().setConnectionManager(connMgr).build();
    }
/**
     * 傳送 GET 請求(HTTP),不帶輸入資料
     * @param url
     * @return
     */
    public static String doGet(String url) {
        return doGet(url, new HashMap<String, Object>());
    }

    /**
     * 傳送 GET 請求(HTTP),K-V形式
     * @param url
     * @param params
     * @return
     */
    public static String doGet(String url, Map<String, Object> params) {
        String apiUrl = url;
        StringBuffer param = new StringBuffer();
        int i = 0;
        for (String key : params.keySet()) {
            if (i == 0) {
                param.append("?");
            }else {
                param.append("&");
            }
            param.append(key).append("=").append(params.get(key));
            i++;
        }
        apiUrl += param;
        String result = null;
       // HttpClient httpclient = new DefaultHttpClient();
        try {
            HttpGet httpPost = new HttpGet(apiUrl);
            HttpResponse response = httpClient.execute(httpPost);
            HttpEntity entity = response.getEntity();
            if (entity != null) {
                InputStream instream = entity.getContent();
                result = IOUtils.toString(instream, "UTF-8");
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        
            
           

相關推薦

HttpClient呼叫關閉引發的問題

最近生產環境出現了一個問題,就是Job服務日誌好端端的不列印日誌了,服務也沒有掛, 現在將此次問題解決過程記錄下來~ 問題描述 生產環境有一臺Job伺服器,是專門用來跑所有定時任務的,然後有一天發現定時任務好像沒有執行,所以上Job伺服器檢視日誌,結果發現的情況是: 最後

玩轉OpenCV 4.0():呼叫Dis光演算法示例

玩轉OpenCV 4.0(一):呼叫Dis光流演算法示例 呼叫DIS光流的原始碼如下: 對應CMakelists如下: 呼叫DIS光流的原始碼如下: #include "opencv2/core/utility.hpp"

new DefaultHttpClient過時處理建議和HTTP呼叫關閉處理

最近寫新的呼叫程式碼時候,發現專案中new DefaultHttpClient()例項過期很久了,翻了翻另一個生產專案呼叫端的程式碼也是如此,於是查閱了些資料,用新版本程式碼替換了手上專案的程式碼並且正常測試完、生產上也正常執行完,算是一次補習,特記錄下替換過程和呼叫完後的處理。 1:來看下原來的呼叫程式碼

繼承的愛恨情仇——鉆石引發的血案

pytho ID 這不 block c中 height 地方 頭發 16px 最近在看PHP手冊,發現了一個稀奇古怪的新玩意——trait。 這引起了我極大的興趣,由於PHP面向對象的部分有很大程度和Java類似,我就自覺不自覺地和Java對比著來

python資料查詢操作之 缺少db.commit()引發的血案……

---恢復內容開始--- 最近大作業用到了python操作資料庫的內容。涉及的庫是pymmysql,我就不詳細介紹這個庫的操作了,直接奔入主題--->開整  背景:        涉及程式中一個實時檢視資料表中state欄位==1的功能,我把這個功能單

Java訪問許可權控制的使用不當,活生生地引發血淋漓的慘案

人在什麼面前最容易失去抵抗力? 美色,算是一個,比如說西施的貢獻薄就是忍辱負重、以身報國、助越滅吳;金錢,算是另外一個,我們古人常說“錢乃身外之物,生不帶來死不帶去”,但我們又都知道“有錢能使鬼推磨”。 除去美色和金錢,我認為還有一個,就是讀者的認可——“二哥,你的文章真的很棒,我特別喜歡。希望能多多更新

小碼農的職人生:由張小平離職引發的一些吐槽

文  ▏村長好忙(訂閱號ID:xiejun_asp)      9月27日刷爆朋友圈的一篇文章 《離職能直接影響中國登月的人才,只配呆在國企底層》,雖然存在誇大其詞且很多細節不實,但是還是讓張小平離職登上了微博熱搜榜。引發了我一丟丟的小感觸,忍不住來說說我這幾年所經歷的職場。 01 — 個人成長     不

由一個s引發的鬱悶

         最近想轉業從事android開發。因為2年前研究過。所以搭建環境什麼的都是從原始地址下載的基本沒有什麼困難。跑書店看到一本名為《第一行程式碼》的書。想買,但是發現網上有PDF版的(昨天發現其實竟然還有word版的)。發現上邊第二章有

由於錯誤使用final宣告方法和CGLib引發的問題

前言: 近期發現某系統在業務高峰時,訪問量回突然下降,開發測試並抓包後發現是呼叫獲取城市列表功能時發生異常所致。下面是發現和解決問題的大概步驟: 1:分析程式碼,發現出問題的介面查詢的所有資料均已快取到redis中,然後查系統日誌,發現在這個時間點有大量的redis訪問超時。 2:問題比

Gitlab:“刪庫”血案引發的反思

過年期間,一條Gitlab誤刪資料的新聞佔領了各大科技網站的頭條。一切令人驚歎感慨,一切又讓人似曾相似。回首2016年,網路重大異常事件其實並不罕見。 << 2016.03全球三分之二網站伺服器用的開源加密工具OpenSSL爆出“水牢漏洞”。<<

Android 4.0 中由ProGuard引發血案

案件還原:         修改Android 4.0原始碼中的Setting,新增一項功能之後,在eng模式下編譯,一切正常,遂提交程式碼到伺服器。第二天,傳來噩耗,Setting上新新增的功能無法使用,一點選則報錯。 案件分析:         上傳程式碼之前,已經在本地編譯測試過,咋會有錯呢??!!管

小米華為強勢互懟!由“非常嚇人的技術”而引發的“血案”……

咳咳,眾位小夥伴們想必已經對小米在5月底的新品釋出會期待不已了吧!不過大家知道麼,在這麼重要的釋

遇到一個因socket關閉引發的檔案控制代碼用完問題

“愛提踢斯”專案最近遇到一個問題,當FTP伺服器磁碟沒有空間時,裝置會不斷復位——這是測試人員反饋的。我們拿到log後,看到一個通訊所用的檔案開啟失敗。不斷列印Too many open file,然後超時裝置復位。同時我們看到資料庫檔案開啟失敗,無法寫入資料。一個現象,看

robotframework當執行用例只打開次瀏覽器的情況下關閉之前關閉的父視窗

小編起初想了很多方法去關閉之前開啟的視窗,例如,close browser,window.close();試了以後發現window.close();這個只能關閉當前的子視窗,這樣就不能完成接下來的用例測試了。後來嘗試萬能的js來嘗試解決這個問題,沒想到竟然成功了,不多說直接

由mknod引發的扯淡

我們註冊完字元、塊驅動裝置後,一般會用mknod去建立,應用層與驅動的管道。例如mtd的字元驅動,我們會用mknod  /dev/mtdchar1  c 30 0,建立/dev/mtdchar1來對映mtd第一個字元裝置。 由於程式碼沒法紅字,程式碼中有color:#ff

precision引發的血案

Accelerated C++ 中文版中 第三章 3.1那個示例程式碼 #include <iomanip> #include <ios> #include <iostream> #include <string> using

IOUtils.closeQuietly:在finally中關閉時不需要再catch遍IOException

在使用 stream 的時,往往要 try catch IOException。eric教導我要把流的關閉放到 finally 中

由fork引發的超時,讓我們重新探討了Redis的抖動問題

摘要:一次由fork引發的時延抖動問題。 背景介紹 華為雲資料庫GaussDB(for Redis) 是一款基於計算儲存分離架構,相容Redis生態的雲原生NoSQL資料庫;它依靠共享儲存池實現了強一致,支援持久化落盤儲存,保證資料的安全可靠。其核心特點是:存算分離、強一致、低成本、超大容量。 GaussDB

今晚巔峰你對編程的認識分享會,再晚就不等你了

今晚一場巔峰你對編程的認識分享會 再晚就不等你了 本文出自 “知乎技術” 博客,請務必保留此出處http://liuzhiying.blog.51cto.com/5850988/1929819今晚一場巔峰你對編程的認識分享會,再晚就不等你了

以電競的名義,遊戲公益杯背後的思考

三星顯示器  在人類社會的發展秩序中,公益一直是連接人性與社會文明的紐帶。隨著社會的不斷進步以及新興事物的影響,這種契合關系形成的影響力除了在大眾群體層面持續發揚之外,在如今的各個領域也已經成為新的潮流,誕生了一些新模式、新打法。  6月13日,京東遊戲公益杯在北京舉行,電競和公益兩個看似毫不相幹的概念被捆綁