溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

如何解決springcloud gateway連接保活問題

發(fā)布時(shí)間:2021-07-19 09:16:53 來源:億速云 閱讀:571 作者:chen 欄目:開發(fā)技術(shù)

這篇文章主要介紹“如何解決springcloud gateway連接?;顔栴}”,在日常操作中,相信很多人在如何解決springcloud gateway連接?;顔栴}問題上存在疑惑,小編查閱了各式資料,整理出簡(jiǎn)單好用的操作方法,希望對(duì)大家解答”如何解決springcloud gateway連接保活問題”的疑惑有所幫助!接下來,請(qǐng)跟著小編一起來學(xué)習(xí)吧!

項(xiàng)目中使用了springcloud gateway作為網(wǎng)關(guān),上游與負(fù)載均衡服務(wù)器連接。

近期通過監(jiān)控系統(tǒng)觀察,發(fā)現(xiàn)網(wǎng)關(guān)與上游負(fù)載均衡服務(wù)器保持的TCP連接有300+,初步懷疑是調(diào)用方未釋放連接

用如下方法進(jìn)行分析:

1)周期性采集當(dāng)前建立的連接及端口數(shù)據(jù)

首先是每隔10分鐘連續(xù)采集2兩個(gè)小時(shí),發(fā)現(xiàn)在兩個(gè)小時(shí)之內(nèi)新出現(xiàn)的端口不到12個(gè),再逐步縮短采樣周期,到最后每秒采集一次,分析發(fā)現(xiàn)每秒種建立一個(gè)連接,同時(shí)關(guān)閉一個(gè)連接,當(dāng)仍存在300+連接,這些連接對(duì)應(yīng)的端口稱為不活躍端口,記錄下這300+不活躍端口。

2)為了進(jìn)一步分析,用whireshark抓包

發(fā)現(xiàn)絕大部分情況下都是正常的連接和關(guān)閉,但這300+個(gè)不活躍端口對(duì)應(yīng)的連接上沒有任何數(shù)據(jù),這300+個(gè)不活躍對(duì)應(yīng)的連接稱為不活躍連接。同步趕緊上馬接口調(diào)用實(shí)時(shí)監(jiān)控功能,發(fā)現(xiàn)實(shí)際的調(diào)用數(shù)量卻非常少(每分鐘不足10個(gè))。

3)與上游的負(fù)載均衡工程師一起檢查

從負(fù)載均衡服務(wù)器看到的活躍連接也是個(gè)位數(shù),并且并未找到在網(wǎng)關(guān)上的不活躍端口。也就是說在負(fù)載均衡服務(wù)器已經(jīng)已經(jīng)拆除了與網(wǎng)關(guān)上的不活躍連接對(duì)應(yīng)的連接。咨詢負(fù)載均衡工程師,負(fù)載均衡設(shè)備對(duì)于1超過1個(gè)小時(shí)的不活躍連接會(huì)主動(dòng)拆除。

經(jīng)過以上分析,確定是外部系統(tǒng)經(jīng)過負(fù)載均衡設(shè)備與網(wǎng)關(guān)建立連接后,并未進(jìn)行任何操作,但網(wǎng)關(guān)會(huì)一直維護(hù)這個(gè)連接,導(dǎo)致網(wǎng)關(guān)的連接數(shù)持續(xù)上升。

為解決這個(gè)問題,需要首先回顧一下傳統(tǒng)的TCP長連接維護(hù)機(jī)制

針對(duì)長連接的維護(hù),傳統(tǒng)的TCP服務(wù)采用心跳來維持,比如服務(wù)端每分鐘發(fā)送一個(gè)心跳報(bào)文,并啟動(dòng)計(jì)數(shù)器并設(shè)置為1,客戶端收到后回應(yīng)一個(gè)報(bào)文,服務(wù)端收到回復(fù)報(bào)文后重置計(jì)數(shù)器,如果為收到應(yīng)答,則一分鐘再發(fā)送一個(gè)心跳報(bào)文,同時(shí)計(jì)數(shù)器加1,連續(xù)發(fā)送三個(gè)心跳報(bào)文并且未收到映帶,則服務(wù)端則認(rèn)為客戶端已經(jīng)失聯(lián),會(huì)主動(dòng)拆除這個(gè)連接,以避免不必要的資源占用。

我們現(xiàn)在使用的springcloud gateway,顯然很難直接修改源碼增加以上的心跳機(jī)制,所以我又想到了操作系統(tǒng)協(xié)議棧的連接?;顧C(jī)制。

TCP協(xié)議棧的保活機(jī)制與應(yīng)用層的長連接維護(hù)機(jī)制類似(當(dāng)然,應(yīng)用層的TCP長連接維護(hù)機(jī)制就是從協(xié)議棧的保護(hù)機(jī)制學(xué)習(xí)來的'&'),只不過是在協(xié)議棧層面完成,這樣避免了應(yīng)用層實(shí)現(xiàn)負(fù)載的長連接維護(hù)

?;顧C(jī)制如下:

1)服務(wù)器端判斷一個(gè)連接在指定的時(shí)間內(nèi)

(缺省為2小時(shí))沒有任何數(shù)據(jù),則發(fā)送一個(gè)探測(cè)報(bào)文,并啟動(dòng)定時(shí)器

2)如果客戶端在正常運(yùn)行并且網(wǎng)絡(luò)可達(dá)

則客戶端則回復(fù)一個(gè)響應(yīng)報(bào)文,服務(wù)端認(rèn)為客戶端正常,則重新開始計(jì)時(shí)。

如果客戶端主機(jī)崩潰或網(wǎng)絡(luò)不可達(dá),服務(wù)端將收不到應(yīng)答,定時(shí)器超時(shí)后(一般為75秒),服務(wù)端將再次發(fā)送探測(cè)報(bào)文,如此連續(xù)發(fā)送若干次(一般為10次),如果均未收到應(yīng)答,則服務(wù)端將主動(dòng)關(guān)閉連接。

當(dāng)然,如果中間有任何一次服務(wù)端收到應(yīng)答,則認(rèn)為連接正常,不再發(fā)送探測(cè)報(bào)文。

使用如下命令可以查看以上?;顣r(shí)間、發(fā)送探測(cè)報(bào)文的間隔和次數(shù):

#sysctl -a|grep keepalive
net.ipv4.tcp_keepalive_time = 7200(單位為秒)
net.ipv4.tcp_keepalive_probes = 9 
net.ipv4.tcp_keepalive_intvl = 75  (單位為秒)

關(guān)于保活參數(shù)中兩個(gè)小時(shí)的時(shí)間設(shè)置存在爭(zhēng)議,通常人們希望這個(gè)值可以小很多,比如分鐘級(jí),但保活間隔時(shí)間是系統(tǒng)級(jí)別的變量,如果改變?cè)撝禃?huì)影響所有使用該功能的用戶。

所以,Host Requirements RFC提出一個(gè)實(shí)現(xiàn)方式,?;铋g隔是可配置的,但缺省不小于兩個(gè)小時(shí),并且需要應(yīng)用程序設(shè)置才啟用。

如果使用協(xié)議棧的?;罟δ?,那么缺省的兩個(gè)小時(shí)的時(shí)間還是太長,如果縮短這個(gè)時(shí)間會(huì)有什么影響,并無把握。

所以還是先想其他辦法,從網(wǎng)上看到可以通過以下代碼修改網(wǎng)關(guān)對(duì)長連接的維護(hù)辦法,以下代碼是設(shè)置保活時(shí)間為3分鐘,如果3分鐘內(nèi)連接上沒有數(shù)據(jù),網(wǎng)關(guān)將主動(dòng)關(guān)閉連接:

配置文件:

server:
    netty:
        idie-timeout: 300
@Configuration
public class NettyConfig {
    @Bean
    publiWebServerFactoryCustomizer<NettyReactiveWebServerFactory> idleTimeoutCustomizer(
        @Value("${server.netty.idle-timeout}") Duration idleTimeout) {
        return factory -> factory.addServerCustomizers(
            server -> server.tcpConfiguration(
                tcp->tcp.bootstrap(
                   bootstrap->bootstrap.childHandler(new ChannelInitializer<Channel>() {
                       @Override
                       protected void initChannel(Channel channel) {
                           channel.pipeline().addLast(
                               new IdleStateHandler(0, 0, idleTimeout.toNanos(), NANOSECONDS) {
                                   private final AtomicBoolean closed = new AtomicBoolean();
                                   @Override
                                   protected void channelIdle(
                                       ChannelHandlerContext ctx, IdleStateEvent evt) {
                                           if (closed.compareAndSet(false, true)) {
                                               ctx.close();
                                           }
                                       }
                                   }
                               );
                           }
                       }))));
    }
}

系統(tǒng)上線后,通過監(jiān)控系統(tǒng)發(fā)現(xiàn)網(wǎng)關(guān)連接數(shù)并未持續(xù)增長,剛松一口氣,線上業(yè)務(wù)系統(tǒng)頻頻報(bào)錯(cuò),請(qǐng)求網(wǎng)關(guān)失敗,趕緊安排網(wǎng)絡(luò)抓包,然后馬上回退恢復(fù)業(yè)務(wù)。

然后對(duì)網(wǎng)絡(luò)抓包進(jìn)行分析,截圖如下:

如何解決springcloud gateway連接保活問題

從抓包結(jié)果來看,客戶端和網(wǎng)關(guān)經(jīng)過3次握手后,建立了連接,但后面的建立SSL的過程中,網(wǎng)關(guān)返回了400 Bad Request,所以導(dǎo)致業(yè)務(wù)系統(tǒng)請(qǐng)求失?。I(yè)務(wù)系統(tǒng)使用https請(qǐng)求網(wǎng)關(guān)),懷疑是上面的代碼中的配置覆蓋了配置文件中SSL的相關(guān)配置,所以導(dǎo)致SSL連接未建立。

我們優(yōu)秀的工程師,本著鍥而不舍的精神對(duì)gateway進(jìn)行源碼分析,經(jīng)過對(duì)代碼的分析,發(fā)現(xiàn)確實(shí)是這個(gè)配置覆蓋了原有的SSL配置,導(dǎo)致SSL配置未生效所致,所以對(duì)以上代碼進(jìn)行改寫,具體如下:

@Configuration
public class NettyConfig {
    @Bean
    publiWebServerFactoryCustomizer<NettyReactiveWebServerFactory> idleTimeoutCustomizer(
        @Value("${server.netty.idle-timeout}") Duration idleTimeout) {
        return factory -> factory.addServerCustomizers(
            server -> server.tcpConfiguration(
                tcp->tcp.bootstrap(bootstrap->{
                  //增加如下代碼,從而可保持原有配置并追加?;?
                   BootstrapHandlers.updateConfiguration(bootstrap, "IdleStateHandler",
                      (connectionObserver, channel) ->{
                       channel.pipeline().addLast(new IdleStateHandler(0, 0,
                           idleTimeout.toNanos(), NANOSECONDS) {
                                 private final AtomicBoolean closed = new AtomicBoolean();
                                 @Override
                                 protected void channelIdle(ChannelHandlerContext ctx,
                                     IdleStateEvent evt) {
                                     if (closed.compareAndSet(false, true)) {
                                         ctx.close();
                                     }
                                 }
                             });
                         });
                        return bootstrap;
                    }
                )));
    }
}

進(jìn)行測(cè)試驗(yàn)證,一切OK!

到此,關(guān)于“如何解決springcloud gateway連接?;顔栴}”的學(xué)習(xí)就結(jié)束了,希望能夠解決大家的疑惑。理論與實(shí)踐的搭配能更好的幫助大家學(xué)習(xí),快去試試吧!若想繼續(xù)學(xué)習(xí)更多相關(guān)知識(shí),請(qǐng)繼續(xù)關(guān)注億速云網(wǎng)站,小編會(huì)繼續(xù)努力為大家?guī)砀鄬?shí)用的文章!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI