监听redis key过期事件出现ERR unknown command `CONFIG`, with args beginning with: `GET`, `notify-keyspace-events`这个错误,主要是因为redis配置禁用了CONFIG命令,一些云服务出于安全考虑都会禁用CONFIG命令。

解决办法有两种,一种就是改配置,一种是基于代码层面解决。

方案一:改redis配置文件

注释掉rename-command CONFIG "",这种方案虽然能解决问题,但是不提倡,禁止redis CONFIG的使用是出于安全考虑的,实际生产中CONFIG 一般是禁止的

方案二:代码层面

只要设置keyspaceNotificationsConfigParameter为null就可以了,为什么这样就能解决这个问题呢?接下来我分析下源码。

public RedisKeyExpirationListener(RedisMessageListenerContainer listenerContainer) {
    super(listenerContainer);
    setKeyspaceNotificationsConfigParameter(null);
}
  •  KeyspaceEventMessageListener源码:
public abstract class KeyspaceEventMessageListener implements MessageListener, InitializingBean, DisposableBean {

	private static final Topic TOPIC_ALL_KEYEVENTS = new PatternTopic("__keyevent@*");

	private final RedisMessageListenerContainer listenerContainer;

    // 默认的notify-keyspace-events参数为"EA"
	private String keyspaceNotificationsConfigParameter = "EA";

	/**
	 * Creates new {@link KeyspaceEventMessageListener}.
	 *
	 * @param listenerContainer must not be {@literal null}.
	 */
	public KeyspaceEventMessageListener(RedisMessageListenerContainer listenerContainer) {

		Assert.notNull(listenerContainer, "RedisMessageListenerContainer to run in must not be null!");
		this.listenerContainer = listenerContainer;
	}

	/*
	 * (non-Javadoc)
	 * @see org.springframework.data.redis.connection.MessageListener#onMessage(org.springframework.data.redis.connection.Message, byte[])
	 */
	@Override
	public void onMessage(Message message, @Nullable byte[] pattern) {

		if (ObjectUtils.isEmpty(message.getChannel()) || ObjectUtils.isEmpty(message.getBody())) {
			return;
		}

		doHandleMessage(message);
	}

	/**
	 * Handle the actual message
	 *
	 * @param message never {@literal null}.
	 */
	protected abstract void doHandleMessage(Message message);

	/**
	 * Initialize the message listener by writing requried redis config for {@literal notify-keyspace-events} and
	 * registering the listener within the container.
     * 初始化方法  
	 */
	public void init() {

        // 判断keyspaceNotificationsConfigParameter是否为空
		if (StringUtils.hasText(keyspaceNotificationsConfigParameter)) {

            // 建立redis连接
			RedisConnection connection = listenerContainer.getConnectionFactory().getConnection();

			try {
                // 获取redis配置文件中notify-keyspace-events的值,
                // 此处相当于在redis中执行config get notify-keyspace-events
				Properties config = connection.getConfig("notify-keyspace-events");
                
                // 判断获取的notify-keyspace-events值是否为空
				if (!StringUtils.hasText(config.getProperty("notify-keyspace-events"))) {
					
                    // 设置redis配置文件中notify-keyspace-events的值
                    // 相当于在redis中执行config set notify-keyspace-events xxx
                    connection.setConfig("notify-keyspace-events", keyspaceNotificationsConfigParameter);
				}

			} finally {
				connection.close();
			}
		}

		doRegister(listenerContainer);
	}

	/**
	 * Register instance within the container.
	 *
	 * @param container never {@literal null}.
	 */
	protected void doRegister(RedisMessageListenerContainer container) {
		listenerContainer.addMessageListener(this, TOPIC_ALL_KEYEVENTS);
	}

	/*
	 * (non-Javadoc)
	 * @see org.springframework.beans.factory.DisposableBean#destroy()
	 */
	@Override
	public void destroy() throws Exception {
		listenerContainer.removeMessageListener(this);
	}

	/**
	 * Set the configuration string to use for {@literal notify-keyspace-events}.
	 *
	 * @param keyspaceNotificationsConfigParameter can be {@literal null}.
	 * @since 1.8
	 */
	public void setKeyspaceNotificationsConfigParameter(String keyspaceNotificationsConfigParameter) {
		this.keyspaceNotificationsConfigParameter = keyspaceNotificationsConfigParameter;
	}

	/*
	 * (non-Javadoc)
	 * @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet()
	 */
	@Override
	public void afterPropertiesSet() throws Exception {
		init();
	}
}

  • 源码分析:

通过上面源码我们可以发现源码中调用了redis的CONFIG命令,顺着这个思路,我是否可以设置代码里面不调用redis的CONFIG命令这个问题不久解决了吗?阅读源码,我们发现只要把keyspaceNotificationsConfigParameter 这个参数赋值值null或者空字符串,在init方法中就不会去调用redis的CONFIG命令,我们在往下看源码发现keyspaceNotificationsConfigParameter是提供了set的方法的。所以我们是可以通setKeyspaceNotificationsConfigParameter方法来改变keyspaceNotificationsConfigParameter属性值的。

  • 启动结果:

设置keyspaceNotificationsConfigParameter参数为null后,发现程序启动正常

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐