某些浏览器的不支持是对其流行的一个巨大挑战.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的默认只接受相同来源的请求.它还容许接受全部或者特定来源列表.这个检验是为浏览器客户端设计的.这里不须要经过修改来源头的值来阻止其余类型的客户端.
有如下三个可能的行为:
只容许相同源头的请求.在这种模式下,当SocketJs启用时,iframe的HTTP响应头的X帧选项设置为SAMEORIGIN和JSONP传输会被禁用,由于它不容许检查请求的起源。所以,IE6和IE7不支持此模式时启用。
容许特定的起源列表:每个提供的请求源必须以http://或https://开头.在这种模式下,若是SockJs启用,那么Iframe和基于JSONP的传输都会被禁止.所以,ie6,ie9不支持这种模式.
容许全部的请求源;你须要提供*做为容许的请求源的值.在这种模式下,全部的传输均可以.
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); } // ... }