集群下WebSocket的解决方案(springboot 基于Redis发布订阅)

一、背景

单机节点下,WebSocket连接成功后,可以直接发送消息。而多节点下,连接时通过nginx会代理到不同节点。

假设一开始用户连接了node1的socket服务。触发消息发送的条件的时候也通过nginx进行代理,假如代理转到了node2节点上,那么node2节点的socket服务就发送不了消息,因为一开始用户注册的是node1节点。这就导致了消息发送失败。

为了解决这一方案,消息发送时,就需要一个中间件来记录,这样,三个节点都可以获取消息,然后在根据条件进行消息推送。

二、解决方案(springboot 基于 Redis发布订阅)

1、依赖

<!-- redis -->

org.springframework.boot

spring-boot-starter-data-redis

<!-- websocket -->

org.springframework.boot

spring-boot-starter-websocket

2、创建业务处理类 Demo.class,该类可以实现MessageListener接口后重写onMessage方法,也可以不实现,自己写方法。

importcom.alibaba.fastjson.JSON;

importcom.dy.service.impl.OrdersServiceImpl;

importorg.slf4j.Logger;

importorg.slf4j.LoggerFactory;

importorg.springframework.beans.factory.annotation.Autowired;

importorg.springframework.data.redis.connection.Message;

importorg.springframework.data.redis.connection.MessageListener;

importorg.springframework.stereotype.Component;

importjava.util.HashMap;

/**

*@program:

*@description: redis消息订阅-业务处理

*@author: zhang yi

*@create: 2021-01-25 16:46

*/

@Component

publicclassDemoimplementsMessageListener{

Logger logger = LoggerFactory.getLogger(this.getClass());

@Override

publicvoidonMessage(Message message,byte[] pattern){

logger.info("消息订阅成功---------");

logger.info("内容:"+message.getBody());

logger.info("交换机:"+message.getChannel());

    }

}

3、创建PubSubConfig配置类

importorg.slf4j.Logger;

importorg.slf4j.LoggerFactory;

importorg.springframework.cache.annotation.EnableCaching;

importorg.springframework.context.annotation.Bean;

importorg.springframework.context.annotation.Configuration;

importorg.springframework.data.redis.connection.RedisConnectionFactory;

importorg.springframework.data.redis.core.StringRedisTemplate;

importorg.springframework.data.redis.listener.PatternTopic;

importorg.springframework.data.redis.listener.RedisMessageListenerContainer;

importorg.springframework.data.redis.listener.adapter.MessageListenerAdapter;

/**

*@program:

*@description: redis发布订阅配置

*@author: zhang yi

*@create: 2021-01-25 16:49

*/

@Configuration

@EnableCaching

publicclassPubSubConfig{

Logger logger = LoggerFactory.getLogger(this.getClass());

//如果是多个交换机,则参数为(RedisConnectionFactory connectionFactory,

//                          MessageListenerAdapter listenerAdapter,

//                          MessageListenerAdapter listenerAdapter2)

@Bean

RedisMessageListenerContainercontainer(RedisConnectionFactory connectionFactory,

                                            MessageListenerAdapter listenerAdapter){

RedisMessageListenerContainer container =newRedisMessageListenerContainer();

        container.setConnectionFactory(connectionFactory);

// 可以添加多个 messageListener,配置不同的交换机

container.addMessageListener(listenerAdapter,newPatternTopic("channel:demo"));

//container.addMessageListener(listenerAdapter2, new PatternTopic("channel:demo2"));

returncontainer;

    }

/**

    * 消息监听器适配器,绑定消息处理器,利用反射技术调用消息处理器的业务方法

*@paramdemo 第一步的业务处理类

*@return

    */

@Bean

MessageListenerAdapterlistenerAdapter(Demo demo){

logger.info("----------------消息监听器加载成功----------------");

// onMessage 就是方法名,基于反射调用

returnnewMessageListenerAdapter(demo,"onMessage");

    }

/**

    * 多个交换机就多写一个

*@paramsubCheckOrder

*@return

    */

//@Bean

//MessageListenerAdapter listenerAdapter2(SubCheckOrder subCheckOrder) {

//    logger.info("----------------消息监听器加载成功----------------");

//    return new MessageListenerAdapter(subCheckOrder, "onMessage");

//}

@Bean

StringRedisTemplatetemplate(RedisConnectionFactory connectionFactory){

returnnewStringRedisTemplate(connectionFactory);

    }

}

4、消息发布

@Autowired

privateRedisTemplate redisTemplate;

redisTemplate.convertAndSend("channel:demo","我是内容");

三、具体用法

socket连接成功。

socket消息推送时,把信息发布到redis中。

socket服务订阅redis的消息,订阅成功后进行推送。集群下的socket都能订阅到消息,但是只有之前连接成功的节点能推送成功,其余的无法推送。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章