【转】 使用Redis的Pub/Sub来实现类似于JMS的消息持久化
http://blog.csdn.net/canot/article/details/52040415
关于个人对Redis提供的Pub/Sub机制的认识在上一篇博客中涉及到了,也提到了关于如何避免Redis的Pub/Sub的一个最大的缺陷的思路—消息的持久化(http://blog.csdn.net/canot/article/details/51975566)。这篇文章主要是关于其思路(Redis的Pub/Sub的消息持久化)的代码实现:
Pub/Sub机制中最核心的Listener的实现:
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPubSub;
import redis.clients.util.RedisInputStream;
public class PubSubListener extends JedisPubSub {
private String clientId;
private HandlerRedis handlerRedis;
// 生成PubSubListener的时候必须制定一个id
public PubSubListener(String cliendId, Jedis jedis) {
this.clientId = cliendId;
jedis.auth("xxxx");
handlerRedis = new HandlerRedis(jedis);
}
@Override
public void onMessage(String channel, String message) {
if ("quit".equals(message)) {
this.unsubscribe(channel);
}
handlerRedis.handler(channel, message);
}
// 真正处理接受的地方
private void message(String channel, String message) {
System.out.println("message receive:" + message + ",channel:" + channel + "...");
}
@Override
public void onPMessage(String pattern, String channel, String message) {
// TODO Auto-generated method stub
}
@Override
public void onSubscribe(String channel, int subscribedChannels) {
// 将订阅者保存在一个"订阅活跃者集合中"
handlerRedis.subscribe(channel);
System.out.println("subscribe:" + channel);
}
@Override
public void onUnsubscribe(String channel, int subscribedChannels) {
handlerRedis.ubsubscribe(channel);
System.out.println("unsubscribe:" + channel);
}
@Override
public void onPUnsubscribe(String pattern, int subscribedChannels) {
// TODO Auto-generated method stub
}
@Override
public void onPSubscribe(String pattern, int subscribedChannels) {
// TODO Auto-generated method stub
}
class HandlerRedis {
private Jedis jedis;
public HandlerRedis(Jedis jedis) {
this.jedis = jedis;
}
public void handler(String channel, String message) {
int index = message.indexOf("/");
if (index < 0) {
// 消息不合法,丢弃
return;
}
Long txid = Long.valueOf(message.substring(0, index));
String key = clientId + "/" + channel;
while (true) {
String lm = jedis.lindex(key, 0);// 获取第一个消息
if (lm == null) {
break;
}
int li = lm.indexOf("/");
// 如果消息不合法,删除并处理
if (li < 0) {
String result = jedis.lpop(key);// 删除当前message
// 为空
if (result == null) {
break;
}
message(channel, lm);
continue;
}
Long lxid = Long.valueOf(lm.substring(0, li));// 获取消息的txid
// 直接消费txid之前的残留消息
if (txid >= lxid) {
jedis.lpop(key);// 删除当前message
message(channel, lm);
continue;
} else {
break;
}
}
}
// 持久化订阅操作
public void subscribe(String channel) {
// 保证在订阅者集合中的格式为 唯一标识符/订阅的通道
String key = clientId + "/" + channel;
// 判断该客户端是否在集合中存在
boolean isExist = jedis.sismember("PERSIS_SUB", key);
if (!isExist) {
// 不存在则添加
jedis.sadd("PERSIS_SUB", key);
}
}
public void ubsubscribe(String channel) {
String key = clientId + "/" + channel;
// 从“活跃订阅者”集合中
jedis.srem("PERSIS_SUB", key);
// 删除“订阅者消息队列”
jedis.del(channel);
}
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
- 121
- 122
- 123
- 124
- 125
Listener中定义了一个内部类HandlerRedis。Listener类将onMessage以及onSubscribe两个方法交付于HandlerRedis。Handler处理这个方法的时候也即进行着队列的维护。Listener类中定义了一个message()方法,该方法是handler的回调方法,即真正的处理消息的地方。
通道的订阅客户端类:
import redis.clients.jedis.Jedis;
public class SubClient {
private Jedis jedis;
private PubSubListener listener;
public SubClient(String host,PubSubListener pubSubListener){
jedis = new Jedis(host);
jedis.auth("XXXXX");
this.listener = pubSubListener;
}
public void sub(String channel){
jedis.subscribe(listener, channel);
}
public void unsubscribe(String channel){
listener.unsubscribe(channel);
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
通道的消息发布的客户端:
import java.util.Set;
import redis.clients.jedis.Jedis;
public class PubClient {
private Jedis jedis;
public PubClient(String host) {
jedis = new Jedis(host);
jedis.auth("wx950709");
}
/**
* 发布的每条消息,都需要在“订阅者消息队列”中持久
*
* @param message
*/
public void put(String message) {
//获取所有活跃的消息接收者客户端 clientID/channel
Set<String> subClients = jedis.smembers("PERSIS_SUB");
for (String subs : subClients) {
// 保存每个客户端的消息
jedis.rpush(subs, message);
}
}
public void publish(String channel, String message) {
// 每个消息,都有具有一个全局唯一的id
// txid为了防止订阅端在数据处理时“乱序”,这就要求订阅者需要解析message
Long txid = jedis.incr("MESSAGE_TXID");
String content = txid + "/" + message;
this.put(content);
jedis.publish(channel, content);//为每个消息设定id,最终消息格式1000/messageContent
}
public void close(String channel){
jedis.publish(channel, "quit");
jedis.del(channel);//删除
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
测试引导类:
import redis.clients.jedis.Jedis;
public class Main {
public static void main(String[] args) throws Exception{
PubClient pubClient = new PubClient("127.0.0.1");
final String channel = "pubsub-channel222";
PubSubListener listener = new PubSubListener("client_one", new Jedis("127.0.0.1"));
SubClient subClient = new SubClient("127.0.0.1", listener);
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
//在API级别,此处为轮询操作,直到unsubscribe调用,才会返回
subClient.sub(channel);
}
});
t1.setDaemon(true);
t1.start();
int i = 0;
while(i < 2){
pubClient.publish(channel, "message"+i);
i++;
Thread.sleep(1000);
}
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29

【转】 使用Redis的Pub/Sub来实现类似于JMS的消息持久化的更多相关文章
- redis的Pub/Sub
redis的Pub/Sub机制类似于广播架构,Subscriber相当于收音机,可以收听多个channel(频道),Publisher(电台)可以在channel中发布信息. 命令介绍 PUBLISH ...
- Redis的Pub/Sub机制存在的问题以及解决方案
Redis的Pub/Sub机制使用非常简单的方式实现了观察者模式,但是在使用过程中我们发现,它仅仅是实现了发布订阅机制,但是很多的场景没有考虑到.例如一下的几种场景: 1.数据可靠性无法保证 一个re ...
- Redis的Pub/Sub客户端实现
前言 在学习T-io框架,从写一个Redis客户端开始一文中,已经简单介绍了Redis客户端的实现思路,并且基础架构已经搭建完成,只不过支持的命令不全,不过后期在加命令就会很简单了.本篇就要实现P ...
- Redis实战——Redis的pub/Sub(订阅与发布)在java中的实现
借鉴:https://blog.csdn.net/canot/article/details/51938955 1.什么是pub/sub Pub/Sub功能(means Publish, Subscr ...
- 分布式缓存技术redis学习系列(三)——redis高级应用(主从、事务与锁、持久化)
上文<详细讲解redis数据结构(内存模型)以及常用命令>介绍了redis的数据类型以及常用命令,本文我们来学习下redis的一些高级特性. 安全性设置 设置客户端操作秘密 redis安装 ...
- 分布式缓存技术redis学习(三)——redis高级应用(主从、事务与锁、持久化)
上文<详细讲解redis数据结构(内存模型)以及常用命令>介绍了redis的数据类型以及常用命令,本文我们来学习下redis的一些高级特性.目录如下: 安全性设置 设置客户端操作秘密 客户 ...
- 分布式缓存技术redis系列(三)——redis高级应用(主从、事务与锁、持久化)
上文<详细讲解redis数据结构(内存模型)以及常用命令>介绍了redis的数据类型以及常用命令,本文我们来学习下redis的一些高级特性. 安全性设置 设置客户端操作秘密 redis安装 ...
- Linux07 /redis的配置、五大数据类型、发布订阅、持久化、主从复制、哨兵配置、集群搭建
Linux07 /redis的配置.五大数据类型.发布订阅.持久化.主从复制.哨兵配置.集群搭建 目录 Linux07 /redis的配置.五大数据类型.发布订阅.持久化.主从复制.哨兵配置.集群搭建 ...
- redis的pub/sub命令
Redis 发布订阅 Redis 发布订阅(pub/sub)是一种消息通信模式:发送者(pub)发送消息,订阅者(sub)接收消息. Redis 客户端可以订阅任意数量的频道. 下图展示了频道 cha ...
随机推荐
- contentprovider的学习实例总结
工作中遇到了contentprovider数据共享机制,下面来总结一下: 一.ContentProvider简介 当应用继承ContentProvider类,并重写该类用于提供数据和存储数 ...
- js:数据结构笔记9--二叉树
树:以分层的方式存储数据:节点:根节点,子节点,父节点,叶子节点(没有任何子节点的节点):层:根节点开始0层: 二叉树:每个节点子节点不超过两个:查找快(比链表),添加,删除快(比数组): BST:二 ...
- Swift 1.1语言函数参数的特殊情况本地参数名外部参数名
Swift 1.1语言函数参数的特殊情况本地参数名外部参数名 7.4 函数参数的特殊情况 声明定义有参函数时,为函数的每一个参数都定义了参数名称.根据参数名定义的形式不同,函数参数包括本地参数和外部 ...
- Android 滑动冲突处理
要想解决滑动冲突就必须好好理解 Android 的事件分发机制.不了解 Android 事件分发机制的请先参考资料学习一下. 一般有 2 种方法 1 外部拦截法 这个非常简单,因为事件是从父 view ...
- Color the Ball[HDU1199]
Color the Ball Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)To ...
- 数据仓库原理<1>:数据库与数据仓库
updated 2015.8.27 updated 2015.8.26 updated 2015.8.23 0. 说明 <数据仓库原理>系列博文,是笔者在学习数据仓库与商业智能时的读书笔记 ...
- 【BZOJ】1189: [HNOI2007]紧急疏散evacuate(二分+bfs+网络流)
http://www.lydsy.com/JudgeOnline/problem.php?id=1189 表示完全不会QAQ.... 于是膜拜题解orz 二分时间........... 于是转换成判定 ...
- HttpClient_HttpClient 4.3.6 HTTP状态管理
HTTP状态管理 最初的HTTP被设计成以状态.请求/应答为导向的协议,它被制作成是没有特殊条款的,以便在状态会话中能交换逻辑关系请求/应答.HTTP协议越来越受欢迎和被采用,越来越多的系统会在应用程 ...
- 平易近人、兼容并蓄——Spark SQL 1.3.0概览
自2013年3月面世以来,Spark SQL已经成为除Spark Core以外最大的Spark组件.除了接过Shark的接力棒,继续为Spark用户提供高性能的SQL on Hadoop解决方案之外, ...
- 关于javascript在作用域中的变量定义你所不知道的一些东西
//先看一段代码 var a = 100; function test(){ alert(a); var a = 200; } test(); /* 结果:undifined 原因:js引擎在执行sc ...