春季启动从 1.2.0 升级到 2.1.3 后,Websocket 推送通知不起作用
Posted
技术标签:
【中文标题】春季启动从 1.2.0 升级到 2.1.3 后,Websocket 推送通知不起作用【英文标题】:Websocket Push Notifications not working AFTER spring boot upgrade from 1.2.0 to 2.1.3 【发布时间】:2019-07-29 17:02:35 【问题描述】:这是关于使用 websocket + stompjs 发送推送通知的 spring boot + angularjs web 应用程序。
我最近从 Spring boot 1.2.0 升级到 2.1.3。 在之前,此升级 websocket(推送通知)工作了好几年。
我刚刚升级了 spring boot 和 websocket 相关的代码完全相同,但它现在不工作了。
不工作意味着:
-
在服务器端执行以下行没有任何错误/异常。
simpMessagingTemplate.convertAndSend("/topic/notify", payload);
-
Chrome 调试器只接收“h”(心跳)而不是实际消息。
我不知道因为,
服务器端代码成功执行直到最后一行。 websocket 会话建立,我可以收到心跳消息,但客户端也没有错误。代码(但同样的代码适用于 Spring boot 1.2.0:
1.配置:
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer
@Value("$server.sessionTimeout")
long sessionTimeoutInSecs;
@Override
public void configureMessageBroker(MessageBrokerRegistry config)
config.enableSimpleBroker("/topic");
config.setApplicationDestinationPrefixes("/app");
@Override
public void registerStompEndpoints(StompEndpointRegistry registry)
registry.addEndpoint("/notify").withSockJS();
@Bean
public ServletServerContainerFactoryBean createWebSocketContainer()
ServletServerContainerFactoryBean container = new ServletServerContainerFactoryBean();
// in milliseconds
container.setMaxSessionIdleTimeout(sessionTimeoutInSecs * 1000);
return container;
2。消息发送代码:
simpMessagingTemplate.convertAndSend("/topic/notify", payload);
3.客户端代码:
(function()
myApp.factory('autoUpdateTasksService', function($resource, $q, $log)
var initSockets, notify, reconnect, socket, _callback;
_callback = null;
socket =
client: null,
stomp: null
;
initSockets = function()
socket.client = new SockJS('/notify');
socket.stomp = Stomp.over(socket.client);
socket.stomp.connect(, function() );
socket.client.onopen = function()
var subscription1;
subscription1 = socket.stomp.subscribe("/topic/notify", notify);
//$log.log('socket connected');
;
;
reconnect = function()
setTimeout(initSockets, 1000);
;
notify = function(message)
try
var taskNotifyObject;
if (message.body)
taskNotifyObject = angular.fromJson(message.body);
//$log.log(taskNotifyObject);
var notificationArray=[];
notificationArray.push(taskNotifyObject);
_callback(notificationArray);
else
//$log.log("empty message");
catch(e)
// alert(e.message);
;
return
init: function(callback)
_callback = callback;
initSockets();
;
);
).call(this);
版本之间的spring框架有什么变化吗?
如何调试/查找消息丢失的位置?
【问题讨论】:
【参考方案1】:根本原因:升级后,我的问题中的代码无法在服务器和客户端之间创建连接(未能创建websocketSession) .
更改代码如下解决问题,但我不确定为什么这个解决方案有效,
如果有人解释为什么这个解决方案是有效的,那将是一个很大的帮助。
1.配置:
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer
@Value("$server.servlet.session.timeout")
long sessionTimeoutInSecs;
@Override
public void configureMessageBroker(MessageBrokerRegistry config)
config.enableSimpleBroker("/queue");
config.setApplicationDestinationPrefixes("/app");
@Override
public void registerStompEndpoints(StompEndpointRegistry registry)
registry.addEndpoint("/notify").addInterceptors(new HttpSessionHandshakeInterceptor());
@Bean
public ServletServerContainerFactoryBean createWebSocketContainer()
ServletServerContainerFactoryBean container = new ServletServerContainerFactoryBean();
// in milliseconds
container.setMaxSessionIdleTimeout(sessionTimeoutInSecs * 1000);
return container;
/**
* DefaultSimpUserRegistry is the replacement of MySessionRegistry ( Custom UserSessionRegistry ) after upgrade to Spring 5.
* Below required with Spring 4.
* import org.springframework.messaging.simp.user.UserSessionRegistry;
@Repository
public class MySessionRegistry implements UserSessionRegistry, ApplicationListener<AbstractSubProtocolEvent>
*
*/
@Bean
public DefaultSimpUserRegistry defaultSimpUserRegistry()
DefaultSimpUserRegistry userRegistry = new DefaultSimpUserRegistry();
return userRegistry;
2。消息发送代码:
import org.springframework.web.socket.messaging.DefaultSimpUserRegistry;
@Autowired
DefaultSimpUserRegistry defaultSimpUserRegistry;
.....
SimpUser simpUser = defaultSimpUserRegistry.getUser(payload.getUserName());
if(simpUser != null && simpUser.hasSessions())
template.convertAndSendToUser(payload.getUserName(), "/queue/notify", payload);
3.客户端代码:
(function()
myApp.factory('autoUpdateTasksService', function($resource, $q, $log)
var initSockets, notify, reconnect, socket, _callback;
_callback = null;
socket =
client: null,
stomp: null
;
getContextPath = function()
return window.location.pathname.substring(0, window.location.pathname.indexOf("/",2));
;
initSockets = function()
//socket.addr = "wss://" + window.location.host + "/notify";
socket.addr = ((window.location.protocol && (window.location.protocol.indexOf("https") >= 0)) ? "wss://" : "ws://") + window.location.host + getContextPath() + "/notify";
socket.client = Stomp.client(socket.addr); //new SockJS('/notify');
socket.client.connect(, function ()
$log.log("Connected to websocket through " + socket.addr);
socket.client.subscribe("/user/queue/notify", notify);
, function (err)
$log.log("Error when connection to websocket " + socket.addr + ".\n" + err);
);
;
如何调试/查找消息丢失的位置?
为了验证客户端-服务器连接(或创建 websocketSession),我在 listener 下方添加。
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.messaging.simp.stomp.StompHeaderAccessor;
import org.springframework.web.socket.messaging.SessionConnectedEvent;
import org.springframework.web.socket.messaging.SessionSubscribeEvent;
@Component
public class WebSocketListener implements ApplicationListener <ApplicationEvent>
//WebSocket session created
if (appEvent instanceof SessionConnectedEvent)
StompHeaderAccessor sha = StompHeaderAccessor.wrap(((SessionConnectedEvent) appEvent).getMessage());
logger.info("SessionConnectedEvent: STOMP WebSocket session created for the user: ", sha.getUser().getName());
//subscribed to websocketSession
if (appEvent instanceof SessionSubscribeEvent)
StompHeaderAccessor sha = StompHeaderAccessor.wrap(((SessionSubscribeEvent) appEvent).getMessage());
logger.info("SessionSubscribeEvent: User subscribed to WebSocket session, destination: ", sha.getUser().getName(), sha.getDestination());
//
// if (appEvent instanceof BrokerAvailabilityEvent)
// logger.info("BrokerAvailabilityEvent: ", appEvent.toString());
//
【讨论】:
以上是关于春季启动从 1.2.0 升级到 2.1.3 后,Websocket 推送通知不起作用的主要内容,如果未能解决你的问题,请参考以下文章
如何仅将 JSON 请求正文的几个字段从 DTO 发布到 URL 春季启动
SpringBoot 升级到 2.1 后,启动程序时控制台不打印 API 的解决方法及一些感想
春季启动中的@value没有从application.properties中提供价值
Spring Boot - 从 2.2.5 升级到 2.5.7 后,应用程序无法启动