Redis发布订阅使用Jedis客户端后收不到订阅消息

项目场景:

BUG场景:使用Redis做轻量级的消息发布订阅,一直没有什么问题,但是当把redis的client-type:换为jedis时,出现订阅消息处理的方法不运行了。

问题描述:

在百度上搜索springboot使用redis发布订阅很容易就能搜到例如以下代码:

	@Bean
    public RedisMessageListenerContainer getRedisMessageListenerContainer(RedisConnectionFactory redisConnectionFactory,
                                                                          MessageListenerAdapter messageListenerAdapter) {
        RedisMessageListenerContainer redisMessageListenerContainer = new RedisMessageListenerContainer();
        redisMessageListenerContainer.setConnectionFactory(redisConnectionFactory);
        redisMessageListenerContainer.addMessageListener(messageListenerAdapter, new PatternTopic("xxxxxx"));
        return redisMessageListenerContainer;
    }

添加消息监听器,即可使用指定方法执行监听到的消息,但是这么写是会有一些问题的,我们查看RedisMessageListenerContainer中源码会发现里面有两个线程池,他们是分别用来订阅和处理消息的,如果没有手动set线程池进去,就默认使用SimpleAsyncTaskExecutor,并且threadFactory默认也是null,这时监听到消息后,最终执行如下代码:

	protected void doExecute(Runnable task) {
		Thread thread = (this.threadFactory != null ? this.threadFactory.newThread(task) : createThread(task));
		thread.start();
	}

会直接createThread,这里如果监听消息处理的方法较慢,同时消息量比较大时,会无限制的创建线程,存在较大风险。所以在创建RedisMessageListenerContainer时需要手动set自己创建的线程池进去如下:

	@Bean
    public RedisMessageListenerContainer getRedisMessageListenerContainer(RedisConnectionFactory redisConnectionFactory,
                                                                          MessageListenerAdapter messageListenerAdapter) {
        RedisMessageListenerContainer redisMessageListenerContainer = new RedisMessageListenerContainer();
        redisMessageListenerContainer.setConnectionFactory(redisConnectionFactory);
        redisMessageListenerContainer.addMessageListener(messageListenerAdapter, new PatternTopic(ISSUE_TASK_TOPIC));
        ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
        taskExecutor.setThreadNamePrefix("RedisMessageListenerContainerExecutor-");
        taskExecutor.initialize();
        redisMessageListenerContainer.setTaskExecutor(taskExecutor);
        return redisMessageListenerContainer;
    }

这里没有设置CorePoolSize,因为我们监听的消息量比较少,频率也低,所有默认有一个线程实际就足够了,实际使用也确实没有问题。
但是当我们不再使用springboot默认的lettuce客户端,换为jedis后出现了问题,导致订阅消息处理的方法不执行。


原因分析:

一开始并没有意识到线程池数量的问题,以为是不是spring-data-redis出BUG了,我只是增加了一行配置client-type: jedis,就导致消息收不到了不应该吧,后来仔细看代码发现线程池只有一个线程,猜想会不会那个线程被jedis的什么任务阻塞了,所以增加了一个线程果然问题解决。
后来看源码发现了原来Jedis在订阅消息时的如下代码:

	@Override
	public void pSubscribe(MessageListener listener, byte[]... patterns) {
		if (isSubscribed()) {
			throw new RedisSubscribedConnectionException(
					"Connection already subscribed; use the connection Subscription to cancel or add new channels");
		}
		if (isQueueing()) {
			throw new UnsupportedOperationException();
		}
		if (isPipelined()) {
			throw new UnsupportedOperationException();
		}

		try {
			BinaryJedisPubSub jedisPubSub = new JedisMessageListener(listener);

			subscription = new JedisSubscription(listener, jedisPubSub, null, patterns);
			jedis.psubscribe(jedisPubSub, patterns);

		} catch (Exception ex) {
			throw convertJedisAccessException(ex);
		}
	}

jedis.psubscribe是阻塞的,相对的Lettuce的实现并没有阻塞。


解决方案:

创建RedisMessageListenerContainer时,subscriptionExecutor和taskExecutor分别创建线程池,或者将taskExecutor线程数增大

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值