Spring中配置WebSocket
專案中需要在瀏覽器中實時檢視 docker 容器內框架的執行日誌, 在伺服器上可以通過 docker 提供的 RESTful 介面, 獲取 tail -f 的執行結果 InputStream 流. 而瀏覽器 HTTP 協議是一個請求 - 響應的協議, 要想獲得資料, 就必須發起一次請求, 顯然和 Java 的 InputStream 的概念是不能協作的, 伺服器無法實時的將日誌流推送到瀏覽器. 使用 AJAX 技術可以以一定的間隔時間非同步方式發起請求. 在瀏覽器沒有發起 AJAX 的時間間隙內, 伺服器需要維持日誌流的快取, 如果有很多不同的頁面檢視日誌, 那麼就需要維持多個佇列, 還有一個問題就是如果一個日誌頁面關閉了, 如何清除這個佇列快取?
所以考慮使用 WebSocket 來實時獲取日誌流.
WebSocket簡介
WebSocket為瀏覽器提供了一個真正的瀏覽器和伺服器之間的全雙工Channel. WebSocket使用HTTP的request protocol upgrade頭部來進行請求建立, 服務端返回101表示協議切換成功, 底層的TCP Channel就會一直保持開啟. 一旦通道建立, 瀏覽器端使用send()向伺服器傳送資料, 通過onmessage事件handler來接收伺服器資料, 且每次傳送資料時, 不需要再次傳輸HTTP Header, 大大減少了資料傳輸量, 適用於瀏覽器和伺服器間進行高頻率低延遲的資料交換. 同時實現了真正的伺服器推送資料到瀏覽器.
瀏覽器端:
目前主流的瀏覽器都支援WebSocket.
瀏覽器 | 支援的版本 |
---|---|
Chrome | Supported in version 4+ |
Firefox | Supported in version 4+ |
Internet Explorer | Supported in version 10+ |
Opera | Supported in version 10+ |
Safari | Supported in version 5+ |
服務端:
JavaEE 7 JSR356提供了對WebSocket的支援, Tomcat從7.047版本開始提供了JSR356的支援, Spring從4.0版本開始支援WebSocket.
Java 實現方法
在 Spring 端可以有以下幾種方法使用 WebSocket
1. 使用 Java EE7 的方式
2. 使用 Spring 提供的介面
3. 使用 STOMP 協議以及 Spring 的 MVC
第三種方式見 Spring 的官方文件, 基於 webSocket, 使用 Simple Text Oriented Message Protocol(STOMP) 協議:Using WebSocket to build an interactive web application
STOM 協議工作在 Socket 之上, 類似於 HTTP 協議, 為面向於文字訊息的中介軟體而設計, 是一種語言無關的協議, 符合該協議的 client 和 broker 之間都能通訊, 無論是使用何種語言開發.
STOM 協議介紹
這裡我將著重介紹使用 Spring 提供的介面開發的方式.
主要分為以下幾個類, 第一個是 WebSocket 連線建立前的攔截類 HandshakeInterceptor, 類似於 Spring MVC 的 HandlerInteceptor, 第二個 WebSocket 的處理類, 負責對生命週期進行管理, 第三個是配置類, 將 websocket 請求與對應的 handler 類進行對映.
**1.HandshakeInterceptor
2.WebSocketHandler
3.WebSocketConfigurer**
依賴
Spring WebSocket 依賴於以下包, 版本為 4.0 版本及以上, Tomcat 7.047 以上版本, Java EE 7
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-messaging</artifactId>
<version>4.1.6.RELEASE</version>
</dependency>
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-websocket</artifactId>
<version>8.0.23</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax</groupId>
<artifactId>javaee-api</artifactId>
<version>7.0</version>
<scope>provided</scope>
</dependency>
HandshakeInterceptor
這個類類似於 Spring MVC 中用於攔截 HTTP 請求的 HandlerInteceptor. 它有兩個方法,
public interface HandshakeInterceptor {
boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response,
WebSocketHandler wsHandler, Map<String, Object> attributes) throws Exception;
void afterHandshake(ServerHttpRequest request, ServerHttpResponse response,
WebSocketHandler wsHandler, Exception exception);
}
如果 beforeHandshake 返回 false, 那麼該 WebSocket 請求被拒絕連線.
其中 request 可以轉換成 ServletServerHttpRequest, 並獲取其中包裝的 HttpServletRequest, 以獲得 http 請求中 parameter 等資訊.
WebSocketHandler
作為 WebSocket 連線建立後的處理介面, 主要有以下方法.
public interface WebSocketHandler {
void afterConnectionEstablished(WebSocketSession session) throws Exception;
void handleMessage(WebSocketSession session, WebSocketMessage<?> message) throws Exception;
void handleTransportError(WebSocketSession session, Throwable exception) throws Exception;
void afterConnectionClosed(WebSocketSession session, CloseStatus closeStatus) throws Exception;
boolean supportsPartialMessages();
}
對於每個 WebSocket 連線, 傳入的引數 session 都有一個唯一的 id, 通過 getId() 方法獲取, 可用於標記這個 WebSocket 連線.
常用的類有 TextWebSocketHandler 和 BinaryWebSocketHandler. 這兩個類繼承自 AbstractWebSocketHandler, 其接收 message 時, 對 message 型別進行了判斷, 並對不同型別的訊息進行了分發. 使用時我們需要分別覆蓋 handleTextMessage 方法或 handleBinaryMessage 方法.
public void handleMessage(WebSocketSession session, WebSocketMessage<?> message) throws Exception {
if (message instanceof TextMessage) {
handleTextMessage(session, (TextMessage) message);
}
else if (message instanceof BinaryMessage) {
handleBinaryMessage(session, (BinaryMessage) message);
}
else if (message instanceof PongMessage) {
handlePongMessage(session, (PongMessage) message);
}
else {
throw new IllegalStateException("Unexpected WebSocket message type: " + message);
}
}
WebSocketConfigurer
上面我們定義了握手時需要的 HandlerShakeInteceptor 和對 WebSocket 進行業務處理的 WebSocketHandler, 我們還需要將訪問連線與對應的 handler 以及攔截器關聯起來.
@Configuration
@EnableWebMvc
@EnableWebSocket
public class WebsocketEndPoint implements WebSocketConfigurer {
@Autowired
LogWebSocketHandler handler;
@Autowired
HandlerShakerInceptor handlerShakerInceptor;
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(handler, "/logs.do").setAllowedOrigins("*").addInterceptors(handlerShakerInceptor);
}
}
@Configuration 是 Spring 基於 Java 類的配置, 可以將 Spring 的 Bean 轉換為 Spring 的配置.
其等效的 XML 配置如下:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:websocket="http://www.springframework.org/schema/websocket"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/websocket
http://www.springframework.org/schema/websocket/spring-websocket.xsd">
<websocket:handlers allowed-origins="*">
<websocket:mapping path="/log.do" handler="myHandler"/>
<websocket:handshake-interceptors>
<ref bean="myHandlerShakeInceptor"/>
</websocket:handshake-interceptors>
</websocket:handlers>
<bean id="myHandlerShakeInceptor" class="com.haoyifen.myHandlerShakeInceptor"/>
<bean id="myHandler" class="com.haoyifen.myHandler"/>
</beans>
對於 java configuration 中的 setAllowedOrigins 和 xml 中的 allowed-origins, 解釋一下, 這是用於設定跨域的, 簡單的說就是一般瀏覽器對於 AJAX 和 WebSocket 是禁止跨域請求, 需要我們在伺服器端進行設定, 允許其他域 (比如主機名不同或者埠不同) 上的網頁與你的 WebSocket 發起連線. “*” 表示任何域上的網頁請求都可以連線, 請設定為信任的域名.
客戶端
1.Chrome 的 Dark WebSocket Terminal 應用
2.Java-WebSocket 包中的 client 類
3.JS
$(document).ready(function () {
var websocket = new WebSocket('ws://localhost:8081/logs.do?path=/web/logs/hadoop/hadoop-hadoop-namenode-vm10244.log');
websocket.onmessage = function (event) {
....
};
websocket.onclose=function (event) {
....
}
websocket.onerror=function (event) {
...
};
});
相關文章
- Spring整合WebSocketSpringWeb
- Spring Boot WebSocketSpring BootWeb
- SpringBoot配置WebSocketSpring BootWeb
- spring中配置swaggerSpringSwagger
- Spring Boot系列22 Spring Websocket實現websocket叢集方案的DemoSpring BootWeb
- Spring Boot系列21 Spring Websocket實現websocket叢集方案討論Spring BootWeb
- Spring Boot中配置 AuditListenerSpring Boot
- 在Spring Boot中實現WebSocket實時通訊Spring BootWeb
- Nginx 支援websocket的配置NginxWeb
- WebSocket 的故事(二)—— Spring 中如何利用 STOMP 快速構建 WebSocket 廣播式訊息模式WebSpring模式
- Spring WebFlux 基礎教程:WebSocket 使用SpringWebUX
- Spring Framework 參考文件(WebSocket API)SpringFrameworkWebAPI
- 何為Spring中的配置類?Spring
- Spring中如何配置Hibernate事務Spring
- nginx配置支援websocket協議wssNginxWeb協議
- SpringCloud之配置WebSocket的最佳方式SpringGCCloudWeb
- spring boot +WebSocket 廣播式例項Spring BootWeb
- Spring Framework 參考文件(WebSocket介紹)SpringFrameworkWeb
- Spring - 配置檔案中的特殊字元Spring字元
- Nginx 伺服器配置支援SignalR (WebSocket)Nginx伺服器SignalRWeb
- 配置SpringSpring
- Spring Boot 框架中配置檔案 application.properties 當中的所有配置大全Spring Boot框架APP
- Spring Boot中的配置管理詳解Spring Boot
- Spring Boot中自動配置Autoconfigure詳解Spring Boot
- Spring框架中配置事務管理器Spring框架
- Spring Boot 整合單機websocket(附github原始碼)Spring BootWebGithub原始碼
- Spring Boot:Spring Boot配置SwaggerSpring BootSwagger
- Spring Boot:Spring Boot配置MybatisSpring BootMyBatis
- Spring配置注入Spring
- Spring Boot & 配置Spring Boot
- 在Spring Batch中配置重試邏輯 - BaeldungSpringBAT
- Spring Boot中使用WebSocket總結(三):使用訊息佇列實現分散式WebSocketSpring BootWeb佇列分散式
- Spring Boot系列十七 Spring Boot 整合 websocket,使用RabbitMQ做為訊息代理Spring BootWebMQ
- Spring Boot + Mybatis + Spring MVC環境配置(三):DataSource配置Spring BootMyBatisMVC
- java WebSocket 即時通訊配置使用說明JavaWeb
- WebSocket的故事(六)—— Springboot中,實現更靈活的WebSocketWebSpring Boot
- Spring Boot系列十六 WebSocket簡介和spring boot整合簡單訊息代理Spring BootWeb
- Spring Boot + Mybatis + Spring MVC環境配置(二):Mybatis Generator配置Spring BootMyBatisMVC
- Spring中的Environment外部化配置管理詳解Spring