3.4Redis实现内部队列

日常工作中会出现操作的异步处理,这时候就需要队列了,redis可以模拟实现队列。原理就是模拟一个topic,然后把消息放到redis的list对象里面,由专门的监听器来捕获队列的内容。

来看看具体实现,模拟这样一个场景,用户注册时,需要发短信,把发短信的这个操作异步处理,放入redis队列。

添加依赖

 <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <junit.version>4.12</junit.version>
        <spring.version>4.3.3.RELEASE</spring.version>
        <jedis.version>2.9.0</jedis.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aop</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aspects</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-beans</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context-support</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-web</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.data</groupId>
            <artifactId>spring-data-redis</artifactId>
            <version>1.7.2.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
            <version>${jedis.version}</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>${junit.version}</version>
        </dependency>
    </dependencies>

这里面主要包括spring依赖和redis依赖。

新建一个web工程,并修改web.xml

 <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>
            classpath:applicationContext-redis.xml
        </param-value>
    </context-param>
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>

这里面的核心是启动时加载applicationContext-redis.xml,利用spring的监听ContextLoaderListener

application.properties 配置

redis.host=127.0.0.1
redis.port=6379
redis.pass=
redis.maxIdle=300
redis.testOnBorrow=true

applicationContext-redis.xml配置

 <!--redis连接池-->
    <bean id="redisConnectionFactory"
         class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
        <property name="hostName" value="${redis.host}"></property>
        <property name="port" value="${redis.port}"></property>
        <property name="usePool" value="true"></property>
    </bean>
    <!-- 注入RedisTemplate -->
    <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"/>
   <!-- 监听代理,用于接收redis消息-->
    <bean id="smsMessageDelegateListener"
          class="com.critc.queue.SmsMessageDelegateListener"/>
    <!-- 消息监听,spring内置的消息监听适配器 -->
    <bean id="smsMessageListener"
          class="org.springframework.data.redis.listener.adapter.MessageListenerAdapter">
        <property name="delegate" ref="smsMessageDelegateListener"/>
        <property name="serializer" ref="jdkSerializer"/>
    </bean>
    <!--消息发送工具 -->
    <bean id="sendMessage" class="com.critc.queue.SendMessage">
        <property name="redisTemplate" ref="redisTemplate"/>
    </bean>
    <!-- 定义消息的topic-->
    <redis:listener-container>
        <redis:listener ref="smsMessageListener" method="handleMessage"
                        serializer="jdkSerializer" topic="sms_queue_shortmessage"/>
    </redis:listener-container>

SmsMessageVo.java 模拟消息的一个VO

public class SmsMessageVo implements Serializable {
    private static final long serialVersionUID = 1L;
    // 手机号
    private String mobile;

    // 短信内容
    private String content;
    //省略get set方法
}

里面包括一个手机号,一个短信内容即验证码

SmsMessageDelegateListener消息队列监听

public class SmsMessageDelegateListener {

    // 监听Redis消息
    public void handleMessage(Serializable message) {
        System.out.println("收到消息" + message);
    }
}

接收到消息后就可以进行后续的处理,比如调用发短信服务等等。
这里面需要把message一般是一个json串,先转换成对应的vo,再进行后续的处理

SendMessage.java 发送消息类

public class SendMessage {
    private RedisTemplate<String, Object> redisTemplate;

    public RedisTemplate<String, Object> getRedisTemplate() {
        return redisTemplate;
    }

    public void setRedisTemplate(RedisTemplate<String, Object> redisTemplate) {
        this.redisTemplate = redisTemplate;
    }

    public void sendMessage(String channel, Serializable message) {
        redisTemplate.convertAndSend(channel, message);
    }
}

这里面最核心的一句redisTemplate.convertAndSend(channel, message),发送到指定通道消息。

模拟发送消息

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath*:applicationContext-redis.xml"})
public class TestSendMessage {

    @Autowired
    private SendMessage sendMessage;

    @Test
    public void testSendMessage() {
        SmsMessageVo smsMessageVo = new SmsMessageVo();
        smsMessageVo.setMobile("13811111111");
        smsMessageVo.setContent("1234");
        //一般消息内容会是一个json串,消费时再把json转成对象
        sendMessage.sendMessage("sms_queue_shortmessage", smsMessageVo.toString());
    }
}

首先启动web工程,然后点击TestSendMessage进行消息的发送。

消息接收结果

利用redis实现的消息队列可以进行事务的异步处理,不过这个消息队列和其他的消息中间件有本质的区别,最大的区别就是redis不能保证同一个消息被一个应用接收,比如当前有多个应用监听redis实现的队列,如果队列里面加入新消息,就会被多个应用同时接收,不能实现消息被唯一应用接收。

当然这只是一个简单的队列,如果有更多需求,需要采用专门的消息中间件来实现,比如rabbitmq、activemq等等

源码下载

[本工程详细源码]
(https://github.com/chykong/java_component/tree/master/chapter3_4_redis_queue)

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,684评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,143评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,214评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,788评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,796评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,665评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,027评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,679评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,346评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,664评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,766评论 1 331
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,412评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,015评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,974评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,073评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,501评论 2 343

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,596评论 18 139
  • 1 消息队列概述 消息队列中间件是分布式系统中重要的组件,主要解决应用耦合,异步消息,流量削锋等问题。实现高性能,...
    Bobby0322阅读 10,847评论 0 24
  • 第六十三章 “跳梁小丑。”徐艾看着刘楠和那位不知姓名的男友之间的亲密互动的模样,冷冷的笑了笑。端起面前冰镇的饮料慢...
    chief风阅读 479评论 0 5
  • 首先我们先看下封装的基础类TabLayoutView.java类 我们再来看一下Activity对应的xml文件 ...
    一克拉战英阅读 677评论 0 1
  • 还有10多天培训结束,到时开始正式带学生以及给学生上课,今天想总结个人以往的一些经历和体会,写下一些我想对学生说的...
    HHzhao阅读 1,301评论 0 2