基于Spring Boot2.x使用Redis(Lettuce)详细教程---Redis 发布订阅 (八)

8.1 应用场景

一般来说,消息队列有两种场景,一种是发布者订阅者模式,一种是生产者消费者模式。利用redis这两种场景的消息队列都能够实现。

定义:
生产者消费者模式:生产者生产消息放到队列里,多个消费者同时监听队列,谁先抢到消息谁就会从队列中取走消息;即对于每个消息只能被最多一个消费者拥有。

​ 发布者订阅者模式:发布者生产消息放到队列里,多个监听队列的消费者都会收到同一份消息;即正常情况下每个消费者收到的消息应该都是一样的。

Pub/Sub 从字面上理解就是发布(Publish)与订阅(Subscribe),在Redis中,你可以设定对某一个key值进行消息发布及消息订阅,当一个key值上进行了消息发布后,所有订阅它的客户端都会收到相应的消息。

这一功能最明显的用法就是构建实时消息系统,比如普通的即时聊天,群聊等功能。

微博,每个用户的粉丝都是该用户的订阅者,当用户发完微博,所有粉丝都将收到他的动态;

新闻,资讯站点通常有多个频道,每个频道就是一个主题,用户可以通过主题来做订阅(如RSS),这样当新闻发布时,订阅者可以获得更新

简单的应用场景的话, 以门户网站为例, 当编辑更新了某推荐板块的内容后:

  1. CMS发布清除缓存的消息到channel (推送者推送消息)
  2. 门户网站的缓存系统通过channel收到清除缓存的消息 (订阅者收到消息),更新了推荐板块的缓存
  3. 还可以做集中配置中心管理,当配置信息发生更改后,订阅配置信息的节点都可以收到通知消息

8.2 Redis 发布订阅架构

Redis提供了发布订阅功能,可以用于消息的传输,Redis的发布订阅机制包括三个部分,发布者,订阅者和Channel。

在这里插入图片描述

发布者和订阅者都是Redis客户端,Channel则为Redis服务器端,发布者将消息发送到某个的频道,订阅了这个频道的订阅者就能接收到这条消息。

Redis的这种发布订阅机制与基于主题的发布订阅类似,Channel相当于主题。

8.3 Redis发布订阅与ActiveMQ的比较

(1)ActiveMQ支持多种消息协议,包括AMQP,MQTT,Stomp等,并且支持JMS规范,但Redis没有提供对这些协议的支持;

(2)ActiveMQ提供持久化功能,但Redis无法对消息持久化存储,一旦消息被发送,如果没有订阅者接收,那么消息就会丢失;

(3)ActiveMQ提供了消息传输保障,当客户端连接超时或事务回滚等情况发生时,消息会被重新发送给客户端,Redis没有提供消息传输保障。

总之,ActiveMQ所提供的功能远比Redis发布订阅要复杂,毕竟Redis不是专门做发布订阅的,

但是如果系统中已经有了Redis,并且需要基本的发布订阅功能,就没有必要再安装ActiveMQ了,

因为可能ActiveMQ提供的功能大部分都用不到,而Redis的发布订阅机制就能满足需求。

8.4 代码示例

8.4.1 订阅消息

需要配置一个消息监听者容器,容器需要连接工厂以及消息监听器,在消息监听器中需要配置消息处理对象以及处理的方法.

代码:com.javablog.redis.demo.config.RedisConfig

@Bean
public RedisMessageListenerContainer container(RedisConnectionFactory connectionFactory,
                                               MessageListenerAdapter listenerAdapter){
    RedisMessageListenerContainer container = new RedisMessageListenerContainer();
    container.setConnectionFactory(connectionFactory);
    container.addMessageListener(listenerAdapter,new PatternTopic(SystemConstants.TOPIC_NAME));
    return container;
}

/**
 * 绑定消息监听者和接收监听的方法,必须要注入这个监听器,不然会报错
 */
@Bean
public MessageListenerAdapter listenerAdapter(){
 //这个地方 是给messageListenerAdapter 传入一个消息接受的处理器,利用反射的方法调用“receiveMessage”
 //也有好几个重载方法,这边默认调用处理器的方法 叫handleMessage 
    return new MessageListenerAdapter(new MessageReceiver(),"receiveMessage");
}

订阅代码

package com.javablog.redis.demo.service.impl;

import com.javablog.redis.demo.service.PublishService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

@Service("publishService")
public class PublishServiceImpl implements PublishService {
    private final static Logger log = LoggerFactory.getLogger(PublishServiceImpl.class);
    @Autowired
    private StringRedisTemplate stringRedisTemplate;
    @Override
    public void publish(String topicName,String message) {
        stringRedisTemplate.convertAndSend(topicName, message);
    }

}
8.4.2 消费消息

代码:com.javablog.redis.demo.service.impl.PublishServiceImpl

package com.javablog.redis.demo.service.impl;

import com.javablog.redis.demo.service.PublishService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

@Service("publishService")
public class PublishServiceImpl implements PublishService {
    private final static Logger log = LoggerFactory.getLogger(PublishServiceImpl.class);
    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    /**
     * 订阅主题
     * @param topicName 发布的管道
     * @param message   发布的内容
     */
    @Override
    public void publish(String topicName,String message) {
        stringRedisTemplate.convertAndSend(topicName, message);
    }

}

8.5 测试用例

代码: com.javablog.redis.demo.RedisPublishTest

package com.javablog.redis.demo;

import com.javablog.redis.demo.constants.SystemConstants;
import com.javablog.redis.demo.service.ListCacheService;
import com.javablog.redis.demo.service.PublishService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;

import java.util.ArrayList;
import java.util.List;


@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = CacheServiceApplication.class)
@WebAppConfiguration
public class RedisPublishTest {
    private final static Logger log = LoggerFactory.getLogger(RedisPublishTest.class);

    @Autowired
    private PublishService publishService;

    @Test
    public void testPublishService(){
        for(int i=0;i<10;i++) {
            publishService.publish(SystemConstants.TOPIC_NAME, "这是我发第"+i+"条的消息啊");
        }
    }

}

github源码地址

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值