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.