集羣下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都能訂閱到消息,但是隻有之前連接成功的節點能推送成功,其餘的無法推送。

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