<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          解決springboot中鏈接redis會(huì)timeout的問題

          共 3681字,需瀏覽 8分鐘

           ·

          2022-01-04 22:12

          ????之前寫項(xiàng)目遇見一個(gè)問題,就是我的項(xiàng)目上線后,總是經(jīng)常會(huì)報(bào)錯(cuò)timeout,我一看是redis鏈接timeout,首先我端口打開了,為啥還報(bào)錯(cuò)我就很納悶,只得先重啟一下防火墻。重啟過后的確可以正常訪問了,但是一段時(shí)間后依然還是會(huì)出現(xiàn)timeout的錯(cuò)誤。只能在重啟,這個(gè)問題始終沒有得到根本性的解決!

          ? ??

          ????因?yàn)槲沂褂玫氖莝pringboot,所以一開始并沒有往代碼方向去考慮,直接懷疑服務(wù)器有問題,(比較springboot目前用的人比較多也沒出啥問題也就比較相信它,同時(shí)我服務(wù)器又是最便宜的哪一款,就懷疑到服務(wù)器頭上了,便宜沒好貨哈哈),但還是沒有發(fā)現(xiàn)有啥問題,最后只能去看代碼...


          ????后面我用jedis重新寫了個(gè)小test,發(fā)現(xiàn)正常鏈接,但是springboot的redisTemplate就是有問題,那原因清楚了,可能真的是springboot的問題~

          ????我看了springboot內(nèi)嵌實(shí)現(xiàn)鏈接redis的源碼,發(fā)現(xiàn)springboot內(nèi)嵌了Luttcure的技術(shù),Luttuce和jedis pool都是一種池化技術(shù),就和數(shù)據(jù)庫的Durid、HkariaCP等這些鏈接池一樣。

          而Luttuce有個(gè)問題就是Luttuce不會(huì)自動(dòng)刷新redis的拓?fù)浣Y(jié)構(gòu)的,所以會(huì)造成一段時(shí)間過后導(dǎo)致鏈接超時(shí),畢竟鏈接池也是有最大等待時(shí)間的。

          拓?fù)浣Y(jié)構(gòu)

          拓?fù)浣Y(jié)構(gòu)也稱為樹狀主從架構(gòu),咱們平常熟悉的主從架構(gòu)便是通過讀寫分離來降低住服務(wù)器的壓力,但是一般master節(jié)點(diǎn)進(jìn)行寫操作,slave節(jié)點(diǎn)進(jìn)行讀操作,在讀多寫少的場景,雖然降低了主服務(wù)器的壓力,比如執(zhí)行keys sort等操作保證了master的穩(wěn)定性,但如果寫并發(fā)某個(gè)時(shí)刻比較高時(shí),主節(jié)點(diǎn)需要向掛載的多個(gè)slave節(jié)點(diǎn)發(fā)送寫命令,這就又可能導(dǎo)致master的負(fù)載過高而影響服務(wù)的穩(wěn)定性。

          因此我們就產(chǎn)生了樹狀主從架構(gòu),slave節(jié)點(diǎn)可以復(fù)制主節(jié)點(diǎn)數(shù)據(jù),也可以作為其他slave節(jié)點(diǎn)的master節(jié)點(diǎn)繼續(xù)向下復(fù)制。解決了主從的不足

          ? ? ? ? ?master --> slave1 --> slave2 --> slave3

          這降低了主節(jié)點(diǎn)的負(fù)載,所以如果master需要掛載多個(gè)slave時(shí),而我們又想保證master的穩(wěn)定性,就可以采用這種拓?fù)浣Y(jié)構(gòu)。


          ????而springboot內(nèi)嵌的Luttuce鏈接池,默認(rèn)不會(huì)改變r(jià)edis集群的拓?fù)浣Y(jié)構(gòu),當(dāng)我們在超時(shí)時(shí)間觸發(fā)后,springboot與redis的鏈接就斷開了,而下次訪問時(shí)還會(huì)訪問這個(gè)已經(jīng)超時(shí)的拓?fù)涔?jié)點(diǎn),導(dǎo)致訪問不成功。這就是springboot操作redis偶爾會(huì)timeout的問題原因所在。

          ????解決方案呢有很多,大致就是及時(shí)更新這個(gè)拓?fù)涔?jié)點(diǎn)就好了,目前可以通過在Luttuce留下的鉤子函數(shù)中去自定義刷新的拓?fù)溥壿嫞热鐔⒁粋€(gè)cron定時(shí)任務(wù)去做一個(gè)心跳檢測去驗(yàn)證鏈接是否異常來完成。


          第一種方案:

          b2a46c1ef730a054e4980f34423aaa67.webp

          springboot中預(yù)留的鉤子函數(shù)中執(zhí)行的customize的方法。

          在springboot預(yù)留的接口中自定義類實(shí)現(xiàn)LettuceClientConfigurationBuilderCustomizer接口,實(shí)現(xiàn)customize方法,其中開啟自定義刷新和定時(shí)刷新,而springboot實(shí)現(xiàn)的鉤子函數(shù)中會(huì)自動(dòng)去執(zhí)行customize方法

          /** * 通過ClusterTopologyRefreshOptions 開啟定時(shí)刷新和自適應(yīng)刷新 * @author azh */@Componentpublic class MyLuttuceCronTask implements LettuceClientConfigurationBuilderCustomizer {

          @Override public void customize(org.springframework.data.redis.connection.lettuce.LettuceClientConfiguration.LettuceClientConfigurationBuilder clientConfigurationBuilder) { // ClusterTopologyRefreshOptions 用于開啟自適應(yīng)刷新和定時(shí)刷新 防止timeout 錯(cuò)誤 ClusterTopologyRefreshOptions topologyRefreshOptions = ClusterTopologyRefreshOptions.builder() // 開始自適應(yīng)刷新 .enableAdaptiveRefreshTrigger(ClusterTopologyRefreshOptions.RefreshTrigger.MOVED_REDIRECT, ClusterTopologyRefreshOptions.RefreshTrigger.PERSISTENT_RECONNECTS) .enableAllAdaptiveRefreshTriggers() .adaptiveRefreshTriggersTimeout(Duration.ofSeconds(10)) //開啟定時(shí)刷新 時(shí)間間隔自己定義 .enablePeriodicRefresh(Duration.ofSeconds(15)) .build(); clientConfigurationBuilder.clientOptions(ClusterClientOptions.builder().topologyRefreshOptions(topologyRefreshOptions).build());
          }}

          582427c8b484c1925156d4d9f1a1a696.webp


          第二種方案:

          ??? Lettuce提供了鏈接校驗(yàn)的方法,只是默認(rèn)沒有開啟,我們可以開啟它,然后通過一個(gè)定時(shí)任務(wù)來解決這個(gè)timeout的問題。

          /** * 開啟獲取鏈接的校驗(yàn) * @author azh */public class GetLettuceConnection implements InitializingBean {
          @Autowired private RedisConnectionFactory redisConnectionFactory;
          @Override public void afterPropertiesSet() throws Exception { if (redisConnectionFactory instanceof LettuceConnectionFactory) { LettuceConnectionFactory factory = (LettuceConnectionFactory) redisConnectionFactory; factory.setValidateConnection(true); } }}
          /** * 每隔2s校驗(yàn)lettuce是否異常,解決lettuce因?yàn)椴桓聄edis拓?fù)浣Y(jié)構(gòu)導(dǎo)致netty無法及時(shí)監(jiān)控到導(dǎo)致timeout的問題 * @author azh */@Componentpublic class RedisCronTask {
          @Autowired private RedisConnectionFactory redisConnectionFactory;
          // 每2s執(zhí)行一下 @Scheduled(cron = "0/2 * * * * ?") public void task() { if (redisConnectionFactory instanceof LettuceConnectionFactory) { LettuceConnectionFactory factory = (LettuceConnectionFactory) redisConnectionFactory; factory.validateConnection(); } }}

          開啟獲取鏈接的校驗(yàn)

          78ba3dfc1237ba5ecd4b52b5a28f9190.webp

          定時(shí)任務(wù)去校驗(yàn)luttuce是否異常

          6fc232f152ce64999c77a2bb887fbfb9.webp



          ???? 最后其實(shí)還有個(gè)比較簡單但頭疼的解決方案,那就是每隔一段時(shí)間都往redis的某個(gè)數(shù)據(jù)庫中去set一個(gè)值,不過這種方案會(huì)耗費(fèi)網(wǎng)絡(luò)資源,但耗費(fèi)也不是太大,算是一種中肯的解決辦法吧。



          瀏覽 323
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評論
          圖片
          表情
          推薦
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  小黄书在线观看 | 久久免费看A片 | 午夜激情操一操 | 国产黄色电影在线 | 天天上天天干天天日 |