spring webSocket (1)

某些瀏覽器的不支持是對其流行的一個巨大挑戰.ie瀏覽器10纔開始支持webSocket.此外,一些限制性的代理會經過進行http升級來阻礙websocket,由於websocket保持鏈接時間過長,這些代理會斷開鏈接php

所以,現在要用websocket,必需要使你的websocket API工做.spring框架經過使用SocketJS 協議提供了透明的可回退的選項.這些選項能夠經過配置完成,不須要額外改變項目.html

###26.1.2 A messaging Architecture (一個消息框架)前端

除了短時間的適配挑戰,使用websocket帶來的重要的挑戰在於對於早期的認知的思考,尤爲是咱們今天對構建web應用認知.java

今天rest是一個普遍接受,理解和支持的構建web應用的架構.它依賴於不少URLs,少數的http方法,還有其餘的原則,如超連接,保持無狀態,等git

可是一個webSocket應用或許只用一個簡單的url用於初始的http握手.此後全部的消息經過同一個TCP連接進行分享和傳輸.它指向一個徹底不一樣的,異步的,事件驅動,基於消息的架構.它更加接近傳統的消息應用(例如,jms,AMQP).github

spring4經過對spring Integer 項目的抽象而引進了一個新的spring消息模塊,這個項目包括Message,MessageChannel,MessageHandler,還有其餘能做爲消息架構基礎的東西.這個模塊還引用了一系列吧消息映射到方法的註解,和spring mvc同樣是基於編程模式的.web

###26.1.3 WebSocket下的子協議支持ajax

webSocket做爲一個消息架構,不強制使用任何特定的消息協議.它是在TCP層之上的一個很是薄的層,並將字節流輸入到消息流(不管是文本仍是字節)中,而不作其餘.它依賴應用來攔截消息的含義.spring

不像http是一個應用層協議,在webSocket協議下的輸入消息沒有足夠的信息讓一個框架或容器來知道如何迴路或者處理它.所以認爲WebSocket是一個沒有價值的應用會很低級.它或許是,但它還能在這之上建立一個框架.就象如今大部分web應用都是使用web框架而不僅僅使用servlet API.編程

基於這個緣由webSocket RFC定義了子協議的使用.在握手期間,客戶端和服務端可使用標頭的Sec-WebSocket-protocol協議來對子協議達成一致,即更高級的額應用層協議的使用.子協議不是必須的,但若是不使用,應用須要選擇一個能讓客戶端和服務端都理解的消息格式.這個格式能夠是自定義的,框架特定的,或者一個標準的消息協議.

spring經過STOMP來支持,它是一個簡單的,基於http的腳本語言框架使用的使用而產生的http協議.STOMP被webSocket和web普遍的支持和適配. simple text oriented message protocol 簡單文本消息協議.

##26.1.4 我該使用webSocket嗎

當全部的設計討圍繞着WebSocket使用時,你確定會問,'我該什麼時候使用它';

對於應用來最好的場景以下,該服務端和客戶端進行高頻率,低延遲的事件傳送.主要場景,但不限於,如財務,遊戲,協做應用等.這些應用對時間延遲比較敏感,且須要高頻率的交互大量的信息.

可是對於其餘類型的應用,這並非緣由.例如,一個新聞或社區認爲這樣就能夠--幾分鐘一次的簡單輪詢.這裏延遲很重要,可是能夠接受幾分鐘的延遲.

即便在延遲比較低的狀況下,若是消息量相對比較低(例如監控網絡失敗),那麼長輪詢是一個比較簡單的可靠高效的選擇(仍是認爲信息量相對較低).

低延遲高併發消息的混合體應該能很好的利用webSocket協議.即便在這些應用中,在客戶端和服務端間的通訊應使用webSocket反對使用http和rest.可是,這個答案會由於應用而改變,爲了給客戶端更多的選擇,應用更傾向於使用webSocket和REST API一塊兒來暴露一些功能.另外,一個rest API調用可能須要經過webSocket來廣播一個消息.

spring框架允許帶有@Controller或@RestController的類擁有既能處理http的又能處理WebSocket請求的方法.另外,一個spring mvc請求處理方法,或者其餘與之相關的方法,能夠輕易的向全部webSocket客戶或特別用戶廣播一個消息.

##26.2 WebSocket API

spring 框架提供了一個webSocket來適配各類webSocket引擎.主流的包含websocket的運行環境列表包括Tomcat7.0.47,Jetty9.1+,GlassFish4.1+,Weblogic 12.1.3+,Undertow 1.0+(WildFly 8.0+).其餘的支持會添加,更多的webSocket運行環境會獲得.

如前面介紹的,直接使用WebSocket API對於應該用來講過低級了,直到他由消息格式組成,且有個別框架經過註解來攔截消息或迴路他們.這也是使用spring STOMP的緣由.

使用一個高級別的協議,那麼WebSocket API的細節會變得弱相關,就像在使用http時,TCP交互的細節不會暴露給用戶同樣.除非這個環節包含了直接使用webSocket的細節.

###26.2.1 建立配置一個WebSocketHandler

建立一個WebSocket服務器很是簡單,只要實現WebSocketHandler或者擴展TextWebSocketHandler或BinaryWebSocketHandler;

import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.TextMessage;

public class MyHandler extends TextWebSocketHandler {

    @Override
    public void handleTextMessage(WebSocketSession session, TextMessage message) {
        // ...
    }

}

這裏有可供選擇的webSocket 的基於java或xml命名空間的配置來支持將上面的webSocket配置到一個特定的URl中.

import org.springframework.web.socket.config.annotation.EnableWebSocket;
import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;

@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {

    @Override
    public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
        registry.addHandler(myHandler(), "/myHandler");
    }

    @Bean
    public WebSocketHandler myHandler() {
        return new MyHandler();
    }

}

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>
        <websocket:mapping path="/myHandler" handler="myHandler"/>
    </websocket:handlers>

    <bean id="myHandler" class="org.springframework.samples.MyHandler"/>

</beans>

以上都是在spring mvc引用中,幷包含了DispatcherServlet的配置.可是,spring 的webSocket的支持並不依賴Spring mvc.經過webSocketHttpRequestHandler的幫助,能夠很輕鬆的將一個WebSocketHandler集成到其餘Http服務環境裏.

###26.2.2 Customizing the WebSocket HandShake 自定義webSocket握手

最簡單的定義初始Http WebSocket握手請求是經過一個HandshakeInterceptor,它暴露了握手先後的方法.這個攔截器能夠排除握手或者使WebSocketSession中的屬性不可用.例如,這裏有一個內置的攔截器,將http的Session屬性轉換爲webSocket的session.

@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {

    @Override
    public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
        registry.addHandler(new MyHandler(), "/myHandler")
            .addInterceptors(new HttpSessionHandshakeInterceptor());
    }

}

相應的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>
        <websocket:mapping path="/myHandler" handler="myHandler"/>
        <websocket:handshake-interceptors>
            <bean class="org.springframework.web.socket.server.support.HttpSessionHandshakeInterceptor"/>
        </websocket:handshake-interceptors>
    </websocket:handlers>

    <bean id="myHandler" class="org.springframework.samples.MyHandler"/>

</beans>

一個更先進的方法是繼承DefaultHandshakeHandler,它推進webSocket 握手的步驟,包括驗證客戶端,協同子協議,以及其餘.當應用須要配置一個自定義的RequestUpgradeStrategy時,也會採用這種方法,會的是適配webSocket服務引擎或沒有支持的版本(詳情見26.2.4部署思考這節).java配置和xml命名空間都能配置一個自定義的HandleshakeHandler.

###26.2.3 WebSocketHandler Decoration

spring提供了一個webSocketHandlerDecorator基礎類能夠經過額外的行爲來裝飾WebSocketHandler.日誌和異常處理默認經過webSocket的java配置或xml命名空間來提供並添加.ExceptionWebSocketHandlerDecorator捕獲全部的從WebSocketHandler方法中拋出的未被捕獲異常並關閉webSocket session,還經過一個1011的狀態來顯示服務器錯誤.

###26.2.4 Deployment Considerations(部署思考)

webSocket能夠輕易的同springmvc集成.也能夠輕易的經過webSocketHttpRequestHandler同其餘http處理場景集成.這個很是容易理解.可是還有些有關於JSR-356運行環境的思考.

java webSocket Api(JSR-356)提供了兩種部署機制.一種是在啓動時Servlet容器進行路徑掃描(Servlet 3的功能),另外一種是在Servlet初始化時使用註冊API.但這兩種方法都不能使用一個單個的前端控制器來處理全部的http進程,包括webSocket握手或者其餘的http請求.,例如spring mvc的 DispatcherServlet.

這是JSR-356的一個明顯的侷限性,因此即便在jsr-356運行環境中,spring經過提供一個服務器特定的RequestUpgradeStrategy來支持其申明.

能夠經過WebSocket_SPEC_211來克服這個.另外tomcat和jetty已經提供了其原生的API備選項來輕易的克服這些侷限.但願其餘服務器跟進.

第二思考的地方,支持jsr-356的servlet容器須要執行ServletContainerInitilizer(SCI)掃描會下降應用啓動速度,在一些狀況下特別明顯.當容器支持JSR-356的升級後,這個影響很是明顯.這個能夠經過使用web.xml裏的<absolute-ordering/>來可用或不可用web框架和SCI掃描.

<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">

    <absolute-ordering/>
</web-app>

若有必要,你能夠經過名稱來選擇性是web框架可用,例如spring本身的SpringServletContainerInitializer來支持Servlet 3的java初始化API.

<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">

    <absolute-ordering>
        <name>spring_web</name>
    </absolute-ordering>

</web-app>

###26.2.5 Configuring the WebSocket Engine 配置webSocket引擎

每個WebSocket引擎暴露了配置屬性能夠控制運行時功能,例如消息緩衝容量,空閒超時,其餘等等.

對於Tomcat,WildFly,GlassFish來講,須要在你的WebSocket的配置class中添加一個ServletServerContainerFactoryBean.

@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {

    @Bean
    public ServletServerContainerFactoryBean createWebSocketContainer() {
        ServletServerContainerFactoryBean container = new ServletServerContainerFactoryBean();
        container.setMaxTextMessageBufferSize(8192);
        container.setMaxBinaryMessageBufferSize(8192);
        return container;
    }

}

或者WebSocket 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">

    <bean class="org.springframework...ServletServerContainerFactoryBean">
        <property name="maxTextMessageBufferSize" value="8192"/>
        <property name="maxBinaryMessageBufferSize" value="8192"/>
    </bean>

</beans>

對於客戶端的webSocket配置,你可使用WebSocketContainerFactoryBean(XML)或者基於java的ContainerProvider.getWebSocketContainer()來配置

對於jetty來言,你須要提供一個前置的jetty的WebSocketServerFactory,經過你的webSocket的java配置將它插入到DefaultHandshakeHandler.

@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {

    @Override
    public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
        registry.addHandler(echoWebSocketHandler(),
            "/echo").setHandshakeHandler(handshakeHandler());
    }

    @Bean
    public DefaultHandshakeHandler handshakeHandler() {

        WebSocketPolicy policy = new WebSocketPolicy(WebSocketBehavior.SERVER);
        policy.setInputBufferSize(8192);
        policy.setIdleTimeout(600000);

        return new DefaultHandshakeHandler(
                new JettyRequestUpgradeStrategy(new WebSocketServerFactory(policy)));
    }

}

webSocket 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>
        <websocket:mapping path="/echo" handler="echoHandler"/>
        <websocket:handshake-handler ref="handshakeHandler"/>
    </websocket:handlers>

    <bean id="handshakeHandler" class="org.springframework...DefaultHandshakeHandler">
        <constructor-arg ref="upgradeStrategy"/>
    </bean>

    <bean id="upgradeStrategy" class="org.springframework...JettyRequestUpgradeStrategy">
        <constructor-arg ref="serverFactory"/>
    </bean>

    <bean id="serverFactory" class="org.eclipse.jetty...WebSocketServerFactory">
        <constructor-arg>
            <bean class="org.eclipse.jetty...WebSocketPolicy">
                <constructor-arg value="SERVER"/>
                <property name="inputBufferSize" value="8092"/>
                <property name="idleTimeout" value="600000"/>
            </bean>
        </constructor-arg>
    </bean>

</beans>

###26.2.6 Configuring allowed origins 配置許可的請求源

從spring4.1.5開始,WebSocket和SockJs的默認只接受相同來源的請求.它還容許接受全部或者特定來源列表.這個檢驗是爲瀏覽器客戶端設計的.這裏不須要經過修改來源頭的值來阻止其餘類型的客戶端.

有如下三個可能的行爲:

WebSocket和SockJs容許以下配置請求頭:

import org.springframework.web.socket.config.annotation.EnableWebSocket;
import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;

@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {

    @Override
    public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
        registry.addHandler(myHandler(), "/myHandler").setAllowedOrigins("http://mydomain.com");
    }

    @Bean
    public WebSocketHandler myHandler() {
        return new MyHandler();
    }

}

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="http://mydomain.com">
        <websocket:mapping path="/myHandler" handler="myHandler" />
    </websocket:handlers>

    <bean id="myHandler" class="org.springframework.samples.MyHandler"/>

</beans>

##26.3 SockJs Fallback Options SockJs撤回選項

如同介紹的解釋的,WebSocket不支持全部的瀏覽器,還可能被嚴格的網絡代理拒絕.所以spring提供了回退選項,以使WebSocket基於SockJs 協議儘量關閉.

###26.3.1 Overview of SockJs

SockJs的目標就是能使項目使用WebSocket API,而且在不支持的運行環境,如ie中安全回退而不改變應用代碼.

SockJs由如下組成:

  • 以執行測試形式定義的SockJs協議

  • SockJs的js客戶端,供瀏覽器使用的客戶端類庫

  • SockJs服務器實現,包括在spring框架的spring-webSocket模塊

  • spring-websocket 4.1也提供了SockJs的java客戶端

SockJs主要在瀏覽器中使用.它使用了大量的技術來儘量的支持各類瀏覽器.你能夠在SockJs客戶端也瞭解全部的SockJS傳輸支持的類型和瀏覽器.運輸通常以如下3種形式:WebSocket,Http Streaming,HTTP Long Polling(http長鏈接).能夠在該博客裏瞭解這三種形式.

SockJs客戶端開始會發送"GET /info"來獲取服務器基本的信息.在這以後,它必須決定使用那種傳輸方式.儘量使用WebSocket.若是不是,瀏覽器會使用http流,或者http長輪詢.

全部的傳輸請求都須要使用一次如下URL 結構:

http://host:port/myApp/myEndpoint/{server-id}/{session-id}/{transport}

  • {server-id} 在集羣迴路請求很是有用,不須要使用其餘.

  • {session-id} 相關的http請求都屬於一個SockJS 會話

  • {transport} 代表傳輸的類型,例如'websocket','xhr-streaming'等

WebSocket傳輸只須要作一個http 請求就能進行webSocket握手.以後全部的消息都會經過socket交換

http傳輸須要更多的請求.例如ajax/xhr 流依賴於一個長期運行的請求(以獲取服務端到客戶端的消息),還須要額外的http Post請求來獲取客戶端-服務端的消息.長輪詢也是同樣的,除非服務器端-客戶端的消息發送完,它會結束了當前的請求

SockJS添加了最簡消息框架.例如,服務器剛開始發送字母"o";消息是以['message1','message2']形式發送的(JSON數組),字母h則是心跳架構,若是25秒內沒有收到消息.字母c(close 框架)用來關閉session.

要了解更多,須要在瀏覽器運行一個例子並觀察其http請求.SockJS客戶端容許固定傳輸列表因此能夠一次查看每個傳輸.SockJS客戶端還提供了一個debug標誌,它能夠在瀏覽器控制檯上打印有用信息.在服務器側啓用TRACE,它能夠記錄org.springframework.web.socket.更多的細節查看Socket協議的narrated test[https://sockjs.github.io/sockjs-protocol/sockjs-protocol-0.3.3.html].

###26.3.2 啓用SockJS 經過java配置,SockJS很容易啓用

@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {

    @Override
    public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
        registry.addHandler(myHandler(), "/myHandler").withSockJS();
    }

    @Bean
    public WebSocketHandler myHandler() {
        return new MyHandler();
    }

}

等同於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>
        <websocket:mapping path="/myHandler" handler="myHandler"/>
        <websocket:sockjs/>
    </websocket:handlers>

    <bean id="myHandler" class="org.springframework.samples.MyHandler"/>

</beans>

上面若是是spring mvc,它還須要引入到DispatcherServlet配置中.雖然,spring的webSocket和SockJS不依賴spring mvc.經過SockJsHttpRequestHandler能夠很簡單的將其整合到http服務環境裏.

在瀏覽器端,應用可使用sockjs-client(version1.0x)來模擬W3C webSocket API和交互,服務器根據運行的瀏覽器來選擇最佳的傳輸選項.查看sockjs-client頁,查看瀏覽器支持的傳輸方式列表.客戶端還提供了幾種配置選項,例如,指定要包含哪些傳輸.

###26.3.3 IE8,9的http 流,Ajax/xhr vs IFrame

ie8,9在一段時間內都會保持通用.他們是使用SockJS的關鍵因素.這節介紹了關於些瀏覽器的思考.

SockJs客戶端經過微軟的XDomainRequest來支持ie8,9的ajax/XHR流.它能夠跨域工做,但不支持發送cookies.Cookies對於java項目來言通常都是必需的.既然SockJS客戶端能夠被不少服務器類型(不光是java)使用,它就須要知道cookies是否重要.若是這樣的話SockJS客戶端選擇ajax/xhr流,則須要另外選擇基於iframe的技術.

第一個從SockJS客戶端發起的"/info"請求,它的信息將影響客戶端傳輸選擇.其中細節之一是服務器程序是否須要cookies,用來作權限驗證或者粘性會話集羣.spring SockJS支持引進一個叫sessionCookieNedded的屬性.由於大多數java項目依賴JESSIONID cookie,默認是啓動的.若是你的應用不須要這個,你可用關閉這個選項,這樣SockJS客戶端會在IE8,9上選擇xdr-streaming.

若是你選擇使用基於iframe的傳輸,在任何狀況下,你最好知道瀏覽器當返回頭的X-Frame-Options設置爲DENY,SAMEORIGIN或者ALLOW-FROM<orgin>時,會被通知在特定的頁面來阻塞Iframes的使用.它被用來阻止clickjacking. 一種ui adress attack.

Spring Security 3.2+提供了對任何迴應設置X-Frame-Options的支持.spring Security的java配置默認將它設置爲DENY.其命名空間默認沒有設置,但你能夠本身加上該配置,之後它可能會改成默認配置.

查看 spring安全文檔7.1'默認安全頭'來查看更多細節來知道如何配置x-frame-Options頭.額外狀況,你還能夠檢查或觀察SEC-2501

若是你 的項目添加了x-Frame-Options的返回頭,且依賴iframe-based傳輸,那麼你須要設置該項的值爲SAMEORIGIN 或者SLLOW_FROM<origin>.儘管Spring SockJS支持但仍需知道SockJS客戶端的位置,由於它被iframe加載的.iframe默認會從一個CDN地址來下載SockJS客戶端.你最好將該選項配置爲該應用相同的請求元的url.

在java配置中你可用這麼作.xml的命名空間也經過websocket:sockjs元素提供了一個相同的選項

@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {

    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry) {
        registry.addEndpoint("/portfolio").withSockJS()
                .setClientLibraryUrl("http://localhost:8080/myapp/js/sockjs-client.js");
    }

    // ...

}

在初始開始接口,可用把SockJs客戶端社會中爲devel模式,這樣能夠阻止瀏覽器緩存SockJS請求(例如iframe),更多細節能夠查看 SockJs client頁面.

###26.3.4 心跳消息 SockJS協議要求服務端發送心跳信息來排除代理是否中斷鏈接.spring SockJS配置有一個屬性叫作heartbeatTime能夠用來自定義這個頻率.默認若是鏈接沒有發送信息,這個頻率是25秒符合ietf對公共網略應用的建議.

當你經過WebSocket/SockJs來使用STOMP,那麼STOMP客戶端和服務端將協商交互的心跳,這個SockJs的心跳會禁用.

Spring SockJS支持也能夠配置TaskScheduler來安排心跳任務.這個任務定時器被一個默認配置的基於有限進程數量任務池支持.應用能夠經過本身的特定須要來自定義這些配置

###26.3.5 Servlet 3 Async Requests servlet3異步請求

http流和http長輪詢的SockJS傳輸須要一個比之前更長的鏈接.查看這些技術簡介請看博客

在Servlet容器裏這個是經過Servlet3異步獲得支持的,它容許Servlet容器的線程處理一個請求並持續的寫出從其餘線程裏獲得迴應.

有一個特定的問題,Servlet Api沒有提供了一個客戶端已經離線的通知,查看Servlet_SPEC_44. 可是,Servlet容器在隨後嘗試寫入迴應時會拋出異常.由於spring的SockJs服務支持服務端發送的心跳(25秒一次),這意味客戶端的失聯一般在此期間是可檢測的,若是頻繁發送會更早發現.

網絡IO失敗的緣由多是很簡單的客戶端失聯,但這會隨着沒必要要的堆棧信息記錄.spring作了最大的努力來區分這些表明客戶端失聯的網絡失敗並使用了在AbstractSockJsSession中定義特定的日誌部分DISCONNECTED_CLIENT_LOG_CATEGORY.若是你須要查看這些堆棧,請在TRACE中設置該日誌章節.

###26.3.6 SockJS的CORS頭

Cross-Origin Resource Sharing 跨域資源共享

若是你容許交互源請求(查看26.2.6,配置容許的源),該SockJS協議使用CORS來支持XHR流和輪詢傳輸中的跨域進行支持.因此CORS頭會被自動加入知道該頭在相應中被檢測到.因此當一個應用已被配置了支持CORS跨域請求,例如經過一個servlet攔截器,那麼spring的SockJsService會自動忽略該部分.

固然能夠經過Spring的SockJsService的suppressCos屬性來禁止添加CORS頭信息.

下面的列表是SockJS指望的頭和值信息:

  • "Access-Control-Allow-origin",初始值是"Origin"的請求頭

  • "Access-Control-Allow-Credentials",長設置爲true.

  • "Access-Control-Request-Headers",初始值是等效的請求頭

  • "Access-Control-Allow-Method"這個http方法一個傳輸支持(查看TransportType枚舉)

  • "Acces-control-Max-Age"- 設置爲31536000(1年)

對於其餘的實現能夠想TransportType枚舉同樣在源碼裏查看AbstractSockJsService裏的addCorsHeaders.

還能夠考慮CORS配置容許它配出URLs中SockJs端點前綴而讓SockJsService來處理它.

###26.3.7 SockJS Client

SockJs的java客戶端是爲了避免須要使用瀏覽器就能鏈接遠程SockJs端點而提供.這就兩個服務器之間在公網上雙向通訊頗有用,這裏網絡代理可能會排除WebSocket協議的使用.一個SockJS的java客戶端對於測試來講也頗有用,例如能夠模擬大量的即時用戶.

SockJS的java客戶端支持"websocket","xhr-streaming",還有"xhr-polling"傳輸.剩下的只有在瀏覽器中使用纔有意義.

WebSocketTransport能夠被以下配置:

  • StandardWebSocketClient ,在JSR-356運行環境中

  • JettyWebSocketClient 能夠在jetty9+中使用的原生WebSocket API

  • Any implementation of Spring's WebSocketClient

XhrTransport被定義爲支持"xhr-Streaming"和"xhr-polling",由於在客戶端上來講,這二者在經過URL鏈接服務器上沒有區別.目前有兩種實現:

  • RestTemplateXhrTransport 使用spring的RestTemplate作http請求.

  • JettyXhrTransport使用Jetty的HttpClient來發起Http請求.

下面的例子展現如何建立一個SockJS客戶端和經過SockJS端點鏈接.

List<Transport> transports = new ArrayList<>(2);
transports.add(new WebSocketTransport(new StandardWebSocketClient()));
transports.add(new RestTemplateXhrTransport());

SockJsClient sockJsClient = new SockJsClient(transports);
sockJsClient.doHandshake(new MyWebSocketHandler(), "ws://example.com:8080/sockjs");

SockJs使用JSON格式的數組來發送信息.默認使用Jackson2並須要他在項目路徑下.其餘選擇是你能夠配置SockJsMessageCodec的自定義實現,並在SockJsClient中配置它.

要使用SockJsClient來模擬大量的即時用戶,你須要配置下層的HTTp 客戶端來容許大量的鏈接和線程.例如jetty:

ttpClient jettyHttpClient = new HttpClient();
jettyHttpClient.setMaxConnectionsPerDestination(1000);
jettyHttpClient.setExecutor(new QueuedThreadPool(1000));

也能夠配置服務器端的SockJs相關屬性來實現,(具體細節看javadoc)

@Configuration
public class WebSocketConfig extends WebSocketMessageBrokerConfigurationSupport {

    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry) {
        registry.addEndpoint("/sockjs").withSockJS()
            .setStreamBytesLimit(512 * 1024)
            .setHttpMessageCacheSize(1000)
            .setDisconnectDelay(30 * 1000);
    }

    // ...

}
相關文章
相關標籤/搜索