Redis中list数据结构,具有“双端队列”的特性,同时redis具有持久数据的能力,因此redis实现分布式队列是非常安全可靠的。它类似于JMS中的“Queue”,只不过功能和可靠性(事务性)并没有JMS严格。

Redis中的队列阻塞时,整个connection都无法继续进行其他操作,因此在基于连接池设计是需要注意。

我们通过spring-data-redis,来实现“同步队列”,设计风格类似与JMS。

一.配置文件:

[java] view
plain
 copy

  1. <beans xmlns="http://www.springframework.org/schema/beans"
  2. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  3. xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd" default-autowire="byName">
  4. <bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig">
  5. <property name="maxActive" value="32"></property>
  6. <property name="maxIdle" value="6"></property>
  7. <property name="maxWait" value="15000"></property>
  8. <property name="minEvictableIdleTimeMillis" value="300000"></property>
  9. <property name="numTestsPerEvictionRun" value="3"></property>
  10. <property name="timeBetweenEvictionRunsMillis" value="60000"></property>
  11. <property name="whenExhaustedAction" value="1"></property>
  12. </bean>
  13. <bean id="jedisConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory" destroy-method="destroy">
  14. <property name="poolConfig" ref="jedisPoolConfig"></property>
  15. <property name="hostName" value="127.0.0.1"></property>
  16. <property name="port" value="6379"></property>
  17. <property name="password" value="0123456"></property>
  18. <property name="timeout" value="15000"></property>
  19. <property name="usePool" value="true"></property>
  20. </bean>
  21. <bean id="jedisTemplate" class="org.springframework.data.redis.core.RedisTemplate">
  22. <property name="connectionFactory" ref="jedisConnectionFactory"></property>
  23. <property name="defaultSerializer">
  24. <bean class="org.springframework.data.redis.serializer.StringRedisSerializer"/>
  25. </property>
  26. </bean>
  27. <bean id="jedisQueueListener" class="com.sample.redis.sdr.QueueListener"/>
  28. <bean id="jedisQueue" class="com.sample.redis.sdr.RedisQueue" destroy-method="destroy">
  29. <property name="redisTemplate" ref="jedisTemplate"></property>
  30. <property name="key" value="user:queue"></property>
  31. <property name="listener" ref="jedisQueueListener"></property>
  32. </bean>
  33. </beans>

二.程序实例:

1) QueueListener:当队列中有数据时,可以执行类似于JMS的回调操作。

[java] view
plain
 copy

  1. public interface RedisQueueListener<T> {
  2. public void onMessage(T value);
  3. }
[java] view
plain
 copy

  1. public class QueueListener<String> implements RedisQueueListener<String> {
  2. @Override
  3. public void onMessage(String value) {
  4. System.out.println(value);
  5. }
  6. }

2) RedisQueue:队列操作,内部封装redisTemplate实例;如果配置了“listener”,那么queue将采用“消息回调”的方式执行,listenerThread是一个后台线程,用来自动处理“队列信息”。如果不配置“listener”,那么你可以将redisQueue注入到其他spring bean中,手动去“take”数据即可。

[java] view
plain
 copy

  1. public class RedisQueue<T> implements InitializingBean,DisposableBean{
  2. private RedisTemplate redisTemplate;
  3. private String key;
  4. private int cap = Short.MAX_VALUE;//最大阻塞的容量,超过容量将会导致清空旧数据
  5. private byte[] rawKey;
  6. private RedisConnectionFactory factory;
  7. private RedisConnection connection;//for blocking
  8. private BoundListOperations<String, T> listOperations;//noblocking
  9. private Lock lock = new ReentrantLock();//基于底层IO阻塞考虑
  10. private RedisQueueListener listener;//异步回调
  11. private Thread listenerThread;
  12. private boolean isClosed;
  13. public void setRedisTemplate(RedisTemplate redisTemplate) {
  14. this.redisTemplate = redisTemplate;
  15. }
  16. public void setListener(RedisQueueListener listener) {
  17. this.listener = listener;
  18. }
  19. public void setKey(String key) {
  20. this.key = key;
  21. }
  22. @Override
  23. public void afterPropertiesSet() throws Exception {
  24. factory = redisTemplate.getConnectionFactory();
  25. connection = RedisConnectionUtils.getConnection(factory);
  26. rawKey = redisTemplate.getKeySerializer().serialize(key);
  27. listOperations = redisTemplate.boundListOps(key);
  28. if(listener != null){
  29. listenerThread = new ListenerThread();
  30. listenerThread.setDaemon(true);
  31. listenerThread.start();
  32. }
  33. }
  34. /**
  35. * blocking
  36. * remove and get last item from queue:BRPOP
  37. * @return
  38. */
  39. public T takeFromTail(int timeout) throws InterruptedException{
  40. lock.lockInterruptibly();
  41. try{
  42. List<byte[]> results = connection.bRPop(timeout, rawKey);
  43. if(CollectionUtils.isEmpty(results)){
  44. return null;
  45. }
  46. return (T)redisTemplate.getValueSerializer().deserialize(results.get(1));
  47. }finally{
  48. lock.unlock();
  49. }
  50. }
  51. public T takeFromTail() throws InterruptedException{
  52. return takeFromHead(0);
  53. }
  54. /**
  55. * 从队列的头,插入
  56. */
  57. public void pushFromHead(T value){
  58. listOperations.leftPush(value);
  59. }
  60. public void pushFromTail(T value){
  61. listOperations.rightPush(value);
  62. }
  63. /**
  64. * noblocking
  65. * @return null if no item in queue
  66. */
  67. public T removeFromHead(){
  68. return listOperations.leftPop();
  69. }
  70. public T removeFromTail(){
  71. return listOperations.rightPop();
  72. }
  73. /**
  74. * blocking
  75. * remove and get first item from queue:BLPOP
  76. * @return
  77. */
  78. public T takeFromHead(int timeout) throws InterruptedException{
  79. lock.lockInterruptibly();
  80. try{
  81. List<byte[]> results = connection.bLPop(timeout, rawKey);
  82. if(CollectionUtils.isEmpty(results)){
  83. return null;
  84. }
  85. return (T)redisTemplate.getValueSerializer().deserialize(results.get(1));
  86. }finally{
  87. lock.unlock();
  88. }
  89. }
  90. public T takeFromHead() throws InterruptedException{
  91. return takeFromHead(0);
  92. }
  93. @Override
  94. public void destroy() throws Exception {
  95. if(isClosed){
  96. return;
  97. }
  98. shutdown();
  99. RedisConnectionUtils.releaseConnection(connection, factory);
  100. }
  101. private void shutdown(){
  102. try{
  103. listenerThread.interrupt();
  104. }catch(Exception e){
  105. //
  106. }
  107. }
  108. class ListenerThread extends Thread {
  109. @Override
  110. public void run(){
  111. try{
  112. while(true){
  113. T value = takeFromHead();//cast exceptionyou should check.
  114. //逐个执行
  115. if(value != null){
  116. try{
  117. listener.onMessage(value);
  118. }catch(Exception e){
  119. //
  120. }
  121. }
  122. }
  123. }catch(InterruptedException e){
  124. //
  125. }
  126. }
  127. }
  128. }

3) 使用与测试:

[java] view
plain
 copy

  1. public static void main(String[] args) throws Exception{
  2. ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("classpath:spring-redis-beans.xml");
  3. RedisQueue<String> redisQueue = (RedisQueue)context.getBean("jedisQueue");
  4. redisQueue.pushFromHead("test:app");
  5. Thread.sleep(15000);
  6. redisQueue.pushFromHead("test:app");
  7. Thread.sleep(15000);
  8. redisQueue.destroy();
  9. }

在程序运行期间,你可以通过redis-cli(客户端窗口)执行“lpush”,你会发现程序的控制台仍然能够正常打印队列信息。

Spring-data-redis: 分布式队列的更多相关文章

  1. Spring Data Redis实现消息队列——发布/订阅模式

    一般来说,消息队列有两种场景,一种是发布者订阅者模式,一种是生产者消费者模式.利用redis这两种场景的消息队列都能够实现. 定义:生产者消费者模式:生产者生产消息放到队列里,多个消费者同时监听队列, ...

  2. Spring Data Redis—Pub/Sub(附Web项目源码)

    一.发布和订阅机制 当一个客户端通过 PUBLISH 命令向订阅者发送信息的时候,我们称这个客户端为发布者(publisher). 而当一个客户端使用 SUBSCRIBE 或者 PSUBSCRIBE ...

  3. Spring Data Redis—Pub/Sub(附Web项目源码) (转)

    一.发布和订阅机制 当一个客户端通过 PUBLISH 命令向订阅者发送信息的时候,我们称这个客户端为发布者(publisher). 而当一个客户端使用 SUBSCRIBE 或者 PSUBSCRIBE ...

  4. Spring Data Redis 详解及实战一文搞定

    SDR - Spring Data Redis的简称. Spring Data Redis提供了从Spring应用程序轻松配置和访问Redis的功能.它提供了与商店互动的低级别和高级别抽象,使用户免受 ...

  5. spring data redis RedisTemplate操作redis相关用法

    http://blog.mkfree.com/posts/515835d1975a30cc561dc35d spring-data-redis API:http://docs.spring.io/sp ...

  6. spring mvc Spring Data Redis RedisTemplate [转]

    http://maven.springframework.org/release/org/springframework/data/spring-data-redis/(spring-data包下载) ...

  7. Spring Data Redis简介以及项目Demo,RedisTemplate和 Serializer详解

    一.概念简介: Redis: Redis是一款开源的Key-Value数据库,运行在内存中,由ANSI C编写,详细的信息在Redis官网上面有,因为我自己通过google等各种渠道去学习Redis, ...

  8. Spring data redis的一个bug

    起因 前两天上线了一个新功能,导致线上业务的缓存总是无法更新,报错也是非常奇怪,redis.clients.jedis.exceptions.JedisConnectionException: Unk ...

  9. spring data redis 理解

    前言 Spring Data Redis project,应用了Spring概念来开发使用键值形式的数据存储的解决方案.我们(官方)提供了一个 "template" ,这是一个高级 ...

  10. Spring Data Redis 让 NoSQL 快如闪电(2)

    [编者按]本文作者为 Xinyu Liu,文章的第一部分重点概述了 Redis 方方面面的特性.在第二部分,将介绍详细的用例.文章系国内 ITOM 管理平台 OneAPM 编译呈现. 把 Redis ...

随机推荐

  1. Ext.Net 使用总结之查询条件中的起始日期

    2.关于查询条件中起始日期的布局方式 首先上一张图,来展示一下我的查询条件的布局,如下: 大多数时候,我们的查询条件都是一个条件占一个格子,但也有不同的时候,如:查询条件是起始日期,则需要将这两个条件 ...

  2. json中头疼的null

    在服务器返回 json 数据的时候,时常会出现如下数据 "somevalue":null 这个时候,json 解析的时候,就会吧这个 null 解析成 NSNull 的对象,我们向 ...

  3. 前端页面优化:javascript图片延迟加载

    自己写了个简单的图片延迟加载小插件. 功能如下: 页面刷新,当前屏幕图片直接加载真实地址,body被卷去的部分和下方未显示的部分的img均加载通用图片如:loding.gif 随着屏幕上下滚动加载相应 ...

  4. C++_基础_类和对象

    内容: (1)引用 (2)类型转换 (3)C++社区给C程序员的建议 (4)面向对象编程的概念 (5)类和对象 (6)构造函数 (7)初始化列表及其必要性 1.引用1.1 指针和引用的使用说明(1)指 ...

  5. 替换 window.location当中的某个参数的值(而其它值不变)JS代码

    在后台与前台的交互操作中,需要替换location当中的某个参数的值(而其它值不变)时,会用到以下函数: 说明: win:传入的窗口句柄,如,window或window.parent等forceAdd ...

  6. poj 1966 Cable TV Network 顶点连通度

    题目链接 给一个图, n个点m条边, 求至少去掉多少个点可以使得图不再联通.随便指定一个点为源点, 枚举其他点为汇点的情况, 跑网络流, 求其中最小的情况. 如果最后ans为inf, 说明是一个完全图 ...

  7. codeforces 242E. XOR on Segment 线段树

    题目链接 给n个数, 两种操作, 一种是求区间内的数的和, 一种是将区间内的数异或x. 异或x没有什么思路, 单个异或肯定超时, 区间异或也没有办法做....后来才知道可以按位建线段树, 这样建20棵 ...

  8. Use eplipse to develop Python project

    Source: This is the example how to use eclipse and python. http://www.360doc.com/content/15/0206/10/ ...

  9. .net mvc笔记4_依赖注入

    一.Building Loosely Coupled Components MVC模式最重要的特点就是关注点分离.我们希望应用中的组件能尽可能的独立,相互之间即使有依赖也要在我们的控制之下. 在理想情 ...

  10. 实现将VirtualBox 虚拟机转换为KVM虚拟机的步骤

    原来在桌面上一直使用virtualbox虚拟机管理程序(VMM)构建虚拟机安装不同的操作系统,现在 研究linux下的KVM,能否将已经建立的virtualBox虚拟客户机(guest)转换为KVM虚 ...