基于Redis实现消息队列典型方案

概述

消息队列,Message Queue,常用于解决并发系统中的资源一致性问题,提升峰值的处理能力,同时保证消息的顺序性、可恢复性、必送达性,对应用进行解耦,或者实现异步通讯等。市面上的 MQ应用有很多(例如:Kafka,RabbitMQ,Disque),同时也可以基于 Redis 来实现,比较典型的方案有:

  • 基于List的 LPUSH+BRPOP 的实现
  • PUB/SUB,订阅/发布模式
  • 基于Sorted-Set的实现
  • 基于Stream类型的实现

讨论之前,先推荐使用 Redis5.0中的Stream方案。

基于List的 LPUSH+BRPOP 的实现

典型的命令为:

LPUSH,将一个或多个消息插入到列表头部
BRPOP,从队列中取出消息,阻塞模式

就是一个典型的基于FIFL队列的解决方案。其中LPUSH是生产者做的事,而BRPOP是消费者做的事。

该模式有很多优点:

  • 实现简单
  • Reids支持持久化消息,意味着消息不会丢失,可以重复查看(注意不是消费,只看不用,LRANGE类的指令)。
  • 可以保证顺序,保证使用LPUSH命令,可以保证消息的顺序性
  • 使用RPUSH,可以将消息放在队列的开头,达到优先消息的目的,可以实现简易的消息优先队列。

同时也有些劣势:

  • 做消费确认ACK比较麻烦,就是不能保证消费者在读取之后,未处理后的宕机问题。导致消息意外丢失。通常需要自己维护一个Pending列表,保证消息的处理确认。
  • 不能做广播模式,例如典型的Pub/Discribe模式。
  • 不能重复消费,一旦消费就会被删除
  • 不支持分组消费,需要自己在业务逻辑层解决

注意,没有好不好的技术,只有适合不适合。

实现:

在java中直接使用redis的时候,直接使用简单的两个指令lpush和rpop或者rpush和lpop就可以实现消息队列的操作。当与spring结合时,可以使用RedisTemplate和StringRedisTemplate;这两个Template是spring封装了对Redis的一些常用的操作,来实现消息队列,这两个区别于序列类。

当redis数据库里面本来存的是字符串数据或者你要存取的数据就是字符串类型数据的时候,那么你就使用StringRedisTemplate即可,如果数据是复杂的对象类型,而取出的时候又不想做任何的数据转换,直接从Redis里面取出一个对象,那么使用RedisTemplate是更好的选择。
 

1-这里使用了RedisTemplate对list列表进行LPUSH和BRPOP操作,省略配置redis部分,直接上代码

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.util.concurrent.TimeUnit;

/**
 * Created by XiChuan on 2018-11-27.
 */
@Component
public class RedisMq {

    private static String key = "redis-test";

    @Autowired
    private RedisTemplate<String,Object> redisTemplate;

    /**
     * 发送消息
     * @param message
     */
    public void push(String message){
        redisTemplate.opsForList().leftPush(key,message);
    }

    /**
     * 获取消息,可以对消息进行监听,没有超过监听事件,则返回消息为null
     * rightPop:1.key,2.超时时间,3.超时时间类型
     * @return
     */
    public String pop(){
        return (String) redisTemplate.opsForList().rightPop(key,60, TimeUnit.SECONDS);
    }
}

2-调用

@Controller
public class Sender{

    @Autowired
    RedisMq redisMq;

    //在redis中存储消息
    @GetMapping("/push")
    public Object pushMsg(@RequestParam("msg")String msg){
        redisMq.push(msg);
        return "SUCCESS";
    }

    //从redis中获取消息
    @GetMapping("/pop")
    public Object popMsg(){
        return redisMq.pop();
    }

}

PUB/SUB发布订阅模式

SUBSCRIBE,用于订阅信道
PUBLISH,向信道发送消息
UNSUBSCRIBE,取消订阅

生产者和消费者通过相同的一个信道(Channel)进行交互。信道其实也就是队列。通常会有多个消费者。多个消费者订阅同一个信道,当生产者向信道发布消息时,该信道会立即将消息逐一发布给每个消费者。可见,该信道对于消费者是发散的信道,每个消费者都可以得到相同的消息。典型的对多的关系。

典型的优点是:

  • 典型的广播模式,一个消息可以发布到多个消费者
  • 多信道订阅,消费者可以同时订阅多个信道,从而接收多类消息
  • 消息即时发送,消息不用等待消费者读取,消费者会自动接收到信道发布的消息

也有些缺点:

  • 消息一旦发布,不能接收。换句话就是发布时若客户端不在线,则消息丢失,不能寻回
  • 不能保证每个消费者接收的时间是一致的
  • 若消费者客户端出现消息积压,到一定程度,会被强制断开,导致消息意外丢失。通常发生在消息的生产远大于消费速度时

可见,Pub/Sub 模式不适合做消息存储,消息积压类的业务,而是擅长处理广播,即时通讯,即时反馈的业务。

redis的发布订阅模式,使发布者和订阅者完全解耦 

首先项目依赖和配置文件

pom.xml 引入redis依赖    application.properties配置略

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>


首先订阅者需要自己实现具体操作方法,并注册为bean

@Component
public class Recv{
    public void recvMsg(String msg){ //具体操作方法 名称随意
        System.out.println("收到消息:"+msg);
    }
}

然后需要一个 RedisMessageListenerContainer监听容器来将订阅者与指定频道绑定

@Configuration
public class RedisConfig {
    @Bean
    RedisMessageListenerContainer container(RedisConnectionFactory connectionFactory,
        MessageListenerAdapter recvAdapter) {
            RedisMessageListenerContainer container = new RedisMessageListenerContainer();
            container.setConnectionFactory(connectionFactory);
            container.addMessageListener(recvAdapter, new PatternTopic("channel1"));//将订阅者1与channel1频道绑定
            return container;
    }
    @Bean
    MessageListenerAdapter recvAdapter(Recv receiver){ //与channel1绑定的适配器
        return new MessageListenerAdapter(receiver, "recvMsg");/*收到消息时执行Recv类中的        
                                                                 recvMsg方法*/
    }
 
    @Bean
    StringRedisTemplate template(RedisConnectionFactory connectionFactory) {
        return new StringRedisTemplate(connectionFactory);
    }
}

这样,频道和订阅者 订阅者处理数据的方法就绑定好了,接下来向指定频道中发送数据时,订阅这个频道的所有client都会收到消息并执行绑定的方法。

@Componet
public class Sender{
    @Autowired
    private StringRedisTemplate stringRedisTemplate;
    /*当这个方法被调用时,将会发布消息到channel1 然后订阅者执行对应的方法
    这里就会在控制台打印hello world*/
    public void send(){
        stringRedisTemplate.convertAndSend("channel1","hello world");
    }
}

 

基于SortedSet有序集合的实现

ZADD KEY score member,压入集合
ZRANGEBYSCORE,依据score获取成员

关于有序集合zSet的使用可以用来实现延迟消息队列:

https://blog.csdn.net/weixin_40663800/article/details/103523949  具体看这篇文章

redis5.0的Stream实现消息队列(springboot+jedis简单例子)

可以看:https://blog.csdn.net/weixin_37703281/article/details/93463032

  • 0
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spring Boot与Redis结合实现消息队列的方法如下: 1. 首先,确保你的Spring Boot项目中已经引入了Redis的依赖。 2. 创建一个消息发布者类,用于发布消息Redis消息队列中。可以使用RedisTemplate来实现消息的发布。以下是一个示例代码: ```java import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Component; @Component public class MessagePublisher { @Autowired private RedisTemplate<String, Object> redisTemplate; public void publish(String channel, Object message) { redisTemplate.convertAndSend(channel, message); } } ``` 3. 创建一个消息订阅者类,用于监听Redis消息队列并处理接收到的消息。可以使用@RedisListener注解来实现消息的订阅。以下是一个示例代码: ```java import org.springframework.data.redis.connection.Message; import org.springframework.data.redis.connection.MessageListener; import org.springframework.stereotype.Component; @Component public class MessageSubscriber implements MessageListener { @Override public void onMessage(Message message, byte[] pattern) { String channel = new String(message.getChannel()); String body = new String(message.getBody()); // 处理接收到的消息 System.out.println("Received message: " + body + " from channel: " + channel); } } ``` 4. 在需要发布消息的地方,通过调用消息发布者类的publish方法来发布消息。以下是一个示例代码: ```java @Autowired private MessagePublisher messagePublisher; public void sendMessage(String channel, Object message) { messagePublisher.publish(channel, message); } ``` 5. 在需要订阅消息的地方,通过在消息订阅者类的方法上添加@RedisListener注解来监听指定的频道。以下是一个示例代码: ```java @RedisListener(channels = "myChannel") public void handleMessage(String message) { // 处理接收到的消息 System.out.println("Received message: " + message); } ``` 通过以上步骤,你就可以使用Spring Boot与Redis结合实现消息队列了。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值