通过RocketMq实现websocket的session共享(干货满满)

目录

1、引入所需依赖

2、RocketMq配置

3、websocket配置文件

4、发送消息

5、监听消息

6、效果展示


        工作中遇到即时聊天的需求,由于服务是集群部署,需要实现session共享,之前写过使用Redis队列实现的博客,供大家参考
通过Redis发布者/订阅者模式实现websocket的session共享_mlwsmqq的博客-CSDN博客_redis websocket由于项目是集群部署,需要实现对websocket的session共享,可websocket的session无法序列化,不能存放到Redis当中,因此我们可以把websocket的session存放在服务器的map上,通过Redis的广播把消息发送到指定的频道上,每个服务器节点都订阅该频道,从而消息一经发布都能收到再从map中获取session完成消息的推送1、引入所需依赖 org.springframewor...https://blog.csdn.net/mlwsmqq/article/details/121736412        本文使用阿里开源的RocketMq实现session共享,闲话不说,干货奉上

1、引入所需依赖


   org.apache.rocketmq
   rocketmq-spring-boot-starter
   2.2.1


   org.springframework.boot
   spring-boot-starter-websocket

2、RocketMq配置

rocketmq:
  name-server: 192.168.xx.xx:xxxx
  producer:
    group: producer-xxxx
    send-message-timeout: 6000

3、websocket配置文件

@Configuration
public class WebSocketConfig {
 
	/**
	 * ServerEndpointExporter 作用
	 * 这个Bean会自动注册使用@ServerEndpoint注解声明的websocket endpoint
	 * @return
	 */
	@Bean
	public ServerEndpointExporter serverEndpointExporter() {
		return new ServerEndpointExporter();
	}
 
}

4、发送消息

        本文只展示推送消息的实现代码,代码贴的不全,主要是思路,大家理解了就能够举一反三

public void sendMessage(String userId, String message) {
		try {
			JSONObject jsonObject = new JSONObject();
			jsonObject.put(RocketMqConstant.KEY_USER_ID,userId);
			jsonObject.put(RocketMqConstant.KEY_MESSAGE,message);
			Message mes = MessageBuilder.withPayload(jsonObject).build();
			rocketMQTemplate.syncSend(RocketMqConstant.DESTINATION_SHARE_SESSION_SEND_MESSAGE,mes);
		} catch (Exception e) {
			log.error("WebsocketServer-sendMessage-exception:",e);
		}
	}

5、监听消息

        注意消费模式是广播消费:messageModel = MessageModel.BROADCASTING,这样消息就会被所有服务上的消费者消费,就能判断session是否存在从而推送消息

package com.zzw.redops.listener.share.sendMessage;

import com.alibaba.fastjson.JSONObject;
import com.zzw.redops.constant.RocketMqConstant;
import com.zzw.redops.utils.WebSocketServer;
import lombok.extern.slf4j.Slf4j;
import org.apache.rocketmq.common.message.MessageExt;
import org.apache.rocketmq.spring.annotation.MessageModel;
import org.apache.rocketmq.spring.annotation.RocketMQMessageListener;
import org.apache.rocketmq.spring.core.RocketMQListener;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.nio.charset.StandardCharsets;

/**
 * @Auther:admin
 * @Date:2022/11/2 16:41
 * session共享:推送消息监听
 */
@Slf4j
@Component
@RocketMQMessageListener(topic = RocketMqConstant.TOPIC_REDOPS_BACKEND,
						consumerGroup = RocketMqConstant.CONSUMER_SHARE_SESSION_SEND_MESSAGE,
						selectorExpression = RocketMqConstant.TAG_SHARE_SESSION_SEND_MESSAGE,
						messageModel = MessageModel.BROADCASTING,
						consumeThreadMax = 128)
public class SendMessageListener implements RocketMQListener {

	@Resource
	private WebSocketServer webSocketServer;

	@Override
	public void onMessage(MessageExt messageExt) {
		try {
			JSONObject parseObject = JSONObject.parseObject(new String(messageExt.getBody(), StandardCharsets.UTF_8));
			String userId = parseObject.getString(RocketMqConstant.KEY_USER_ID);
			log.info("share-session:监听到推送消息:{}",userId);
			webSocketServer.sendToFront(userId,parseObject.getString(RocketMqConstant.KEY_MESSAGE));
		}catch (Exception e){
			log.error("SendMessageListener exception:",e);
		}
	}

}
public void sendToFront(String userId, String message){
		try {
			if (StringUtils.isBlank(userId)) {
				log.info("sendToFront-用户ID为空:message:{} userId:{}",message,userId);
				return;
			}
			// 根据用户ID获取session
			List list = getWebSocketSession(userId);
			if (CollectionUtil.isNotEmpty(list)) {
				for (WebSocketSession item : list) {
					sendMessage(item, message, userId);
				}
			}else {
				log.info("不推送消息:userId:{} session:{}",userId,list);
			}
		} catch (Exception e) {
			log.error("WebsocketServer-sendToFront-exception:",e);
		}
	}
public static void sendMessage(WebSocketSession wssession, String message, String userId){
		if (Objects.isNull(wssession)) {
			log.info("向前端推送消息时session为空:message:{} session:{}",message,wssession);
			return;
		}
		try {
			synchronized (wssession) {
				wssession.getSession().getBasicRemote().sendText(message);
				log.info("推送消息成功:用户ID:{}",userId);
			}
		} catch (Exception e) {
			log.error("sendText exception:",e);
		}
	}
    // 根据用户ID获取session
	public List getWebSocketSession(String userId) {
		try {
			if (StringUtils.isBlank(userId)) {
				return Collections.emptyList();
			}
			List result = new ArrayList<>();
			for (String key : webSocketMap.keySet()) {
				if (userId.equals(key)) {
					result.add(webSocketMap.get(key));
				}
			}
			// 如果一个用户ID对应多个会话,取最新的会话返回
			if (result.size() > 1) {
				// 按照创建时间倒序排序
				List collect = result.parallelStream().sorted(Comparator.comparing(WebSocketSession::getCreateTime).reversed()).limit(1).collect(Collectors.toList());
				log.info("会话重复时返回最新会话:{}", collect);
				return collect;
			}
			return result;
		} catch (Exception e) {
			log.error("WebsocketServer-getWebSocketSession-exception:",e);
		}
		return Collections.emptyList();
	}

6、效果展示

        线上和本地分别启动一台服务,从RocketMq控制台可看出消费组已经有两个实例注册上了

通过RocketMq实现websocket的session共享(干货满满)_第1张图片

        在作战室中发送消息看下打印日志

        线上日志:

        

        本地日志:                

通过RocketMq实现websocket的session共享(干货满满)_第2张图片

         从日志可看出:发送消息的用户session信息是保存在线上服务的,线上能获取到session就向前端推送消息,本地获取不到就不推送,这样就达到想要的效果了

        欢迎大家指正错误,转载请注明出处!!!

你可能感兴趣的:(RocketMq,websocket,rocketmq,websocket,spring,boot,java)