最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

redis在spring boot中異常退出的問題解決方案

 更新時間:2025年05月22日 11:25:31   作者:簡誠  
這篇文章主要介紹了redis在spring boot中異常退出的問題解決方案,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友參考下吧

問題:

Exception in thread "rtsp-consumer-3" org.springframework.data.redis.RedisConnectionFailureException: Unable to connect to Redis; nested exception is io.lettuce.core.RedisConnectionException: Unable to connect to localhost:6379
    at org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory$ExceptionTranslatingConnectionProvider.translateException(LettuceConnectionFactory.java:1689)
    at org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory$ExceptionTranslatingConnectionProvider.getConnection(LettuceConnectionFactory.java:1597)
    at org.springframework.data.redis.connection.lettuce.LettuceConnection.doGetAsyncDedicatedConnection(LettuceConnection.java:1006)
    at org.springframework.data.redis.connection.lettuce.LettuceConnection.getOrCreateDedicatedConnection(LettuceConnection.java:1069)
    at org.springframework.data.redis.connection.lettuce.LettuceConnection.getAsyncDedicatedConnection(LettuceConnection.java:990)
    at org.springframework.data.redis.connection.lettuce.LettuceStreamCommands.getAsyncDedicatedConnection(LettuceStreamCommands.java:395)
    at org.springframework.data.redis.connection.lettuce.LettuceStreamCommands.xReadGroup(LettuceStreamCommands.java:346)
    at org.springframework.data.redis.connection.DefaultedRedisConnection.xReadGroup(DefaultedRedisConnection.java:592)
    at org.springframework.data.redis.core.DefaultStreamOperations$4.inRedis(DefaultStreamOperations.java:310)
    at org.springframework.data.redis.core.DefaultStreamOperations$RecordDeserializingRedisCallback.doInRedis(DefaultStreamOperations.java:387)
    at org.springframework.data.redis.core.DefaultStreamOperations$RecordDeserializingRedisCallback.doInRedis(DefaultStreamOperations.java:382)
    at org.springframework.data.redis.core.RedisTemplate.execute(RedisTemplate.java:222)
    at org.springframework.data.redis.core.RedisTemplate.execute(RedisTemplate.java:189)
    at org.springframework.data.redis.core.AbstractOperations.execute(AbstractOperations.java:96)
    at org.springframework.data.redis.core.DefaultStreamOperations.read(DefaultStreamOperations.java:305)
    at com.ruoyi.vedioFrame.utils.RedisStreamOperations.readGroup(RedisStreamOperations.java:70)
    at com.ruoyi.vedioFrame.service.impl.StreamConsumerService.consumeFrames(StreamConsumerService.java:200)
    at com.ruoyi.vedioFrame.service.impl.StreamConsumerService.lambda$null$0(StreamConsumerService.java:108)
    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)
Caused by: io.lettuce.core.RedisConnectionException: Unable to connect to localhost:6379
    at io.lettuce.core.RedisConnectionException.create(RedisConnectionException.java:78)
    at io.lettuce.core.RedisConnectionException.create(RedisConnectionException.java:56)
    at io.lettuce.core.AbstractRedisClient.getConnection(AbstractRedisClient.java:330)
    at io.lettuce.core.RedisClient.connect(RedisClient.java:216)
    at org.springframework.data.redis.connection.lettuce.StandaloneConnectionProvider.lambda$getConnection$1(StandaloneConnectionProvider.java:115)
    at java.util.Optional.orElseGet(Optional.java:267)
    at org.springframework.data.redis.connection.lettuce.StandaloneConnectionProvider.getConnection(StandaloneConnectionProvider.java:115)
    at org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory$ExceptionTranslatingConnectionProvider.getConnection(LettuceConnectionFactory.java:1595)
    ... 19 more
Caused by: io.netty.channel.AbstractChannel$AnnotatedSocketException: Address already in use: no further information: localhost/127.0.0.1:6379
Caused by: java.net.BindException: Address already in use: no further information
    at sun.nio.ch.SocketChannelImpl.checkConnect(Native Method)
    at sun.nio.ch.SocketChannelImpl.finishConnect(SocketChannelImpl.java:717)
    at io.netty.channel.socket.nio.NioSocketChannel.doFinishConnect(NioSocketChannel.java:337)
    at io.netty.channel.nio.AbstractNioChannel$AbstractNioUnsafe.finishConnect(AbstractNioChannel.java:334)
    at io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:776)
    at io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:724)
    at io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:650)
    at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:562)
    at io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:997)
    at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74)
    at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
    at java.lang.Thread.run(Thread.java:745) 

解決

根據(jù)代碼和現(xiàn)象分析,根本原因可能是處理時間過長導(dǎo)致Redis消息積壓觸發(fā)超時異常。以下是詳細(xì)分析和解決方案:

?? 問題根源

同步阻塞模型

  • frameProcessor.process()調(diào)用外部接口時存在網(wǎng)絡(luò)延遲,導(dǎo)致消息處理時間超過Redis的BLOCK超時(2000ms)。
  • 未及時ACK會導(dǎo)致消息重新進(jìn)入Pending狀態(tài),積累后觸發(fā)READONLY錯誤或消費(fèi)者組失效。

Thread.sleep(1000)加劇阻塞

  • 即使沒有消息,每次循環(huán)強(qiáng)制休眠1秒,降低消費(fèi)吞吐量,無法及時處理積壓消息。

異常未妥善處理

  • 注釋掉的try-catch導(dǎo)致處理過程中的異常(如網(wǎng)絡(luò)超時)未被捕獲,中斷循環(huán)后Redis連接未正確恢復(fù)。

??? 解決方案

1. 異步處理 + 提前ACK(關(guān)鍵步驟)

將消息處理與ACK分離,處理前立即ACK避免消息積壓,異步處理業(yè)務(wù)邏輯:

ExecutorService executor = Executors.newCachedThreadPool(); // 全局線程池
while (!Thread.currentThread().isInterrupted()) {
    List<MapRecord<String, Object, Object>> records = redisStreamOperations.readGroup(...);
    if (records != null) {
        for (MapRecord<String, Object, Object> record : records) {
            // 提前ACK釋放消息
            redisStreamOperations.ack(streamKey, groupName, record.getId());
            // 提交異步任務(wù)處理業(yè)務(wù)
            executor.submit(() -> {
                try {
                    String strdata = (String) record.getValue().get("frameData");
                    byte[] frameData = Base64.getDecoder().decode(strdata);
                    BufferedImage image = ImageIO.read(new ByteArrayInputStream(frameData));
                    frameProcessor.process(image, streamId, commonQueues, regions);
                } catch (Exception e) {
                    log.error("處理幀失敗: {}", e.getMessage());
                }
            });
        }
    }
    // 移除Thread.sleep(1000) 提升吞吐量
}

2. 調(diào)整Redis消費(fèi)者組參數(shù)

增加XGROUP CREATE時的BLOCKIDLE超時:

XGROUP CREATE stream:1234 mygroup $ MKSTREAM BLOCK 5000 IDLE 30000

3. 優(yōu)化Redis連接配置

確保RedisTemplate的讀寫超時大于業(yè)務(wù)處理最長時間:

@Bean
public RedisTemplate<String, Object> redisTemplate() {
    RedisTemplate<String, Object> template = new RedisTemplate<>();
    template.setConnectionFactory(redisConnectionFactory());
    // 設(shè)置超時時間(示例:10秒)
    RedisConnectionFactory factory = template.getConnectionFactory();
    if (factory instanceof LettuceConnectionFactory) {
        ((LettuceConnectionFactory) factory).setTimeout(Duration.ofSeconds(10));
    }
    return template;
}

4. 監(jiān)控Pending隊列

定期檢查Pending消息數(shù)量,異常時重置消費(fèi)者:

// 在循環(huán)中添加檢查
Long pending = redisTemplate.opsForStream().pending(streamKey, groupName);
if (pending > 1000) { // 閾值根據(jù)業(yè)務(wù)設(shè)定
    redisTemplate.opsForStream().destroyGroup(streamKey, groupName);
    redisTemplate.opsForStream().createGroup(streamKey, groupName);
}

5. 添加熔斷機(jī)制

當(dāng)外部接口連續(xù)失敗時,暫停消費(fèi)避免雪崩:

// 全局熔斷器
CircuitBreaker breaker = CircuitBreaker.ofDefaults("frameProcessor");
// 處理邏輯改為
breaker.executeSupplier(() -> {
    frameProcessor.process(...);
    return null;
});

?? 驗證方案

日志監(jiān)控

// 在異步任務(wù)中添加耗時日志
long start = System.currentTimeMillis();
frameProcessor.process(...);
log.info("處理耗時: {}ms", System.currentTimeMillis() - start);

確保單次處理時間低于Redis超時閾值。

Redis CLI檢查

XINFO GROUPS stream:1234  # 查看消費(fèi)者組狀態(tài)
XPENDING stream:1234 mygroup # 查看Pending消息

?? 關(guān)鍵優(yōu)化點(diǎn)總結(jié)

措施作用
異步處理 + 提前ACK徹底解耦處理耗時與Redis消費(fèi)
增大Redis超時參數(shù)避免服務(wù)端主動斷開連接
線程池隔離防止外部接口故障拖垮整個消費(fèi)服務(wù)
熔斷機(jī)制快速失敗,保護(hù)系統(tǒng)穩(wěn)定性

通過以上改造,即使frameProcessor.process()調(diào)用外部平臺接口,也能通過異步化和資源隔離保證Redis消費(fèi)鏈路的穩(wěn)定性。最終只需確保線程池和熔斷參數(shù)適配業(yè)務(wù)負(fù)載即可。

代碼具體添加位置:

以下是針對 優(yōu)化Redis連接配置、監(jiān)控Pending隊列添加熔斷機(jī)制 的代碼具體添加位置的詳細(xì)說明:

一、優(yōu)化Redis連接配置

位置:Redis配置類
在Spring Boot的Redis配置類中調(diào)整超時參數(shù)(通常為RedisConfig.java):

@Configuration
public class RedisConfig {
    @Bean
    public RedisTemplate<String, Object> redisTemplate(LettuceConnectionFactory lettuceConnectionFactory) {
        // 設(shè)置連接超時和讀寫超時(關(guān)鍵參數(shù))
        lettuceConnectionFactory.setTimeout(Duration.ofSeconds(10));  // 命令超時時間
        lettuceConnectionFactory.setShareNativeConnection(false);    // 禁用共享連接,避免阻塞
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(lettuceConnectionFactory);
        template.setKeySerializer(new StringRedisSerializer());
        template.setValueSerializer(new GenericJackson2JsonRedisSerializer());
        return template;
    }
}

關(guān)鍵參數(shù)說明

  • setTimeout(10秒):確保超時時間大于frameProcessor.process()的最長處理時間
  • setShareNativeConnection(false):避免多個線程共享同一個連接導(dǎo)致阻塞。

二、監(jiān)控Pending隊列

位置:consumeFrames方法內(nèi)的循環(huán)中
在消費(fèi)消息的主循環(huán)中定期檢查Pending隊列:

private void consumeFrames(String streamId, String groupName, String consumerName,
                           CommonQueues commonQueues, String regions) throws InterruptedException, IOException {
    // ... 其他初始化代碼 ...
    int checkPendingInterval = 10; // 每處理10次循環(huán)檢查一次Pending隊列
    int loopCount = 0;
    while (!Thread.currentThread().isInterrupted()) {
        // ... 原有代碼讀取消息 ...
        // 監(jiān)控Pending隊列的邏輯(添加位置)
        loopCount++;
        if (loopCount % checkPendingInterval == 0) {
            String streamKey = "stream:" + streamId;
            PendingMessages pending = redisStreamOperations.pending(streamKey, groupName);
            if (pending != null && pending.getTotalPendingMessages() > 1000) { // 閾值根據(jù)業(yè)務(wù)調(diào)整
                log.warn("檢測到Pending消息積壓 {} 條,重置消費(fèi)者組", pending.getTotalPendingMessages());
                redisStreamOperations.destroyGroup(streamKey, groupName);
                redisStreamOperations.createGroup(StreamKey.of(streamKey), groupName);
            }
        }
        // ... 后續(xù)處理代碼 ...
    }
}

說明

  • 通過redisStreamOperations.pending()獲取當(dāng)前Pending消息數(shù)。
  • 當(dāng)Pending消息超過閾值時,強(qiáng)制銷毀并重建消費(fèi)者組,避免消息卡死。

三、添加熔斷機(jī)制

位置:處理消息的業(yè)務(wù)邏輯外層
使用Resilience4j熔斷器包裹frameProcessor.process()調(diào)用:

1. 熔斷器配置類

@Configuration
public class CircuitBreakerConfig {
    @Bean
    public CircuitBreaker frameProcessorCircuitBreaker() {
        CircuitBreakerConfig config = CircuitBreakerConfig.custom()
            .failureRateThreshold(50)          // 失敗率閾值50%
            .slidingWindowType(SlidingWindowType.COUNT_BASED)
            .slidingWindowSize(10)             // 基于最近10次調(diào)用統(tǒng)計
            .minimumNumberOfCalls(5)           // 最少5次調(diào)用后開始計算
            .waitDurationInOpenState(Duration.ofSeconds(30)) // 熔斷后30秒進(jìn)入半開狀態(tài)
            .build();
        return CircuitBreakerRegistry.of(config).circuitBreaker("frameProcessor");
    }
}

2. 在消費(fèi)代碼中使用熔斷器

public class YourConsumerClass {
    @Autowired
    private CircuitBreaker frameProcessorCircuitBreaker; // 注入熔斷器
    private void consumeFrames(...) {
        // ... 原有代碼 ...
        for (MapRecord<String, Object, Object> record : records) {
            redisStreamOperations.ack(...); // 提前ACK
            // 使用熔斷器保護(hù)處理邏輯(添加位置)
            Try.runRunnable(() -> frameProcessorCircuitBreaker.executeRunnable(() -> {
                String strdata = (String) record.getValue().get("frameData");
                byte[] frameData = Base64.getDecoder().decode(strdata);
                BufferedImage image = ImageIO.read(new ByteArrayInputStream(frameData));
                frameProcessor.process(image, streamId, commonQueues, regions);
            })).onFailure(e -> log.error("處理失敗且熔斷: {}", e.getMessage()));
        }
        // ... 后續(xù)代碼 ...
    }
}

熔斷邏輯說明

  • 當(dāng)frameProcessor.process()連續(xù)失敗觸發(fā)閾值時,熔斷器會暫時阻止后續(xù)調(diào)用,避免雪崩效應(yīng)。
  • 熔斷期間直接跳過處理,但仍會ACK消息(根據(jù)業(yè)務(wù)需求選擇是否重試)。

四、代碼集成位置總結(jié)

優(yōu)化措施代碼位置關(guān)鍵注解
Redis連接配置Redis配置類(如RedisConfig.java調(diào)整超時時間和連接池參數(shù)
Pending隊列監(jiān)控consumeFrames方法的主循環(huán)內(nèi)定期檢查+自動重置消費(fèi)者組
熔斷機(jī)制業(yè)務(wù)處理代碼外層(包裹frameProcessor.process依賴熔斷器庫(如Resilience4j)

五、參數(shù)調(diào)整建議

Redis超時

  • lettuceConnectionFactory.setTimeout應(yīng)大于frameProcessor.process()的最大處理時間 + 網(wǎng)絡(luò)抖動余量(如設(shè)置為實際最大處理時間的2倍)。

Pending隊列閾值

  • 如果每秒處理100條消息,閾值可設(shè)置為1000(相當(dāng)于10秒積壓量)。

熔斷器參數(shù)

  • failureRateThreshold:根據(jù)外部接口的穩(wěn)定性調(diào)整(如頻繁超時可設(shè)為70%)。
  • waitDurationInOpenState:根據(jù)外部服務(wù)恢復(fù)時間調(diào)整(如30秒到5分鐘)。

通過以上改造,即使frameProcessor.process()調(diào)用外部平臺接口,也能通過資源隔離、快速失敗和自動恢復(fù)機(jī)制保障Redis消費(fèi)鏈路的穩(wěn)定性。

到此這篇關(guān)于redis在spring boot中異常退出的文章就介紹到這了,更多相關(guān)redis spring boot異常退出內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 銀河麒麟V10sp1服務(wù)器系統(tǒng)安裝redis不能使用的快速解決辦法

    銀河麒麟V10sp1服務(wù)器系統(tǒng)安裝redis不能使用的快速解決辦法

    這篇文章主要介紹了銀河麒麟V10sp1服務(wù)器系統(tǒng)安裝redis不能使用的快速解決辦法,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2021-01-01
  • 基于Redis實現(xiàn)登錄功能思路詳解(手機(jī)號+驗證碼)

    基于Redis實現(xiàn)登錄功能思路詳解(手機(jī)號+驗證碼)

    本文介紹了使用手機(jī)號和驗證碼登錄的方式,驗證碼通過控制臺輸出,重點(diǎn)解釋了UserServiceImpl實現(xiàn)類的結(jié)構(gòu),包括sendCode、login和createUserWithPhone方法,還介紹了攔截器框架,包括preHandle和攔截器鏈的配置,感興趣的朋友跟隨小編一起看看吧
    2026-01-01
  • 深入探究RedisJSON模塊的工作原理以及使用操作

    深入探究RedisJSON模塊的工作原理以及使用操作

    Redis推出了RedisJSON模塊,它允許開發(fā)者在Redis數(shù)據(jù)庫中直接存儲、查詢和處理JSON數(shù)據(jù),本文將詳細(xì)介紹RedisJSON的工作原理、關(guān)鍵操作、性能優(yōu)勢以及使用場景,需要的朋友可以參考下
    2024-05-05
  • redis實現(xiàn)排行榜功能

    redis實現(xiàn)排行榜功能

    排行榜在很多地方都能使用到,redis的zset可以很方便地用來實現(xiàn)排行榜功能,本文就來簡單的介紹一下如何使用,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2021-05-05
  • redis中的配置以及密碼設(shè)置方式

    redis中的配置以及密碼設(shè)置方式

    這篇文章主要介紹了redis中的配置以及密碼設(shè)置方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-12-12
  • redis加鎖的三種方式小結(jié)

    redis加鎖的三種方式小結(jié)

    本文主要介紹了redis加鎖的三種方式小結(jié),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-01-01
  • 詳談redis跟數(shù)據(jù)庫的數(shù)據(jù)同步問題

    詳談redis跟數(shù)據(jù)庫的數(shù)據(jù)同步問題

    文章討論了在Redis和數(shù)據(jù)庫數(shù)據(jù)一致性問題上的解決方案,主要比較了先更新Redis緩存再更新數(shù)據(jù)庫和先更新數(shù)據(jù)庫再更新Redis緩存兩種方案,文章指出,刪除Redis緩存后再更新數(shù)據(jù)庫的方案更優(yōu),因為它可以避免數(shù)據(jù)不一致的問題,但可能產(chǎn)生高并發(fā)問題
    2025-01-01
  • 解決redis服務(wù)啟動失敗的問題

    解決redis服務(wù)啟動失敗的問題

    今天小編就為大家分享一篇解決redis服務(wù)啟動失敗的問題,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2018-05-05
  • Redis緩存lettuce更換為Jedis的實現(xiàn)步驟

    Redis緩存lettuce更換為Jedis的實現(xiàn)步驟

    在springboot中引入spring-boot-starter-data-redis依賴時,默認(rèn)使用的是lettuce,如果不想使用lettuce而是使用Jedis連接池,本文主要介紹了Redis緩存lettuce更換為Jedis的實現(xiàn)步驟,感興趣的可以了解一下
    2024-08-08
  • Redis服務(wù)器優(yōu)化方式

    Redis服務(wù)器優(yōu)化方式

    文章分享了常見的Redis服務(wù)器優(yōu)化技巧和策略,主要包括內(nèi)存管理、持久化配置、連接配置和網(wǎng)絡(luò)優(yōu)化四個方面,內(nèi)存管理主要是設(shè)置maxmemory參數(shù)和選擇合適的內(nèi)存淘汰策略,持久化配置包括RDB持久化和AOF持久化
    2024-09-09

最新評論

盐源县| 澄江县| 德庆县| 松溪县| 梨树县| 衡水市| 乐平市| 隆子县| 大邑县| 上虞市| 白山市| 将乐县| 桂平市| 资源县| 万安县| 河东区| 益阳市| 杭锦旗| 边坝县| 阳高县| 安塞县| 彰武县| 渑池县| 施甸县| 绥阳县| 太和县| 宁津县| 三原县| 神农架林区| 仙桃市| 阆中市| 中西区| 吴忠市| 翁牛特旗| 五指山市| 安塞县| 康乐县| 鲁甸县| 谷城县| 景德镇市| 胶州市|