Redis 发布与订阅(五)
来源:互联网 发布:编程有什么用 编辑:程序博客网 时间:2024/05/23 22:17
1.什么是pub/sub
Pub/Sub功能(means Publish, Subscribe)即发布及订阅功能。基于事件的系统中,Pub/Sub是目前广泛使用的通信模型,它采用事件作为基本的通信机制,提供大规模系统所要求的松散耦合的交互模式:订阅者(如客户端)以事件订阅的方式表达出它有兴趣接收的一个事件或一类事件;发布者(如服务器)可将订阅者感兴趣的事件随时通知相关订阅者。熟悉设计模式的朋友应该了解这与23种设计模式中的观察者模式极为相似。
同样,Redis的pub/sub是一种消息通信模式,主要的目的是解除消息发布者和消息订阅者之间的耦合,redis作为一个pub/sub的server,在订阅者和发布者之间起到了消息路由的功能。
2.Redis pub/sub的实现
Redis通过publish和subscribe命令实现订阅和发布的功能。订阅者可以通过subscribe向redis server订阅自己感兴趣的消息类型。redis将信息类型称为通道(channel)。当发布者通过publish命令向redis server发送特定类型的信息时,订阅该消息类型的全部订阅者都会收到此消息。
命令实践
- PUBLISH
- 将信息 message 发送到指定的频道 channel。返回收到消息的客户端数量。
- SUBSCRIBE
- 订阅给指定频道的信息。
- 一旦客户端进入订阅状态,客户端就只可接受订阅相关的命令SUBSCRIBE、PSUBSCRIBE、UNSUBSCRIBE和PUNSUBSCRIBE除了这些命令,其他命令一律失效。
- UNSUBSCRIBE
- 取消订阅指定的频道,如果不指定,则取消订阅所有的频道。
127.0.0.1:6379> PUBLISH channel1.1 test(integer) 0 //有0个客户端收到消息127.0.0.1:6379> SUBSCRIBE channel1.1Reading messages... (press Ctrl-C to quit)1) "subscribe" //"subscribe"表示订阅成功的信息2) "channel1.1" //表示订阅成功的频道3) (integer) 1 //表示当前订阅客户端的数量//当发布者发布消息时,订阅者会收到如下消息1) "message" //表示接收到消息2) "channel1.1" //表示产生消息的频道3) "test" //表示消息的内容//当订阅者取消订阅时会显示如下:127.0.0.1:6379> UNSUBSCRIBE channel1.11) "unsubscribe" //表示成功取消订阅2) "channel1.1" //表示取消订阅的频道3) (integer) 0 //表示当前订阅客户端的数量//注:在redis-cli中无法测试UNSUBSCRIBE命令
- PSUBSCRIBE
- 订阅给定的模式(patterns)。
- PUNSUBSCRIBE
- 可以退订指定的规则,如果没有参数会退订所有的规则。
127.0.0.1:6379> PSUBSCRIBE channel1.*Reading messages... (press Ctrl-C to quit)1) "psubscribe"2) "channel1.*"3) (integer) 1//等待发布者发布消息127.0.0.1:6379> PUBLISH channel1.1 test1.1(integer) 1 //发布者在channel1.1发布消息1) "pmessage" //表示通过PSUBSCRIBE命令订阅而收到的2) "channel1.*" //表示订阅时使用的通配符3) "channel1.1" //表示收到消息的频道4) "test1.1" //表示消息内容127.0.0.1:6379> PUBLISH channel1.2 test1.2(integer) 1 //发布者在channel1.2发布消息1) "pmessage"2) "channel1.*"3) "channel1.2"4) "test1.2"127.0.0.1:6379> PUBLISH channel1.3 test1.3(integer) 1 //发布者在channel1.3发布消息1) "pmessage"2) "channel1.*"3) "channel1.3"4) "test1.3"127.0.0.1:6379> PUNSUBSCRIBE channal1.*1) "punsubscribe" //退订成功2) "channal1.*" //退订规则的通配符3) (integer) 0 //表示当前订阅客户端的数量
PUNSUBSCRIBE应该注意一下两点:
- 使用PUNSUBSCRIBE命令只能退订通过PSUBSCRIBE命令订阅的规则,不会影响SUBSCRIBE订阅的频道。
- 使用PUNSUBSCRIBE命令退订某个规则时不会将其中通配符展开,而是严格的进行==字符串匹配==,所以
PUNSUBSCRIBE *
无法退订PUNSUBSCRIBE channal1.*
规则,而必须使用PUNSUBSCRIBE channal1.*
才能退订
Pub/Sub在Java中的实现:
Redis引用:
<dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> <version>2.9.0</version></dependency>Redis驱动包提供了一个抽象类:JedisPubSub…继承这个类就完成了对客户端对订阅的监听。示例代码:
public class TestPubSub extends JedisPubSub { @Override public void onMessage(String channel, String message) { // TODO Auto-generated method stub System.out.println(channel + "," + message); } @Override public void onPMessage(String pattern, String channel, String message) { // TODO Auto-generated method stub System.out.println(pattern + "," + channel + "," + message); } @Override public void onSubscribe(String channel, int subscribedChannels) { // TODO Auto-generated method stub System.out.println("onSubscribe: channel[" + channel + "]," + "subscribedChannels[" + subscribedChannels + "]"); } @Override public void onUnsubscribe(String channel, int subscribedChannels) { // TODO Auto-generated method stub System.out.println( "onUnsubscribe: channel[" + channel + "], " + "subscribedChannels[" + subscribedChannels + "]"); } @Override public void onPUnsubscribe(String pattern, int subscribedChannels) { // TODO Auto-generated method stub System.out.println("onPUnsubscribe: pattern[" + pattern + "]," + "subscribedChannels[" + subscribedChannels + "]"); } @Override public void onPSubscribe(String pattern, int subscribedChannels) { System.out.println("onPSubscribe: pattern[" + pattern + "], " + "subscribedChannels[" + subscribedChannels + "]"); }}
如上所示,抽象类中存在六个方法。分别表示
监听到订阅模式接受到消息时的回调 (onPMessage)监听到订阅频道接受到消息时的回调 (onMessage )订阅频道时的回调( onSubscribe )取消订阅频道时的回调( onUnsubscribe )订阅频道模式时的回调 ( onPSubscribe )取消订阅模式时的回调( onPUnsubscribe )
运行我们刚刚编写的类:
@Test public void pubsubjava() { // TODO Auto-generated method stub Jedis jr = null; try { jr = new Jedis("127.0.0.1", 6379, 0);// redis服务地址和端口号 jr.auth("wx950709"); TestPubSub sp = new TestPubSub(); // jr客户端配置监听两个channel sp.subscribe(jr.getClient(), "news.share", "news.blog"); } catch (Exception e) { e.printStackTrace(); } finally{ if(jr!=null){ jr.disconnect(); } } }
从代码中我们不难看出,我们声明的一个redis链接在设置监听后就可以执行一些操作,例如发布消息,订阅消息等。。。
当运行上述代码后会在控制台输出:
onSubscribe: channel[news.share],subscribedChannels[1]onSubscribe: channel[news.blog],subscribedChannels[2]//onSubscribe方法成功运行
此时当在有客户端向new.share或者new.blog通道publish消息时,onMessage方法即可被相应。(jedis.publish(channel, message))。
Pub/Sub在spring中的实践
导入依赖jar<dependency> <groupId>org.springframework.data</groupId> <artifactId>spring-data-redis</artifactId> <version>1.8.3.RELEASE</version></dependency><dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> <version>2.9.0</version></dependency>
三大组件作用:
使用 Jedis作为Java客户端,需要三个组件:连接Redis的工厂 -> JedisConnectionFactoryredis操作模板-> RedisTemplate消息监听者-> RedisMessageListenerContainer
Spring配置redis的链接:
import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;import org.springframework.data.redis.core.RedisTemplate;import org.springframework.data.redis.listener.ChannelTopic;import org.springframework.data.redis.listener.RedisMessageListenerContainer;import org.springframework.data.redis.listener.adapter.MessageListenerAdapter;import org.springframework.data.redis.serializer.GenericToStringSerializer;import org.springframework.data.redis.serializer.StringRedisSerializer;import cn.com.qiqi.springMvcTestRedis.listener.RedisMessageListener;@Configurationpublic class AppConfig {@BeanJedisConnectionFactory jedisConnectionFactory(){JedisConnectionFactory jedisConnectionFactory=new JedisConnectionFactory();jedisConnectionFactory.setHostName("192.168.252.2");jedisConnectionFactory.setPort(6389);return jedisConnectionFactory;}@BeanRedisTemplate<String, Object> redisTemplate(){// final RedisTemplate<String, Object> template = new RedisTemplate<String, Object>();// template.setConnectionFactory(jedisConnectionFactory());// template.setDefaultSerializer(new StringRedisSerializer()); final RedisTemplate< String, Object > template = new RedisTemplate< String, Object >(); template.setConnectionFactory( jedisConnectionFactory() ); template.setKeySerializer( new StringRedisSerializer() ); template.setHashValueSerializer( new GenericToStringSerializer< Object >( Object.class ) ); template.setValueSerializer( new GenericToStringSerializer< Object >( Object.class ) ); return template;}@BeanMessageListenerAdapter messageListener(){return new MessageListenerAdapter(new RedisMessageListener());}@BeanRedisMessageListenerContainer redisContainer(){final RedisMessageListenerContainer container=new RedisMessageListenerContainer();container.setConnectionFactory(jedisConnectionFactory());container.addMessageListener(messageListener(), topic());return container;}// 这里 是自己 ---------- 可不用//RedisPublisher redispublisher(){//return new RedisPublisherImpl(redisTemplate(), topic());//}@BeanChannelTopic topic(){return new ChannelTopic("pubsub:queue");}}
如上的配置即配置了对Redis的链接。在RedisTemplate中没有设置ip端口等信息则全部为默认的。在配置类中的将ChannelTopic加入IOC容器。则在Spring启动时会在一个RedisTemplate(一个对Redis的链接)中设置的一个channel,即pubsub:queue。
在上述配置中,RedisMessageListener是我们生成的,这个类即为核心监听类,RedisTemplate接受到数据如何处理就是在该类中处理的。
public class RedisMessageListener implements MessageListener { @Override public void onMessage( final Message message, final byte[] pattern ) { System.out.println("Message received: " + message.toString() ); }}
现在我们在获取RedisTemplate,并给pubsub:queue这个channel publish数据。
public class PubSubMain { RedisTemplate<String,Object> redisTemplate; public void execute() { String channel = "pubsub:queue"; redisTemplate.convertAndSend(channel, "from testData"); } public static void main(String[] args) { ApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class); PubSubMain pubSubMain = new PubSubMain(); pubSubMain.redisTemplate = (RedisTemplate<String, Object>) applicationContext.getBean("redisTemplate"); pubSubMain.execute(); }}
此时运行main 方法:
Message received: from testate//表明接受成功,当在命令行中启动一个客户端并publish时依然可以在客户端打印出message
阅读全文
0 0
- Redis 发布与订阅(五)
- Redis 发布与订阅
- Redis 发布与订阅
- Redis 订阅与发布
- Redis 发布与订阅
- Redis-发布与订阅
- redis发布与订阅
- Redis 订阅与发布
- redis-订阅与发布
- redis点滴(三)发布与订阅
- Redis发布与订阅(pub/sub)
- Redis的订阅与发布
- redis发布与订阅-初探
- redis 订阅与发布功能
- Redis发布与订阅模式
- redis-消息发布与订阅
- 【Redis基础】发布与订阅
- redis发布与订阅机制
- CRegKey操作注册表
- Add and Search Word
- FFMPEG视音频编解码学习笔记
- JDK1.8新特性测试☞排序
- 数据库性能问题诊断与分析
- Redis 发布与订阅(五)
- 极限学习机(ELM)的使用
- [leetcode]40. Combination Sum II(Java)
- HeadFirstPython---------(五)第七章(Web开发__集成在一起)
- CountDownTimer使用之页面即时跳转
- 离散基础 (15). 调和级数分析
- Math
- HashMap集合键是Student值是String的案例
- Python图像批处理(图像非监督分类)