WebSocket 应用程序在部署在 Tomcat8 服务器上时无法正常工作

WebSocket application doesn't work properly when deployed on Tomcat8 server

本文关键字:常工作 工作 部署 应用程序 Tomcat8 服务器 WebSocket      更新时间:2023-09-26

问题

我正在尝试以源代码中的最小更改运行此示例:https://spring.io/guides/gs/messaging-stomp-websocket/

我已经设法使用 Eclipse 和 Maven 以及终端窗口运行它,再次使用 maven。我还设法将其打包成 .war 文件并使用 java -jar myfile.war 在终端中运行它 在这些情况下,它按预期工作,即 localhost:8080 显示简单的界面,并在同一窗口中连接并返回问候语。当我将 .war 文件部署到我在 RaspberryPi 上运行的 tomcat8 服务器时,问题就来了。在这种情况下,索引.html显示,但"连接"按钮什么也不做,这让我相信这是某种与javascript相关的问题。服务器工作正常,顺便说一句,我在其上部署了其他东西。

我尝试过的事情

  • 编辑了 .war 存档,以便索引.html和.js文件位于 WEB-INF 目录之外,以便外部世界可以访问它们。问题仍然存在。
  • 将对索引中.js文件的脚本引用.html更改为

<script src="http://cdn.sockjs.org/sockjs-0.3.4.js"></script>
<script src="http://cdnjs.cloudflare.com/ajax/libs/stomp.js/2.3.3/stomp.js"></script>

以便可以通过 CDN 交付它们,以防 .war 文件出现任何问题。问题仍然存在。

  • 为 servlet 添加了额外的功能,以便它在不同的目录中显示一些数据,以确保它正常工作,这不是 .war 文件的问题。看起来不错,但上述问题显然仍然存在。

法典

入口点

@SpringBootApplication
public class Application extends SpringBootServletInitializer {
    @Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
        return application.sources(Application.class);
    }
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

将返回的问候。

public class Greeting {
    private String content;
    public Greeting(String content) {
        this.content = content;
    }
    public String getContent() {
        return content;
    }
}

控制器

@Controller
public class GreetingController {

    @MessageMapping("/hello")
    @SendTo("/topic/greetings")
    public Greeting greeting(HelloMessage message) throws Exception {
        Thread.sleep(3000); // simulated delay
        return new Greeting("Hello, " + message.getName() + "!");
    }
}

要发送的消息

public class HelloMessage {
    private String name;
    public String getName() {
        return name;
    }
}

网络套接字配置

@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {
    @Override
    public void configureMessageBroker(MessageBrokerRegistry config) {
        config.enableSimpleBroker("/topic");
        config.setApplicationDestinationPrefixes("/app");
    }
    public void registerStompEndpoints(StompEndpointRegistry registry) {
        registry.addEndpoint("/hello").withSockJS();
    }
}

索引.html

<!DOCTYPE html>
<html>
<head>
    <title>Hello WebSocket</title>
    <script src="sockjs-0.3.4.js"></script>
    <script src="stomp.js"></script>
    <script>
        var stompClient = null;
        
        function setConnected(connected) {
            document.getElementById('connect').disabled = connected;
            document.getElementById('disconnect').disabled = !connected;
            document.getElementById('conversationDiv').style.visibility = connected ? 'visible' : 'hidden';
            document.getElementById('response').innerHTML = '';
        }
        
        function connect() {
            var socket = new SockJS('/hello');
            stompClient = Stomp.over(socket);            
            stompClient.connect({}, function(frame) {
                setConnected(true);
                console.log('Connected: ' + frame);
                stompClient.subscribe('/topic/greetings', function(greeting){
                    showGreeting(JSON.parse(greeting.body).content);
                });
            });
        }
        
        function disconnect() {
            if (stompClient != null) {
                stompClient.disconnect();
            }
            setConnected(false);
            console.log("Disconnected");
        }
        
        function sendName() {
            var name = document.getElementById('name').value;
            stompClient.send("/app/hello", {}, JSON.stringify({ 'name': name }));
        }
        
        function showGreeting(message) {
            var response = document.getElementById('response');
            var p = document.createElement('p');
            p.style.wordWrap = 'break-word';
            p.appendChild(document.createTextNode(message));
            response.appendChild(p);
        }
    </script>
</head>
<body onload="disconnect()">
<noscript><h2 style="color: #ff0000">Seems your browser doesn't support Javascript! Websocket relies on Javascript being enabled. Please enable
    Javascript and reload this page!</h2></noscript>
<div>
    <div>
        <button id="connect" onclick="connect();">Connect</button>
        <button id="disconnect" disabled="disabled" onclick="disconnect();">Disconnect</button>
    </div>
    <div id="conversationDiv">
        <label>What is your name?</label><input type="text" id="name" />
        <button id="sendName" onclick="sendName();">Send</button>
        <p id="response"></p>
    </div>
</div>
</body>
</html>

绒球.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>org.springframework</groupId>
    <artifactId>gs-messaging-stomp-websocket</artifactId>
    <version>0.1.0</version>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.3.3.RELEASE</version>
    </parent>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-websocket</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-messaging</artifactId>
        </dependency>
    </dependencies>
    <properties>
        <java.version>1.8</java.version>
    </properties>
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
    <packaging>war</packaging>
</project>

上次部署的日志

  .   ____          _            __ _ _
 /'' / ___'_ __ _ _(_)_ __  __ _ ' ' ' '
( ( )'___ | '_ | '_| | '_ '/ _` | ' ' ' '
 ''/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_'__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v1.3.3.RELEASE)
2016-04-21 22:39:51.727  INFO 18022 --- [o-8080-exec-129] hello.Application                        : Starting Application on raspberrypi with PID 18022 (/var/lib/tomcat8/webapps/gs-messaging-stomp-websocket-0.1.0/WEB-INF/classes/hello/Application.class started by tomcat8 in /var/lib/tomcat8)
2016-04-21 22:39:51.800  INFO 18022 --- [o-8080-exec-129] hello.Application                        : No active profile set, falling back to default profiles: default
2016-04-21 22:39:52.752  INFO 18022 --- [o-8080-exec-129] ationConfigEmbeddedWebApplicationContext : Refreshing org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext@167fc4: startup date [Thu Apr 21 22:39:52 EEST 2016]; root of context hierarchy
2016-04-21 22:40:09.630  INFO 18022 --- [o-8080-exec-129] o.s.b.f.s.DefaultListableBeanFactory     : Overriding bean definition for bean 'beanNameViewResolver' with a different definition: replacing [Root bean: class [null]; scope=; abstract=false; lazyInit=false; autowireMode=3; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=org.springframework.boot.autoconfigure.web.ErrorMvcAutoConfiguration$WhitelabelErrorViewConfiguration; factoryMethodName=beanNameViewResolver; initMethodName=null; destroyMethodName=(inferred); defined in class path resource [org/springframework/boot/autoconfigure/web/ErrorMvcAutoConfiguration$WhitelabelErrorViewConfiguration.class]] with [Root bean: class [null]; scope=; abstract=false; lazyInit=false; autowireMode=3; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration$WebMvcAutoConfigurationAdapter; factoryMethodName=beanNameViewResolver; initMethodName=null; destroyMethodName=(inferred); defined in class path resource [org/springframework/boot/autoconfigure/web/WebMvcAutoConfiguration$WebMvcAutoConfigurationAdapter.class]]
2016-04-21 22:40:16.208  INFO 18022 --- [o-8080-exec-129] o.s.web.context.ContextLoader            : Root WebApplicationContext: initialization completed in 23468 ms
2016-04-21 22:40:30.525  INFO 18022 --- [o-8080-exec-129] b.a.w.TomcatWebSocketContainerCustomizer : NonEmbeddedServletContainerFactory detected. Websockets support should be native so this normally is not a problem.
2016-04-21 22:40:40.343  INFO 18022 --- [o-8080-exec-129] o.s.b.c.e.ServletRegistrationBean        : Mapping servlet: 'dispatcherServlet' to [/]
2016-04-21 22:40:40.360  INFO 18022 --- [o-8080-exec-129] o.s.b.c.embedded.FilterRegistrationBean  : Mapping filter: 'errorPageFilter' to: [/*]
2016-04-21 22:40:40.363  INFO 18022 --- [o-8080-exec-129] o.s.b.c.embedded.FilterRegistrationBean  : Mapping filter: 'characterEncodingFilter' to: [/*]
2016-04-21 22:40:40.366  INFO 18022 --- [o-8080-exec-129] o.s.b.c.embedded.FilterRegistrationBean  : Mapping filter: 'hiddenHttpMethodFilter' to: [/*]
2016-04-21 22:40:40.369  INFO 18022 --- [o-8080-exec-129] o.s.b.c.embedded.FilterRegistrationBean  : Mapping filter: 'httpPutFormContentFilter' to: [/*]
2016-04-21 22:40:40.372  INFO 18022 --- [o-8080-exec-129] o.s.b.c.embedded.FilterRegistrationBean  : Mapping filter: 'requestContextFilter' to: [/*]
2016-04-21 22:40:41.904  INFO 18022 --- [o-8080-exec-129] o.s.s.concurrent.ThreadPoolTaskExecutor  : Initializing ExecutorService  'clientInboundChannelExecutor'
2016-04-21 22:40:42.137  INFO 18022 --- [o-8080-exec-129] o.s.s.concurrent.ThreadPoolTaskExecutor  : Initializing ExecutorService  'clientOutboundChannelExecutor'
2016-04-21 22:40:42.743  INFO 18022 --- [o-8080-exec-129] o.s.s.c.ThreadPoolTaskScheduler          : Initializing ExecutorService  'messageBrokerTaskScheduler'
2016-04-21 22:40:44.228  INFO 18022 --- [o-8080-exec-129] o.s.w.s.s.s.WebSocketHandlerMapping      : Mapped URL path [/hello/**] onto handler of type [class org.springframework.web.socket.sockjs.support.SockJsHttpRequestHandler]
2016-04-21 22:40:44.635  INFO 18022 --- [o-8080-exec-129] o.s.s.concurrent.ThreadPoolTaskExecutor  : Initializing ExecutorService  'brokerChannelExecutor'
2016-04-21 22:40:49.712  INFO 18022 --- [o-8080-exec-129] .WebSocketAnnotationMethodMessageHandler : Mapped "{[/hello],messageType=[MESSAGE]}" onto public hello.Greeting hello.GreetingController.greeting(hello.HelloMessage) throws java.lang.Exception
2016-04-21 22:40:57.059  INFO 18022 --- [o-8080-exec-129] s.w.s.m.m.a.RequestMappingHandlerAdapter : Looking for @ControllerAdvice: org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext@167fc4: startup date [Thu Apr 21 22:39:52 EEST 2016]; root of context hierarchy
2016-04-21 22:40:58.966  INFO 18022 --- [o-8080-exec-129] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/error],produces=[text/html]}" onto public org.springframework.web.servlet.ModelAndView org.springframework.boot.autoconfigure.web.BasicErrorController.errorHtml(javax.servlet.http.HttpServletRequest,javax.servlet.http.HttpServletResponse)
2016-04-21 22:40:58.993  INFO 18022 --- [o-8080-exec-129] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/error]}" onto public org.springframework.http.ResponseEntity<java.util.Map<java.lang.String, java.lang.Object>> org.springframework.boot.autoconfigure.web.BasicErrorController.error(javax.servlet.http.HttpServletRequest)
2016-04-21 22:40:59.092  INFO 18022 --- [o-8080-exec-129] o.s.w.s.c.a.WebMvcConfigurerAdapter      : Adding welcome page: ServletContext resource [/index.html]
2016-04-21 22:40:59.622  INFO 18022 --- [o-8080-exec-129] o.s.w.s.handler.SimpleUrlHandlerMapping  : Root mapping to handler of type [class org.springframework.web.servlet.mvc.ParameterizableViewController]
2016-04-21 22:40:59.948  INFO 18022 --- [o-8080-exec-129] o.s.w.s.handler.SimpleUrlHandlerMapping  : Mapped URL path [/webjars/**] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
2016-04-21 22:40:59.950  INFO 18022 --- [o-8080-exec-129] o.s.w.s.handler.SimpleUrlHandlerMapping  : Mapped URL path [/**] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
2016-04-21 22:41:01.167  INFO 18022 --- [o-8080-exec-129] o.s.w.s.handler.SimpleUrlHandlerMapping  : Mapped URL path [/**/favicon.ico] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
2016-04-21 22:41:06.289  INFO 18022 --- [o-8080-exec-129] o.s.j.e.a.AnnotationMBeanExporter        : Registering beans for JMX exposure on startup
2016-04-21 22:41:06.561  INFO 18022 --- [o-8080-exec-129] o.s.c.support.DefaultLifecycleProcessor  : Starting beans in phase 2147483647
2016-04-21 22:41:06.570  INFO 18022 --- [o-8080-exec-129] o.s.m.s.b.SimpleBrokerMessageHandler     : Starting...
2016-04-21 22:41:06.578  INFO 18022 --- [o-8080-exec-129] o.s.m.s.b.SimpleBrokerMessageHandler     : BrokerAvailabilityEvent[available=true, SimpleBrokerMessageHandler [DefaultSubscriptionRegistry[cache[0 destination(s)], registry[0 sessions]]]]
2016-04-21 22:41:06.597  INFO 18022 --- [o-8080-exec-129] o.s.m.s.b.SimpleBrokerMessageHandler     : Started.
2016-04-21 22:41:06.830  INFO 18022 --- [o-8080-exec-129] hello.Application                        : Started Application in 93.812 seconds (JVM running for 2602144.954)
Apr 21, 2016 10:41:07 PM org.apache.catalina.startup.HostConfig deployWAR
INFO: Deployment of web application archive /var/lib/tomcat8/webapps/gs-messaging-stomp-websocket-0.1.0.war has finished in 137,294 ms
2016-04-21 22:41:31.098  INFO 18022 --- [o-8080-exec-131] o.s.web.servlet.DispatcherServlet        : FrameworkServlet 'dispatcherServlet': initialization started
2016-04-21 22:41:31.455  INFO 18022 --- [o-8080-exec-131] o.s.web.servlet.DispatcherServlet        : FrameworkServlet 'dispatcherServlet': initialization completed in 355 ms
2016-04-21 22:41:44.554  INFO 18022 --- [MessageBroker-1] o.s.w.s.c.WebSocketMessageBrokerStats    : WebSocketSession[0 current WS(0)-HttpStream(0)-HttpPoll(0), 0 total, 0 closed abnormally (0 connect failure, 0 send limit, 0 transport error)], stompSubProtocol[processed CONNECT(0)-CONNECTED(0)-DISCONNECT(0)], stompBrokerRelay[null], inboundChannel[pool size = 0, active threads = 0, queued tasks = 0, completed tasks = 0], outboundChannelpool size = 0, active threads = 0, queued tasks = 0, completed tasks = 0], sockJsScheduler[pool size = 1, active threads = 1, queued tasks = 0, completed tasks = 0]
2016-04-21 23:11:44.549  INFO 18022 --- [MessageBroker-1] o.s.w.s.c.WebSocketMessageBrokerStats    : WebSocketSession[0 current WS(0)-HttpStream(0)-HttpPoll(0), 0 total, 0 closed abnormally (0 connect failure, 0 send limit, 0 transport error)], stompSubProtocol[processed CONNECT(0)-CONNECTED(0)-DISCONNECT(0)], stompBrokerRelay[null], inboundChannel[pool size = 0, active threads = 0, queued tasks = 0, completed tasks = 0], outboundChannelpool size = 0, active threads = 0, queued tasks = 0, completed tasks = 0], sockJsScheduler[pool size = 1, active threads = 1, queued tasks = 0, completed tasks = 1]
2016-04-21 23:41:44.549  INFO 18022 --- [MessageBroker-1] o.s.w.s.c.WebSocketMessageBrokerStats    : WebSocketSession[0 current WS(0)-HttpStream(0)-HttpPoll(0), 0 total, 0 closed abnormally (0 connect failure, 0 send limit, 0 transport error)], stompSubProtocol[processed CONNECT(0)-CONNECTED(0)-DISCONNECT(0)], stompBrokerRelay[null], inboundChannel[pool size = 0, active threads = 0, queued tasks = 0, completed tasks = 0], outboundChannelpool size = 0, active threads = 0, queued tasks = 0, completed tasks = 0], sockJsScheduler[pool size = 1, active threads = 1, queued tasks = 0, completed tasks = 2]

链接到我的 .war 文件

http://s000.tinyupload.com/index.php?file_id=67742984079178858914

结语

同样,通过消除过程,要么.js库未正确导入,要么我的服务器的 WebSocket 支持有问题。我真的被困在这个问题上,任何和所有的帮助将不胜感激。

所以基本上可能发生的情况是,一旦你部署到服务,你的所有路径都有点偏离,所以假设你有链接说 <a href="/home" > home </a>它可以在本地工作,但在服务器上不起作用,因为您的应用程序 URL 现在是 host:8080/appName/并且当您引用任何带有/home 的内容而不是将您带到 host:8080/appName/home 时,它会将您带到 host:8080/home,此时它是一个损坏的 URL。所以我认为您可以通过在此处更改您的/hello 来解决您的问题

 function connect() {
            var socket = new SockJS('hello');
            stompClient = Stomp.over(socket);            
            stompClient.connect({}, function(frame) {
                setConnected(true);
                console.log('Connected: ' + frame);
                stompClient.subscribe('/topic/greetings', function(greeting){
                    showGreeting(JSON.parse(greeting.body).content);
                });
            });
        }

我尝试了弹簧指南示例,它与谷歌浏览器版本54.0.2840.100(64位)完美配合

友情链接 : https://github.com/spring-guides/gs-messaging-stomp-websocket/tree/master/complete

当我快速看到时,我发现您在依赖项下方缺少

<dependency>
    <groupId>org.webjars</groupId>
    <artifactId>webjars-locator</artifactId>
</dependency>
<dependency>
    <groupId>org.webjars</groupId>
    <artifactId>sockjs-client</artifactId>
    <version>1.0.2</version>
</dependency>
<dependency>
    <groupId>org.webjars</groupId>
    <artifactId>stomp-websocket</artifactId>
    <version>2.3.3</version>
</dependency>
<dependency>
    <groupId>org.webjars</groupId>
    <artifactId>bootstrap</artifactId>
    <version>3.3.7</version>
</dependency>
<dependency>
    <groupId>org.webjars</groupId>
    <artifactId>jquery</artifactId>
    <version>3.1.0</version>
</dependency>