Lettuce客户端 Redis异常 Can't assign requested address

Redis异常 Can't assign requested address

记一次很久之前的Redis连接异常。当时使用的是Redis Cluster集群,微服务的商品服务需要保存商品到Redis中,短时间内会连续执行HSET和HGET操作,程序启动跑了几分钟,就抛了一些错误异常,让我措手不及。异常信息如下:

Caused by: io.lettuce.core.RedisConnectionException: Unable to connect to 127.0.0.1<7994
at io.lettuce.core.RedisConnectionException.create RedisConnectionException.jav a:56 
at io.lettuce.core.cluster.PooledClusterConnectionProvider.lambda$getConnectio nAsync$6 PooledClusterConnectionProvider.java:322 
at java.util.concurrent.CompletableFuture.uniHandle CompletableFuture.java:822 
at java.util.concurrent.CompletableFuture$UniHandle.tryFire CompletableFuture.ja va:797 
at java.util.concurrent.CompletableFuture.postComplete CompletableFuture.java: 474 
at java.util.concurrent.CompletableFuture.completeExceptionally CompletableFutu re.java:1977 
at io.lettuce.core.AbstractRedisClient.lambda$initializeChannelAsync$1 AbstractR edisClient.java:275 
at io.netty.util.concurrent.DefaultPromise.notifyListener0 DefaultPromise.java:511 
at io.netty.util.concurrent.DefaultPromise.notifyListenersNow DefaultPromise.java: 485 
at io.netty.util.concurrent.DefaultPromise.notifyListeners DefaultPromise.java: 424 
at io.netty.util.concurrent.DefaultPromise.tryFailure DefaultPromise.java: 121 
at io.netty.channel.nio.AbstractNioChannel$AbstractNioUnsafe.connect Abstract NioChannel.java:290 
at io.netty.channel.DefaultChannelPipeline$HeadContext.connect DefaultChannel Pipeline.java:1366 
at io.netty.channel.AbstractChannelHandlerContext.invokeConnect AbstractChan
nelHandlerContext.java:545  at
io.netty.channel.AbstractChannelHandlerContext.connect AbstractChannelHan dlerContext.java:530 
at io.netty.channel.ChannelDuplexHandler.connect ChannelDuplexHandler.java: 50 
at io.netty.channel.AbstractChannelHandlerContext.invokeConnect AbstractChan nelHandlerContext.java:545 
at io.netty.channel.AbstractChannelHandlerContext.connect AbstractChannelHan dlerContext.java:530 
at io.netty.channel.ChannelOutboundHandlerAdapter.connect ChannelOutboundH andlerAdapter.java:47 
at io.netty.channel.AbstractChannelHandlerContext.invokeConnect AbstractChan nelHandlerContext.java:545 
at io.netty.channel.AbstractChannelHandlerContext.connect AbstractChannelHan dlerContext.java:530 
at io.netty.channel.ChannelDuplexHandler.connect ChannelDuplexHandler.java: 50 
at io.netty.channel.AbstractChannelHandlerContext.invokeConnect AbstractChan nelHandlerContext.java:545 
at io.netty.channel.AbstractChannelHandlerContext.connect AbstractChannelHan dlerContext.java:530 
at io.netty.channel.AbstractChannelHandlerContext.connect AbstractChannelHan dlerContext.java:512 
at io.netty.channel.DefaultChannelPipeline.connect DefaultChannelPipeline.java: 1024 
at io.netty.channel.AbstractChannel.connect AbstractChannel.java:259  at io.netty.bootstrap.Bootstrap$3.run Bootstrap.java:252 
at
io.netty.util.concurrent.AbstractEventExecutor.safeExecute AbstractEventExecu tor.java:163 
at io.netty.util.concurrent.SingleThreadEventExecutor.runAllTasks SingleThreadEv entExecutor.java:404 
at io.netty.channel.nio.NioEventLoop.run NioEventLoop.java:463 
at io.netty.util.concurrent.SingleThreadEventExecutor$5.run SingleThreadEventEx ecutor.java:886 
at io.netty.util.concurrent.FastThreadLocalRunnable.run FastThreadLocalRunnable .java:30 
at java.lang.Thread.run Thread.java:748 
Caused by: java.util.concurrent.CompletionException: io.netty.channel.AbstractChannel$AnnotatedSocketException: Can't assign requested address: /127.0.0.1<7994
at java.util.concurrent.CompletableFuture.encodeThrowable CompletableFuture.ja va:292 
at java.util.concurrent.CompletableFuture.completeThrowable CompletableFuture. java:308 
at java.util.concurrent.CompletableFuture.uniApply CompletableFuture.java:593 
at java.util.concurrent.CompletableFuture$UniApply.tryFire CompletableFuture.jav a:577 
... 30 common frames omitted
Caused by: io.netty.channel.AbstractChannel$AnnotatedSocketException: Can't assign requested address: /127.0.0.1<7994
at sun.nio.ch.Net.connect0 Native Method 
at sun.nio.ch.Net.connect Net.java:454 
at sun.nio.ch.Net.connect Net.java:446 
at sun.nio.ch.SocketChannelImpl.connect SocketChannelImpl.java:648  at io.netty.util.internal.SocketUtils$3.run SocketUtils.java:83 
at io.netty.util.internal.SocketUtils$3.run SocketUtils.java:80  at java.security.AccessController.doPrivileged Native Method  at io.netty.util.internal.SocketUtils.connect SocketUtils.java:80  at
io.netty.channel.socket.nio.NioSocketChannel.doConnect NioSocketChannel.ja va:310 
at io.netty.channel.nio.AbstractNioChannel$AbstractNioUnsafe.connect Abstract NioChannel.java:254 
... 22 common frames omitted
Caused by: java.net.BindException: Can't assign requested address
... 32 common frames omitted

原因是 按单条商品记录保存到Redis,hset(key, field, value),而且是两个线程同时操作不同商品插入Redis与从Redis获取商品,这个操作是单条命令连续执行,而不是批量保存和批量获取,另外在构建Lettuce客户端是没有使用线程连接池,最终导致Lettuce Redis客户端频繁连接Redis服务器,由于每次连接短时间内结束,导致很多TCP TIME_WAIT,这样旧连接还没结束,连接数有限,新连接没办法绑定端口,即Cannot assign requested address

netstat -nat | grep 127.0.0.1:7994 会看到连接127.0.0.1:7994的状态,你会发现很多TIME_WAIT

为什么单个客户端多次操作,会出现这么多连接了呢?

这是lettuce redis没有完善的地方:传送门

要到Spring Data Redis 2.1 发布才行。

其中的spring boot redis maven依赖如下:

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
        <!-- redis lettuce 客户端需要的依赖 -->
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-pool2</artifactId>
            <version>2.4.2</version>
        </dependency>

解决方案:

  1. 把单条执行改批量执行,如HSET改为HMSET,HGET改HMGET

  2. 需要加redis 线程连接池。幸好spring boot redis 已经帮我们构建好了一个LettuceConnectionFactory连接工厂Bean,该工厂类Bean已经实现了连接池。

    直接依赖注入就行。

    附上实现代码:

    package xxxx.xxxx.xxxx.xxxx;
    
    import com.fasterxml.jackson.databind.ObjectMapper;
    import com.fasterxml.jackson.databind.ObjectMapper.DefaultTyping;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.boot.context.properties.ConfigurationProperties;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.data.redis.connection.RedisClusterConfiguration;
    import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
    import org.springframework.data.redis.core.RedisTemplate;
    import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
    import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
    import org.springframework.data.redis.serializer.RedisSerializer;
    import org.springframework.data.redis.serializer.StringRedisSerializer;
    
    import java.util.List;
    import java.util.Objects;
    
    import lombok.Data;
    
    @Configuration
    @Data
    public class RedisClusterConfig {
    
      private LettuceConnectionFactory lettuceConnectionFactory;
    
      @Autowired
      public RedisClusterConfig(LettuceConnectionFactory lettuceConnectionFactory) {
        // 依赖注入LettuceConnectionFactory工厂类
        // LettuceConnectionConfiguration自动注入了含lettuce Pool连接池和集群Cluster的LettuceConnectionFactory连接工厂类
        this.lettuceConnectionFactory = lettuceConnectionFactory;
      }
    
    
      @Bean
      RedisTemplate<String, Object> lettuceRedisTemplate() {
        // 设置序列化
        Jackson2JsonRedisSerializer<String> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<String>(String.class);
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.enableDefaultTyping(DefaultTyping.NON_FINAL);
        jackson2JsonRedisSerializer.setObjectMapper(objectMapper);
    
        // 配置redisTemplate
        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<String, Object>();
        redisTemplate.setConnectionFactory(lettuceConnectionFactory);
        RedisSerializer<?> stringSerializer = new StringRedisSerializer();
        // key 序列化
        redisTemplate.setKeySerializer(stringSerializer);
        // value 序列化
        redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
    
        // Hash Key序列化
        redisTemplate.setHashKeySerializer(stringSerializer);
        // Hash Value 序列化
        redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);
        // 初始化赋值
        redisTemplate.afterPropertiesSet();
        return redisTemplate;
      }
    }
    

    ​参考链接
    https://blog.csdn.net/hguisu/article/details/10241519

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,313评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,369评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,916评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,333评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,425评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,481评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,491评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,268评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,719评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,004评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,179评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,832评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,510评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,153评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,402评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,045评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,071评论 2 352

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,644评论 18 139
  • Spring Boot 参考指南 介绍 转载自:https://www.gitbook.com/book/qbgb...
    毛宇鹏阅读 46,796评论 6 342
  • 缓存是最直接有效提升系统性能的手段之一。个人认为用好用对缓存是优秀程序员的必备基本素质。 本文结合实际开发经验,从...
    Java小生阅读 805评论 1 3
  • Redis是key-value存储的非关系型数据库。Spring Data Redis包含了多个模板实现,用来完成...
    fad2aa506f5e阅读 517评论 0 0
  • 节日快乐呀! 谁还不是三岁的小宝贝了? 刚给可欣发了一长段煽情的话,希望她可以留任,现在,心情有点down,想睡觉...
    大海说天真蓝啊阅读 166评论 0 0