在工业生产设计中,我们往往需要实现一个基于消息订阅的模式,用来对非定时的的消息进行监听订阅。

这种设计模式在 总线设计模式中得到体现。微软以前的WCF中实现了服务总线 ServiceBus的设计模式。然并卵。WCF已经好像是上个世纪的产物................

基于事件订阅的模式,比如 EventBus类的组件产品。但是往往设计比较复杂。

如果依赖于 Redis做事件消息推送。那就大大简化了这种设计模式,而且性能也比较客观。

Redis在 2.0之后的版本中 实现了 事件推送的  pub/sub命令

PSUBSCRIBE订阅一个或多个符合给定模式的频道
PUBLISH将信息message 发送到指定的频道channel
PUBSUB是一个查看订阅与发布系统状态的内省命令
PUBSUB CHANNELS pattern 列出当前的活跃频道
PUBSUB NUMSUB channel-1 channel-N 返回给定频道的订阅者数量
PUBSUB NUMPAT 返回订阅模式的数量
PUNSUBSCRIBE 指示客户端退订所有给定模式
SUBSCRIBE 订阅给定的一个或多个频道的信息
UNSUBSCRIBE 指示客户端退订给定的频道

P 开头的 (pattern)支持通配符模式。

简单例子(来自:http://www.yiibai.com/redis/redis_pub_sub.html)

以下举例说明如何发布用户的概念工作。在下面的例子给出一个客户端订阅一个通道名为redisChat

redis 127.0.0.1:6379> SUBSCRIBE redisChat

Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "redisChat"
3) (integer) 1

现在,两个客户端都发布在同一个通道名redisChat消息及以上的订阅客户端接收消息。

redis 127.0.0.1:6379> PUBLISH redisChat "Redis is a great caching technique"

(integer) 1

redis 127.0.0.1:6379> PUBLISH redisChat "Learn redis by tutorials point"

(integer) 1

1) "message"
2) "redisChat"
3) "Redis is a great caching technique"
1) "message"
2) "redisChat"
3) "Learn redis by tutorials point"

上面的代码简单的演示了,订阅信道,向指定的信道发布消息。然后消息推送到订阅者。

在 redis-cli客户端中 推送消息的时候,返回成功发送到订阅者的数目。

如:(integer) 1

原理:

RedisServer包含两个重要的结构:
1. channels:实际上就是一个key-value的Map结构,key为订阅地频道,value为Client的List
2. patterns:存放模式+client地址的列表

 

流程:从pubsub_channels中找出跟publish中channel相符的clients-list,然后再去pubsub_patterns中找出每一个相符的pattern和client。向这些客户端发送publish的消息。

订阅信道

消息推送

在C#中的实现

基于

ServiceStack.Redis

             Task.Factory.StartNew(() =>
{
var client = new RedisClient("192.168.1.100", , "你的密码");
try
{
var isOpen = client.Ping();
if (isOpen == false)
{
return;
}
var sub1 = client.CreateSubscription(); //接受消息的委托
sub1.OnMessage = (chanel, message) =>
{
Console.WriteLine("chanel is :{0}", chanel);
Console.WriteLine("message is :{0}", message);
};
sub1.SubscribeToChannels(new string[] { "testchat" });//注意:订阅信道的时候 会开启阻塞模式,所以,需要将监听放到单独的线程里
}
catch (Exception)
{ throw;
} });

注意:在程序终止或者类的实例被销毁的时候,请将订阅者实例注销掉,否则,在redis中一直存在这个订阅者。

1 使用idispose 显示释放

2 使用析构函数 CLR回收的时候 释放

sub1.Dispose();

例如:

  public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
} protected virtual void Dispose(bool disposing)
{
if (disposing)
{
if (redisClient != null)
redisClient.Dispose();
if (subscription != null)
subscription.Dispose();
if (log != null)
log.Dispose();
}
} ~RedisEx()
{
Dispose(false);
}

官方推荐这种写法

 var clientsManager = new PooledRedisClientManager(new string[] { "密码@192.168.1.200:6379" });
var redisPubSub = new RedisPubSubServer(clientsManager, new string[] { "testchat" })
{
OnMessage = (channel, msg) => {
Console.WriteLine("方式2订阅演示.............");
Console.WriteLine("Received '{0}' from '{1}'", msg, channel);
}
}.Start();

To use RedisPubSubServer, initialize it with the channels you want to subscribe to and assign handlers for each of the events you want to handle.

At a minimum you'll want to handle OnMessage:

Calling Start() after it's initialized will get it to start listening and processing any messages published to the subscribed channels.

官方文档:https://github.com/ServiceStack/ServiceStack.Redis

附加文档

 
 
 

PSUBSCRIBE(订阅一个或多个符合给定模式的频道)

PSUBSCRIBE pattern [pattern …] 
订阅一个或多个符合给定模式的频道。 
每个模式以* 作为匹配符,比如it* 匹配所有以it 开头的频道( it.news 、it.blog 、it.tweets 等等), 
news.* 匹配所有以news. 开头的频道( news.it 、news.global.today 等等),诸如此类。 
可用版本: >= 2.0.0 
时间复杂度: O(N),N 是订阅的模式的数量。 
返回值: 接收到的信息(请参见下面的代码说明)。

redis> psubscribe news.*
Reading messages... (press Ctrl-C to quit)
1) "psubscribe" # 返回值的类型:显示订阅成功
2) "news.*" # 订阅的模式
3) (integer) 1 # 目前已订阅的模式的数量 1) "pmessage" # 返回值的类型:信息
2) "news.*" # 信息匹配的模式
3) "news.it" # 信息本身的目标频道
4) "Google buy Motorola" # 信息的内容

PUBLISH(将信息message 发送到指定的频道channel )

PUBLISH channel message 
将信息message 发送到指定的频道channel 。 
可用版本: >= 2.0.0 
时间复杂度: O(N+M),其中N 是频道channel 的订阅者数量,而M 则是使用模式订阅(subscribed 
patterns) 的客户端的数量。 
返回值: 接收到信息message 的订阅者数量。

# 对没有订阅者的频道发送信息
redis> publish bad_channel "can any body hear me?"
(integer) 0
# 向有一个订阅者的频道发送信息
redis> publish msg "good morning"
(integer) 1
# 向有多个订阅者的频道发送信息
redis> publish chat_room "hello~ everyone"
(integer) 3

PUBSUB(是一个查看订阅与发布系统状态的内省命令)

PUBSUB [argument [argument …]] 
PUBSUB 是一个查看订阅与发布系统状态的内省命令,它由数个不同格式的子命令组成,以下将分别对这 
些子命令进行介绍。 
可用版本: >= 2.8.0

PUBSUB CHANNELS [pattern] (列出当前的活跃频道)

列出当前的活跃频道。 
活跃频道指的是那些至少有一个订阅者的频道,订阅模式的客户端不计算在内。 
pattern 参数是可选的: 
• 如果不给出pattern 参数,那么列出订阅与发布系统中的所有活跃频道。 
• 如果给出pattern 参数,那么只列出和给定模式pattern 相匹配的那些活跃频道。 
复杂度: O(N) ,N 为活跃频道的数量(对于长度较短的频道和模式来说,将进行模式匹配的复杂度视为常 
数)。 
返回值: 一个由活跃频道组成的列表。

# client-1 订阅news.it 和news.sport 两个频道
client-1> SUBSCRIBE news.it news.sport
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "news.it"
3) (integer) 1
1) "subscribe"
2) "news.sport"
3) (integer) 2
# client-2 订阅news.it 和news.internet 两个频道
client-2> SUBSCRIBE news.it news.internet
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "news.it"
3) (integer) 1
1) "subscribe"
2) "news.internet"
3) (integer) 2
# 首先, client-3 打印所有活跃频道
# 注意,即使一个频道有多个订阅者,它也只输出一次,比如news.it
client-3> PUBSUB CHANNELS
1) "news.sport"
2) "news.internet"
3) "news.it"
# 接下来, client-3 打印那些与模式news.i* 相匹配的活跃频道
# 因为news.sport 不匹配news.i* ,所以它没有被打印
redis> PUBSUB CHANNELS news.i*
1) "news.internet"
2) "news.it"

PUBSUB NUMSUB [channel-1 … channel-N] (返回给定频道的订阅者数量)

返回给定频道的订阅者数量,订阅模式的客户端不计算在内。 
复杂度: O(N) ,N 为给定频道的数量。 
返回值: 一个多条批量回复( Multi-bulk reply),回复中包含给定的频道,以及频道的订阅者数量。格式为:频道 channel-1 ,channel-1 的订阅者数量,频道 channel-2 ,channel-2 的订阅者数量,诸如此类。 
回复中频道的排列顺序和执行命令时给定频道的排列顺序一致。不给定任何频道而直接调用这个命令也是可以的,在这种情况下,命令只返回一个空列表。

# client-1 订阅 news.it 和 news.sport 两个频道
client-1> SUBSCRIBE news.it news.sport
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "news.it"
3) (integer) 1
1) "subscribe"
2) "news.sport"
3) (integer) 2
# client-2 订阅 news.it 和 news.internet 两个频道
client-2> SUBSCRIBE news.it news.internet
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "news.it"
3) (integer) 1
1) "subscribe"
2) "news.internet"
3) (integer) 2
# client-3 打印各个频道的订阅者数量
client-3> PUBSUB NUMSUB news.it news.internet news.sport news.music
1) "news.it" # 频道
2) "2" # 订阅该频道的客户端数量
3) "news.internet"
4) "1"
5) "news.sport"
6) "1"
7) "news.music" # 没有任何订阅者
8) "0"

PUBSUB NUMPAT (返回订阅模式的数量)

注意,这个命令返回的不是订阅模式的客户端的数量,而是客户端订阅的所有模式的数量总和。 
复杂度: O(1) 。 
返回值: 一个整数回复( Integer reply)。

# client-1 订阅 news.* 和 discount.* 两个模式
client-1> PSUBSCRIBE news.* discount.*
Reading messages... (press Ctrl-C to quit)
1) "psubscribe"
2) "news.*"
3) (integer) 1
1) "psubscribe"
2) "discount.*"
3) (integer) 2
# client-2 订阅 tweet.* 一个模式
client-2> PSUBSCRIBE tweet.*
Reading messages... (press Ctrl-C to quit)
1) "psubscribe"
2) "tweet.*"
3) (integer) 1
# client-3 返回当前订阅模式的数量为 3
client-3> PUBSUB NUMPAT
(integer) 3
# 注意,当有多个客户端订阅相同的模式时,相同的订阅也被计算在 PUBSUB NUMPAT 之内
# 比如说,再新建一个客户端 client-4 ,让它也订阅 news.* 频道
client-4> PSUBSCRIBE news.*
Reading messages... (press Ctrl-C to quit)
1) "psubscribe"
2) "news.*"
3) (integer) 1
# 这时再计算被订阅模式的数量,就会得到数量为 4
client-3> PUBSUB NUMPAT
(integer) 4
  • 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

PUNSUBSCRIBE (指示客户端退订所有给定模式)

PUNSUBSCRIBE [pattern [pattern …]] 
指示客户端退订所有给定模式。 
如果没有模式被指定,也即是,一个无参数的 PUNSUBSCRIBE 调用被执行,那么客户端使用PSUBSCRIBE 
命令订阅的所有模式都会被退订。在这种情况下,命令会返回一个信息,告知客户端所有被退订的模式。 
可用版本: >= 2.0.0 
时间复杂度: O(N+M) ,其中 N 是客户端已订阅的模式的数量,M 则是系统中所有客户端订阅的模式的数量。 
返回值: 这个命令在不同的客户端中有不同的表现。

SUBSCRIBE (订阅给定的一个或多个频道的信息)

订阅给定的一个或多个频道的信息。 
可用版本: >= 2.0.0 
时间复杂度: O(N),其中 N 是订阅的频道的数量。 
返回值: 接收到的信息 (请参见下面的代码说明)。

# 订阅 msg 和 chat_room 两个频道
# 1 - 6 行是执行 subscribe 之后的反馈信息
# 第 7 - 9 行才是接收到的第一条信息
# 第 10 - 12 行是第二条
redis> subscribe msg chat_room
Reading messages... (press Ctrl-C to quit)
1) "subscribe" # 返回值的类型:显示订阅成功
2) "msg" # 订阅的频道名字
3) (integer) 1 # 目前已订阅的频道数量
1) "subscribe"
2) "chat_room"
3) (integer) 2
1) "message" # 返回值的类型:信息
2) "msg" # 来源 (从那个频道发送过来)
3) "hello moto" # 信息内容
1) "message"
2) "chat_room"
3) "testing...haha"

UNSUBSCRIBE (指示客户端退订给定的频道)

UNSUBSCRIBE [channel [channel …]] 
指示客户端退订给定的频道。 
如果没有频道被指定,也即是,一个无参数的 UNSUBSCRIBE 调用被执行,那么客户端使用SUBSCRIBE 命令订阅的所有频道都会被退订。在这种情况下,命令会返回一个信息,告知客户端所有被退订的频道。 
可用版本: >= 2.0.0 
时间复杂度: O(N) ,N 是客户端已订阅的频道的数量。 
返回值: 这个命令在不同的客户端中有不同的表现。

 
 

参考文档:

http://blog.csdn.net/u011506468/article/details/47337839

http://my.oschina.net/itblog/blog/601284

http://www.oschina.net/code/snippet_584165_52231

http://redis.io/topics/pubsub

基于Redis的消息订阅/发布的更多相关文章

  1. 基于redis的消息订阅与发布

    Redis 的 SUBSCRIBE 命令可以让客户端订阅任意数量的频道, 每当有新信息发送到被订阅的频道时, 信息就会被发送给所有订阅指定频道的客户端. 作为例子, 下图展示了频道 channel1  ...

  2. [SpingBoot guides系列翻译]Redis的消息订阅发布

    Redis的消息 部分参考链接 原文 CountDownLatch 概述 目的 这节讲的是用Redis来实现消息的发布和订阅,这里会使用Spring Data Redis来完成. 这里会用到两个东西, ...

  3. SpringBoot+Redis 实现消息订阅发布

    什么是 Redis Redis 是一个开源的使用 ANSI C语言编写的内存数据库,它以 key-value 键值对的形式存储数据,高性能,读取速度快,也提供了持久化存储机制. Redis 通常在项目 ...

  4. Redis的消息订阅/发布 Utils工具类

    package cn.cicoding.utils; import org.json.JSONException; import org.json.JSONObject; import redis.c ...

  5. Redis的消息订阅及发布及事务机制

    Redis的消息订阅及发布及事务机制 订阅发布 SUBSCRIBE PUBLISH 订阅消息队列及发布消息. # 首先要打开redis-cli shell窗口 一个用于消息发布 一个用于消息订阅 # ...

  6. Redis之Redis消息订阅发布简介

    概念: Redis消息订阅发布是进程间的一种消息通信模式,发送者pub发送消息,订阅者sub接收消息. 使用须知: 需要先订阅后发布,才能接收到消息.在订阅时,相当于创建了可供发布的频道. 案例: ( ...

  7. Redis实现消息的发布/订阅

    利用spring-boot结合redis进行消息的发布与订阅: 发布: class Publish { private static String topicName = “Topic:chat”; ...

  8. spring boot: 用redis的消息订阅功能更新应用内的caffeine本地缓存(spring boot 2.3.2)

    一,为什么要更新caffeine缓存? 1,caffeine缓存的优点和缺点 生产环境中,caffeine缓存是我们在应用中使用的本地缓存, 它的优势在于存在于应用内,访问速度最快,通常都不到1ms就 ...

  9. Spring Boot使用Redis进行消息的发布订阅

    今天来学习如何利用Spring Data对Redis的支持来实现消息的发布订阅机制.发布订阅是一种典型的异步通信模型,可以让消息的发布者和订阅者充分解耦.在我们的例子中,我们将使用StringRedi ...

随机推荐

  1. HDOJ 1330 Deck(叠木块-物理题啊!贪心算法用到了一点)

    Problem Description A single playing card can be placed on a table, carefully, so that the short edg ...

  2. 让PV10000+的秘诀

    原文地址:http://www.phonegap100.com/article-410-1.html 让PV10000+的秘诀 2015-5-4 21:49| 发布者: admin| 查看: 122| ...

  3. [转]The culture name list in C#

    Culture Names [C#] This example shows how to get all culture names in the .NET Framework. Use static ...

  4. StoryBoard 设置TabBar SelectImage 和tintColor

    如图:StoryBoard 结构是 Tabbar + Navi + ViewController 需求:需要修改TabBar的Image 和SelectImage 设置Image 设置SelectIm ...

  5. Object-C 类定义 -- 笔记

    OC类分为两个文件,一个是.h文件,一个是.m文件 .h文件 存放类,函数的申明 .文件 存放类的具体实现 类申明使用关键字 @interface @end来申明 类实现使用关键字@implement ...

  6. 关于AppStore上传相关问题

    1.电脑本地证书CertificateSigningRequest.certSigningRequest一定要一致(包括开发者证书,尤其是发布证书要一致,否则无法正常上传),此类错误Xcode一般会提 ...

  7. RTX51 Tiny实时操作系统学习笔记—初识RTX51 Tiny

     一,RTX51 Tiny简单介绍    RTX51 Tiny是一种实时操作系统(RTOS),能够用它来建立多个任务(函数)同一时候运行的应用(从宏观上看是同一时候运行的,但从微观上看,还是独立运行的 ...

  8. 基于live555的视频直播 DM368IPNC RTSP分析

    因需要,从个人的理解顺序和需求角度对live555的分析与开发整理,包含RTSP Server与RTSP Client.如何直播H.264流与JPEG流等,均进行了探讨,对live555的初学者有一定 ...

  9. 程序员实用的 MySQL sql 语句

    这儿只讲究实用,  程序员编程时常用到的 MySQL的 sql语句(不包括基本的 select, update, delete 等语句). 1. 添加一个用户build,并赋予所有权限的命令 gran ...

  10. linux shell 切换到ROOT用户

    #!/bin/bash expect -c "        set timeout 1000        spawn /bin/su - root         expect \&qu ...