redis消息订阅与发布
redis除了做缓存外,还提供了消息订阅发布的功能。
redis是绝大多数系统都会引入的中间件,如果项目中仅仅是一小块部分需要消息订阅与发布功能,特地为此引入消息中间件有点太重了,因此,使用redis做消息订阅与发布性价比更高。
Java实现redis的订阅与发布
@Configuration
@EnableCaching
public class RedisConfiguration extends CachingConfigurerSupport {
/**
* 驱动事件监听容器线程
* <p>
* 这里为了线程安全,用一个线程来监听。而且这里不需要很大的并发~
*
* @return SimpleAsyncTaskExecutor
*/
@Bean
public ThreadPoolExecutor simpleEventAsyncTaskExecutor() {
ThreadPoolExecutor executor = new ThreadPoolExecutor(1, 1, 10, TimeUnit.SECONDS, new ArrayBlockingQueue<>(10));
return executor;
}
/**
* redis消息监听器容器
* 可以添加多个去监听不同话题的redis监听器,只需要把消息监听器和相应的消息订阅处理器绑定,
* 该消息监听器通过反射技术调用消息订阅处理器的相关方法进行一些业务处理
*
* @param connectionFactory RedisConnectionFactory
* @param catAdapter MessageListenerAdapter
* @param fishAdapter MessageListenerAdapter
* @param executor ThreadPoolExecutor
* @return RedisMessageListenerContainer
*/
@Bean
RedisMessageListenerContainer container(RedisConnectionFactory connectionFactory,
MessageListenerAdapter catAdapter,
MessageListenerAdapter fishAdapter,
ThreadPoolExecutor executor) {
RedisMessageListenerContainer container = new RedisMessageListenerContainer();
container.setConnectionFactory(connectionFactory);
// 设置线程池
container.setTaskExecutor(executor);
// 这个container 可以添加多个 messageListener 订阅了一个叫cat 的通道
container.addMessageListener(catAdapter, new PatternTopic(Channel.Cat_Channel.getChannel()));
container.addMessageListener(fishAdapter, new PatternTopic(Channel.fish_Channel.getChannel()));
return container;
}
/**
* 消息监听器适配器,绑定消息处理器
*
* @param redisTemplate RedisTemplate
* @return MessageListenerAdapter
*/
@Bean
MessageListenerAdapter catAdapter(RedisTemplate redisTemplate) {
return new MessageListenerAdapter(new CatListener(redisTemplate));
}
/**
* 消息监听器适配器,绑定消息处理器
*
* @param redisTemplate RedisTemplate
* @return MessageListenerAdapter
*/
@Bean
MessageListenerAdapter fishAdapter(RedisTemplate redisTemplate) {
return new MessageListenerAdapter(new FishListener(redisTemplate));
}
@Bean
StringRedisTemplate stringRedisTemplate(RedisConnectionFactory connectionFactory) {
return new StringRedisTemplate(connectionFactory);
}
@Bean
public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<Object, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(redisConnectionFactory);
// 使用Jackson2JsonRedisSerialize 替换默认序列化
Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jackson2JsonRedisSerializer.setObjectMapper(objectMapper);
// 设置value的序列化规则和 key的序列化规则
redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.afterPropertiesSet();
return redisTemplate;
}
}
@Component
@Slf4j
public class CatListener implements MessageListener {
private final RedisTemplate redisTemplate;
public CatListener(RedisTemplate redisTemplate) {
this.redisTemplate = redisTemplate;
}
@Override
public void onMessage(Message message, byte[] bytes) {
log.debug("CatListener message: {}", message.toString());
}
}
@Component
@Slf4j
public class FishListener implements MessageListener {
private final RedisTemplate redisTemplate;
public FishListener(RedisTemplate redisTemplate) {
this.redisTemplate = redisTemplate;
}
@Override
public void onMessage(Message message, byte[] bytes) {
log.debug("FishListener message: {}", message.toString());
}
}
@EnableScheduling
@Component
public class MessageSender {
private final StringRedisTemplate stringRedisTemplate;
public MessageSender(StringRedisTemplate stringRedisTemplate) {
this.stringRedisTemplate = stringRedisTemplate;
}
/**
* 间隔2s 通过StringRedisTemplate对象向redis消息队列cat频道发布消息
*/
@Scheduled(fixedRate = 5000)
public void sendCatMessage() {
stringRedisTemplate.convertAndSend(Channel.Cat_Channel.getChannel(), "i am cat");
}
/**
* 间隔1s 通过StringRedisTemplate对象向redis消息队列fish频道发布消息
*/
@Scheduled(fixedRate = 10000)
public void sendFishMessage() {
stringRedisTemplate.convertAndSend(Channel.fish_Channel.getChannel(), "i am fish");
}
}
public enum Channel {
Cat_Channel("cat"),
fish_Channel("fish");
private String channel;
Channel(String channel) {
this.channel = channel;
}
public String getChannel() {
return this.channel;
}
}
配置文件
spring:
redis:
host: ${SPRING_REDIS_HOST:localhost}
port: ${SPRING_REDIS_PORT:6379}
database: ${SPRING_REDIS_DATABASE:1}
password: ${SPRING_REDIS_PASSWORD:}
jedis:
pool:
# 资源池中最大连接数
# 默认8,-1表示无限制;可根据服务并发redis情况及服务端的支持上限调整
max-active: ${SPRING_REDIS_POOL_MAX_ACTIVE:50}
# 资源池运行最大空闲的连接数
# 默认8,-1表示无限制;可根据服务并发redis情况及服务端的支持上限调整,一般建议和max-active保持一致,避免资源伸缩带来的开销
max-idle: ${SPRING_REDIS_POOL_MAX_IDLE:50}
# 当资源池连接用尽后,调用者的最大等待时间(单位为毫秒)
# 默认 -1 表示永不超时,设置5秒
max-wait: ${SPRING_REDIS_POOL_MAX_WAIT:5000}