stomp over sockjs angular 5 client with spring boot后端每个生命周期随机断开一次

时间:2017-12-14 12:13:01

标签: angular spring-boot websocket stomp sockjs

我有一个使用sockjs + stomp连接到弹簧启动后端的角度5客户端。它工作正常,除了每个客户端连接生命周期一次"客户端随机(大约20-30秒后)丢失与后端的套接字连接。在错误回调中再次调用connect(..)函数之后,套接字连接在运行时的其余部分保持不变。

最新版Chrome和Firefox的行为相同。

angular客户端对stomp和socketjs具有以下依赖关系:

"sockjs": "^0.3.19",
"stompjs": "^2.3.3",

这是在客户端管理连接的方式:

private sockAddress: string = '/websocket';
private socket;
private stompClient;
private subscriptions: [string, (message: String) => void][];
...
connect() {
    const headers = {};
    this.socket = new SockJS(this.sockAddress);
    this.stompClient = Stomp.over(this.socket);
    this.stompClient.heartbeat.incoming = 1000;
    this.stompClient.heartbeat.outgoing = 1000;
    this.stompClient.connect(headers, this.connectCallback.bind(this), this.errorCallback.bind(this));
  }

connectCallback(frame): void {
    // called back after the client is connected and authenticated to the STOMP server
    this.isConnected = true;

    for (let subscription of this.subscriptions) {
      this.subscribeToEndpoint(subscription[0], subscription[1]);
    }
  }

errorCallback(): void {
    console.log('error callback called, socket not connected!');
    this.isConnected = false;
    // reconnecting
    this.connect();
  }

sendMessage(message: string, endpoint: string): void {
    if (this.stompClient != null && this.isConnected) {
      this.stompClient.send(endpoint, {}, message);
    } else {
      console.log('socket not connected');
    }
  }

private subscribeToEndpoint(endpoint: string, callback: (message: String) => void) {
    if (this.isConnected == true) {
      this.stompClient.subscribe(endpoint, callback);
    }
  }

这是配置的相关服务器端部分:

@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {

    @Override
    public void configureMessageBroker(MessageBrokerRegistry config) {
        ThreadPoolTaskScheduler te = new ThreadPoolTaskScheduler();
        te.setPoolSize(2);
        te.setThreadNamePrefix("wss-heartbeat-thread-");
        te.initialize();
        config.enableSimpleBroker("/data").setTaskScheduler(te).setHeartbeatValue(new long[]{1000, 1000});
        config.setApplicationDestinationPrefixes("/app");
    }

    @Override
    public void registerStompEndpoints(StompEndpointRegistry stompEndpointRegistry) {
        stompEndpointRegistry.addEndpoint("/websocket").withSockJS();
    }
}

如何处理消息:

@MessageMapping("/element/create")
@SendTo("/data/element/created")
public ApprovedElementDTO createElement(CreateElementDTO element) {
    LOG.debug("createElement called: " + element.getType().toString());
    return this.socketService.approveElementId(element);
}

用于套接字的相关maven依赖项:

<dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-websocket</artifactId>
        <version>1.5.9.RELEASE</version>
    </dependency>

客户端上的错误消息如下所示:

zone.js:2933 POST http://localhost:4200/websocket/461/1uzrn1r4/xhr_send?    t=1513249327232 404 (Not Found)
scheduleTask @ zone.js:2933
ZoneDelegate.scheduleTask @ zone.js:411
onScheduleTask @ zone.js:301
ZoneDelegate.scheduleTask @ zone.js:405
Zone.scheduleTask @ zone.js:236
Zone.scheduleMacroTask @ zone.js:259
(anonymous) @ zone.js:2966
proto.(anonymous function) @ zone.js:1366
AbstractXHRObject._start @ abstract-xhr.js:132
(anonymous) @ abstract-xhr.js:21
ZoneDelegate.invokeTask @ zone.js:425
onInvokeTask @ core.js:4621
ZoneDelegate.invokeTask @ zone.js:424
Zone.runTask @ zone.js:192
ZoneTask.invokeTask @ zone.js:499
ZoneTask.invoke @ zone.js:488
timer @ zone.js:2040
setTimeout (async)
scheduleTask @ zone.js:2056
ZoneDelegate.scheduleTask @ zone.js:411
onScheduleTask @ zone.js:301
ZoneDelegate.scheduleTask @ zone.js:405
Zone.scheduleTask @ zone.js:236
Zone.scheduleMacroTask @ zone.js:259
(anonymous) @ zone.js:2072
proto.(anonymous function) @ zone.js:1366
AbstractXHRObject @ abstract-xhr.js:20
XHRCorsObject @ xhr-cors.js:8
(anonymous) @ ajax-based.js:21
BufferedSender.sendSchedule @ buffered-sender.js:59
BufferedSender.send @ buffered-sender.js:26
SockJS.send @ main.js:163
Client._transmit @ stomp.js:159
Client.send @ stomp.js:366
SocketService.sendMessage @ socket.service.ts:79
ElementTransmission.sendElementCreationEvent @ ElementTransmission.ts:56
PluginHandler.sendElementCreationEvent @ PluginHandler.ts:266
DiagramComponent.onClick @ diagram.component.ts:150
dispatch @ scripts.bundle.js:4
q.handle @ scripts.bundle.js:4
ZoneDelegate.invokeTask @ zone.js:425
onInvokeTask @ core.js:4621
ZoneDelegate.invokeTask @ zone.js:424
Zone.runTask @ zone.js:192
ZoneTask.invokeTask @ zone.js:499
invokeTask @ zone.js:1540
globalZoneAwareCallback @ zone.js:1566
stomp.js:134 Whoops! Lost connection to http://localhost:4200/websocket

有时我也会随机收到此错误消息但对连接没有任何影响:

VM3454:164 WebSocket connection to 'ws://localhost:4200/websocket/954/i52geoxw/websocket' failed: Connection closed before receiving a handshake response
WrappedWebSocket @ VM3454:164
WebSocketBrowserDriver @ websocket.js:6
WebSocketTransport @ websocket.js:32
SockJS._connect @ main.js:219
SockJS._receiveInfo @ main.js:193
g @ emitter.js:30
EventEmitter.emit @ emitter.js:50
(anonymous) @ info-receiver.js:67
g @ emitter.js:30
EventEmitter.emit @ emitter.js:50
(anonymous) @ info-ajax.js:37
g @ emitter.js:30
EventEmitter.emit @ emitter.js:50
xhr.onreadystatechange @ abstract-xhr.js:124
wrapFn @ zone.js:1166
ZoneDelegate.invokeTask @ zone.js:425
onInvokeTask @ core.js:4621
ZoneDelegate.invokeTask @ zone.js:424
Zone.runTask @ zone.js:192
ZoneTask.invokeTask @ zone.js:499
invokeTask @ zone.js:1540
globalZoneAwareCallback @ zone.js:1566

在Spring引导后端,当连接丢失发生时,我得到以下异常,这与通过简单地关闭浏览器选项卡终止客户端时相同。

2017-12-14 12:02:09.507  INFO 16672 --- [tboundChannel-6] o.apache.coyote.http11.Http11Processor   : An error occurred in processing while on a non-container thread. The connection will be closed immediately

java.io.IOException: Eine bestehende Verbindung wurde softwaregesteuert
durch den Hostcomputer abgebrochen
    at sun.nio.ch.SocketDispatcher.write0(Native Method) ~[na:1.8.0_152]
    at sun.nio.ch.SocketDispatcher.write(SocketDispatcher.java:51) ~[na:1.8.0_152]
    at sun.nio.ch.IOUtil.writeFromNativeBuffer(IOUtil.java:93) ~[na:1.8.0_152]
    at sun.nio.ch.IOUtil.write(IOUtil.java:65) ~[na:1.8.0_152]
    at sun.nio.ch.SocketChannelImpl.write(SocketChannelImpl.java:471) ~[na:1.8.0_152]
    at org.apache.tomcat.util.net.NioChannel.write(NioChannel.java:134) ~[tomcat-embed-core-8.5.6.jar:8.5.6]
    at org.apache.tomcat.util.net.NioBlockingSelector.write(NioBlockingSelector.java:101) ~[tomcat-embed-core-8.5.6.jar:8.5.6]
    at org.apache.tomcat.util.net.NioSelectorPool.write(NioSelectorPool.java:157) ~[tomcat-embed-core-8.5.6.jar:8.5.6]
    at org.apache.tomcat.util.net.NioEndpoint$NioSocketWrapper.doWrite(NioEndpoint.java:1221) ~[tomcat-embed-core-8.5.6.jar:8.5.6]
    at org.apache.tomcat.util.net.SocketWrapperBase.flushBlocking(SocketWrapperBase.java:451) ~[tomcat-embed-core-8.5.6.jar:8.5.6]
    at org.apache.tomcat.util.net.SocketWrapperBase.flush(SocketWrapperBase.java:441) ~[tomcat-embed-core-8.5.6.jar:8.5.6]
    at org.apache.coyote.http11.Http11OutputBuffer.flushBuffer(Http11OutputBuffer.java:514) ~[tomcat-embed-core-8.5.6.jar:8.5.6]
    at org.apache.coyote.http11.Http11OutputBuffer.flush(Http11OutputBuffer.java:243) ~[tomcat-embed-core-8.5.6.jar:8.5.6]
    at org.apache.coyote.http11.Http11Processor.flush(Http11Processor.java:1495) ~[tomcat-embed-core-8.5.6.jar:8.5.6]
    at org.apache.coyote.AbstractProcessor.action(AbstractProcessor.java:284) ~[tomcat-embed-core-8.5.6.jar:8.5.6]
    at org.apache.coyote.Response.action(Response.java:167) [tomcat-embed-core-8.5.6.jar:8.5.6]
    at org.apache.catalina.connector.OutputBuffer.doFlush(OutputBuffer.java:336) [tomcat-embed-core-8.5.6.jar:8.5.6]
    at org.apache.catalina.connector.OutputBuffer.flush(OutputBuffer.java:303) [tomcat-embed-core-8.5.6.jar:8.5.6]
    at org.apache.catalina.connector.Response.flushBuffer(Response.java:541) [tomcat-embed-core-8.5.6.jar:8.5.6]
    at org.apache.catalina.connector.ResponseFacade.flushBuffer(ResponseFacade.java:312) [tomcat-embed-core-8.5.6.jar:8.5.6]
    at javax.servlet.ServletResponseWrapper.flushBuffer(ServletResponseWrapper.java:176) [tomcat-embed-core-8.5.6.jar:8.5.6]
    at javax.servlet.ServletResponseWrapper.flushBuffer(ServletResponseWrapper.java:176) [tomcat-embed-core-8.5.6.jar:8.5.6]
    at org.springframework.security.web.util.OnCommittedResponseWrapper.flushBuffer(OnCommittedResponseWrapper.java:158) [spring-security-web-4.1.3.RELEASE.jar:4.1.3.RELEASE]
    at javax.servlet.ServletResponseWrapper.flushBuffer(ServletResponseWrapper.java:176) [tomcat-embed-core-8.5.6.jar:8.5.6]
    at org.springframework.security.web.util.OnCommittedResponseWrapper.flushBuffer(OnCommittedResponseWrapper.java:158) [spring-security-web-4.1.3.RELEASE.jar:4.1.3.RELEASE]
    at org.springframework.http.server.ServletServerHttpResponse.flush(ServletServerHttpResponse.java:96) [spring-web-4.3.4.RELEASE.jar:4.3.4.RELEASE]
    at org.springframework.web.socket.sockjs.transport.session.AbstractHttpSockJsSession.writeFrameInternal(AbstractHttpSockJsSession.java:350) [spring-websocket-4.3.4.RELEASE.jar:4.3.4.RELEASE]
    at org.springframework.web.socket.sockjs.transport.session.AbstractSockJsSession.writeFrame(AbstractSockJsSession.java:322) [spring-websocket-4.3.4.RELEASE.jar:4.3.4.RELEASE]
    at org.springframework.web.socket.sockjs.transport.session.StreamingSockJsSession.flushCache(StreamingSockJsSession.java:86) [spring-websocket-4.3.4.RELEASE.jar:4.3.4.RELEASE]
    at org.springframework.web.socket.sockjs.transport.session.AbstractHttpSockJsSession.sendMessageInternal(AbstractHttpSockJsSession.java:290) [spring-websocket-4.3.4.RELEASE.jar:4.3.4.RELEASE]
    at org.springframework.web.socket.sockjs.transport.session.AbstractSockJsSession.sendMessage(AbstractSockJsSession.java:166) [spring-websocket-4.3.4.RELEASE.jar:4.3.4.RELEASE]
    at org.springframework.web.socket.handler.ConcurrentWebSocketSessionDecorator.tryFlushMessageBuffer(ConcurrentWebSocketSessionDecorator.java:132) [spring-websocket-4.3.4.RELEASE.jar:4.3.4.RELEASE]
    at org.springframework.web.socket.handler.ConcurrentWebSocketSessionDecorator.sendMessage(ConcurrentWebSocketSessionDecorator.java:104) [spring-websocket-4.3.4.RELEASE.jar:4.3.4.RELEASE]
    at org.springframework.web.socket.messaging.StompSubProtocolHandler.sendToClient(StompSubProtocolHandler.java:439) [spring-websocket-4.3.4.RELEASE.jar:4.3.4.RELEASE]
    at org.springframework.web.socket.messaging.StompSubProtocolHandler.handleMessageToClient(StompSubProtocolHandler.java:426) [spring-websocket-4.3.4.RELEASE.jar:4.3.4.RELEASE]
    at org.springframework.web.socket.messaging.SubProtocolWebSocketHandler.handleMessage(SubProtocolWebSocketHandler.java:338) [spring-websocket-4.3.4.RELEASE.jar:4.3.4.RELEASE]
    at org.springframework.messaging.support.ExecutorSubscribableChannel$SendTask.run(ExecutorSubscribableChannel.java:135) [spring-messaging-4.3.4.RELEASE.jar:4.3.4.RELEASE]
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) [na:1.8.0_152]
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) [na:1.8.0_152]
    at java.lang.Thread.run

(Thread.java:748) [na:1.8.0_152]

您是否有任何建议我的代码可能出错,导致这些连接丢失? 这对我来说是一个严重的问题,因为在重新连接时发送数据包当然不起作用。此外,只有在I(或心跳)尝试发送数据包时才会调用错误回调。

1 个答案:

答案 0 :(得分:0)

就我而言,这是问题的根源,没有为 websockets 正确配置代理,proxy.conf.json 中的以下条目修复了它:"/websocket": { "target": "localhost:8080", "secure": false, "logLevel": "warn", "ws": true } 其中缺少 "ws": true 部分.