Spring WebSocket踩坑指南

时间:2023-03-09 16:28:40
Spring WebSocket踩坑指南

Spring WebSocket踩坑指南

本次公司项目中需要在后台与安卓App间建立一个长连接,这里采用了Spring的WebSocket,协议为Stomp。

关于Stomp协议这里就不多介绍了,网上一搜一大把,这里主要说下在配置过程的踩过的那些坑。

官网才是首选

首先在我们第一次尝试WebSocket肯定会搜寻各种各样的博客,在看完关于Stomp和长连接的基础知识,确定使用Spring WebSocket后,请马上进入官网Spring WebSocket,并下载该网站右侧的源码,这将节省大量时间。网上的各个博客都是对官网的一定翻译而已,重复性的内容过多,直接阅读官网就好。接下来就是一步步排雷组装到自己的项目中了。

正因为官网的存在,本篇文章只讲踩坑,配置步骤详见官网文档与源码。

依赖

    <!-- stomp协议websocket长连接 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-websocket</artifactId>
<!-- 该部分的版本号一般与当前项目的Spring版本号一致即可 -->
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-messaging</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- 长连接中消息的传递使用JSON格式,
该依赖帮助Spring自动在Object与JSON之间转换,
不加的话会在传递消息时报错-->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.5.3</version>
</dependency>
<dependency>
<groupId>javax.websocket</groupId>
<artifactId>javax.websocket-api</artifactId>
<version>1.1</version>
<scope>provided</scope>
</dependency>

spring扫描问题

在项目配置过程中,我出现了客户端无法向服务器发送请求的错误。事实上服务器已经收到了请求,此时打印日志:

No matching message handler methods.

此时我们的目标是客户端向服务器的指定接口发送数据,日志的意思为服务器已经接收到消息但没有合适的方法去处理它。这是因为在所需处理的方法上的注解@Message并没有被Spring MVC扫描到。在Spring Boot中不会出现这个问题,但在Spring MVC中可以查看配置文件spring-mvc.xml中在组件扫描中是否加入了use-default-filters="false"。use-default-filters="true"为默认配置,即允许Spring扫描配置包下的所有组件;而设为false后仅允许Spring控制网站的跳转逻辑,忽略了@Message注解。删除即可。

web.xml 3.0

Java Async support must be enabled on a servlet and for all filters involved in async request processing. This is done in Java code using the Servlet API or by adding "<async-supported>true</async-supported>" to servlet and filter declarations in web.xml. Also you must use a Servlet 3.0+ container

报错很清晰了,请将web.xml从2.0修改为3.0,并在filter中加入配置<async-supported>true</async-supported>,作用是支持异步处理。

<web-app xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
version="3.0">

302

这是在使用SockJS时出现的问题。这是因为我们项目中使用了Shiro作为权限管理,将所需暴露的接口配置一下即可。

https - wss 403

如果我们网站正式服务器采用了https协议,那么相对应的WebSocket的协议必须为wss。否则出现403问题。解决的另一种情况可以参见Nginx反向代理WebSocket响应403的解决办法,不过我没有使用它,也没有测过。

handshake: Unexpected response code: 400

这是由于正式服务器中采用了NGINX作为反向代理,这里需要更新下NGINX配置,具体如下:

    location /{
proxy_pass http://wsbackend;
// 解决下面60s自动断开的问题
proxy_read_timeout 600s;
# WebSocket support (nginx 1.4),加入以下几行
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
} }

WebSocket 60s自动断开连接

还是NGINX问题。解决方案已经在上面指出。proxy_read_timeout 默认为60s,如果NGINX对一个长连接在读取一次数据60s后没有再次接收到消息,则认为已超时,并关闭该连接,所以前端很准时的60s后开始报错。这里将时间修改到了10分钟,可以根据自己情况调整。

心跳

继续上面的话题,即使修改了超时时间为10分钟也没有实质性的改变,这时候心跳包就需要登场了。心跳包设计可以参考一种心跳,两种设计| 徐靖峰|个人博客高效保活长连接:手把手教你实现 自适应的心跳保活机制这两篇博客。第二篇博客更多关于安卓端。

安卓端SDK

这里我在GitHub上找了一个包:

'com.github.forresthopkinsa:StompProtocolAndroid:17.09.1'
```;
具体见[GitHub](https://github.com/NaikSoftware/StompProtocolAndroid)。 ## 安卓端进程防杀死补充 这部分可以参考[FV悬浮球的说明](https://www.kancloud.cn/sealt/fooview/382748)。同时也推荐下这款软件,在安卓上的手势辅助功能很好用。 End.