欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

restTemplate未設(shè)置連接數(shù)導(dǎo)致服務(wù)雪崩問(wèn)題以及解決

 更新時(shí)間:2024年10月29日 14:24:16   作者:萊特昂  
面對(duì)線上問(wèn)題,仔細(xì)分析原因,及時(shí)調(diào)整配置,能有效解決問(wèn)題,本文詳細(xì)描述了線上遇到流量突增引發(fā)的問(wèn)題,通過(guò)查看代碼和連接池信息,分析出問(wèn)題的原因是連接池滿了,連接池大小配置不足以應(yīng)對(duì)大并發(fā)流量,通過(guò)調(diào)整連接池大小配置

背景

昨天發(fā)版遇到個(gè)線上問(wèn)題,由于運(yùn)維操作放量時(shí)隔離機(jī)器過(guò)多,導(dǎo)致只有大概三分之一的機(jī)器承載全部流量,等于單臺(tái)機(jī)器的流量突增至正常時(shí)候的三倍。

前置對(duì)外的api服務(wù)開(kāi)始瘋狂報(bào)錯(cuò):

ConnectionPoolTimeoutException:Timeout warning for connection from pool

問(wèn)題分析

連接池滿了。

查看下相關(guān)代碼,用了restTemplate去調(diào)用另外一個(gè)子系統(tǒng),繼續(xù)查看關(guān)于連接池的信息:

org.apache.http.conn.ConnectionPoolTimeoutException 是 Apache HttpClient 拋出的一種 山異常,表示從連接池獲取連接超時(shí)。

這種異常通常是由以下原因?qū)е拢?/p>

1.連接池中沒(méi)有可用的連接。當(dāng)請(qǐng)求到達(dá)時(shí),如果連接池中沒(méi)有可用的連接,就會(huì)嘗試創(chuàng)建新的連接。如果創(chuàng)建連接的速度很慢,或者連接池中的連接已經(jīng)用完了,就會(huì)出現(xiàn)ConnectionPoolTimeoutException 異常。

2.連接池中的連接都被占用。如果連接池中的所有連接都正在被占用,而且沒(méi)有連接釋放回池中,就會(huì)導(dǎo)致連接池超時(shí)異常。3.請(qǐng)求超時(shí)時(shí)間設(shè)置過(guò)短。如果設(shè)置的請(qǐng)求超時(shí)時(shí)間過(guò)短,就可能在等待連接的過(guò)程中超時(shí)。

迅速去查看連接池大小配置了多少,發(fā)現(xiàn)并沒(méi)有進(jìn)行相關(guān)的配置,那默認(rèn)的就是2,這樣遠(yuǎn)遠(yuǎn)不夠應(yīng)對(duì)當(dāng)前瞬時(shí)的大并發(fā)流量的。

問(wèn)題解決

立刻進(jìn)行了相關(guān)配置:

@Bean
public RestTemplate buildRestTemplate(){
    final ConnectionKeepAliveStrategy myStrategy = (response, context) -> {
        return 5 * 1000;//設(shè)置一個(gè)鏈接的最大存活時(shí)間
    };
    MultiThreadedHttpConnectionManager connectionManager = new MultiThreadedHttpConnectionManager();
    PoolingHttpClientConnectionManager pollingConnectionManager = new PoolingHttpClientConnectionManager(30, TimeUnit.SECONDS);
    // 總連接數(shù)
    pollingConnectionManager.setMaxTotal(1000);
    // 同路由的并發(fā)數(shù)
    pollingConnectionManager.setDefaultMaxPerRoute(1000);
    HttpClient httpClient = HttpClientBuilder.create().setConnectionManager(pollingConnectionManager)
    .setKeepAliveStrategy(myStrategy).build();
    HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory(
    httpClient);
    factory.setConnectTimeout(3000);
    factory.setReadTimeout(5000);
    return new RestTemplate(factory);
}

直接設(shè)置成了1000,交給測(cè)試壓測(cè),瞬時(shí)200的并發(fā)量壓測(cè)也沒(méi)有出現(xiàn)ConnectionPoolTimeoutException的問(wèn)題,看來(lái)成功解決了。

restTemplate優(yōu)化點(diǎn)補(bǔ)充

另外,restTemplate還有個(gè)點(diǎn)能夠優(yōu)化。

1. RestTemplate 介紹

RestTemplate和Spring提供的JdbcTemplate類似,對(duì)象一旦構(gòu)建(使用過(guò)程中不對(duì)其屬性進(jìn)行修改)就是線程安全的,多線程環(huán)境下可以安全使用。

2. 場(chǎng)景描述

A系統(tǒng)接口需要訪問(wèn)B系統(tǒng)接口,正常請(qǐng)求時(shí),這部分代碼耗時(shí)不是很明顯(約10ms)。后來(lái),進(jìn)行接口壓力測(cè)試,發(fā)現(xiàn)請(qǐng)求耗時(shí)長(zhǎng)達(dá)500ms,導(dǎo)致整個(gè)接口的tps很難上去,調(diào)整線程池參數(shù)效果努力無(wú)果,后來(lái)對(duì)請(qǐng)求B系統(tǒng)接口做了內(nèi)存級(jí)別的緩存(guava),tps增長(zhǎng)了3倍左右(約500)。

3.問(wèn)題分析

B系統(tǒng)給出的壓測(cè)數(shù)據(jù)顯示,單實(shí)例接口的tps在1000+,因此初步排除了B系統(tǒng)接口性能差的可能。于是,開(kāi)始深究系統(tǒng)本身代碼可能存在的問(wèn)題。

最開(kāi)始的代碼編寫(xiě)方式如下:

String url = "xxx.com/api"';
RestTemplate restTemplate = new RestTemplate();
MemeberCardCodeRspDTO result = restTemplate.getForObject(url, MemeberCardCodeRspDTO.class);
System.out.println(result != null ? result.getMsg() : "null");

看上去簡(jiǎn)單明了,兩行代碼搞定,非常優(yōu)雅。

后來(lái)在組長(zhǎng)大大的幫助下,大致定位了問(wèn)題點(diǎn),覺(jué)得該部分代碼可能存在部分性能問(wèn)題,應(yīng)該抽取成單實(shí)例,即不能每次使用時(shí)重新new一個(gè)新的對(duì)象。

改造后的代碼為:

@Bean
public RestTemplate buildRestTemplate(){
    final ConnectionKeepAliveStrategy myStrategy = (response, context) -> {
        return 5 * 1000;//設(shè)置一個(gè)鏈接的最大存活時(shí)間
    };
    MultiThreadedHttpConnectionManager connectionManager = new MultiThreadedHttpConnectionManager();
    PoolingHttpClientConnectionManager pollingConnectionManager = new PoolingHttpClientConnectionManager(30, TimeUnit.SECONDS);
    // 總連接數(shù)
    pollingConnectionManager.setMaxTotal(1000);
    // 同路由的并發(fā)數(shù)
    pollingConnectionManager.setDefaultMaxPerRoute(1000);
    HttpClient httpClient = HttpClientBuilder.create().setConnectionManager(pollingConnectionManager)
    .setKeepAliveStrategy(myStrategy).build();
    HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory(
    httpClient);
    factory.setConnectTimeout(3000);
    factory.setReadTimeout(5000);
    return new RestTemplate(factory);
}

直接依賴spring bean注解將其定義成單實(shí)例對(duì)象,其他地方直接屬性注入后使用。

4. 性能分析

問(wèn)題得到解決后,課余時(shí)間又對(duì)該部分代碼做了一個(gè)粗略的定量分析,本機(jī)跑的數(shù)據(jù),還是能比較清晰地得出結(jié)論。

private final String url = "xxxx.com/api";
private int loopCount = 400;
private int concurrentThread = 400;
private RestTemplate restTemplate;

@BeforeTest
public void init() {
   final ConnectionKeepAliveStrategy myStrategy = (response, context) -> {
        return 5 * 1000;//設(shè)置一個(gè)鏈接的最大存活時(shí)間
   };
   PoolingHttpClientConnectionManager pollingConnectionManager = new PoolingHttpClientConnectionManager(30, TimeUnit.SECONDS);
   // 總連接數(shù)
   pollingConnectionManager.setMaxTotal(1000);
   // 同路由的并發(fā)數(shù)
   pollingConnectionManager.setDefaultMaxPerRoute(1000);
   HttpClient httpClient = HttpClientBuilder.create().setConnectionManager(pollingConnectionManager)
   .setKeepAliveStrategy(myStrategy).build();
   HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory(
   httpClient);
   factory.setConnectTimeout(3000);
   factory.setReadTimeout(5000);
   restTemplate = new RestTemplate(factory);
}
@Test
public void testHttp1() {
  long start = System.currentTimeMillis();
  ExecutorService executor = Executors.newFixedThreadPool(concurrentThread);
  for (int i =0 ; i< loopCount; i++){
      executor.submit(() -> {
          MemeberCardCodeRspDTO result = restTemplate.getForObject(url, MemeberCardCodeRspDTO.class);
          System.out.println(result != null ? result.getMsg() : "null");
      });
  }
  try {
      executor.shutdown();
      executor.awaitTermination(30, TimeUnit.MINUTES); // or longer.
   } catch (InterruptedException e) {
      e.printStackTrace();
   }
   long time = System.currentTimeMillis() - start;
   System.out.printf("Tasks1 took %d ms to run%n", time);
}

@Test
public void testHttp2() {
   long start = System.currentTimeMillis();
   ExecutorService executor = Executors.newFixedThreadPool(concurrentThread);
   for (int i =0 ; i< loopCount; i++){
       executor.submit(() -> {
           RestTemplate restTemplate = new RestTemplate();
           MemeberCardCodeRspDTO result = restTemplate.getForObject(url, MemeberCardCodeRspDTO.class);
           ystem.out.println(result != null ? result.getMsg() : "null");
       });
   }
   try {
        executor.shutdown();
        executor.awaitTermination(30, TimeUnit.MINUTES); // or longer.
   } catch (InterruptedException e) {
        e.printStackTrace();
   }
   long time = System.currentTimeMillis() - start;
   System.out.printf("Tasks2 took %d ms to run%n", time);
}

調(diào)整threads數(shù)量,跑了6組數(shù)據(jù),結(jié)果對(duì)比如下:

圖中比較清晰的可以看出,優(yōu)化過(guò)后的代碼性能提升比較明顯,且隨著并發(fā)任務(wù)數(shù)增加,耗時(shí)波動(dòng)不會(huì)太大。

問(wèn)題總結(jié)

第三方庫(kù)提供的各種方便的類,簡(jiǎn)化了編碼復(fù)雜度,方便了開(kāi)發(fā)者。使用不恰當(dāng)時(shí),細(xì)微的編碼可能埋藏著大的隱患。

以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • Java集合中的LinkedHashSet源碼解讀

    Java集合中的LinkedHashSet源碼解讀

    這篇文章主要介紹了Java集合中的LinkedHashSet源碼解讀,在LinkedHashMap中,雙向鏈表的遍歷順序通過(guò)構(gòu)造方法指定,如果沒(méi)有指定,則使用默認(rèn)順序?yàn)椴迦腠樞?即accessOrder=false,需要的朋友可以參考下
    2023-12-12
  • Java結(jié)構(gòu)型設(shè)計(jì)模式之橋接模式詳細(xì)講解

    Java結(jié)構(gòu)型設(shè)計(jì)模式之橋接模式詳細(xì)講解

    橋接,顧名思義,就是用來(lái)連接兩個(gè)部分,使得兩個(gè)部分可以互相通訊。橋接模式將系統(tǒng)的抽象部分與實(shí)現(xiàn)部分分離解耦,使他們可以獨(dú)立的變化。本文通過(guò)示例詳細(xì)介紹了橋接模式的原理與使用,需要的可以參考一下
    2022-09-09
  • IDEA如何實(shí)現(xiàn)右鍵翻譯

    IDEA如何實(shí)現(xiàn)右鍵翻譯

    這篇文章主要介紹了IDEA如何實(shí)現(xiàn)右鍵翻譯問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-05-05
  • SpringSecurity自定義登錄接口的實(shí)現(xiàn)

    SpringSecurity自定義登錄接口的實(shí)現(xiàn)

    本文介紹了使用Spring Security實(shí)現(xiàn)自定義登錄接口,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2025-01-01
  • java8快速實(shí)現(xiàn)List轉(zhuǎn)map 、分組、過(guò)濾等操作

    java8快速實(shí)現(xiàn)List轉(zhuǎn)map 、分組、過(guò)濾等操作

    這篇文章主要介紹了java8快速實(shí)現(xiàn)List轉(zhuǎn)map 、分組、過(guò)濾等操作,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2019-09-09
  • Java四種遍歷Map的方法

    Java四種遍歷Map的方法

    今天小編就為大家分享一篇關(guān)于Java四種遍歷Map的方法,小編覺(jué)得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來(lái)看看吧
    2019-01-01
  • Java?C++題解eetcode940不同的子序列?II

    Java?C++題解eetcode940不同的子序列?II

    這篇文章主要為大家介紹了Java?C++題解eetcode940不同的子序列?II實(shí)現(xiàn)示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-10-10
  • 詳解Java的Proxy動(dòng)態(tài)代理機(jī)制

    詳解Java的Proxy動(dòng)態(tài)代理機(jī)制

    Java有兩種代理方式,一種是靜態(tài)代理,另一種是動(dòng)態(tài)代理。對(duì)于靜態(tài)代理,其實(shí)就是通過(guò)依賴注入,對(duì)對(duì)象進(jìn)行封裝,不讓外部知道實(shí)現(xiàn)的細(xì)節(jié)。很多 API 就是通過(guò)這種形式來(lái)封裝的
    2021-06-06
  • spring-boot項(xiàng)目啟動(dòng)遲緩異常排查解決記錄

    spring-boot項(xiàng)目啟動(dòng)遲緩異常排查解決記錄

    這篇文章主要為大家介紹了spring-boot項(xiàng)目啟動(dòng)遲緩異常排查解決記錄,突然在本地啟動(dòng)不起來(lái)了,表象特征就是在本地IDEA上運(yùn)行時(shí),進(jìn)程卡住也不退出,應(yīng)用啟動(dòng)時(shí)加載相關(guān)組件的日志也不輸出
    2022-02-02
  • 如何在maven本地倉(cāng)庫(kù)中添加oracle的jdbc驅(qū)動(dòng)

    如何在maven本地倉(cāng)庫(kù)中添加oracle的jdbc驅(qū)動(dòng)

    文章介紹了在Maven項(xiàng)目中添加Oracle數(shù)據(jù)庫(kù)驅(qū)動(dòng)ojdbc5時(shí)遇到的問(wèn)題以及解決問(wèn)題的兩種方法,方法一為簡(jiǎn)單粗暴,但沒(méi)有體現(xiàn)Maven倉(cāng)庫(kù)的作用,需要手動(dòng)管理jar包,方法二為在Maven本地倉(cāng)庫(kù)中添加Oracle的JDBC驅(qū)動(dòng),過(guò)程較為繁瑣,但配置一次后可以多次使用
    2024-11-11

最新評(píng)論