Redis cung cấp chức năng Publish/Subscribe (Pub/Sub) từ phiên bản 2.8 trở lên. Chức năng này cho phép các ứng dụng lắng nghe và xử lý các sự kiện liên quan đến các khóaRedis.
Quan sát các khóa Redis đã hết hạn
Để quan sát các khóaRedis đã hết hạn, bạn cần đăng ký các sự kiện hết hạn của khóaRedis vào một kênh cụ thể. Kênh mặc định để quan sát các sự kiện hết hạn của khóaRedis là __keyevent@*__:expired, trong đó * đại diện cho tất cả các cơ sở dữ liệu (db0 đến db15) của Redis. Nếu bạn chỉ muốn quan sát các sự kiện hết hạn trong một cơ sở dữ liệu cụ thể, ví dụ db0, bạn có thể sử dụng kênh __keyevent@0__:expired.
Thực hiện bằng Spring Data Redis
Spring Data Redis cung cấp một监听器 mặc định là KeyExpirationEventMessageListener để xử lý các sự kiện hết hạn của khóaRedis. Bạn có thể thừa kế từ监听器 này và viết lại phương thức onMessage để xử lý các sự kiện.
Code ví dụ:
package com.example.redis.listener;
import org.springframework.data.redis.connection.Message;
import org.springframework.data.redis.listener.KeyExpirationEventMessageListener;
import org.springframework.data.redis.listener.RedisMessageListenerContainer;
import org.springframework.lang.Nullable;
import org.springframework.stereotype.Component;
@Component
public class KeyExpirationListener extends KeyExpirationEventMessageListener {
public KeyExpirationListener(RedisMessageListenerContainer listenerContainer) {
super(listenerContainer);
}
@Override
public void onMessage(Message message, @Nullable byte[] pattern) {
String key = message.toString();
System.out.println("KeyId đã hết hạn: " + key);
}
}
Bạn cần đăng ký监听器 trong cấu hình của Redis:
package com.example.redis.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.listener.RedisMessageListenerContainer;
@Configuration
public class RedisConfig {
@Bean
public RedisMessageListenerContainer redisContainer(RedisConnectionFactory connectionFactory) {
RedisMessageListenerContainer container = new RedisMessageListenerContainer();
container.setConnectionFactory(connectionFactory);
return container;
}
}
Tự động hóa xử lý các sự kiện hết hạn
Nếu bạn muốn xử lý các sự kiện hết hạn một cách khác nhau, bạn có thể implements giao diện MessageListener và đăng ký监听器 của bạn vào kênh __keyevent@*__:expired.
Code ví dụ:
package com.example.redis.listener;
import org.springframework.data.redis.connection.Message;
import org.springframework.data.redis.connection.MessageListener;
import org.springframework.lang.Nullable;
import org.springframework.stereotype.Component;
@Component
public class CustomKeyExpirationListener implements MessageListener {
@Override
public void onMessage(Message message, @Nullable byte[] pattern) {
String key = message.toString();
if (!key.startsWith("expired_")) {
return;
}
System.out.println("KeyId đã hết hạn: " + key);
}
}
Đăng ký监听器 vào kênh:
package com.example.redis.config;
import com.example.redis.listener.CustomKeyExpirationListener;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.listener.PatternTopic;
import org.springframework.data.redis.listener.RedisMessageListenerContainer;
@Configuration
public class RedisConfig {
@Bean
public RedisMessageListenerContainer redisContainer(RedisConnectionFactory connectionFactory, CustomKeyExpirationListener customListener) {
RedisMessageListenerContainer container = new RedisMessageListenerContainer();
container.setConnectionFactory(connectionFactory);
container.addMessageListener(customListener, new PatternTopic("__keyevent@*__:expired"));
return container;
}
}
Cảnh báo và khuyến nghị
- Khi Redis khởi động lại, các kênh Publish/Subscribe sẽ bị끊. Để tránh điều này, bạn cần thêm dòng cấu hình notify-keyspace-events EA vào file redis.conf.
- Khi quan sát các sự kiện hết hạn, khóaRedis đã bị xóa khỏi cơ sở dữ liệu. Do đó, bạn không thể truy cập giá trị của khóaRedis đó một cách trực tiếp.
- Trong trường hợp hệ thống phân tán, một sự kiện hết hạn có thể được gửi đến nhiều监听器 khác nhau. Bạn cần sử dụng các kỹ thuật khóa (lock) để đảm bảo rằng mỗi sự kiện chỉ được xử lý một lần.
Thực hiện Publish/Subscribe
Để gửi và nhận tin nhắn qua các kênh Publish/Subscribe, bạn cần implements các lớp Publish và Listen tương ứng.
Code Publish:
package com.example.redis.publisher;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
@Component
public class RedisPublisher {
@Autowired
private RedisTemplate redisTemplate;
public void send(String channel, String message) {
redisTemplate.convertAndSend(channel, message);
}
}
Code Listen:
package com.example.redis.listener;
import org.springframework.data.redis.connection.Message;
import org.springframework.data.redis.connection.MessageListener;
import org.springframework.lang.Nullable;
import org.springframework.stereotype.Component;
@Component
public class CustomerListener implements MessageListener {
@Override
public void onMessage(Message message, @Nullable byte[] pattern) {
// Xử lý tin nhắn nhận được
System.out.println("Tin nhắn nhận được từ kênh: " + message.getChannel().toString() + " - Nội dung: " + message.toString());
}
}
Cấu hình:
package com.example.redis.config;
import com.example.redis.listener.CustomerListener;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.listener.ChannelTopic;
import org.springframework.data.redis.listener.RedisMessageListenerContainer;
@Configuration
public class RedisConfig {
@Bean
public RedisMessageListenerContainer redisContainer(RedisConnectionFactory connectionFactory, CustomerListener customerListener) {
RedisMessageListenerContainer container = new RedisMessageListenerContainer();
container.setConnectionFactory(connectionFactory);
container.addMessageListener(customerListener, new ChannelTopic("customer"));
return container;
}
}
Test Publish/Subscribe:
@Autowired
private RedisPublisher redisPublisher;
public void testSend() {
redisPublisher.send("customer", "Xin chào Redis!");
}
Quan sát các khóaRedis đã hết hạn bằng Jedis
Bạn có thể sử dụng thư viện Jedis để lắng nghe các sự kiện hết hạn của khóaRedis:
Code ví dụ:
package com.example.redis.jedis;
import org.springframework.stereotype.Component;
import redis.clients.jedis.JedisPubSub;
@Component
public class JedisKeyExpirationListener extends JedisPubSub {
@Override
public void onMessage(String channel, String message) {
System.out.println("Kênh: " + channel + " - Tin nhắn: " + message);
}
}
Quan sát các sự kiện hết hạn:
@Autowired
private JedisKeyExpirationListener jedisListener;
public void runListener() {
new Thread(() -> {
Jedis jedis = new Jedis("localhost", 6379);
jedis.subscribe(jedisListener, "__keyevent@0__:expired");
}).start();
}
Quan sát các sự kiện hết hạn:
Khi một khóaRedis hết hạn, nó sẽ gửi một tin nhắn đến kênh __keyevent@0__:expired. Bạn có thể sử dụng các kỹ thuật khóa (lock) như Redisson hoặc RedisLock để xử lý các sự kiện này một cách an toàn.