从Angular 2到Spring后端的后续websocket会话阻止了以前的websocket会话工作

时间:2017-04-25 06:06:24

标签: spring angular spring-boot websocket spring-websocket

我正在使用Spring Boot v1.5.1来实现前端的websocket后端和Angular 2。基本上,后端(Spring)轮询数据库以查找更改(如果有),将更改推送到前端(Angular)。

如果第一个用户A访问该网站,则会看到所有更改。只要其他用户B访问该网站,她就会看到更改,但用户A不再进行更改。通过打开2个浏览器(一个接一个;后者将看到更改,但前者将停止),可以在同一台计算机上复制此行为。

在后端,我的websocket处理程序如下所示。

package demo.web;

import org.springframework.stereotype.Component;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler;

import java.util.stream.Collectors;
import java.util.stream.IntStream;

@Component
public class TimestampWsHandler extends TextWebSocketHandler {
    private WebSocketSession session;

    @Override
    public void afterConnectionEstablished(WebSocketSession session) {
        this.session = session;
        start();
    }

    @Override
    protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
        String payload = message.getPayload();
        if ("CLOSE".equalsIgnoreCase(payload)) {
            session.close();
            return;
        }
    }

    void push() {
        if (null != session && session.isOpen()) {
            String s = IntStream.range(0, 10)
                    .mapToObj(i -> System.currentTimeMillis())
                    .map(i -> i.toString())
                    .collect(Collectors.joining(","));
            try {
                session.sendMessage(new TextMessage(s));
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    void start() {
        new Thread(() -> {
            while (true) {
                push();
                try {
                    Thread.sleep(500L);
                } catch (Exception e) { }
            }
        }).start();
    }
}

My Spring Boot应用程序入口点如下所示。

package demo;

import demo.web.TimestampWsHandler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.web.socket.config.annotation.EnableWebSocket;
import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;

@SpringBootApplication
@EnableScheduling
@EnableWebSocket
@EnableAutoConfiguration(exclude = {org.springframework.boot.autoconfigure.security.SecurityAutoConfiguration.class})
public class App implements WebSocketConfigurer {
    @Autowired
    private TimestampWsHandler timestampWsHandler;

    public static void main(String[] args) {
        SpringApplication.run(App.class, args);
    }

    @Override
    public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
        registry.addHandler(timestampWsHandler, "/api/ws/topic/timestamp").setAllowedOrigins("*");
    }
}

在前端(使用ng cli创建项目后),我的app.component.ts已修改为如下所示。

import { Component, OnInit, OnDestroy, AfterViewInit } from '@angular/core';
import { Observable } from 'rxjs/Rx';
import { Subject } from 'rxjs/Subject';
import { Subscription } from 'rxjs/Subscription';
import { IntervalObservable } from 'rxjs/observable/IntervalObservable';
import { $WebSocket, WebSocketSendMode } from 'angular2-websocket/angular2-websocket';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  title = 'app works!';
  data: Array<String>;
  ws: $WebSocket;
  wsSubscription: Subscription;

  ngOnInit() {
  }

  ngAfterViewInit() {
    this.initWebSocket();
  }

  ngOnDestroy() {
    this.destroyWebSocket();
  }

  private initWebSocket(): void {
    this.ws = new $WebSocket('ws://localhost:8080/api/ws/topic/timestamp');
    this.wsSubscription = this.ws.getDataStream().subscribe(
      msgEvent => {
        this.data = msgEvent.data.split(',');
      },
      err => {
        console.error(err);
      }
    );
  }

  private destroyWebSocket(): void {
    if (this.wsSubscription) {
      try {
        this.wsSubscription.unsubscribe();
        this.wsSubscription = null;
      } catch (err) { }
    }

    if (this.ws) {
      try {
        this.ws.close(true);
        this.ws = null;
      } catch (err) { }
    }
  }
}

相应的HTML,app.component.html如下所示。

<h1>
  {{title}}
</h1>
<table *ngIf="data">
  <thead>
    <tr>
      <th>timestamp</th>
    </tr>
  </thead>
  <tbody>
    <tr *ngFor="let t of data">
      <td>{{t}}</td>
    </tr>
  </tbody>
</table>

请注意,在我创建Angular应用程序后,我必须安装angular2-websocketnpm install angular2-websocket --save

关于我做错的任何想法?

我确实看到以下IllegalStateException一遍又一遍地重复。我不确定这些问题是否相关。

java.lang.IllegalStateException: The remote endpoint was in state [TEXT_PARTIAL_WRITING] which is an invalid state for called method
        at org.apache.tomcat.websocket.WsRemoteEndpointImplBase$StateMachine.checkState(WsRemoteEndpointImplBase.java:1224)
        at org.apache.tomcat.websocket.WsRemoteEndpointImplBase$StateMachine.textPartialStart(WsRemoteEndpointImplBase.java:1182)
        at org.apache.tomcat.websocket.WsRemoteEndpointImplBase.sendPartialString(WsRemoteEndpointImplBase.java:222)
        at org.apache.tomcat.websocket.WsRemoteEndpointBasic.sendText(WsRemoteEndpointBasic.java:49)
        at org.springframework.web.socket.adapter.standard.StandardWebSocketSession.sendTextMessage(StandardWebSocketSession.java:203)
        at org.springframework.web.socket.adapter.AbstractWebSocketSession.sendMessage(AbstractWebSocketSession.java:101)
        at demo.web.TimestampWsHandler.push(TimestampWsHandler.java:37)
        at demo.web.TimestampWsHandler.lambda$start$2(TimestampWsHandler.java:47)
        at java.lang.Thread.run(Thread.java:745)

感谢任何帮助。

0 个答案:

没有答案