基于 Servlet API 并部署到 Servlet 容器
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了基于 Servlet API 并部署到 Servlet 容器相关的知识,希望对你有一定的参考价值。
2. REST 客户端
本节介绍客户端访问 REST 终结点的选项。
2.1. RestTemplate
RestTemplate
是执行 HTTP 请求的同步客户端。它是原始的 Spring REST 客户端,并在底层 HTTP 客户端上公开一个简单的模板方法 API 图书馆。
2.2. WebClient
WebClient
是执行 HTTP 请求的非阻塞、反应式客户端。它是 在 5.0 中引入,并提供了一种现代替代方案,具有高效的 支持同步和异步以及流式处理方案。RestTemplate
相反,支持以下内容:RestTemplate
WebClient
- 非阻塞 I/O。
- 反应流背压。
- 高并发性,更少的硬件资源。
- 函数式、流畅的 API,利用了 Java 8 lambda。
- 同步和异步交互。
- 从服务器流式传输或向式传输。
有关更多详细信息,请参阅网络客户端。
2.3. HTTP接口
Spring 框架允许您将 HTTP 服务定义为带有 HTTP 的 Java 接口 交换方法。然后,您可以生成实现此接口的代理,并且 执行交换。这有助于简化 HTTP 远程访问并提供额外的 灵活选择 API 样式,例如同步或反应式。
有关详细信息,请参阅REST 终结点。
3. 测试
在Spring WebFlux中相同
本节总结了Spring MVC应用程序中可用的选项。spring-test
- Servlet API Mocks:单元测试控制器的 Servlet API 合约的模拟实现, 筛选器和其他 Web 组件。有关更多详细信息,请参阅Servlet API模拟对象。
- TestContext Framework:支持在 JUnit 和 TestNG 测试中加载 Spring 配置, 包括跨测试方法对加载的配置进行高效缓存,并支持 加载 awith a. 有关更多详细信息,请参阅TestContext 框架。
WebApplicationContext
MockServletContext
- Spring MVC 测试:用于测试带注释的控制器的框架,也称为 通过(即支持注释),完成与 Spring MVC 基础架构,但没有 HTTP 服务器。 有关更多详细信息,请参阅Spring MVC 测试。
MockMvc
DispatcherServlet
- 客户端 REST:提供可以用作 用于测试内部使用的客户端代码的模拟服务器。 有关更多详细信息,请参阅客户端 REST 测试。
spring-test
MockRestServiceServer
RestTemplate
-
WebTestClient
:专为测试 WebFlux 应用程序而构建,但也可用于 通过 HTTP 连接对任何服务器进行端到端集成测试。这是一个 非阻塞、反应式客户端,非常适合测试异步和流式处理 场景。
4. 网络套接字
网络通量
参考文档的这一部分涵盖了对Servlet堆栈,WebSocket的支持。 消息传递,包括原始 WebSocket 交互、通过 SockJS 的 WebSocket 仿真,以及 通过 STOMP 作为 WebSocket 上的子协议发布-订阅消息传递。
= WebSocket 简介
WebSocket协议RFC 6455提供了一个标准化的 在客户端和服务器之间建立全双工双向通信通道的方法 通过单个 TCP 连接。它是与HTTP不同的TCP协议,但旨在 通过 HTTP 工作,使用端口 80 和 443,并允许重用现有防火墙规则。
WebSocket 交互以使用 HTTPheader 的 HTTP 请求开始 以升级,或者在这种情况下,切换到 WebSocket 协议。以下示例 显示了这样的交互:Upgrade
GET /spring-websocket-portfolio/portfolio HTTP/1.1
Host: localhost:8080
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: Uc9l9TMkWGbHFD2qnFHltg==
Sec-WebSocket-Protocol: v10.stomp, v11.stomp
Sec-WebSocket-Version: 13
Origin: http://localhost:8080
标题。 |
使用连接。 |
支持 WebSocket 的服务器返回输出,而不是通常的 200 状态代码 类似于以下内容:
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: 1qVdfYHU9hPOl4JYYNXF623Gzn0=
Sec-WebSocket-Protocol: v10.stomp
协议切换 |
握手成功后,HTTP 升级请求的 TCP 套接字将保留 为客户端和服务器打开以继续发送和接收消息。
有关 WebSocket 工作原理的完整介绍超出了本文档的范围。 请参阅 RFC 6455、html5 的 WebSocket 章节,或许多介绍中的任何一个和 网络上的教程。
请注意,如果 WebSocket 服务器在 Web 服务器(例如 nginx)后面运行,则 可能需要将其配置为将 WebSocket 升级请求传递到 WebSocket 服务器。同样,如果应用程序在云环境中运行,请检查 与 WebSocket 支持相关的云提供商的说明。
== HTTP 与 WebSocket
即使 WebSocket 被设计为与 HTTP 兼容并且以 HTTP 请求开始, 重要的是要了解这两种协议导致非常不同的 体系结构和应用程序编程模型。
在 HTTP 和 REST 中,应用程序被建模为多个 URL。要与应用程序交互, 客户端访问这些 URL,请求-响应样式。服务器将请求路由到 基于 HTTP URL、方法和标头的适当处理程序。
相比之下,在 WebSocket 中,初始连接通常只有一个 URL。 随后,所有应用程序消息都在同一 TCP 连接上流动。这指向 一种完全不同的异步、事件驱动的消息传递体系结构。
WebSocket也是一种低级传输协议,与HTTP不同,它没有规定 消息内容的任何语义。这意味着无法路由或处理 消息,除非客户端和服务器在消息语义上达成一致。
WebSocket 客户端和服务器可以协商使用更高级别的消息传递协议 (例如,STOMP),通过 HTTP 握手请求上的标头。 如果没有这一点,他们需要提出自己的公约。Sec-WebSocket-Protocol
== 何时使用 WebSockets
WebSockets可以使网页具有动态性和交互性。但是,在许多情况下, Ajax 和 HTTP 流或长轮询的组合可以提供简单和 有效的解决方案。
例如,新闻、邮件和社交源需要动态更新,但可能是 完全可以每隔几分钟这样做一次。协作、游戏和金融应用, 另一方面,需要更接近实时。
延迟本身并不是决定性因素。如果消息量相对较低(例如, 监控网络故障)HTTP 流或轮询可以提供有效的解决方案。 低延迟、高频和高容量的组合使最佳 使用 WebSocket 的案例。
另请记住,在互联网上,您无法控制的限制性代理 可能会排除 WebSocket 交互,因为它们未配置为传递标头,或者因为它们关闭了显示为空闲的长期连接。这 意味着将WebSocket用于防火墙内的内部应用程序是一个更多的 比面向公众的应用程序更直接的决定。Upgrade
4.1. 网络套接字接口
网络通量
Spring 框架提供了一个 WebSocket API,您可以使用它来编写客户端和 处理 WebSocket 消息的服务器端应用程序。
4.1.1. WebSocketHandler
网络通量
创建 WebSocket 服务器就像实现器一样简单,更多 很可能,扩展任一。以下 示例用途:WebSocketHandler
TextWebSocketHandler
BinaryWebSocketHandler
TextWebSocketHandler
public class MyHandler extends TextWebSocketHandler
@Override
public void handleTextMessage(WebSocketSession session, TextMessage message)
// ...
有专用的 WebSocket Java 配置和 XML 命名空间支持,用于映射前面的内容 特定 URL 的 WebSocket 处理程序,如以下示例所示:
@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
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/websocket
https://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 的配置中。然而,春天的 WebSocket 支持不依赖于 Spring MVC。相对简单的是 在WebSocketHttpRequestHandler的帮助下集成到其他HTTP服务环境中。WebSocketHandler
当直接使用API与间接使用API时,例如通过STOMP消息传递,应用程序必须同步消息的发送 因为底层标准 WebSocket 会话 (JSR-356) 不允许并发 发送。一种选择是使用ConcurrentWebSocketSessionDecorator 包装。WebSocketHandler
WebSocketSession
4.1.2. 网络套接字握手
网络通量
自定义初始 HTTP WebSocket 握手请求的最简单方法是通过 a,它公开了握手之前和“之后”的方法。 您可以使用此类拦截器来排除握手或创建任何属性 可用到。以下示例使用内置侦听器 要将 HTTP 会话属性传递给 WebSocket 会话,请执行以下操作:HandshakeInterceptor
WebSocketSession
@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
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/websocket
https://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>
更高级的选项是扩展该执行 WebSocket 握手的步骤,包括验证客户端源, 谈判子议定书和其他细节。应用程序可能还需要使用它 选项,如果它需要配置自定义以便 适应尚不支持的 WebSocket 服务器引擎和版本 (有关此主题的详细信息,请参阅部署)。 Java 配置和 XML 命名空间都使配置自定义成为可能。DefaultHandshakeHandler
RequestUpgradeStrategy
HandshakeHandler
4.1.3. 部署
Spring WebSocket API很容易集成到Spring MVC应用程序中,其中 同时提供 HTTP WebSocket 握手和其他 HTTP 请求。它也很容易集成到其他 HTTP 处理场景中 通过调用。这既方便又容易 理解。但是,JSR-356 运行时需要特别注意。DispatcherServlet
WebSocketHttpRequestHandler
Java WebSocket API(JSR-356)提供了两种部署机制。第一个 涉及在启动时进行 Servlet 容器类路径扫描(Servlet 3 功能)。 另一个是在 Servlet 容器初始化时使用的注册 API。 这两种机制都无法使用单个“前端控制器” 用于所有 HTTP 处理 — 包括 WebSocket 握手和所有其他 HTTP 请求 — 例如 Spring MVC 的请求。DispatcherServlet
这是JSR-356的一个重大限制,Spring的WebSocket支持解决了 特定于服务器的实现,即使在 JSR-356 运行时也是如此。 Tomcat,Jetty,GlassFish,WebLogic,WebSphere和 暗流(和野蝇)。RequestUpgradeStrategy
第二个考虑因素是支持 JSR-356 的 Servlet 容器是预期的。 执行可能会减慢应用程序速度的 (SCI) 扫描 创业 - 在某些情况下,戏剧性。如果在 升级到支持 JSR-356 的 Servlet 容器版本,它应该 可以有选择地启用或禁用 Web 片段(和 SCI 扫描) 通过使用元素,如以下示例所示:ServletContainerInitializer
<absolute-ordering />
web.xml
<web-app xmlns="https://jakarta.ee/xml/ns/jakartaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
https://jakarta.ee/xml/ns/jakartaee
https://jakarta.ee/xml/ns/jakartaee/web-app_5_0.xsd"
version="5.0">
<absolute-ordering/>
</web-app>
然后,您可以有选择地按名称启用Web片段,例如Spring自己的为Servlet 3提供支持。 Java 初始化 API。以下示例演示如何执行此操作:SpringServletContainerInitializer
<web-app xmlns="https://jakarta.ee/xml/ns/jakartaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
https://jakarta.ee/xml/ns/jakartaee
https://jakarta.ee/xml/ns/jakartaee/web-app_5_0.xsd"
version="5.0">
<absolute-ordering>
<name>spring_web</name>
</absolute-ordering>
</web-app>
4.1.4. 服务器配置
网络通量
每个基础 WebSocket 引擎都公开控制 运行时特征,例如消息缓冲区大小、空闲超时、 等。
对于Tomcat,WildFly和GlassFish,您可以将a添加到您的 WebSocket Java 配置,如以下示例所示:ServletServerContainerFactoryBean
@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer
@Bean
public ServletServerContainerFactoryBean createWebSocketContainer()
ServletServerContainerFactoryBean container = new ServletServerContainerFactoryBean();
container.setMaxTextMessageBufferSize(8192);
container.setMaxBinaryMessageBufferSize(8192);
return container;
以下示例显示了与上述示例等效的 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
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/websocket
https://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 Java配置进入Spring。 以下示例演示如何执行此操作:WebSocketServerFactory
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)));
以下示例显示了与上述示例等效的 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
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/websocket
https://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>
4.1.5. 允许的来源
网络通量
从 Spring Framework 4.1.5 开始,WebSocket 和 SockJS 的默认行为是接受 仅同源请求。也可以允许所有或指定的源列表。 此检查主要针对浏览器客户端设计。没有什么能阻止其他类型 的客户端修改标头值(有关更多详细信息,请参阅RFC 6454:Web 起源概念)。Origin
三种可能的行为是:
- 仅允许同源请求(默认):在此模式下,启用 SockJS 时, Iframe HTTP 响应标头设置为 JSONP 传输被禁用,因为它不允许检查请求的来源。 因此,启用此模式时不支持 IE6 和 IE7。
X-Frame-Options
SAMEORIGIN
- 允许指定的源列表:每个允许的源必须以 or 开头。在此模式下,启用 SockJS 时,将禁用 IFrame 传输。 因此,在以下情况下不支持 IE6 到 IE9 模式已启用。
http://
https://
- 允许所有源:要启用此模式,应提供允许的源 价值。在此模式下,所有传输都可用。
*
您可以配置 WebSocket 和 SockJS 允许的源,如以下示例所示:
@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer
@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry)
registry.addHandler(myHandler(), "/myHandler").setAllowedOrigins("https://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
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/websocket
https://www.springframework.org/schema/websocket/spring-websocket.xsd">
<websocket:handlers allowed-origins="https://mydomain.com">
<websocket:mapping path="/myHandler" handler="myHandler" />
</websocket:handlers>
<bean id="myHandler" class="org.springframework.samples.MyHandler"/>
</beans>
4.2. SockJS 回退
在公共互联网上,您无法控制的限制性代理可能会排除 WebSocket 交互,因为它们未配置为传递标头或 因为它们关闭了看似空闲的长期连接。Upgrade
此问题的解决方案是 WebSocket 仿真,即尝试使用 WebSocket。 首先,然后回退到模拟WebSocket的基于HTTP的技术 交互并公开相同的应用程序级 API。
在Servlet堆栈上,Spring 框架同时提供服务器(和客户端)支持 对于 SockJS 协议。
4.2.1. 概述
SockJS的目标是让应用程序使用WebSocket API,但回退到。 在运行时需要时,非 WebSocket 替代方案,无需 更改应用程序代码。
SockJS包括:
- 以可执行叙述测试的形式定义的SockJS 协议。
- SockJS javascript 客户端 — 用于浏览器的客户端库。
- SockJS服务器实现,包括Spring Frameworkmodule中的一个。
spring-websocket
- 模块中的SockJS Java客户端(从4.1版本开始)。
spring-websocket
SockJS是为在浏览器中使用而设计的。它使用多种技术 支持各种浏览器版本。 有关 SockJS 传输类型和浏览器的完整列表,请参阅SockJS 客户端页面。运输 分为三大类:WebSocket、HTTP 流和 HTTP 长轮询。 有关这些类别的概述,请参阅此博客文章。
SockJS 客户端首先发送至 从服务器获取基本信息。之后,它必须决定什么运输方式 来使用。如果可能,将使用 WebSocket。如果没有,在大多数浏览器中, 至少有一个 HTTP 流式处理选项。如果不是,则 HTTP(长) 使用轮询。GET /info
所有传输请求都具有以下 URL 结构:
https://host:port/myApp/myEndpoint/server-id/session-id/transport
哪里:
-
server-id
可用于在集群中路由请求,但不用于其他用途。 -
session-id
关联属于 SockJS 会话的 HTTP 请求。 -
transport
指示传输类型(例如,,,等)。websocket
xhr-streaming
WebSocket 传输只需要一个 HTTP 请求即可执行 WebSocket 握手。 此后的所有消息都在该套接字上交换。
HTTP 传输需要更多请求。例如,Ajax/XHR 流依赖于 一个长时间运行的请求,用于服务器到客户端消息和其他 HTTP POST 客户端到服务器消息的请求。长轮询类似,只是它 在每次服务器到客户端发送后结束当前请求。
SockJS添加了最少的消息框架。例如,服务器最初发送字母(“打开”帧),消息作为(JSON编码数组)发送,如果没有消息流,则发送字母(“心跳”帧) 25 秒(默认情况下),以及字母(“关闭”框架)关闭会话。o
a["message1","message2"]
h
c
若要了解详细信息,请在浏览器中运行示例并观察 HTTP 请求。 SockJS客户端允许修复传输列表,因此可以 一次查看一个传输。SockJS 客户端还提供了一个调试标志, 这会在浏览器控制台中启用有用的消息。在服务器端,您可以启用日志记录。 有关更多详细信息,请参阅 SockJS 协议叙述测试。TRACE
org.springframework.web.socket
4.2.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
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/websocket
https://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(版本 1.0.x)。它 模拟 W3C WebSocket API 并与服务器通信以选择最佳 传输选项,具体取决于它运行的浏览器。请参阅sockjs-client页面和 浏览器支持的传输类型。客户端还提供了几个 配置选项 — 例如,指定要包括的传输。
4.2.3. IE 8 和 9
Internet Explorer 8 和 9 仍在使用中。他们是 拥有SockJS的一个关键原因。本节介绍重要的 有关在这些浏览器中运行的注意事项。
SockJS客户端通过使用Microsoft的XDomainRequest在IE 8和9中支持Ajax/XHR流。 这适用于跨域,但不支持发送 Cookie。 Cookie 对于 Java 应用程序通常是必不可少的。 但是,由于SockJS客户端可以与许多服务器一起使用 类型(不仅仅是Java类型),它需要知道cookie是否重要。 如果是这样,SockJS 客户端更喜欢 Ajax/XHR 进行流式传输。否则,它 依赖于基于 iframe 的技术。
来自 SockJS 客户端的第一个请求是 可能影响客户端选择传输的信息。 其中一个细节是服务器应用程序是否依赖于 cookie (例如,用于身份验证目的或使用粘性会话进行群集)。 Spring 的 SockJS 支持包括一个名为的属性。 默认情况下启用它,因为大多数Java应用程序都依赖于cookie。如果您的应用程序不需要它,您可以关闭此选项, 然后,SockJS客户端应该在IE 8和9中选择。/info
sessionCookieNeeded
JSESSIONID
xdr-streaming
如果您确实使用基于 iframe 的传输,请记住 可以通过以下方式指示浏览器阻止在给定页面上使用 IFrames 将 HTTP 响应标头设置为 、、 或。这用于防止点击劫持。X-Frame-Options
DENY
SAMEORIGIN
ALLOW-FROM <origin>
如果您的应用程序添加了响应标头(应该如此! 并且依赖于基于 iframe 的传输,您需要将标头值设置为 toor。春袜子 支持还需要知道 SockJS 客户端的位置,因为它已加载 从 iframe 。默认情况下,iframe 设置为下载 SockJS 客户端 从 CDN 位置。最好将此选项配置为使用 与应用程序来自同一源的 URL。X-Frame-Options
SAMEORIGIN
ALLOW-FROM <origin>
以下示例显示了如何在 Java 配置中执行此操作:
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer
@Override
public void registerStompEndpoints(StompEndpointRegistry registry)
registry.addEndpoint("/portfolio").withSockJS()
.setClientLibraryUrl("http://localhost:8080/myapp/js/sockjs-client.js");
// ...
XML 命名空间通过元素提供类似的选项。<websocket:sockjs>
4.2.4. 心跳
SockJS 协议要求服务器发送心跳消息以排除代理 从断定连接挂起。Spring SockJS 配置具有一个属性 调用可用于自定义频率。默认情况下,a 检测信号在 25 秒后发送,假设没有发送其他消息 连接。此 25 秒值符合以下针对公共互联网应用程序的IETF 建议。heartbeatTime
Spring SockJS支持还允许您配置以下 计划检测信号任务。任务调度程序由线程池支持, 使用基于可用处理器数量的默认设置。你 应考虑根据您的特定需求自定义设置。TaskScheduler
4.2.5. 客户端断开连接
HTTP 流和 HTTP 长轮询 SockJS 传输需要保持连接 开放时间比平时长。有关这些技术的概述,请参阅此博客文章。
在 Servlet 容器中,这是通过 Servlet 3 异步支持完成的 允许退出 Servlet 容器线程,处理请求并继续 写入来自另一个线程的响应。
一个特定的问题是 Servlet API 不为客户端提供通知 这已经消失了。请参阅eclipse-ee4j/servlet-api#44。 但是,Servlet 容器在后续尝试写入时引发异常 到响应。由于 Spring 的 SockJS 服务支持服务器发送的心跳(每个 默认为 25 秒),这意味着通常会在该范围内检测到客户端断开连接 时间段(或更早,如果消息发送更频繁)。
4.2.6. SockJS 和 CORS
如果您允许跨源请求(请参阅允许的来源),SockJS 协议 使用 CORS 在 XHR 流式处理和轮询传输中提供跨域支持。因此 将自动添加 CORS 标头,除非响应中存在 CORS 标头 被检测到。因此,如果应用程序已配置为提供 CORS 支持(例如, 通过 Servlet 过滤器),Spring 跳过了这部分。SockJsService
也可以通过在 Spring 的 SockJsService 中设置属性来禁用这些 CORS 标头的添加。suppressCors
SockJS 需要以下标头和值:
-
Access-Control-Allow-Origin
:从请求标头的值初始化。Origin
-
Access-Control-Allow-Credentials
:始终设置为。true
-
Access-Control-Request-Headers
:从等效请求标头中的值初始化。 -
Access-Control-Allow-Methods
:传输支持的 HTTP 方法(参见)。TransportType
-
Access-Control-Max-Age
:设置为 31536000(1 年)。
对于确切的实现,请参阅和 源代码中的 theenum。addCorsHeaders
AbstractSockJsService
TransportType
或者,如果 CORS 配置允许,请考虑排除具有 SockJS端点前缀,从而让Spring处理它。SockJsService
4.2.7. SockJsClient
Spring 提供了一个 SockJS Java 客户端来连接到远程 SockJS 端点,而无需 使用浏览器。当需要双向时,这尤其有用 两台服务器之间通过公共网络进行通信(即,网络代理可以 排除使用 WebSocket 协议)。SockJS Java客户端也非常有用 用于测试目的(例如,模拟大量并发用户)。
SockJS Java 客户端支持 、 和 transports。其余的仅在浏览器中使用有意义。websocket
xhr-streaming
xhr-polling
您可以配置:WebSocketTransport
-
StandardWebSocketClient
在 JSR-356 运行时中。 -
JettyWebSocketClient
通过使用 Jetty 9+ 本机 WebSocket API。 - 春天的任何实现。
WebSocketClient
根据定义,An支持两者,因为, 从客户端的角度来看,除了用于连接的 URL 之外,没有其他区别 到服务器。目前有两种实现:XhrTransport
xhr-streaming
xhr-polling
-
RestTemplateXhrTransport
使用 Spring 的 HTTP 请求。RestTemplate
-
JettyXhrTransport
使用 Jettys for HTTP 请求。HttpClient
以下示例演示如何创建 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");
若要用于模拟大量并发用户,请 需要配置底层 HTTP 客户端(用于 XHR 传输)以允许足够的 连接数和线程数。以下示例演示如何使用 Jetty 执行此操作:SockJsClient
HttpClient 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);
// ...
将属性设置为 512KB(默认值为 128KB —)。 |
将属性设置为 1,000(默认值为)。 |
将属性设置为 30 属性秒(默认值为 5 秒 —)。 |
4.3. 跺脚
WebSocket 协议定义了两种类型的消息(文本和二进制),但它们 内容未定义。该协议定义了客户端和服务器协商 子协议(即更高级别的消息传递协议),用于 WebSocket 之上 定义每个人可以发送的消息类型,格式是什么,每个消息的内容 消息,等等。子协议的使用是可选的,但无论哪种方式,客户端和 服务器需要就定义消息内容的某个协议达成一致。
4.3.1. 概述
跺脚(简单) 面向文本的消息传递协议)最初是为脚本语言创建的 (如 Ruby、Python 和 Perl)连接到企业消息代理。是的 旨在解决常用消息传递模式的最小子集。跺脚可以是 通过任何可靠的双向流网络协议(如 TCP 和 WebSocket)使用。 尽管 STOMP 是一种面向文本的协议,但消息有效负载可以是 文本或二进制。
STOMP 是一种基于帧的协议,其帧基于 HTTP 建模。以下清单显示了结构 的踩踏帧:
COMMAND
header1:value1
header2:value2
Body^@
客户端可以使用 theorcommand 发送或订阅 消息,以及描述什么的标头 消息是关于以及谁应该接收它。这使得 可用于通过代理发送消息的发布-订阅机制 到其他连接的客户端或向服务器发送消息以请求 执行一些工作。SEND
SUBSCRIBE
destination
当您使用 Spring 的 STOMP 支持时,Spring WebSocket 应用程序会起作用。 作为客户的 STOMP 代理。消息路由到消息处理 方法或跟踪订阅和 向订阅用户广播消息。您还可以将 Spring 配置为工作 使用专用的STOMP代理(例如RabbitMQ,ActiveMQ等)进行实际操作 消息广播。在这种情况下,弹簧保持 与代理的 TCP 连接,向其中继消息,并传递消息 从它向下到连接的 WebSocket 客户端。因此,Spring Web应用程序可以 依靠基于 HTTP 的统一安全性、通用验证和熟悉的编程 消息处理模型。@Controller
以下示例显示订阅以接收股票的客户端,该 服务器可能会定期发出(例如,通过发送消息的计划任务) 通过ATO经纪人):SimpMessagingTemplate
SUBSCRIBE
id:sub-1
destination:/topic/price.stock.*
^@
以下示例显示发送交易请求的客户端,服务器 可以通过方法处理:@MessageMapping
SEND
destination:/queue/trade
content-type:application/json
content-length:44
"action":"BUY","ticker":"MMM","shares",44^@
执行后,服务器可以 向客户广播交易确认消息和详细信息。
目的地的含义在 STOMP 规范中故意保持不透明。它可以 是任何字符串,完全由 STOMP 服务器来定义语义和 它们支持的目标的语法。然而,这是很常见的 目标为类似路径的字符串,其中暗示发布-订阅 (一对多)并暗示点对点(一对一)消息 交流。/topic/..
/queue/
STOMP 服务器可以使用该命令向所有订阅者广播消息。 以下示例显示服务器向订阅的客户端发送:MESSAGE
MESSAGE
message-id:nxahklf6-1
subscription:sub-1
destination:/topic/price.stock.MMM
"ticker":"MMM","price":129.45^@
服务器无法发送未经请求的消息。所有消息 来自服务器的标头必须响应特定的客户端订阅,并且服务器消息的标头必须与 客户端订阅。subscription
id
前面的概述旨在提供对 踩踏协议。我们建议完整查看协议规范。
4.3.2. 好处
使用STOMP作为子协议可以让Spring Framework和Spring Security。 与使用原始 WebSocket 相比,提供更丰富的编程模型。相同的点可以是 关于HTTP与原始TCP以及它如何让Spring MVC和其他Web框架 提供丰富的功能。以下是好处列表:
- 无需发明自定义消息传递协议和消息格式。
- STOMP 客户端,包括 Spring Framework 中的Java 客户端,是可用的。
- 您可以(可选)使用消息代理(例如 RabbitMQ、ActiveMQ 等)来 管理订阅和广播消息。
- 应用程序逻辑可以组织在任意数量的实例中,消息可以是 根据 STOMP 目标标头路由到它们与处理原始 WebSocket 消息 与单用于给定连接。
@Controller
WebSocketHandler
- 您可以使用 Spring 安全性根据 STOMP 目标和消息类型来保护消息。
4.3.3. 启用踩踏
在 andmodules 中提供了 Stomp over WebSocket 支持。拥有这些依赖项后,可以公开 STOMP 端点,通过 WebSocket 与SockJS 回退,如以下示例所示:spring-messaging
spring-websocket
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer
@Override
public void r以上是关于基于 Servlet API 并部署到 Servlet 容器的主要内容,如果未能解决你的问题,请参考以下文章