最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

springboot websocket集群(stomp協(xié)議)連接時候傳遞參數(shù)

 更新時間:2019年07月06日 08:31:24   作者:郝二驢  
這篇文章主要介紹了springboot websocket集群(stomp協(xié)議)連接時候傳遞參數(shù),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧

最近在公司項目中接到個需求。就是后臺跟前端瀏覽器要保持長連接,后臺主動往前臺推數(shù)據(jù)。

網(wǎng)上查了下,websocket stomp協(xié)議處理這個很簡單。尤其是跟springboot 集成。

但是由于開始是單機玩的,很順利。

但是后面部署到生產(chǎn)搞集群的話,就會出問題了。

假如集群兩個節(jié)點,瀏覽器A與節(jié)點A建立連接,A節(jié)點發(fā)的消息瀏覽器A節(jié)點肯定能收到。但是B節(jié)點由于沒有跟瀏覽器A建立連接。B節(jié)點發(fā)的消息瀏覽器就收不到了。

網(wǎng)上也查了好多,但是沒有一個說的很清楚的,也很多都是理論層面的。

還有很多思路都是通過session獲取信息的。但是這都不是我需要的。我需要的是從前臺傳遞參數(shù),連接的時候每個節(jié)點保存下。然后通過SimpleUserRegistry.getUser獲取。

話不多說,直接上代碼。

<script type="text/javascript" src="${request.contextPath}/scripts/sockjs.min.js"></script>
<script type="text/javascript" src="${request.contextPath}/scripts/stomp.min.js"></script>
var WEB_SOCKET = {
 
  topic : "",
  url : "",
  stompClient : null,
  
  connect : function(url, topic, callback,userid) {
   this.url = url;
   this.topic = topic;
   var socket = new SockJS(url); //連接SockJS的endpoint名稱為"endpointOyzc"
   WEB_SOCKET.stompClient = Stomp.over(socket);//使用STMOP子協(xié)議的WebSocket客戶端
   WEB_SOCKET.stompClient.connect({userid:userid},function(frame){//連接WebSocket服務(wù)端
    // console.log('Connected:' + frame);
    //通過stompClient.subscribe訂閱/topic/getResponse 目標(destination)發(fā)送的消息
    WEB_SOCKET.stompClient.subscribe(topic, callback);
   });
  }
};

這是響應(yīng)的前端代碼。只需要引入兩個js。調(diào)用new SockJS(url) 就代表跟服務(wù)器建立連接了。

@Configuration

//注解開啟使用STOMP協(xié)議來傳輸基于代理(message broker)的消息,這時控制器支持使用@MessageMapping,就像使用@RequestMapping一樣
@EnableWebSocketMessageBroker
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {

 @Autowired
 private GetHeaderParamInterceptor getHeaderParamInterceptor;

 @Override
 //注冊STOMP協(xié)議的節(jié)點(endpoint),并映射指定的url
 public void registerStompEndpoints(StompEndpointRegistry registry) {
  //注冊一個STOMP的endpoint,并指定使用SockJS協(xié)議
  registry.addEndpoint("/endpointOyzc")
    .setAllowedOrigins("*")
    .withSockJS();
  /* registry.addEndpoint("/endpointOyzc")
    .setAllowedOrigins("*")
    .setHandshakeHandler(xlHandshakeHandler)
    .withSockJS();*/
 }

 @Override
 //配置消息代理(Message Broker)
 public void configureMessageBroker(MessageBrokerRegistry registry) {
  //點對點應(yīng)配置一個/user消息代理,廣播式應(yīng)配置一個/topic消息代理
  registry.enableSimpleBroker("/topic", "/user");
  // 全局使用的消息前綴(客戶端訂閱路徑上會體現(xiàn)出來)
  //registry.setApplicationDestinationPrefixes("/app");
  //點對點使用的訂閱前綴(客戶端訂閱路徑上會體現(xiàn)出來),不設(shè)置的話,默認也是/user/
  registry.setUserDestinationPrefix("/user");
 }

 /**
  * 采用自定義攔截器,獲取connect時候傳遞的參數(shù)
  *
  * @param registration
  */
 @Override
 public void configureClientInboundChannel(ChannelRegistration registration) {
  registration.interceptors(getHeaderParamInterceptor);
 }
}

注:上面的endpointOyzc就是前端的url。后面注冊端點,前臺鏈接。

然后注意下configureClientInboundChannel這個方法,這個方法里面注入攔截器就是為了鏈接時候接收參數(shù)的。

/**
 * @author : hao
 * @description : websocket建立鏈接的時候獲取headeri里認證的參數(shù)攔截器。
 * @time : 2019/7/3 20:42
 */
@Component
public class GetHeaderParamInterceptor extends ChannelInterceptorAdapter {

 @Override
 public Message<?> preSend(Message<?> message, MessageChannel channel) {
  StompHeaderAccessor accessor = MessageHeaderAccessor.getAccessor(message, StompHeaderAccessor.class);
  if (StompCommand.CONNECT.equals(accessor.getCommand())) {
   Object raw = message.getHeaders().get(SimpMessageHeaderAccessor.NATIVE_HEADERS);
   if (raw instanceof Map) {
    Object name = ((Map) raw).get("userid");
    if (name instanceof LinkedList) {
     // 設(shè)置當前訪問的認證用戶
     accessor.setUser(new JqxxPrincipal(((LinkedList) name).get(0).toString()));
    }
   }
  }
  return message;
 }
}
/**
 * @author : hao
 * @description : 自定義的java.security.Principal
 * @time : 2019/7/3 20:42
 */
public class JqxxPrincipal implements Principal {

 private String loginName;

 public JqxxPrincipal(String loginName) {
  this.loginName = loginName;
 }

 @Override
 public String getName() {
  return loginName;
 }
}

這樣就存入的前臺傳的參數(shù)。

后臺發(fā)消息的時候怎么發(fā)呢?

/**
 * @author : hao
 * @description : websocket發(fā)送代理,負責發(fā)送消息
 * @time : 2019/7/4 11:01
 */
@Component
@Slf4j
public class WebsocketSendProxy<T> {
 @Autowired
 private SimpMessagingTemplate template;

 @Autowired
 private SimpUserRegistry userRegistry;

 @Resource(name = "redisServiceImpl")
 private RedisService redisService;

 @Value("spring.redis.message.topic-name")
 private String topicName;

 public void sendMsg(RedisWebsocketMsg<T> redisWebsocketMsg) {
  SimpUser simpUser = userRegistry.getUser(redisWebsocketMsg.getReceiver());
  log.info("發(fā)送消息前獲取接收方為{},根據(jù)Registry獲取本節(jié)點上這個用戶{}", redisWebsocketMsg.getReceiver(), simpUser);
  if (simpUser != null && StringUtils.isNotBlank(simpUser.getName())) {
   //2. 獲取WebSocket客戶端的訂閱地址
   WebSocketChannelEnum channelEnum = WebSocketChannelEnum.fromCode(redisWebsocketMsg.getChannelCode());
   if (channelEnum != null) {
    //3. 給WebSocket客戶端發(fā)送消息
    template.convertAndSendToUser(redisWebsocketMsg.getReceiver(), channelEnum.getSubscribeUrl(), redisWebsocketMsg.getContent());
   }
  } else {
   //給其他訂閱了主題的節(jié)點發(fā)消息,因為本節(jié)點沒有
   redisService.convertAndSend(topicName, redisWebsocketMsg);
  }

 }
}

可以發(fā)現(xiàn)上面代碼利用了redis監(jiān)聽模型,也就是redis模型的消息隊列

/**
 * @author : hao
 * @description : redis消息監(jiān)聽實現(xiàn)類,接收處理類
 * @time : 2019/7/3 14:00
 */
@Component
@Slf4j
public class MessageReceiver {

 @Autowired
 private SimpMessagingTemplate messagingTemplate;

 @Autowired
 private SimpUserRegistry userRegistry;

 /**
  * 處理WebSocket消息
  */
 public void receiveMessage(RedisWebsocketMsg redisWebsocketMsg) {
  log.info(MessageFormat.format("Received Message: {0}", redisWebsocketMsg));
  //1. 取出用戶名并判斷是否連接到當前應(yīng)用節(jié)點的WebSocket
  SimpUser simpUser = userRegistry.getUser(redisWebsocketMsg.getReceiver());

  if (simpUser != null && StringUtils.isNotBlank(simpUser.getName())) {
   //2. 獲取WebSocket客戶端的訂閱地址
   WebSocketChannelEnum channelEnum = WebSocketChannelEnum.fromCode(redisWebsocketMsg.getChannelCode());
   if (channelEnum != null) {
    //3. 給WebSocket客戶端發(fā)送消息
    messagingTemplate.convertAndSendToUser(redisWebsocketMsg.getReceiver(), channelEnum.getSubscribeUrl(), redisWebsocketMsg.getContent());
   }
  }
 }
}

redis消息模型只貼部分代碼就好了

/**
  * 消息監(jiān)聽器
  */
 @Bean
 MessageListenerAdapter messageListenerAdapter(MessageReceiver messageReceiver, Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer){
  //消息接收者以及對應(yīng)的默認處理方法
  MessageListenerAdapter messageListenerAdapter = new MessageListenerAdapter(messageReceiver, "receiveMessage");
  //消息的反序列化方式
  messageListenerAdapter.setSerializer(jackson2JsonRedisSerializer);

  return messageListenerAdapter;
 }

 /**
  * message listener container
  */
 @Bean
 RedisMessageListenerContainer container(RedisConnectionFactory connectionFactory
   , MessageListenerAdapter messageListenerAdapter){
  RedisMessageListenerContainer container = new RedisMessageListenerContainer();
  container.setConnectionFactory(connectionFactory);
  //添加消息監(jiān)聽器
  container.addMessageListener(messageListenerAdapter, new PatternTopic(topicName));

  return container;
 }

上面的思路大體如下:客戶端簡歷鏈接時候,傳過來userid保存起來。發(fā)消息的時候 通過userRegistry獲取,能獲取到就證明是跟本節(jié)點建立的鏈接,直接用本節(jié)點發(fā)消息就好了。

如果不是就利用redis消息隊列,把消息推出去。每個節(jié)點去判斷獲取看下是不是本節(jié)點的userid。這樣就實現(xiàn)了集群的部署。

以上就是本文的全部內(nèi)容,希望對大家的學習有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • SpringBoot使用iText7實現(xiàn)將HTML轉(zhuǎn)成PDF并添加頁眉頁腳水印

    SpringBoot使用iText7實現(xiàn)將HTML轉(zhuǎn)成PDF并添加頁眉頁腳水印

    這篇文章主要為大家詳細介紹了SpringBoot使用iText7實現(xiàn)將HTML轉(zhuǎn)成PDF并添加頁眉頁腳水印的相關(guān)知識,感興趣的小伙伴可以跟隨小編一起學習一下
    2024-03-03
  • Java中ReUtil正則表達式工具庫的使用

    Java中ReUtil正則表達式工具庫的使用

    ReUtil是Hutool庫中的正則表達式工具類,提供了多種常用正則表達式操作方法,下面就來介紹一下ReUtil的使用,具有一定的參考價值,感興趣的可以了解一下
    2025-02-02
  • Java反射技術(shù)詳解

    Java反射技術(shù)詳解

    這篇文章主要結(jié)合實例形式分析了介紹了Java反射技術(shù),小編覺得這篇文章寫的還不錯,需要的朋友可以參考下,希望能夠給你帶來幫助
    2021-11-11
  • 一個注解搞定Spring Security基于Oauth2的SSO單點登錄功能

    一個注解搞定Spring Security基于Oauth2的SSO單點登錄功能

    本文主要介紹 同域 和 跨域 兩種不同場景單點登錄的實現(xiàn)原理,并使用 Spring Security 來實現(xiàn)一個最簡單的跨域 SSO客戶端。對Spring Security基于Oauth2的SSO單點登錄功能感興趣的朋友一起看看吧
    2021-09-09
  • Java矢量隊列Vector使用示例

    Java矢量隊列Vector使用示例

    Vector類實現(xiàn)了一個動態(tài)數(shù)組。和ArrayList很相似,但是兩者是不同的Vector是同步訪問的;Vector包含了許多傳統(tǒng)的方法,這些方法不屬于集合框架
    2023-01-01
  • Java中的轉(zhuǎn)換流InputStreamReader解讀

    Java中的轉(zhuǎn)換流InputStreamReader解讀

    InputStreamReader是Java.io包中的一個類,用于將字節(jié)輸入流轉(zhuǎn)換為字符輸入流,它繼承自java.io.Reader類,提供了兩種構(gòu)造方法,可以使用默認或指定字符集創(chuàng)建實例,常用方法包括讀取字符、判斷是否準備好讀取數(shù)據(jù)和關(guān)閉流
    2024-09-09
  • Java中定時任務(wù)的6種實現(xiàn)方式

    Java中定時任務(wù)的6種實現(xiàn)方式

    這篇文章主要給大家分享的是Java中定時任務(wù)的6種實現(xiàn)方式,幾乎在所有的項目中,定時任務(wù)的使用都是不可或缺的,如果使用不當甚至會造成資損,下面文章我們就來看看Java中定時任務(wù)的具體使用方式吧
    2021-10-10
  • Spring Security OAuth2集成短信驗證碼登錄以及第三方登錄

    Spring Security OAuth2集成短信驗證碼登錄以及第三方登錄

    這篇文章主要介紹了Spring Security OAuth2集成短信驗證碼登錄以及第三方登錄,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-04-04
  • Spring AOP方法內(nèi)部調(diào)用不生效的解決方案

    Spring AOP方法內(nèi)部調(diào)用不生效的解決方案

    最近有個需求,統(tǒng)計某個方法的調(diào)用次數(shù),開始使用 Spring AOP 實現(xiàn),后來發(fā)現(xiàn)當方法被內(nèi)部調(diào)用時,切面邏輯將不會生效,所以本文就給大家介紹了Spring AOP方法內(nèi)部調(diào)用不生效的解決方案,需要的朋友可以參考下
    2025-01-01
  • Java MD5消息摘要算法原理及實現(xiàn)代碼

    Java MD5消息摘要算法原理及實現(xiàn)代碼

    這篇文章主要介紹了Java MD5消息摘要算法原理及實現(xiàn)代碼,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
    2020-09-09

最新評論

治多县| 个旧市| 宁阳县| 明溪县| 舒兰市| 双城市| 牟定县| 利辛县| 安化县| 江门市| 枣庄市| 内黄县| 保康县| 建阳市| 莲花县| 黄浦区| 中超| 东方市| 县级市| 咸阳市| 崇仁县| 娄烦县| 视频| 涿州市| 蕲春县| 广河县| 依兰县| 延津县| 射阳县| 古蔺县| 威宁| 同心县| 安阳县| 衢州市| 襄汾县| 承德市| 卢氏县| 理塘县| 尉犁县| 会宁县| 二手房|