Redis消息队列

本文介绍了任务异步化,可将服务器额外事情异步处理,常采用队列结构。阐述了生产消费与队列的生产者消费模型,重点讲解了Redis队列的生产消费模式和发布订阅模式,还说明了Spring中使用消息队列的步骤,包括下载包、配置XML等,最后给出了发布/订阅模式的指令及实例代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

参考链接
https://www.jianshu.com/p/9c04890615ba
https://www.jianshu.com/p/83d17d48c2c5

任务异步化

打开浏览器,输入地址,按下回车,打开了页面。于是一个HTTP请求(request)就由客户端发送到服务器,服务器处理请求,返回响应(response)内容。
我们每天都在浏览网页,发送大大小小的请求给服务器。有时候,服务器接到了请求,会发现他也需要给另外的服务器发送请求,或者服务器也需要做另外一些事情,于是最初们发送的请求就被阻塞了,也就是要等待服务器完成其他的事情。
更多的时候,服务器做的额外事情,并不需要客户端等待,这时候就可以把这些额外的事情异步去做。从事异步任务的工具有很多。主要原理还是处理通知消息,针对通知消息通常采取是队列结构。生产和消费消息进行通信和业务实现。

自己项目中:客户端可能同时上传多个应用去执行,这时候服务端可能无法快速处理完所有哦请求。但是这时候客户端可能会处于阻塞状态。需要进行异步处理,让客户端去做其他事情。

主要原理还是处理通知消息,针对通知消息通常采取是队列结构。生产和消费消息进行通信和业务实现。

生产消费与队列

上述异步任务的实现,可以抽象为生产者消费模型。如同一个餐馆,厨师在做饭,吃货在吃饭。如果厨师做了很多,暂时卖不完,厨师就会休息;如果客户很多,厨师马不停蹄的忙碌,客户则需要慢慢等待。实现生产者和消费者的方式用很多

Redis 队列

Python内置了一个好用的队列结构。我们也可以是用redis实现类似的操作。并做一个简单的异步任务。

Redis提供了两种方式来作消息队列。一个是使用生产者消费模式模式,另外一个方法就是发布订阅者模式
前者会让一个或者多个客户端监听消息队列,一旦消息到达,消费者马上消费,谁先抢到算谁的,如果队列里没有消息,则消费者继续监听。
后者也是一个或多个客户端订阅消息频道,只要发布者发布消息,所有订阅者都能收到消息,订阅者都是平等的。

生产消费模式(不建议使用)

主要使用了redis提供的blpop获取队列数据,如果队列没有数据则阻塞等待,也就是监听。

import redis

class Task(object):
    def __init__(self):
        self.rcon = redis.StrictRedis(host='localhost', db=5)
        self.queue = 'task:prodcons:queue'

    def listen_task(self):
        while True:
            task = self.rcon.blpop(self.queue, 0)[1]
            print "Task get", task

if __name__ == '__main__':
    print 'listen task queue'
    Task().listen_task()

发布订阅模式

使用redis的pubsub功能,订阅者订阅频道,发布者发布消息到频道了,频道就是一个消息队列

import redis


class Task(object):

    def __init__(self):
        self.rcon = redis.StrictRedis(host='localhost', db=5)
        self.ps = self.rcon.pubsub()
        self.ps.subscribe('task:pubsub:channel')

    def listen_task(self):
        for i in self.ps.listen():
            if i['type'] == 'message':
                print "Task get", i['data']

if __name__ == '__main__':
    print 'listen task channel'
    Task().listen_task()

Spring中使用消息队列

1.下载对应的包

jar:jedis-2.6.2.jar , spring+data+redis-1.4.2.jar

2.配置spring中支持的XML文件

    <bean id="redisConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
        <property name="hostName" value="127.0.0.1"></property>
        <property name="port" value="6379"></property>
        <property name="usePool" value="true"></property>
    </bean> 
    <bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate">
        <property name="connectionFactory" ref="redisConnectionFactory"></property>
    </bean>     
    <bean id="jdkSerializer" class="org.springframework.data.redis.serializer.JdkSerializationRedisSerializer" />
    <bean id="messageListener" class="org.springframework.data.redis.listener.adapter.MessageListenerAdapter">
        <property name="delegate" ref="messageDelegateListener" /> <!--这里的messageDelegateListener在后面的文件中注解的-->
        <property name="serializer" ref="jdkSerializer" />
    </bean>  
    
    <redis:listener-container>
        <redis:listener ref="messageListener" method="handleMessage" serializer="jdkSerializer" topic="java"/>
    </redis:listener-container>

3.接收回调的类

package com.moensun.laipengtou.webapi.redis;

import java.io.Serializable;

import org.springframework.stereotype.Component;

@Component(value="messageDelegateListener")
public class ListenMessage {
    public void handleMessage(Serializable message){
        System.out.println(message);
    }
}

4.发送消息的类

package com.moensun.laipengtou.webapi.redis;

import java.io.Serializable;

import javax.annotation.Resource;

import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

@Component
public class SendMessage {
    @Resource(name="redisTemplate")
    private RedisTemplate<String, Object> redisTemplate;
    
    public void sendMessage(String channel, Serializable message) {
        redisTemplate.convertAndSend(channel, message);
    }
}

controller里调用

package com.moensun.laipengtou.webapi.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

import com.moensun.laipengtou.webapi.redis.SendMessage;

@Controller
@RequestMapping(value="/test")
public class TestController {
    @Autowired SendMessage sendMessage;
    
    @RequestMapping(value="/redis")
    public void redis(){
        for (int i = 0; i <1000; i++) {
            sendMessage.sendMessage("java",i);
        }
    }
}

学习笔记

Redis不仅可作为缓存服务器,还可用作消息队列。它的列表类型天生支持用作消息队列。如下图所示:

在这里插入图片描述
由于Redis的列表是使用双向链表实现的,保存了头尾节点,所以在列表头尾两边插取元素都是非常快的。

所以可以直接使用Redis的List实现消息队列,只需简单的两个指令lpush和rpop或者rpush和lpop。简单示例如下:

但上述例子中消息消费者有一个问题存在,即需要不停的调用rpop方法查看List中是否有待处理消息。每调用一次都会发起一次连接,这会造成不必要的浪费。也许你会使用Thread.sleep()等方法让消费者线程隔一段时间再消费,但这样做有两个问题:
1)、如果生产者速度大于消费者消费速度,消息队列长度会一直增大,时间久了会占用大量内存空间。

2)、如果睡眠时间过长,这样不能处理一些时效性的消息,睡眠时间过短,也会在连接上造成比较大的开销。

所以可以使用brpop指令,这个指令只有在有元素时才返回,没有则会阻塞直到超时返回null

实例代码

jedis = MyJedisFactory.getLocalJedis();
String message = jedis.rpop(Producer.MESSAGE_KEY);

发布/订阅模式

Redis除了对消息队列提供支持外,还提供了一组命令用于支持发布/订阅模式。

1)发布
PUBLISH指令可用于发布一条消息,格式 PUBLISH channel message
返回值表示订阅了该消息的数量。
2)订阅
SUBSCRIBE指令用于接收一条消息,格式 SUBSCRIBE channel

可以看到使用SUBSCRIBE指令后进入了订阅模式,但没有接收到publish发送的消息,这是因为只有在消息发出去前订阅才会接收到。在这个模式下其他指令,只能看到回复。回复分为三种类型:
1、如果为subscribe,第二个值表示订阅的频道,第三个值表示是第几个订阅的频道?(理解成序号?)
2、如果为message(消息),第二个值为产生该消息的频道,第三个值为消息
3、如果为unsubscribe,第二个值表示取消订阅的频道,第三个值表示当前客户端的订阅数量。
实例代码

 jedis.publish(CHANNEL_KEY, message);
  JedisPubSub jps = new JedisPubSub() {
            /**
             * JedisPubSub类是一个没有抽象方法的抽象类,里面方法都是一些空实现
             * 所以可以选择需要的方法覆盖,这儿使用的是SUBSCRIBE指令,所以覆盖了onMessage
             * 如果使用PSUBSCRIBE指令,则覆盖onPMessage方法
             * 当然也可以选择BinaryJedisPubSub,同样是抽象类,但方法参数为byte[]
             */
            @Override
            public void onMessage(String channel, String message) {
                if(Publisher.CHANNEL_KEY.equals(channel)) {
                    System.out.println("接收到消息: channel : " + message);
                    //接收到exit消息后退出
                    if(EXIT_COMMAND.equals(message)) {
                        System.exit(0);
                    }
 
                }
            }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值