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. 获取和设置iframe中的元素

    http://www.cnblogs.com/gao-qiang/archive/2012/09/19/2694336.html http://java-my-life.iteye.com/blog/ ...

  2. Chain of Responsibility模式

    熟悉VC/MFC的都知道,VC是“基于消息,事件驱动”,消息在VC开发中起着举足轻重的作用.MFC提供了消息的处理的链式处理策略,处理消息的请求将沿着预定好的路径依次进行处理.消息的发送者并不知道该消 ...

  3. grunt打包过程中的注意点

    1.安装nodeJS   nodeJS下载地址: http://www.nodejs.org/download/ 2.   在Node.js command prompt 这个控制面板输入 npm i ...

  4. ngCookies模块

    Angular中ngCookies模块介绍 1.Cookie介绍 Cookie总是保存在客户端中,按在客户端中的存储位置,可分为内存Cookie和硬盘Cookie.内存Cookie由浏览器维护,保存在 ...

  5. setFocus一定要写在setLayout设置的后面,否则不起作用——使用setFocusPolicy为控件设置不同的焦点策略:Tab焦点,Click焦点,Wheel焦点和没有焦点

    QLineEdit* pEditor = new QLineEdit(m_strText);    pEditor->resize(.......);    pEditor->move(. ...

  6. CodeForces 235C Cyclical Quest(后缀自动机)

    [题目链接] http://codeforces.com/contest/235/problem/C [题目大意] 给出一个字符串,给出一些子串,问每个子串分别在母串中圆环匹配的次数,圆环匹配的意思是 ...

  7. Maven项目 Spring 单元测试

    使用maven创建web工程,将Spring配置文件applicationContext.xml放在src/resource下,用eclipse编译时提示class path resource [ap ...

  8. addEventlistener监听的事件完成一次后自动取消与jquery的one方法比较;animate动画制作,arguments.callee的用法

    <!doctype html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  9. 第八届河南省赛B.最大岛屿(dfs)

    B.最大岛屿 Time Limit: 1 Sec  Memory Limit: 128 MB Submit: 30  Solved: 18 [Submit][Status][Web Board] De ...

  10. Cocos2d-X 动作展示《一》

    因为Cocos2d-X中的动作较多,我将全部的动作制作成了一个滚动视图.每一个滚动视图上都有动作名,单击滚动视图就能够展示对应的动作 程序效果图: 使用滚动视图实现动作切换 动作展示 程序代码: 首先 ...