用Spring data Redis实现消息的发布与订阅

       在SDR(Spring Data Redis)中提供了一套基于Redis的消息集成,这个消息集成与在Sping框架中的JMS的在功能上和命名上非常相似,正因为如此开发者可以轻而易举的通过Redis的服务器实现消息的发布和订阅。

       在SDR中实现消息发布订阅有两种方法,这里我只介绍其中一种比较简便的方法。这个方法是基于SDR提供的RedisMessageListenerContainer来实现的。

        首先,添加Maven的依赖,如下图所示:

       194754_Hbwz_2555033.png

          其次,加入Redis的配置文件:

          194949_qaKq_2555033.png

          接下来,由于Redis的消息机制是通过拆分为两个领域来实现的--发布和订阅。因而需要进行分别实现。

        首先实现发布端:

package com.pcitc.lbopen.acs.sdk.redis.service;

import java.lang.management.ManagementFactory;
import java.net.Inet4Address;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.util.Enumeration;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import com.pcitc.lbopen.acs.sdk.security.message.SessionMessage;
import com.pcitc.lbopen.tools.basic.testcase.SpringContextTestCase;

/**
 * Title: (一句话功能简述) Description: (功能详细描述)
 * 
 * @Author :LiangPeng
 * @Date :2015年7月17日
 * @Version:1.0
 */
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:config/demo-springredis.xml")
public class RedisServiceTest extends SpringContextTestCase {
    private StringRedisTemplate<String,String> redisTemplate;
    private String uncacheChannel = "shiro.session.uncache";

    @Test
    public void SendMessageTest() {
	String sessionId = "1111";
	String host = ManagementFactory.getRuntimeMXBean().getName();
	SessionMessage.MessageBody messageBody = new SessionMessage.MessageBody(sessionId, host);
	redisTemplate.convertAndSend(uncacheChannel, messageBody);
	System.out.println("发送成功");
    }
}

        在这里,采用了SDR提供的RedisTemplate类提供的CoverntAndSend方法进行发布。在这个方法中需要两个参数,一个是channel,另一个是消息体。定义的channel(也就是图中的uncacheChannel),这个channel要与订阅端的channel要保持一致,

只有这样,订阅端才能够接收到订阅的消息。同时,为了能够采用SDR的消息对象Message,使用的好处是这个对象可以在消息通道中自动实现序列化,我封装了一个继承DefaultMessage的ShiroMessage,该类中的静态类MessageBdoy正是需要channel传送的

消息对象体。以下ShiroMessage的实现。

package com.pcitc.lbopen.acs.sdk.security.message;
import java.io.Serializable;
import org.springframework.data.redis.connection.DefaultMessage;
import org.springframework.data.redis.serializer.JdkSerializationRedisSerializer;

/**
 * Session的消息对象
 */
public class SessionMessage extends DefaultMessage {

    /**   
	* serialVersionUID:TODO(用一句话描述这个变量表示什么)   
	*   
	* @since Ver 1.0  
	*/
	private static final long serialVersionUID = 1L;

	private JdkSerializationRedisSerializer redisSerializer = new JdkSerializationRedisSerializer();

    public final MessageBody msgBody;

    public SessionMessage(byte[] channel, byte[] body) {
        super(channel, body);
        msgBody = (MessageBody) redisSerializer.deserialize(body);
    }

    public static class MessageBody implements Serializable {
	public final Serializable sessionId;
        public final String host;
        public String msg = "";

        public MessageBody(Serializable sessionId, String host) {
            this.sessionId = sessionId;
            this.host = host;
        }


        public MessageBody(Serializable sessionId, String host,String msg) {
            this.sessionId = sessionId;
            this.host = host;
            this.msg = msg;
        }

        @Override
        public String toString() {
            return "MessageBody{" +
                    "sessionId='" + sessionId + '\'' +
                    ", nodeId='" + host + '\'' +
                    ", msg='" + msg + '\'' +
                    '}';
        }
    }
}

     接下来是实现订阅端。订阅端的类首先要实现MessageListener,在这个接口中只有onMessage方法,这个方法是用来实现当消息接收到后要进行的操作逻辑。为了便于统一,我封装了一个继承它的RedisMessageListener,这样在具体实现阶段,只需要实现该抽象类的handleMessage方法即可。

/**   
* @Author :LiangPeng
* @version :0.1.1  
* Date     :2016年1月22日
*/
package com.pcitc.lbopen.acs.sdk.redis.service.impl;

import org.springframework.data.redis.connection.Message;
import org.springframework.data.redis.connection.MessageListener;

/** 
 * Description:信息接收
 */
public abstract class RedisMessageListener implements MessageListener{
    @Override
    public final void onMessage(Message message, byte[] pattern){
        handleMessage(message);
    }
    public abstract void handleMessage(Message message);	
}

下面是具体的订阅端的实现类。

* @Author :LiangPeng
 * @version :0.1.1  
 * Date     :2016年1月22日
 */
package com.pcitc.lbopen.acs.sdk.redis.service;

import java.io.Serializable;
import java.lang.management.ManagementFactory;
import java.util.Date;

import org.apache.commons.lang3.StringUtils;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.data.redis.connection.Message;
import org.springframework.data.redis.serializer.JdkSerializationRedisSerializer;

import com.pcitc.lbopen.acs.sdk.redis.service.impl.RedisMessageListener;
import com.pcitc.lbopen.acs.sdk.security.message.SessionMessage;

/**
 * Description: (功能详细描述)
 */
public class RedisMessageListenerService extends RedisMessageListener{
    
    public static void main(String[] args) {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(new String[] {"classpath:config/demo-springredis.xml"});
        while (true) { // 这里是一个死循环,目的就是让进程不退出,用于接收发布的消息
            try {
                System.out.println("current time: " + new Date());
                Thread.sleep(3000);
                
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    
    public void handleMessage(Message message){
        JdkSerializationRedisSerializer Serializer=new JdkSerializationRedisSerializer();       
        
        if (message == null) {
            System.out.println("null");
        } 
        else{
            String channel =new String(message.getChannel());
            System.out.println(channel);
            System.out.println("----------");
            SessionMessage.MessageBody msgbody=(SessionMessage.MessageBody)Serializer.deserialize(message.getBody());
            Serializable sessionId=msgbody.sessionId;
            String strHost=StringUtils.substringAfter(msgbody.host,"@");
            System.out.println(sessionId);
            System.out.println("----------");
            System.out.println(strHost);
            System.out.println("----------");
        }
    }
}

由于之前采用的DefaultMessage的继承类,因而在订阅端的实现方法中,还需要将消息体反序列化,这样才是我们发布前的对象(在这个类中加入的Main方法主要是为了能不间断的接收来自发布方的消息)。

最后,我还需要加入订阅方的配置,由于采用SDR的订阅机制,因而要进行如下配置:

<bean id="RedisMessageListenerService" class="com.pcitc.lbopen.acs.sdk.redis.service.RedisMessageListenerService"/>
<redis:listener-container connection-factory="jedisConnectionFactory">
    <!-- the method attribute can be skipped as the default method name is "handleMessage" -->
    <redis:listener ref="RedisMessageListenerService"  topic="shiro.session.uncache"/>
</redis:listener-container>

配置中的Topic属性是用来指定名称的频道和模式匹配的频道。

这样就实现了一个SDR的消息发布和订阅的功能。




  

转载于:https://my.oschina.net/u/2555033/blog/608658

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值