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 Boot WebSocketSpring BootWeb
- Spring整合WebSocketSpringWeb
- 學習WebSocket(一):Spring WebSocket的簡單使用WebSpring
- Spring Boot系列22 Spring Websocket實現websocket叢集方案的DemoSpring BootWeb
- Spring Boot系列21 Spring Websocket實現websocket叢集方案討論Spring BootWeb
- 玩轉spring boot——websocketSpring BootWeb
- SpringBoot配置WebSocketSpring BootWeb
- Nginx 支援websocket的配置NginxWeb
- WebSocket 的故事(二)—— Spring 中如何利用 STOMP 快速構建 WebSocket 廣播式訊息模式WebSpring模式
- Spring WebFlux 基礎教程:WebSocket 使用SpringWebUX
- Spring Framework 參考文件(WebSocket API)SpringFrameworkWebAPI
- 何為Spring中的配置類?Spring
- 如果使用spring中的jdni配置Spring
- 學習WebSocket(二):使用Spring WebSocket做一個簡單聊天室WebSpring
- Spring Framework 參考文件(WebSocket介紹)SpringFrameworkWeb
- spring boot +WebSocket 廣播式例項Spring BootWeb
- SpringCloud之配置WebSocket的最佳方式SpringGCCloudWeb
- nginx,tornado,websocket,supervisord配置成型NginxWeb
- Spring - 配置檔案中的特殊字元Spring字元
- Spring中如何配置Hibernate事務Spring
- Spring Cloud中Feign配置詳解SpringCloud
- Spring+Websocket實現訊息的推送SpringWeb
- spring配置中classpath和classpath*的區別Spring
- Nginx 伺服器配置支援SignalR (WebSocket)Nginx伺服器SignalRWeb
- 配置SpringSpring
- Spring配置Spring
- Spring Boot中使用WebSocket總結(三):使用訊息佇列實現分散式WebSocketSpring BootWeb佇列分散式
- Spring Boot 整合單機websocket(附github原始碼)Spring BootWebGithub原始碼
- Spring Boot 配置中的敏感資訊如何保護?Spring Boot
- java WebSocket 即時通訊配置使用說明JavaWeb
- Spring的DataSource配置、將Hibernate配置全部寫到Spring配置Spring
- WebSocket的故事(六)—— Springboot中,實現更靈活的WebSocketWebSpring Boot
- Spring Boot系列十七 Spring Boot 整合 websocket,使用RabbitMQ做為訊息代理Spring BootWebMQ
- [譯] 實時通訊:使用 Spring Boot 實現 WebSocketSpring BootWeb
- Spring中的Environment外部化配置管理詳解Spring
- spring application.xml中載入配置檔案SpringAPPXML
- 在spring,mybatis整合配置中走的彎路(1)SpringMyBatis
- Spring Boot中自動配置Autoconfigure詳解Spring Boot