redis 5.0 之后新推出了stream数据结构,可以实现一个轻量级的消息队列,下面通过自定义注解和springboot使用一下redis stream
1.自定义注解
java
@Target(ElementType.METHOD)
@Retention(value = RetentionPolicy.RUNTIME)
public @interface MsgStreamListener
{
String stream();
String group();
String name();
}
2.定义一个listener的父类,让所有的消费者继承该类
java
public abstract class AbstractMsgListener {
}
3.将我们的所有消费者注册到redis stream的listener中
java
@Configuration
public class RedisConfig {
@Autowired
private List<AbstractMsgListener> msgListeners;
@Autowired
private RedisTemplate redisTemplate;
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
RedisTemplate<String, Object> template = new RedisTemplate<String, Object>();
template.setConnectionFactory(factory);
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jackson2JsonRedisSerializer.setObjectMapper(om);
StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
// key采用String的序列化方式
template.setKeySerializer(stringRedisSerializer);
// hash的key也采用String的序列化方式
template.setHashKeySerializer(stringRedisSerializer);
// value序列化方式采用jackson
template.setValueSerializer(stringRedisSerializer);
// hash的value序列化方式采用jackson
template.setHashValueSerializer(stringRedisSerializer);
template.afterPropertiesSet();
return template;
}
@Bean
public StreamMessageListenerContainer<String, ObjectRecord<String,String>> streamMessageListenerContainer(RedisConnectionFactory connectionFactory)
{
StreamMessageListenerContainer.StreamMessageListenerContainerOptions<String, ObjectRecord<String,String>> options =
StreamMessageListenerContainer.StreamMessageListenerContainerOptions.builder()
.pollTimeout(java.time.Duration.ofSeconds(1))
.targetType(String.class)
.build();
//创建监听redis流的消息监听容器
StreamMessageListenerContainer<String, ObjectRecord<String,String>> listenerContainer =
StreamMessageListenerContainer.create(connectionFactory, options);
//找到所有继承AbstractMsgService的类
for (AbstractMsgListener service : msgListeners) {
for (Method method : service.getClass().getMethods()) {
if(method.isAnnotationPresent(MsgStreamListener.class)){
MsgStreamListener annotation = method.getAnnotation(MsgStreamListener.class);
String stream = annotation.stream();
String group = annotation.group();
String name = annotation.name();
StreamListener<String,ObjectRecord<String,String>> listener = (StreamListener<String, ObjectRecord<String,String>>) message -> {
try {
method.invoke(service,message);
}catch (Exception e){
}
};
//创建redis流的消息监听器
listenerContainer.receive(Consumer.from(group,name),
StreamOffset.create(stream, ReadOffset.lastConsumed()),
listener);
initializeStream(stream,name);
}
}
}
listenerContainer.start();
return listenerContainer;
}
public void initializeStream(String stream,String group) {
StreamOperations<String, Object, Object> streamOperations = redisTemplate.opsForStream();
// 创建一个流
try {
streamOperations.createGroup(stream, ReadOffset.from("0"), group);
} catch (Exception e) {
// 流可能已存在,忽略异常
}
}
}
4.生产者生产消息
java
@GetMapping("/add")
public ResponseEntity<String> addApplication() {
// 保存应用逻辑...
// 发布消息到 Redis Stream
Map<String, Object> messageData = new HashMap<>();
messageData.put("app", "Hello World");
RecordId applications_stream = redisTemplate.opsForStream().add("applications_stream", messageData);
return ResponseEntity.ok("Application added successfully");
}
5.消费者消费消息,需要继承上面定义的listener的抽象类
java
@Component
public class Consumer extends AbstractMsgListener {
@MsgStreamListener(group = "test1",name = "test1",stream = "applications_stream")
public void onMessage(ObjectRecord<String, String> message)
{
String stream = message.getStream();
String s = message.getId().toString();
String value = message.getValue();
System.out.printf("receive test1 msg stream:%s msgId:%s msgBody:%s",stream,s,value);
}
}