解决方案:使用redis消息发布订阅解决多个tomcat应用服务器下,连接不共享问题;具体如下
@Configuration
public class WebSocketConfig {
//TODEO如果用外置tomcat,要注释掉以下代码,否则启动项目会报错,用springboot内置tomcat就得放开以下代码
@Bean
public ServerEndpointExporter serverEndpointExporter() {
return new ServerEndpointExporter();
}
}
以及实现 WebSocketServer
private final static Logger log = LoggerFactory.getLogger(WebSocketServer.class);
//静态变量,用来记录当前在线连接数。应该把它设计成线程安全的。
private static int onlineCount = 0;
//concurrent包的线程安全Set,用来存放每个客户端对应的MyWebSocket对象。
private static CopyOnWriteArraySet<WebSocketServer> webSocketSet = new CopyOnWriteArraySet<WebSocketServer>();
//与某个客户端的连接会话,需要通过它来给客户端发送数据
private Session session;
//接收userId
private String userId = "";
/**
* 连接建立成功调用的方法
*/
@OnOpen
public void onOpen(Session session, @PathParam("userId") String userId) {
this.session = session;
webSocketSet.add(this); //加入set中
addOnlineCount(); //在线数加1
log.info("有新窗口开始监听:" + userId + ",当前在线人数为" + getOnlineCount());
this.userId = userId;
try {
sendMessage("连接成功");
} catch (IOException e) {
log.error("websocket IO异常");
}
}
/**
* 连接关闭调用的方法
*/
@OnClose
public void onClose() {
boolean flag = webSocketSet.remove(this); //从set中删除
if (flag) {
subOnlineCount(); //在线数减1
log.info("有一连接关闭!当前在线人数为" + getOnlineCount());
}
}
/**
* 收到客户端消息后调用的方法
*
* @param message 客户端发送过来的消息
*/
@OnMessage
public void onMessage(String message, Session session) {
log.info("收到来自窗口" + userId + "的信息:" + message);
//群发消息
// for (WebSocketServer item : webSocketSet) {
// try {
// item.sendMessage(message);
// } catch (IOException e) {
// e.printStackTrace();
// }
// }
}
/**
* @param session
* @param error
*/
@OnError
public void onError(Session session, Throwable error) {
log.error("发生错误:" + error.getMessage());
// error.printStackTrace();
}
/**
* 实现服务器主动推送
*/
public void sendMessage(String message) throws IOException {
this.session.getBasicRemote().sendText(message);
}
/**
* 群发自定义消息
*/
public static boolean sendInfo(String message, @PathParam("userId") String userId) {
log.info("推送消息到窗口" + userId + ",推送内容:" + message);
for (WebSocketServer item : webSocketSet) {
try {
//这里可以设定只推送给这个userId的,为null则全部推送
// if (userId == null) {
// item.sendMessage(message);
// } else if (item.userId.equals(userId)) {
if (item.userId.equals(userId)) {
item.sendMessage(message);
return true;
}
} catch (IOException e) {
continue;
}
}
return false;
}
public static synchronized int getOnlineCount() {
return onlineCount;
}
public static synchronized void addOnlineCount() {
WebSocketServer.onlineCount++;
}
public static synchronized void subOnlineCount() {
WebSocketServer.onlineCount--;
}
}
设置监听类
@Configuration
public class RedisSubListenerConfig {
//初始化监听器
@Bean
RedisMessageListenerContainer container(RedisConnectionFactory connectionFactory,
MessageListenerAdapter listenerAdapter) {
RedisMessageListenerContainer container = new RedisMessageListenerContainer();
container.setConnectionFactory(connectionFactory);
container.addMessageListener(listenerAdapter, new PatternTopic(RedisKeyConstants.TOPIC_CHANNEL_SENDWEBSOCKET));
return container;
}
//利用反射来创建监听到消息之后的执行方法
@Bean
MessageListenerAdapter listenerAdapter(OutpatientRedisDaoImpl redisReceiver) {
return new MessageListenerAdapter(redisReceiver, "sendMessageByOpen");
}
}
//实现监听后执行的方法
@Component("outpatientRedisDao")
public class OutpatientRedisDaoImpl {
private Logger logger = LoggerFactory.getLogger(this.getClass());
public void sendMessageByOpen(String message) {
logger.info("reids订阅消息收到的参数message:{}", message);
message = message.substring(1, message.length()-1);
String [] info = message.split(";");
JSONObject content = JSONObject.fromObject(info[1].replaceAll("\\\\\"", "\""));
WebSocketServer.sendInfo(content.toString(), info[0]);
}
}
详情见GitHub地址