基于 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 框架。WebApplicationContextMockServletContext
  • Spring MVC 测试:用于测试带注释的控制器的框架,也称为 通过(即支持注释),完成与 Spring MVC 基础架构,但没有 HTTP 服务器。 有关更多详细信息,请参阅Spring MVC 测试。MockMvcDispatcherServlet
  • 客户端 REST:提供可以用作 用于测试内部使用的客户端代码的模拟服务器。 有关更多详细信息,请参阅客户端 REST 测试。spring-testMockRestServiceServerRestTemplate
  • ​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

标题。​​Upgrade​

使用连接。​​Upgrade​

支持 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-OptionsSAMEORIGIN
  • 允许指定的源列表:每个允许的源必须以 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​​指示传输类型(例如,,,等)。websocketxhr-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 —)。​​streamBytesLimit​​​​128 * 1024​

将属性设置为 1,000(默认值为)。​​httpMessageCacheSize​​​​100​

将属性设置为 30 属性秒(默认值为 5 秒 —)。​​disconnectDelay​​​​5 * 1000​

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 消息 与单用于给定连接。@ControllerWebSocketHandler
  • 您可以使用 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 容器的主要内容,如果未能解决你的问题,请参考以下文章

使用servlet API作为参数

Servlet

JSP/Servlet技术—第七章 Servel基础

关于servlet-api.jar和jsp-api.jar的选择和使用

手动写一个Servlet

JavaWeb核心——Servlet