【Spring】Spring Framework Reference Documentation中文版23

来源:互联网 发布:plc编程前景 编辑:程序博客网 时间:2024/05/16 14:00

26. WebSocket Support

WebSocker支持

 

This part of the reference documentation covers Spring Frameworks support for WebSocket-style messaging in web applications including use of STOMP as an application level WebSocket sub-protocol.

参考文档的这一部分包含了spring框架对于WebSocket风格信息在web应用中支持包括使用STOMP作为一个应用级别的WebSocket子协议。

 

Section 26.1, Introductionestablishes a frame of mind in which to think about WebSocket, covering adoption challenges, design considerations, and thoughts on when it is a good fit.

章节26.1,“介绍”用于建立概念关于WebSocker,包括允许改变、设计思想和考虑适合的场景。

 

Section 26.2, WebSocket APIreviews the Spring WebSocket API on the server-side, while Section 26.3,SockJS Fallback Optionsexplains the SockJS protocol and shows how to configure and use it.

章节26.2,“WebSocket API”展示了SpringWebSocketAPI对于服务器方面,章节26.3,“SockJS回调选项”解释了SockJS协议和展示了如何配置并使用。

 

Section 26.4.1, Overview of STOMPintroduces the STOMP messaging protocol. Section 26.4.2,Enable STOMP over WebSocketdemonstrates how to configure STOMP support in Spring. Section 26.4.4,Annotation Message Handlingand the following sections explain how to write annotated message handling methods, send messages, choose message broker options, as well as work with the special "user" destinations. Finally, Section 26.4.17,Testing Annotated Controller Methodslists three approaches to testing STOMP/WebSocket applications.

章节26.4.1,“回顾STOMP”介绍了STOMP消息协议。章节26.4.2,“允许STOMPWebSocket上”描述了如何配置STOMP支持在spring中。章节26.4.4,“注解消息处理”和下面的章节解释了如何书写注解消息处理方法、发送消息、选择消息处理选项,使用特定的用户目的地。最后,章节26.4.17,“测试注解控制器方法”列出了三种方式来测试STOMPWebSocket应用。

 

26.1 Introduction

介绍

 

The WebSocket protocol RFC 6455 defines an important new capability for web applications: full-duplex, two-way communication between client and server. It is an exciting new capability on the heels of a long history of techniques to make the web more interactive including Java Applets, XMLHttpRequest, Adobe Flash, ActiveXObject, various Comet techniques, server-sent events, and others.

WebSocket协议RFC6455定义了一个重要的新的功能用于web应用:全副本、双向通信在客户端和服务端。并且有一个很好的功能对于长期的技术历史使得web更加具有交互性包括Java AppletsXMLHttpRequestAdobe FlashActiveXObject、不同的web技术、服务发送事件等等。

 

A proper introduction to the WebSocket protocol is beyond the scope of this document. At a minimum however its important to understand that HTTP is used only for the initial handshake, which relies on a mechanism built into HTTP to request a protocol upgrade (or in this case a protocol switch) to which the server can respond with HTTP status 101 (switching protocols) if it agrees. Assuming the handshake succeeds the TCP socket underlying the HTTP upgrade request remains open and both client and server can use it to send messages to each other.

对于WebSocket协议的介绍基于这个文档的范围。最小的他是理解HTTP对于握手协议的理解,依赖于策略构建在HTTP来请求一个协议更新(或协议的转换)对于服务器可以响应HTTP的状态101(切换协议)如果可以的话。假设握手成功TCP连接在HTTP更新请求之下保留打开并且客户端和服务端可以使用它来互相发送消息。

 

Spring Framework 4 includes a new spring-websocket module with comprehensive WebSocket support. It is compatible with the Java WebSocket API standard (JSR-356) and also provides additional value-add as explained in the rest of the introduction.

spring的框架4引入了一个新的spring-websocker模块用于复杂的WebSocket支持。适用于标准的Java WebSocketAPI标准和支持额外的添加来解释剩余的部分。

 

26.1.1 WebSocket Fallback Options

WebSocket回调选项

 

An important challenge to adoption is the lack of support for WebSocket in some browsers. Notably the first Internet Explorer version to support WebSocket is version 10 (see http://caniuse.com/websockets for support by browser versions). Furthermore, some restrictive proxies may be configured in ways that either preclude the attempt to do an HTTP upgrade or otherwise break connection after some time because it has remained opened for too long. A good overview on this topic from Peter Lubbers is available in the InfoQ article "How HTML5 Web Sockets Interact With Proxy Servers".

一个重要的挑战是缺乏对于一些浏览器的WebSocket支持。首先IE的版本支持WebSocket是从10开始的。此外,一些限制的代理可以配置来排除HTTP跟新和有时断开连接因为持续的时间过长。对于这个话题可以参考Peter LubbersInfoQ的文章"How HTML5 Web Sockets Interact With Proxy Servers"

 

Therefore to build a WebSocket application today, fallback options are required in order to simulate the WebSocket API where necessary. The Spring Framework provides such transparent fallback options based on the SockJS protocol. These options can be enabled through configuration and do not require modifying the application otherwise.

现在构建一个WebSocket应用,回调选项是要求模仿WebSocketAPI根据需要。spirng框架提供了这样的透明的回调基于SockJS协议。这些选项可以被允许通过配置和不需要改变应用。

 

26.1.2 A Messaging Architecture

一个消息架构

 

Aside from short-to-midterm adoption challenges, using WebSocket brings up important design considerations that are important to recognize early on, especially in contrast to what we know about building web applications today.

一个较小的挑战是,使用WebSocket来提高重要的设计思想是重要的,尤其是对于我们知道的如何构建web应用的今天。

 

Today REST is a widely accepted, understood, and supported architecture for building web applications. It is an architecture that relies on having many URLs (nouns), a handful of HTTP methods (verbs), and other principles such as using hypermedia (links), remaining stateless, etc.

现在REST风格是广泛被接受的、易于理解和支持架构用于构建web应用。他是一个架构依赖于许多URL,一个有用的HTTP方法和其他原则例如使用超链接,保留状态等等。

 

By contrast a WebSocket application may use a single URL only for the initial HTTP handshake. All messages thereafter share and flow on the same TCP connection. This points to an entirely different, asynchronous, event-driven, messaging architecture. One that is much closer to traditional messaging applications (e.g. JMS, AMQP).

WebS应用相比可以使用单一的URL用于初始化HTTP握手协议。所有的消息其后共享并传输通过相同的HTTP连接。这一点是与众不同的、异步的、事件驱动的、消息架构。更加接近于传统的消息应用(例如JMSAMQP)。

 

Spring Framework 4 includes a new spring-messaging module with key abstractions from the Spring Integration project such as Message, MessageChannel, MessageHandler, and others that can serve as a foundation for such a messaging architecture. The module also includes a set of annotations for mapping messages to methods, similar to the Spring MVC annotation based programming model.

spring框架4包括一个新的spirng消息模块用于关键的架构来自spring集成工程例如消息、消息渠道、消息处理和其他可以服务作为基础例如消息架构。模块也包括一系列注解用于匹配消息对于方法,相似于springmvc注解编程模型。

 

26.1.3 Sub-Protocol Support in WebSocket

WebSocket中的子协议支持

 

WebSocket does imply a messaging architecture but does not mandate the use of any specific messaging protocol. It is a very thin layer over TCP that transforms a stream of bytes into a stream of messages (either text or binary) and not much more. It is up to applications to interpret the meaning of a message.

WebSocket引入了一个消息架构但是不授权使用任何特定的消息协议。这是简单的层在TCP之上来传输字节流到消息流中(包括文本或二进制)并不是很多。他开启应用来打断消息中的意义。

 

Unlike HTTP, which is an application-level protocol, in the WebSocket protocol there is simply not enough information in an incoming message for a framework or container to know how to route it or process it. Therefore WebSocket is arguably too low level for anything but a very trivial application. It can be done, but it will likely lead to creating a framework on top. This is comparable to how most web applications today are written using a web framework rather than the Servlet API alone.

不像HTTP,是一个应用层协议,在WebSocket协议中他是简单的但是不够的对于输入消息对于框架或容器关于如何路由和处理。因此WebSocket是低级的对于一些不重要的应用。可以实现但是将导致创建一个框架在上面。这是可比较的关于大部分现在的web应用可以书写使用一个web框架而不是ServletAPI

 

For this reason the WebSocket RFC defines the use of sub-protocols. During the handshake, the client and server can use the header Sec-WebSocket-Protocol to agree on a sub-protocol, i.e. a higher, application-level protocol to use. The use of a sub-protocol is not required, but even if not used, applications will still need to choose a message format that both the client and server can understand. That format can be custom, framework-specific, or a standard messaging protocol.

由于这个理由WebSocketRFC定义了使用子协议。在握手期间,客户端和服务器可以使用头Sec-WebSocket-Protocol来基于子协议之上,例如,一个更高的,应用级别的协议来使用。使用子协议不是必须的,但是如果不使用,应用将依然需要选择一个消息格式,包括客户端和服务器可以理解。这个格式可以自定义、绑定域框架或标准的消息协议。

 

The Spring Framework provides support for using STOMP — a simple, messaging protocol originally created for use in scripting languages with frames inspired by HTTP. STOMP is widely supported and well suited for use over WebSocket and over the web.

spring的框架提供了支持对于使用STOMP————一个简答的消息协议创建使用在脚本语言中对于框架受启发于HTTPSTOMP的广泛支持并且适合于在WebSocket之上使用对于web

 

26.1.4 Should I Use WebSocket?

应该使用WebSocket吗?

 

With all the design considerations surrounding the use of WebSocket, it is reasonable to ask, "When is it appropriate to use?".

对于所有的设计思想关于使用WebSocket,可以询问,“什么时候适合使用?”

 

The best fit for WebSocket is in web applications where the client and server need to exchange events at high frequency and with low latency. Prime candidates include, but are not limited to, applications in finance, games, collaboration, and others. Such applications are both very sensitive to time delays and also need to exchange a wide variety of messages at a high frequency.

对于WebSocket最适合的在web应用中当客户端和服务器需要交换事件以很高的频率和低延时。初级包括但是不限制于应用在金融、游戏、合作等中的使用。例如应用对于时间延迟敏感并且需要交换数据以很高的频率。

 

For other application types, however, this may not be the case. For example, a news or social feed that shows breaking news as it becomes available may be perfectly okay with simple polling once every few minutes. Here latency is important, but it is acceptable if the news takes a few minutes to appear.

对于其他的应用类型,他们可能不是例如。例如,一个欣慰或社交消费展示了消息并且可能很快传播在每分钟。这个因素是重要的,但是可接受的如果新闻出现了几分钟。

 

Even in cases where latency is crucial, if the volume of messages is relatively low (e.g. monitoring network failures) the use of long polling should be considered as a relatively simple alternative that works reliably and is comparable in terms of efficiency (again assuming the volume of messages is relatively low).

对于潜在因素是关键的,如果消息的量是很小的(例如,监控网络失败)使用长暴露应当考虑相应的简单替代可以工作可靠并且适用于有效的部分(假设消息的量是较小的)。

 

It is the combination of both low latency and high frequency of messages that can make the use of the WebSocket protocol critical. Even in such applications, the choice remains whether all client-server communication should be done through WebSocket messages as opposed to using HTTP and REST. The answer is going to vary by application; however, it is likely that some functionality may be exposed over both WebSocket and as a REST API in order to provide clients with alternatives. Furthermore, a REST API call may need to broadcast a message to interested clients connected via WebSocket.

可以合并低延时和高频率的消息使用WebSocket协议。在一些应用中,选择保留所有客户服务端通信应当通过WebSocket消息作为替代使用HTTPREST。对于不同的应用,相同的功能可以暴露通过WebSocket和作为RESTAPI用于提供客户端交互。此外,RESTAPI调用可以需要广播消息对于有意义的客户端通过WebSocket来连接。

 

The Spring Framework allows @Controller and @RestController classes to have both HTTP request handling and WebSocket message handling methods. Furthermore, a Spring MVC request handling method, or any application method for that matter, can easily broadcast a message to all interested WebSocket clients or to a specific user.

spring框架允许@Controller@RestController类来处理HTTP请求和WebSocket消息处理方法。此外,一个SpringMVC请求处理方法和人恶化应用方法处于这个原因可以简单的广播消息对于所有感兴趣的WebSocket客户端或指定的用户。

 

26.2 WebSocket API

 

The Spring Framework provides a WebSocket API designed to adapt to various WebSocket engines. Currently the list includes WebSocket runtimes such as Tomcat 7.0.47+, Jetty 9.1+, GlassFish 4.1+, WebLogic 12.1.3+, and Undertow 1.0+ (and WildFly 8.0+). Additional support may be added as more WebSocket runtimes become available.

spring框架提供了WebSocketAPI设计适用于不同WebSocket引擎。当前列表包括WebSocket运行时例如Tomcat7.0.47以上、Jetty9.1以上、WebLogic12.1.3以上和Undertow1.0以上(和WildFly8.0以上)。此外支持可以添加更多的WebSocket运行时是可行的。

 

[Note]

注意

 

As explained in the introduction, direct use of a WebSocket API is too low level for applications — until assumptions are made about the format of a message there is little a framework can do to interpret messages or route them via annotations. This is why applications should consider using a sub-protocol and Springs STOMP over WebSocket support.

对于介绍中的解释,直接使用WebSocketAPI是低级的对于应用来说————直到假设消息的格式化是小的一个框架可以实现来打断消息或路由通过注解。这是为什么应用应当考虑使用子协议和springSTOMPWebSocket支持上。

 

When using a higher level protocol, the details of the WebSocket API become less relevant, much like the details of TCP communication are not exposed to applications when using HTTP. Nevertheless this section covers the details of using WebSocket directly.

当使用高级别协议,WebSocketAPI细节变得不在重要,大部分TCP通信的细节没有被暴露当使用HTTP协议是。这个章节直接包含了使用WebSocket的细节内容。

 

26.2.1 Create and Configure a WebSocketHandler

创建并配置一个WebSocketHandler

 

Creating a WebSocket server is as simple as implementing WebSocketHandler or more likely extending either TextWebSocketHandler or BinaryWebSocketHandler:

创建一个WebSocket服务器是简单的作为实现WebSocketHandler或其他扩展例如TextWebSocketHandlerBinaryWebSocketHandler

 

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) {

        // ...

    }

 

}

 

There is dedicated WebSocket Java-config and XML namespace support for mapping the above WebSocket handler to a specific URL:

这是一个专用的WebSocketJava配置和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 configuration equivalent:

相应的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>

 

The above is for use in Spring MVC applications and should be included in the configuration of a DispatcherServlet. However, Springs WebSocket support does not depend on Spring MVC. It is relatively simple to integrate a WebSocketHandler into other HTTP serving environments with the help of WebSocketHttpRequestHandler.

上面对于使用在SpringMVC应用中和应当包括在DispatcherServlet的配置中。然而,springWebSocket支持没有依赖springmvc。他是简单的集成了WebSocketHandler到其他的HTTP服务环境通过WebSocketHttpRequestHandler的帮助。

 

26.2.2 Customizing the WebSocket Handshake

自定义WebSocket握手

 

The easiest way to customize the initial HTTP WebSocket handshake request is through a HandshakeInterceptor, which exposes "before" and "after" the handshake methods. Such an interceptor can be used to preclude the handshake or to make any attributes available to the WebSocketSession. For example, there is a built-in interceptor for passing HTTP session attributes to the WebSocket session:

简答的方式来自定义HTTPWebSocket握手请求时通过HandshakeInterceptor,暴露的beforeafter握手方法。这样的拦截器可以被使用来排除握手或使用属性对于WebSocketSession。例如,内置的拦截器对于传递HTTP的会话属性对于WebSocket的会话:

 

@Configuration

@EnableWebSocket

public class WebSocketConfig implements WebSocketConfigurer {

 

    @Override

    public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {

        registry.addHandler(new MyHandler(), "/myHandler")

            .addInterceptors(new HttpSessionHandshakeInterceptor());

    }

 

}

 

And the XML configuration equivalent:

相应的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>

 

A more advanced option is to extend the DefaultHandshakeHandler that performs the steps of the WebSocket handshake, including validating the client origin, negotiating a sub-protocol, and others. An application may also need to use this option if it needs to configure a custom RequestUpgradeStrategy in order to adapt to a WebSocket server engine and version that is not yet supported (also see Section 26.2.4,Deployment Considerationsfor more on this subject). Both the Java-config and XML namespace make it possible to configure a custom HandshakeHandler.

一个更加高级的选项是扩展DefaultHandshakeHandler执行WebSocket握手中的步骤,包括验证客户端的开端,协商子协议等等。一个应用也可能需要这些选项如果需要配置一个自定义的RequestUpgradeStrategy用于适应WebSocket服务器引擎和版本还没有被支持(见章节26.2.4,“部署Considerations”用于这个项目)。Java配置和xml命名空间使得可以实现配置自定义的HandshakeHandler

 

26.2.3 WebSocketHandler Decoration

WebSocketHandler装饰

 

Spring provides a WebSocketHandlerDecorator base class that can be used to decorate a WebSocketHandler with additional behavior. Logging and exception handling implementations are provided and added by default when using the WebSocket Java-config or XML namespace. The ExceptionWebSocketHandlerDecorator catches all uncaught exceptions arising from any WebSocketHandler method and closes the WebSocket session with status 1011 that indicates a server error.

spirng提供了WebSocketHandlerDecorator基类可以被使用用于装饰WebSocketHandler根据额外的行为。登录和异常处理实现被提供和默认添加使用WebSocketjava配置或xml命名空间。ExceptionWebSocketHandlerDecorator捕获所有非捕获异常来自任何的WebSocketHandler方法并且关闭WebSocket会话使用101状态码来显示服务器错误。

 

26.2.4 Deployment Considerations

部署注意事项

 

The Spring WebSocket API is easy to integrate into a Spring MVC application where the DispatcherServlet serves both HTTP WebSocket handshake as well as other HTTP requests. It is also easy to integrate into other HTTP processing scenarios by invoking WebSocketHttpRequestHandler. This is convenient and easy to understand. However, special considerations apply with regards to JSR-356 runtimes.

springWebSocketAPI简单的集成在springmvc应用中当DispatcherServlet服务于HTTPWebSocket握手相对于其他的HTTP请求。可以简单的集成进入其他的HTTP处理场景通过调用WebSocketHttpRequestHandler。这是方便的和易于理解的。然而特定的注意事项应用于JSR356运行时。

 

The Java WebSocket API (JSR-356) provides two deployment mechanisms. The first involves a Servlet container classpath scan (Servlet 3 feature) at startup; and the other is a registration API to use at Servlet container initialization. Neither of these mechanism makes it possible to use a single "front controller" for all HTTP processing — including WebSocket handshake and all other HTTP requests — such as Spring MVCs DispatcherServlet.

JavaWebSocketAPI提供了两个部署的策略。首先调用一个Servlet容器类路径扫描(Servlet3的特性)在开始时,并且其他的登记的API来使用在Servlet容器初始化中。不管策略使用的简单的“前端控制器”用于HTTP处理————包括WebSocket握手协议和其他HTTP请求————例如spirngmvcDispatcherServlet

 

This is a significant limitation of JSR-356 that Springs WebSocket support addresses by providing a server-specific RequestUpgradeStrategy even when running in a JSR-356 runtime.

这是一个重要的限制对于JSR356就是springWebSocket支持地址提供一个特定的服务端RequestUpgradeStrategy当运行于JSR356运行时。

 

[Note]

注意

 

A request to overcome the above limitation in the Java WebSocket API has been created and can be followed at WEBSOCKET_SPEC-211. Also note that Tomcat and Jetty already provide native API alternatives that makes it easy to overcome the limitation. We are hopeful that more servers will follow their example regardless of when it is addressed in the Java WebSocket API.

请求构建于上面的限制之上在JavaWebSocketAPI中已经被创建并且遵循WEBSOCKET_SPEC-211。同时也要注意TomcatJetty已经提供了内部的API用于简单的克服限制。我们希望更多的服务器符合这样的例子由于他们定义在JavaWebSocketAPI中。

 

A secondary consideration is that Servlet containers with JSR-356 support are expected to perform a ServletContainerInitializer (SCI) scan that can slow down application startup, in some cases dramatically. If a significant impact is observed after an upgrade to a Servlet container version with JSR-356 support, it should be possible to selectively enable or disable web fragments (and SCI scanning) through the use of the <absolute-ordering /> element in web.xml:

第二个考虑是Servlet容器对于JSR356的支持是执行ServletContainerInitializerSCI)扫描可以减缓应用的启动,在一些情况。如果重要在更新之后对于Servlet容器版本对于JSR356的支持,应当有选择的开启或关闭web部分(和SCI扫描)通过使用<absolute-ordering />元素在web.xml中。

 

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

 

You can then selectively enable web fragments by name, such as Springs own SpringServletContainerInitializer that provides support for the Servlet 3 Java initialization API, if required:

你可以有选择的通过名字来启动web部分,例如spring自身的SpringServletContainerInitializer提供了支持对于Servlet3Java初始化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引擎

 

Each underlying WebSocket engine exposes configuration properties that control runtime characteristics such as the size of message buffer sizes, idle timeout, and others.

每个底层的WebSocket引擎暴露配置属性控制运行时的特性例如消息缓冲大小、空闲超时等等。

 

For Tomcat, WildFly, and GlassFish add a ServletServerContainerFactoryBean to your WebSocket Java config:

对于TomcatWildFlyGlassFish添加ServletServerContainerFactoryBean到你WebSocketJava配置中:

 

@Configuration

@EnableWebSocket

public class WebSocketConfig implements WebSocketConfigurer {

 

    @Bean

    public ServletServerContainerFactoryBean createWebSocketContainer() {

        ServletServerContainerFactoryBean container = new ServletServerContainerFactoryBean();

        container.setMaxTextMessageBufferSize(8192);

        container.setMaxBinaryMessageBufferSize(8192);

        return container;

    }

 

}

 

or WebSocket XML namespace:

WebSocketxml配置片段:

 

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

 

[Note]

注意

 

For client side WebSocket configuration, you should use WebSocketContainerFactoryBean (XML) or ContainerProvider.getWebSocketContainer() (Java config).

对于客户端WebSocket的配置,你应当使用WebSocketContainerFactoryBeanContainerProvider.getWebSocketContainer()

 

For Jetty, youll need to supply a pre-configured Jetty WebSocketServerFactory and plug that into Springs DefaultHandshakeHandler through your WebSocket Java config:

对于Jetty,你需要支持前置配置JettyWebSocketServerFactory和插件用于springDefaultHandshakeHandler通过你的WebSocketjava配置:

 

@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)));

    }

 

}

 

or WebSocket XML namespace:

WebSocketxml配置片段:

 

<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

配置允许来源

 

As of Spring Framework 4.1.5, the default behavior for WebSocket and SockJS is to accept only same origin requests. It is also possible to allow all or a specified list of origins. This check is mostly designed for browser clients. There is nothing preventing other types of clients from modifying the Origin header value (see RFC 6454: The Web Origin Concept for more details).

spring框架4.1.5中,默认的WebSocket行为和SockJS接收相同来源的请求。允许接收所有或特定列表是重要的。这个检查是为了浏览器客户端设计的。不需要提前修改其他类型的客户端对于修改原始的头部信息值(见PFC6454:详见Web的原始内容)。

 

The 3 possible behaviors are:

三个可能的行为是:

 

    Allow only same origin requests (default): in this mode, when SockJS is enabled, the Iframe HTTP response header X-Frame-Options is set to SAMEORIGIN, and JSONP transport is disabled since it does not allow to check the origin of a request. As a consequence, IE6 and IE7 are not supported when this mode is enabled.

只允许相同的请求(默认):在这个模式中,当SockJS被启用,内嵌框架HTTP响应头X-Frame-Options被设置为SAMEORIGINJSONP传输被关闭不需要检查原始请求。因此,当这个模式时不支持IE6iE7

    Allow a specified list of origins: each provided allowed origin must start with http:// or https://. In this mode, when SockJS is enabled, both IFrame and JSONP based transports are disabled. As a consequence, IE6 through IE9 are not supported when this mode is enabled.

允许特定的原始列表:每个提供允许原始必须开始于http://https://。在这个模式中,当SockJS被启用,内嵌框架和JSONP基于传输被关闭。因此,IE6IE9是不支持的当这个模式被开启时。

    Allow all origins: to enable this mode, you should provide * as the allowed origin value. In this mode, all transports are available.

允许所有的原始:为了开启这个模式,你应当提供*作为允许原始数据。在这个模式中,所有的传输是可用的。

 

WebSocket and SockJS allowed origins can be configured as shown bellow:

wWebSocketSockJS允许原始可以不配置如下:

 

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 configuration equivalent:

相同的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回调选项

 

As explained in the introduction, WebSocket is not supported in all browsers yet and may be precluded by restrictive network proxies. This is why Spring provides fallback options that emulate the WebSocket API as close as possible based on the SockJS protocol (version 0.3.3).

在介绍中解释,WebSocket并不支持所有的浏览器并且可以被排除通过有限制的网络代理。这是为什么spring提供回调选项来模拟WebSocketAPI作为关闭基于SockJS协议(版本0.3.3

 

26.3.1 Overview of SockJS

概述SockJS

 

The goal of SockJS is to let applications use a WebSocket API but fall back to non-WebSocket alternatives when necessary at runtime, i.e. without the need to change application code.

SockJS的目标是让应用使用WebSocketAPI但是返回非WebSocket作为代替在运行时,例如,不需要改变应用的代码。

 

SockJS consists of:

SockJS的组成:

 

    The SockJS protocol defined in the form of executable narrated tests.

SockJS协议定义在执行缩小测试的表单中。

    The SockJS JavaScript client - a client library for use in browsers.

SockJSJavaScript的客户端————一个客户端库用于浏览器中。

    SockJS server implementations including one in the Spring Framework spring-websocket module.

SockJS服务实现包括spring-websocket的模块中。

    As of 4.1 spring-websocket also provides a SockJS Java client.

由于4.1spring-websocket也提供了一个SockJSJava客户端。

 

SockJS is designed for use in browsers. It goes to great lengths to support a wide range of browser versions using a variety of techniques. For the full list of SockJS transport types and browsers see the SockJS client page. Transports fall in 3 general categories: WebSocket, HTTP Streaming, and HTTP Long Polling. For an overview of these categories see this blog post.

SockJS被设计用于浏览器中。他获得大量的支持大部分浏览器的版本使用不同的技术。对于SockJS传输类型的列表和浏览器看到的SockJS的客户端页面。传输使用三种策略:WebSocketHTTP流和HTTP的长Polling。对于这些技术参考博客。

 

The SockJS client begins by sending "GET /info" to obtain basic information from the server. After that it must decide what transport to use. If possible WebSocket is used. If not, in most browsers there is at least one HTTP streaming option and if not then HTTP (long) polling is used.

SockJS客户端开始传递"GET /info"用于获得基本的信息来自服务器。之后他必须决定使用什么样的传输。如果可用的传输被使用。如果没有,在大部分浏览器中将只有一个HTTP流选项和如果没有则HTTP的长Polling被使用。

 

All transport requests have the following URL structure:

所有的传输请求有下面的URL结构:

 

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

 

    {server-id} - useful for routing requests in a cluster but not used otherwise.

用于路由请求在集群中。

    {session-id} - correlates HTTP requests belonging to a SockJS session.

相关的HTTP请求基于SockJS的会话。

    {transport} - indicates the transport type, e.g. "websocket", "xhr-streaming", etc.

指定传输的类型,例如"websocket""xhr-streaming"等等。

 

The WebSocket transport needs only a single HTTP request to do the WebSocket handshake. All messages thereafter are exchanged on that socket.

WebSocket传输只需要一个简单的HTTP请求用于WebSocket握手协议。所有的消息传递在socket上进行交换。

 

HTTP transports require more requests. Ajax/XHR streaming for example relies on one long-running request for server-to-client messages and additional HTTP POST requests for client-to-server messages. Long polling is similar except it ends the current request after each server-to-client send.

HTTP传输需要更多的请求。Ajax/XHR流依赖于一个保持的请求对于服务器到客户端的消息和额外的HTTPPOST请求对于客户端到服务器消息。Longpolling是类似的除了他以当前请求为结束在每个服务器到客户端发送之后。

 

SockJS adds minimal message framing. For example the server sends the letter o ("open" frame) initially, messages are sent as a["message1","message2"] (JSON-encoded array), the letter h ("heartbeat" frame) if no messages flow for 25 seconds by default, and the letter c ("close" frame) to close the session.

SockJS添加最小的消息帧。例如服务器最初发送字母oopen帧),消息被发送作为["message1","message2"]JSON编码的数组),字母h(心跳帧)如果没有消息流在默认的25秒内,并且字母cclose帧)用于关闭会话。

 

To learn more, run an example in a browser and watch the HTTP requests. The SockJS client allows fixing the list of transports so it is possible to see each transport one at a time. The SockJS client also provides a debug flag which enables helpful messages in the browser console. On the server side enable TRACE logging for org.springframework.web.socket. For even more detail refer to the SockJS protocol narrated test.

为了了解更多,在浏览器执行案例并观察HTTP的请求。SockJS客户端允许修复传输的列表因此可以同时看到每个传输。SockJS客户端也提供了一个调试标志允许有用的消息在浏览器的控制台显示中。对于服务器运行TRACE记录对于org.springframework.web.socket。关于更多细节参考SockJS协议缩小测试。

 

26.3.2 Enable SockJS

开启SockJS

 

SockJS is easy to enable through Java configuration:

SockJS简单的启用通过Java配置

 

@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();

    }

 

}

 

and the XML configuration equivalent:

相同的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>

 

The above is for use in Spring MVC applications and should be included in the configuration of a DispatcherServlet. However, Springs WebSocket and SockJS support does not depend on Spring MVC. It is relatively simple to integrate into other HTTP serving environments with the help of SockJsHttpRequestHandler.

上面用于使用在springmvc应用中并且应当包括在DispatcherServlet的配置中。然而,springWebSocketSockJS支持没有依赖于springmvc。他是简单的集成计入其他的HTTP服务环境通过SockJsHttpRequestHandler的帮助。

 

On the browser side, applications can use the sockjs-client (version 1.0.x) that emulates the W3C WebSocket API and communicates with the server to select the best transport option depending on the browser its running in. Review the sockjs-client page and the list of transport types supported by browser. The client also provides several configuration options, for example, to specify which transports to include.

对于浏览器方面,应用可以使用SockJS客户端(版本1.0.x)来模仿W3CWebSocketAPI并且和服务器进行通讯来选择最好的传输选项依赖于浏览器的运行。回顾SockJS的客户端页面和浏览器支持的传输类型的列表。客户端也提供了一些服务选项,例如,指定包含的传输。

 

26.3.3 HTTP Streaming in IE 8, 9: Ajax/XHR vs IFrame

HTTP流在IE89Ajax/XHR vs IFrame

 

Internet Explorer 8 and 9 are and will remain common for some time. They are a key reason for having SockJS. This section covers important considerations about running in those browsers.

IE89将保留一些相同点在一段时间内。他们的关键理由对于使用SockJS。这个章节包含了重要的思路有关运行在浏览器中。

 

The SockJS client supports Ajax/XHR streaming in IE 8 and 9 via Microsofts XDomainRequest. That works across domains but does not support sending cookies. Cookies are very often essential for Java applications. However since the SockJS client can be used with many server types (not just Java ones), it needs to know whether cookies matter. If so the SockJS client prefers Ajax/XHR for streaming or otherwise it relies on a iframe-based technique.

SockJS客户端支持Ajax/XHR流在IE89中通过MicrosoftXDomainRequest。他们工作跨越主机但是不支持传输cookieCookie是非常必要的对于Java的应用来说。然而自从SockJS的客户端可以被使用以许多服务的类型(不是Java),他需要知道cookie是否有问题。如果这样的SockJS客户端倾向于Ajax/XHR用于流火其他依赖于嵌入帧技术。

 

The very first "/info" request from the SockJS client is a request for information that can influence the clients choice of transports. One of those details is whether the server application relies on cookies, e.g. for authentication purposes or clustering with sticky sessions. Springs SockJS support includes a property called sessionCookieNeeded. It is enabled by default since most Java applications rely on the JSESSIONID cookie. If your application does not need it, you can turn off this option and the SockJS client should choose xdr-streaming in IE 8 and 9.

首先"/info"请求来自SockJS客户端是一个请求对于消息可以影响客户端的传输选择。这些细节是服务器依赖于cookie,例如,对于验证的目的或集群粘合会话。springSockJS支持包括一个属性名字为sessionCookieNeeded。他默认允许大部分Java应用依赖于JSESSIONIDcookie。如果你的应用不需要他,你可以关闭这个选项并且SockJS客户端应当选择xdr-streamingIE89中。

 

If you do use an iframe-based transport, and in any case, it is good to know that browsers can be instructed to block the use of IFrames on a given page by setting the HTTP response header X-Frame-Options to DENY, SAMEORIGIN, or ALLOW-FROM <origin>. This is used to prevent clickjacking.

如果你使用基于帧的传输,并且在一些情况下,可以了解浏览器可以被要求来阻塞嵌入框架的使用对于给定的页面通过设置HTTP响应头信息X-Frame-OptionsDENYSAMEORIGINALLOW-FROM <origin>。这是为了用于防止clickjacking

 

[Note]

注意

 

Spring Security 3.2+ provides support for setting X-Frame-Options on every response. By default the Spring Security Java config sets it to DENY. In 3.2 the Spring Security XML namespace does not set that header by default but may be configured to do so, and in the future it may set it by default.

spring安全3.2以上的版本提供了支持用于设置X-Frame-Options对于每一个响应。默认的spring安全的Java配置设置它诶DENY。在3.2spring安全的xml的命名空间没有默认设置头信息但是可以这样的进行配置,并且在未来可能被设置为默认的。

 

See Section 7.1. "Default Security Headers" of the Spring Security documentation for details on how to configure the setting of the X-Frame-Options header. You may also check or watch SEC-2501 for additional background.

见章节7.1,“默认的安全头”关于spring的安全文档来了解详细的内容以及如何设置X-Frame-Options的头信息。你也可以检查或观察SEC-2501来了解跟多的背景内容。

 

If your application adds the X-Frame-Options response header (as it should!) and relies on an iframe-based transport, you will need to set the header value to SAMEORIGIN or ALLOW-FROM <origin>. Along with that the Spring SockJS support also needs to know the location of the SockJS client because it is loaded from the iframe. By default the iframe is set to download the SockJS client from a CDN location. It is a good idea to configure this option to a URL from the same origin as the application.

如果你的应用添加了X-Frame-Options响应头()并且依赖于基于嵌入框架的传输,你将需要设置头内容为SAMEORIGINALLOW-FROM <origin>。对于springSockJS的支持也需要知道SockJS的客户端的位置因为他用于加载iframe中的内容。默认的iframe被设置为下周SockJS客户端来自CDN。这是一个好的注意用于配置这个选项对于URL来自相同的源在应用中。

 

In Java config this can be done as shown below. The XML namespace provides a similar option via the <websocket:sockjs> element:

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

    }

 

    // ...

 

}

 

[Note]

注意

 

During initial development, do enable the SockJS client devel mode that prevents the browser from caching SockJS requests (like the iframe) that would otherwise be cached. For details on how to enable it see the SockJS client page.

在初始化开发中,允许SockJS客户端的模式避免浏览器来捕获SockJS请求(例如iframe)可以被缓存。相关细节关于如何启用见SockJS的客户端页面。

 

26.3.4 Heartbeat Messages

心跳信息

 

The SockJS protocol requires servers to send heartbeat messages to preclude proxies from concluding a connection is hung. The Spring SockJS configuration has a property called heartbeatTime that can be used to customize the frequency. By default a heartbeat is sent after 25 seconds assuming no other messages were sent on that connection. This 25 seconds value is in line with the following IETF recommendation for public Internet applications.

SockJS协议要求服务器发送心跳信息来排除代理当连接被挂起时。spirngSockJS配置有一个属性名字为heartbeatTime可以被使用用于自定义频率。默认的心跳在每25秒钟发送假设其他的信息已经通过连接来发送。这个25秒的值对于是符合IETF建议对于公用的互联网应用。

 

[Note]

注意

 

When using STOMP over WebSocket/SockJS, if the STOMP client and server negotiate heartbeats to be exchanged, the SockJS heartbeats are disabled.

当使用STOMPWebSocket/SockJS之上,如果STOMP的客户端和服务器忽略心跳的支持,SockJS的心跳可以被关闭。

 

The Spring SockJS support also allows configuring the TaskScheduler to use for scheduling heartbeats tasks. The task scheduler is backed by a thread pool with default settings based on the number of available processors. Applications should consider customizing the settings according to their specific needs.

springSockJS支持允许配置TaskScheduler来使用对于计划的心跳任务。认为计划是通过线程池执行的根据默认的设置基于可用的活动。应用应当考虑自定义设置基于这些特定的需要。

 

26.3.5 Servlet 3 Async Requests

Servlet3的异步请求

 

HTTP streaming and HTTP long polling SockJS transports require a connection to remain open longer than usual. For an overview of these techniques see this blog post.

HTTP流和HTTP的长pollingSockJS传输要求连接来保留比一般的连接要长。对于这些技术的回顾参考博客中的内容。

 

In Servlet containers this is done through Servlet 3 async support that allows exiting the Servlet container thread processing a request and continuing to write to the response from another thread.

Servlet容器中是完成通过Servlet3异步支持来允许已有的Servlet容器线程处理请求和继续书写响应来自其他的线程。

 

A specific issue is that the Servlet API does not provide notifications for a client that has gone away, see SERVLET_SPEC-44. However, Servlet containers raise an exception on subsequent attempts to write to the response. Since Springs SockJS Service supports sever-sent heartbeats (every 25 seconds by default), that means a client disconnect is usually detected within that time period or earlier if messages are sent more frequently.

特定的问题是ServletAPI没有提供通知对于客户端,见SERVLET_SPEC-44。然而,Servlet容器抛出一个异常尝试写入响应中。自从springSockJS服务支持发送服务心跳(默认没25秒钟),也就意味着客户端断开连接是被探测的在一段时间中或更早如果信息发送频繁的话。

 

[Note]

注意

 

As a result network IO failures may occur simply because a client has disconnected, which can fill the log with unnecessary stack traces. Spring makes a best effort to identify such network failures that represent client disconnects (specific to each server) and log a minimal message using the dedicated log category DISCONNECTED_CLIENT_LOG_CATEGORY defined in AbstractSockJsSession. If you need to see the stack traces, set that log category to TRACE.

由于网络IO失败可以经常发送因为一个客户端断开连接,可以记录日志对于不必要的栈信息。spring做最好的选择来影响这样的网络失败来避免客户端断开连接(特定于每个服务器)并且记录最小的信息使用日志DISCONNECTED_CLIENT_LOG_CATEGORY定义在AbstractSockJsSession中。如果你需啊哟哦看到栈信息,设置日志的categoryTRACE

 

26.3.6 CORS Headers for SockJS

CORS头信息用于SockJS

 

If you allow cross-origin requests (see Section 26.2.6, Configuring allowed origins), the SockJS protocol uses CORS for cross-domain support in the XHR streaming and polling transports. Therefore CORS headers are added automatically unless the presence of CORS headers in the response is detected. So if an application is already configured to provide CORS support, e.g. through a Servlet Filter, Springs SockJsService will skip this part.

如果你允许跨源的请求(见章节26.2.6,“配置允许源”),SockJS协议使用CORS用于跨注解的支持在XHR流和polling传输中。CORS头信息被自动添加除非避免了CORS头信息在被探测的响应中。因此如果应用已经被配置来避免CORS支持,例如,通过Servlet过滤器,springSockJsService将会跳过这个部分。

 

It is also possible to disable the addition of these CORS headers via the suppressCors property in Springs SockJsService.

也可以关闭这个CORS头的其他部分通过springSockJsService中的suppressCors属性。

 

The following is the list of headers and values expected by SockJS:

下面是头信息的列表和支持默认通过SockJS

 

    "Access-Control-Allow-Origin" - initialized from the value of the "Origin" request header.

初始化来自请求头中Origin的值。

    "Access-Control-Allow-Credentials" - always set to true.

一直设置为true

    "Access-Control-Request-Headers" - initialized from values from the equivalent request header.

初始化值来自相同的请求头。

    "Access-Control-Allow-Methods" - the HTTP methods a transport supports (see TransportType enum).

HTTP方法传输支持(见TransportType的枚举)

    "Access-Control-Max-Age" - set to 31536000 (1 year).

设置为31536000(一年)

 

For the exact implementation see addCorsHeaders in AbstractSockJsService as well as the TransportType enum in the source code.

对于额外的实现见AbstractSockJsService中的addCorsHeaders也可以参照TransportType枚举类的源代码。

 

Alternatively if the CORS configuration allows it consider excluding URLs with the SockJS endpoint prefix thus letting Springs SockJsService handle it.

作为替代如果CORS配置允许他考虑URL对于SockJS端点前缀使得springSockJsService处理他。

 

26.3.7 SockJS Client

SockJS客户端

 

A SockJS Java client is provided in order to connect to remote SockJS endpoints without using a browser. This can be especially useful when there is a need for bidirectional communication between 2 servers over a public network, i.e. where network proxies may preclude the use of the WebSocket protocol. A SockJS Java client is also very useful for testing purposes, for example to simulate a large number of concurrent users.

SockJS客户端被提供用于连接远程的SockJS端点而不需要使用浏览器。这是有用的当需要双向通信在两个服务器之间架设在一个公共的网络上,例如,网络协议可以排除WebSocket协议。一个SockJSJava客户端也是很有用的处于测试的目的,例如模仿大量的并发用户。

 

The SockJS Java client supports the "websocket", "xhr-streaming", and "xhr-polling" transports. The remaining ones only make sense for use in a browser.

SockJSjava客户端支持"websocket""xhr-streaming""xhr-polling"传输。剩余的用于使用在浏览器中。

 

The WebSocketTransport can be configured with:

WebSocketTransport可以被配置通过:

 

    StandardWebSocketClient in a JSR-356 runtime

JSR356运行时中的StandardWebSocketClient

    JettyWebSocketClient using the Jetty 9+ native WebSocket API

JettyWebSocketClient使用Jetty9本地的WebSocketAPI

    Any implementation of Springs WebSocketClient

任何springWebSocketClient的实现

 

An XhrTransport by definition supports both "xhr-streaming" and "xhr-polling" since from a client perspective there is no difference other than in the URL used to connect to the server. At present there are two implementations:

定义支持XhrTransport包括"xhr-streaming""xhr-polling"对于来自客户端是没有什么区别的对于URL的是用来连接服务器。之前有两个实现:

 

    RestTemplateXhrTransport uses Springs RestTemplate for HTTP requests.

RestTemplateXhrTransport使用springRestTemplate用于HTTP的请求。

    JettyXhrTransport uses Jettys HttpClient for HTTP requests.

JettyXhrTransport使用JettyHttpClient用于HTTP请求。

 

The example below shows how to create a SockJS client and connect to a SockJS endpoint:

下面的例子展示了如何创建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");

 

[Note]

注意

 

SockJS uses JSON formatted arrays for messages. By default Jackson 2 is used and needs to be on the classpath. Alternatively you can configure a custom implementation of SockJsMessageCodec and configure it on the SockJsClient.

SockJS使用JSON格式的数组用于消息。默认的Jackson2被使用并且需要放在classpath中。作为代替你可以配置一个自定义的SockJsMessageCodec实现并且配置在SockJsClient上。

 

To use the SockJsClient for simulating a large number of concurrent users you will need to configure the underlying HTTP client (for XHR transports) to allow a sufficient number of connections and threads. For example with Jetty:

为了使用SockJsClient来模拟大量的当前用户你需要配置底层的HTTP客户端(用于XHR传输)来允许足够数量的连接和线程。例如使用Jetty

 

HttpClient jettyHttpClient = new HttpClient();

jettyHttpClient.setMaxConnectionsPerDestination(1000);

jettyHttpClient.setExecutor(new QueuedThreadPool(1000));

 

Consider also customizing these server-side SockJS related properties (see Javadoc for details):

 

@Configuration

public class WebSocketConfig extends WebSocketMessageBrokerConfigurationSupport {

 

    @Override

    public void registerStompEndpoints(StompEndpointRegistry registry) {

        registry.addEndpoint("/sockjs").withSockJS()

            .setStreamBytesLimit(512 * 1024)

            .setHttpMessageCacheSize(1000)

            .setDisconnectDelay(30 * 1000);

    }

 

    // ...

 

}

 

26.4 STOMP Over WebSocket Messaging Architecture

WebSocket消息架构上的STOMP

 

The WebSocket protocol defines two types of messages, text and binary, but their content is undefined. Its expected that the client and server may agree on using a sub-protocol (i.e. a higher-level protocol) to define message semantics. While the use of a sub-protocol with WebSocket is completely optional either way client and server will need to agree on some kind of protocol to help interpret messages.

WebSocket协议定义了两种类型的消息、文本和二进制,但是他们的内容是未定义的。他期望客户端和服务器可以同意使用一个子协议(例如,一个高级的协议)来定义消息语义。当使用WebSocket的子协议试过是完整的选项不管客户端和服务器需啊哟同意一些类型的协议来协助翻译消息。

 

26.4.1 Overview of STOMP

STOMP的概述

 

STOMP is a simple text-oriented messaging protocol that was originally created for scripting languages such as Ruby, Python, and Perl to connect to enterprise message brokers. It is designed to address a subset of commonly used messaging patterns. STOMP can be used over any reliable 2-way streaming network protocol such as TCP and WebSocket. Although STOMP is a text-oriented protocol, the payload of messages can be either text or binary.

STOMP是一个简单的面向测试的信息协议创建用于脚本语言例如RubyPythonPerl来连接企业级消息代理。他被设计处理通用的信息模式。STOMP可以被用于依赖双向的网络流协议例如TCPWebSocket。尽管STOMP是一个面向文本的协议,消息的类型也还是文本和二进制。

 

STOMP is a frame based protocol whose frames are modeled on HTTP. The structure of a STOMP frame:

STOMP是一个基于帧的协议其中帧可以被模型化基于HTTPSTOMP的帧结构如下:

 

COMMAND

header1:value1

header2:value2

 

Body^@

 

Clients can use the SEND or SUBSCRIBE commands to send or subscribe for messages along with a "destination" header that describes what the message is about and who should receive it. This enables a simple publish-subscribe mechanism that can be used to send messages through the broker to other connected clients or to send messages to the server to request that some work be performed.

客户端可以使用SENDSUBSCRIBE指令来发送和定义消息通过destination的头部信息描述消息的内容和谁来接收这个消息。这允许一个简单的发布订阅策略可以使用来发送消息通过代理连接其他的客户端或发送信息给服务器请求执行一些命令。

 

When using Springs STOMP support, the Spring WebSocket application acts as the STOMP broker to clients. Messages are routed to @Controller message-handling methods or to a simple, in-memory broker that keeps track of subscriptions and broadcasts messages to subscribed users. You can also configure Spring to work with a dedicated STOMP broker (e.g. RabbitMQ, ActiveMQ, etc) for the actual broadcasting of messages. In that case Spring maintains TCP connections to the broker, relays messages to it, and also passes messages from it down to connected WebSocket clients. Thus Spring web applications can rely on unified HTTP-based security, common validation, and a familiar programming model message-handling work.

当使用springSTOMP支持,springWebSocket应用扮演STOMP代理的角色对于客户端。消息被路由对于@Controller消息处理方法或对于简单的在内存中的代理保证订阅的跟踪和广播消息给订阅的用户。你也可以配置spring使用定义的STOMP代理(例如RabbitMQActiveMQ等等)对于实际的消息的广播。因此spring保留的TCP的连接到代理,传递消息给他并且获取消息传送给已经连接的WebSocket客户端。springweb应用可以依赖于未定义的基于HTTP的安全通用的验证和相似的编程模型处理消息。

 

Here is an example of a client subscribing to receive stock quotes which the server may emit periodically e.g. via a scheduled task sending messages through a SimpMessagingTemplate to the broker:

这是一个客户端的例子来订阅接受股票信息,来自服务器发出的内容,例如,通过计划任务发送消息通过SimpMessagingTemplate给消息代理:

 

SUBSCRIBE

id:sub-1

destination:/topic/price.stock.*

 

^@

 

Here is an example of a client sending a trade request, which the server may handle through an @MessageMapping method and later on, after the execution, broadcast a trade confirmation message and details down to the client:

这是一个例子有关客户端发送一个交易请求,服务端可以处理通过一个@MessageMapping方法并且在处理过后,广播一个交易确认消息和细节给客户端:

 

SEND

destination:/queue/trade

content-type:application/json

content-length:44

 

{"action":"BUY","ticker":"MMM","shares",44}^@

 

The meaning of a destination is intentionally left opaque in the STOMP spec. It can be any string, and its entirely up to STOMP servers to define the semantics and the syntax of the destinations that they support. It is very common, however, for destinations to be path-like strings where "/topic/.." implies publish-subscribe (one-to-many) and "/queue/" implies point-to-point (one-to-one) message exchanges.

目的地的意义是故意不透明保留的在STOMP这个I报告,他可以是任何字符串并且完全取决于STOMP的服务器来定义语义和他支持的目的地的描述。这是十分普遍的,然而,对于目的地是类似路径的字符串其中"/topic/.."指示发布订阅(一对多)和"/queue/"指示点对点(一对一)的消息交换。

 

STOMP servers can use the MESSAGE command to broadcast messages to all subscribers. Here is an example of a server sending a stock quote to a subscribed client:

STOMP服务器可以使用MESSAGE命令来广播消息给所有的订阅者。这是一个服务器发送股票消息给客户端的例子:

 

MESSAGE

message-id:nxahklf6-1

subscription:sub-1

destination:/topic/price.stock.MMM

 

{"ticker":"MMM","price":129.45}^@

 

It is important to know that a server cannot send unsolicited messages. All messages from a server must be in response to a specific client subscription, and the "subscription-id" header of the server message must match the "id" header of the client subscription.

这是重要的有关了解服务器不能发送非请求型的消息。所有来自服务器的消息必须有特定返回客户端订阅并且"subscription-id"的服务器消息的头信息必须匹配客户端定于的"id"的头信息。

 

The above overview is intended to provide the most basic understanding of the STOMP protocol. It is recommended to review the protocol specification in full.

上面的概述目的是提供基本的例如对于STOMP的协议。建议参考协议定义的完整描述。

 

The benefits of using STOMP as a WebSocket sub-protocol:

使用STOMP作为WebSocket子协议的好处:

 

    No need to invent a custom message format

不需要自定义消息的格式

    Use existing stomp.js client in the browser

使用已有的stomp.js客户端在浏览器中

    Ability to route messages to based on destination

可以路由消息基于目的地

    Option to use full-fledged message broker such as RabbitMQ, ActiveMQ, etc. for broadcasting

选择使用完全的信息代理例如RabbitMQActiveMQ等等用于广播

 

Most importantly the use of STOMP (vs plain WebSocket) enables the Spring Framework to provide a programming model for application-level use in the same way that Spring MVC provides a programming model based on HTTP.

最重要的是STOMP的使用(通过普通的WebS)允许spring的框架提供一个编程模型用于应用级别使用相同springmvc提供的编程模型基于HTTP

 

26.4.2 Enable STOMP over WebSocket

允许在WebSocket上使用STOMP

 

The Spring Framework provides support for using STOMP over WebSocket through the spring-messaging and spring-websocket modules. Here is an example of exposing a STOMP WebSocket/SockJS endpoint at the URL path /portfolio where messages whose destination starts with "/app" are routed to message-handling methods (i.e. application work) and messages whose destinations start with "/topic" or "/queue" will be routed to the message broker (i.e. broadcasting to other connected clients):

spring框架提供了支持有关使用STOMPWebSocket上通过spring-messaging模块和spring-websocket模块。这里是一个展示STOMPWebSocket/SockJS端点对于URL路径/portfolio中消息的目的地开始于"/app"被路由DAO消息处理方法(例如,应用工作)和消息中定义开始于"/topic""/queue"将被路由到消息代理 (例如广播给其他连接的客户端):

 

import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;

import org.springframework.web.socket.config.annotation.StompEndpointRegistry;

 

@Configuration

@EnableWebSocketMessageBroker

public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {

 

    @Override

    public void registerStompEndpoints(StompEndpointRegistry registry) {

        registry.addEndpoint("/portfolio").withSockJS();

    }

 

    @Override

    public void configureMessageBroker(MessageBrokerRegistry config) {

        config.setApplicationDestinationPrefixes("/app");

        config.enableSimpleBroker("/topic", "/queue");

    }

 

}

 

and in XML:

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:message-broker application-destination-prefix="/app">

        <websocket:stomp-endpoint path="/portfolio">

            <websocket:sockjs/>

        </websocket:stomp-endpoint>

        <websocket:simple-broker prefix="/topic, /queue"/>

    </websocket:message-broker>

 

</beans>

 

[Note]

注意

 

The "/app" prefix is arbitrary. You can pick any prefix. Its simply meant to differentiate messages to be routed to message-handling methods to do application work vs messages to be routed to the broker to broadcast to subscribed clients.

"/app"的前缀是任意的。你可以使用任何前缀。简单的意思就是对于不同的消息被路由到消息处理方法对于应用和消息路由到消息代理给订阅的客户端。

 

The "/topic" and "/queue" prefixes depend on the broker in use. In the case of the simple, in-memory broker the prefixes do not have any special meaning; its merely a convention that indicates how the destination is used (pub-sub targetting many subscribers or point-to-point messages typically targeting an individual recipient). In the case of using a dedicated broker, most brokers use "/topic" as a prefix for destinations with pub-sub semantics and "/queue" for destinations with point-to-point semantics. Check the STOMP page of the broker to see the destination semantics it supports.

"/topic""/queue"前缀依赖于使用的消息代理。为了简单,内存中的消息代理前缀没有特定的含义,他是简单的协议指定目的地的使用(公开对于多个订阅者或点对点的消息通常只是目标一个独立的接收者)。由于使用了消息代理,大部分消息代理使用"/topic"作为一个前缀用于目的地使用发布订阅的语义并且"/queue"用于点对点的语义。检查消息代理的STOMP页面可以了解到所支持的目的地语义。

 

On the browser side, a client might connect as follows using stomp.js and the sockjs-client:

对于浏览器方面,一个客户端可以连接如下使用stomp.jssockjs-client

 

var socket = new SockJS("/spring-websocket-portfolio/portfolio");

var stompClient = Stomp.over(socket);

 

stompClient.connect({}, function(frame) {

}

 

Or if connecting via WebSocket (without SockJS):

或者如果通过WebSocket连接(不是使用SockJS):

 

var socket = new WebSocket("/spring-websocket-portfolio/portfolio");

var stompClient = Stomp.over(socket);

 

stompClient.connect({}, function(frame) {

}

 

Note that the stompClient above does not need to specify login and passcode headers. Even if it did, they would be ignored, or rather overridden, on the server side. See the sections Section 26.4.8,Connections To Full-Featured Brokerand Section 26.4.10, Authenticationfor more information on authentication.

注意上面的stompClient不需要指定登录和密码的头信息。他们是被忽略的或者被覆盖了,对于服务器方面。见章节26.4.8,“连接到全功能的消息代理”和章节26.4.10,“验证”来了解更多有关验证的细节。

 

26.4.3 Flow of Messages

消息流

 

When a STOMP endpoint is configured, the Spring application acts as the STOMP broker to connected clients. This section provides a big picture overview of how messages flow within the application.

当一个STOMP端点被配置,spring应用扮演一个STOMP代理的角色对于已经连接的客户端。章节提供了消息流在应用中走向的概述。

 

The spring-messaging module provides the foundation for asynchronous message processing. It contains a number of abstractions that originated in the Spring Integration project and are intended for use as building blocks in messaging applications:

spring-messaging模块提供了对于异步消息处理的支持。他包括一些抽象的原型在spring的集成项目中并且尝试使用构建块在消息应用中:

 

    Message — a message with headers and a payload.

Message————一个消息有头信息和内容

    MessageHandler — a contract for handling a message.

MessageHandler————用于处理message

    MessageChannel — a contract for sending a message enabling loose coupling between senders and receivers.

MessageChannel————一个连接用于发送消息在发送者和接受者之间

    SubscribableChannel — extends MessageChannel and sends messages to registered MessageHandler subscribers.

SubscribableChannel————继承自MessageChannel并且发送消息给注册的MessageHandler订阅者

    ExecutorSubscribableChannel — a concrete implementation of SubscribableChannel that can deliver messages asynchronously via a thread pool.

ExecutorSubscribableChannel————一个连接实现SubscribableChannel可以异步传递消息通过线程池

 

The @EnableWebSocketMessageBroker Java config and the <websocket:message-broker> XML config both assemble a concrete message flow. Below is a diagram of the part of the setup when using the simple, in-memory broker:

@EnableWebSocketMessageBrokerJava配置和<websocket:message-broker>xml配置可以指定相关的消息流。下面是一个图表有关设置的一部分当使用简单的内存中消息代理:

 

 

The above setup that includes 3 message channels:

上面的设置包含3个消息通道:

 

    "clientInboundChannel" for messages from WebSocket clients.

"clientInboundChannel"用于传递来自WebSocket客户端的消息

    "clientOutboundChannel" for messages to WebSocket clients.

"clientOutboundChannel"用于传递给WebSocket客户端的消息

    "brokerChannel" for messages to the broker from within the application.

"brokerChannel"用于消息代理来自应用中

 

The same three channels are also used with a dedicated broker except here a "broker relay" takes the place of the simple broker:

相同的三个通道被使用对于专有的消息代理除了一个"broker relay"用作一个简单的消息代理:

 

Messages on the "clientInboundChannel" can flow to annotated methods for application handling (e.g. a stock trade execution request) or can be forwarded to the broker (e.g. client subscribing for stock quotes). The STOMP destination is used for simple prefix-based routing. For example the "/app" prefix could route messages to annotated methods while the "/topic" and "/queue" prefixes could route messages to the broker.

"clientInboundChannel"可以传递给注解方法用于应用处理(例如,股票交易执行请求)或可以转发给消息代理(例如,客户端订阅对于股票价格)。STOMP目的地被用于简单的基于前缀的路由。例如“/app”前缀可以路由消息给注解方法而"/topic""/queue"前缀可以路由给消息代理。

 

When a message-handling annotated method has a return type, its return value is sent as the payload of a Spring Message to the "brokerChannel". The broker in turn broadcasts the message to clients. Sending a message to a destination can also be done from anywhere in the application with the help of a messaging template. For example, an HTTP POST handling method can broadcast a message to connected clients, or a service component may periodically broadcast stock quotes.

当一个消息处理注解方法有一个返回类型,他的返回值被发送作为spring消息的payload"brokerChannel"。消息代理广播消息给客户端。发送消息给目的地可以在应用的任意地方进行处理由于消息模板的帮助。例如,一个HTTPPOST处理方法可以广播一个消息给连接的客户端或一个服务模块可以定义广播股票价格。

 

Below is a simple example to illustrate the flow of messages:

下面是一个简单的例子来展示消息的流:

 

@Configuration

@EnableWebSocketMessageBroker

public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {

 

    @Override

    public void registerStompEndpoints(StompEndpointRegistry registry) {

        registry.addEndpoint("/portfolio");

    }

 

    @Override

    public void configureMessageBroker(MessageBrokerRegistry registry) {

        registry.setApplicationDestinationPrefixes("/app");

        registry.enableSimpleBroker("/topic");

    }

 

}

 

@Controller

public class GreetingController {

 

    @MessageMapping("/greeting") {

    public String handle(String greeting) {

        return "[" + getTimestamp() + ": " + greeting;

    }

 

}

 

The following explains the message flow for the above example:

下面解释了上面例子中的消息流:

 

    WebSocket clients connect to the WebSocket endpoint at "/portfolio".

WebSocket客户端连接到WebSocket端点在"/portfolio"

    Subscriptions to "/topic/greeting" pass through the "clientInboundChannel" and are forwarded to the broker.

"/topic/greeting"的订阅者传递消息通过"clientInboundChannel"并且转发给消息代理

    Greetings sent to "/app/greeting" pass through the "clientInboundChannel" and are forwarded to the GreetingController. The controller adds the current time, and the return value is passed through the "brokerChannel" as a message to "/topic/greeting" (destination is selected based on a convention but can be overridden via @SendTo).

发送"/app/greeting"通过"clientInboundChannel"并且转发给GreetingController。控制器添加当前的时间并且返回值传递通过"brokerChannel"作为消息给"/topic/greeting"(目的地的选择基于协议但是可以被@SendTo覆盖)

    The broker in turn broadcasts messages to subscribers, and they pass through the "clientOutboundChannel".

消息代理广播消息给订阅者并且他们传递通过"clientOutboundChannel"

 

The next section provides more details on annotated methods including the kinds of arguments and return values supported.

下面的章节提供了更多的细节有关注解方法包括参数的种类和返回值的支持。

 

26.4.4 Annotation Message Handling

注解消息处理

 

The @MessageMapping annotation is supported on methods of @Controller classes. It can be used for mapping methods to message destinations and can also be combined with the type-level @MessageMapping for expressing shared mappings across all annotated methods within a controller.

@MessageMapping注解被支持于@Controller类中的方法。可以被使用用于匹配消息目的地的方法并且可以结合类型级别的@MessageMapping对于表达共享的匹配跨一个控制器中的多个注解方法。

 

By default destination mappings are treated as Ant-style, slash-separated, path patterns, e.g. "/foo*", "/foo/**". etc. They can also contain template variables, e.g. "/foo/{id}" that can then be referenced via @DestinationVariable-annotated method arguments.

默认的目的地匹配可以是Ant风格的、斜线分隔例如"/foo*""/foo/**"等等。他们可以包含模板变量,例如"/foo/{id}"可以引用通过@DestinationVariable的注解方法参数。

 

[Note]

注意

 

Applications can also use dot-separated destinations (vs slash). See Section 26.4.9,Using Dot as Separator in @MessageMapping Destinations.

应用也可以使用点分给目的地(也可以使用斜线)。见章节26.4.9,“在@MessageMapping目的地中使用点分隔”

 

The following method arguments are supported for @MessageMapping methods:

下面的方法参数被支持用于@MessageMapping方法:

 

    Message method argument to get access to the complete message being processed.

Message方法参数用于访问复杂的被处理的消息。

    @Payload-annotated argument for access to the payload of a message, converted with a org.springframework.messaging.converter.MessageConverter. The presence of the annotation is not required since it is assumed by default. Payload method arguments annotated with validation annotations (like @Validated) will be subject to JSR-303 validation.

@Payload注解参数用于访问消息的payload,使用org.springframework.messaging.converter.MessageConverter消息转换器。注解的表现不需要默认的假设。Payload方法参数注解使用了验证注解(例如@Validated)是JSR303验证的一个子集。

    @Header-annotated arguments for access to a specific header value along with type conversion using an org.springframework.core.convert.converter.Converter if necessary.

@Header注解参数用于访问特定的头信息对于类型转换根据需要使用org.springframework.core.convert.converter.Converter

    @Headers-annotated method argument that must also be assignable to java.util.Map for access to all headers in the message.

@Headers注解方法参数必须定义为java.util.Map用于访问所有消息中的头信息。

    MessageHeaders method argument for getting access to a map of all headers.

MessageHeaders方法参数用于访问所有headersmap

    MessageHeaderAccessor, SimpMessageHeaderAccessor, or StompHeaderAccessor for access to headers via typed accessor methods.

MessageHeaderAccessorSimpMessageHeaderAccessorStompHeaderAccessor用于访问头信息通过类型访问方法。

    @DestinationVariable-annotated arguments for access to template variables extracted from the message destination. Values will be converted to the declared method argument type as necessary.

@DestinationVariable注解的参数用于访问模板变量来自消息的目的地。变量将更加需要被转换为定义的方法参数类型。

    java.security.Principal method arguments reflecting the user logged in at the time of the WebSocket HTTP handshake.

java.security.Principal方法参数发射用户登录的时间对于WebSocketHTTP的握手。

 

The return value from an @MessageMapping method is converted with a org.springframework.messaging.converter.MessageConverter and used as the body of a new message that is then sent, by default, to the "brokerChannel" with the same destination as the client message but using the prefix "/topic" by default. An @SendTo message level annotation can be used to specify any other destination instead. It can also be set a class-level to share a common destination.

通过@MessageMapping方法的返回值对于org.springframework.messaging.converter.MessageConverter使用新消息的内容被默认发送,对于相同目的地的"brokerChannel"的客户端信息但是默认使用了前缀“/topic”。@SendTo消息级别注解可以被使用指定其他的目的地作为代替。可以被设置为类级别来共享通用的目的地。

 

An @SubscribeMapping annotation can also be used to map subscription requests to @Controller methods. It is supported on the method level, but can also be combined with a type level @MessageMapping annotation that expresses shared mappings across all message handling methods within the same controller.

@SubscribeMapping注解也可以被使用来匹配定于请求对于@Controller方法。如果支持在方法级别,但是可以合并类型级别的@MessageMapping注解来表达共享匹配跨所有的消息处理方法在相同的控制器中。

 

By default the return value from an @SubscribeMapping method is sent as a message directly back to the connected client and does not pass through the broker. This is useful for implementing request-reply message interactions; for example, to fetch application data when the application UI is being initialized. Or alternatively an @SubscribeMapping method can be annotated with @SendTo in which case the resulting message is sent to the "brokerChannel" using the specified target destination.

默认的返回值来自@SubscribeMapping方法被发送作为一个消息直接返回给已经连接的客户端并且没有通过消息代理来床底。这是有用的实现依赖请求的消息;例如为了他侧应用数据当应用的UI被初始化。或作为替代@SubscribeMapping方法可以被注解使用@SendTo使得结果消息发送给"brokerChannel"使用指定的目的地。

 

[Note]

注意

 

In some cases a controller may need to be decorated with an AOP proxy at runtime. One example is if you choose to have @Transactional annotations directly on the controller. When this is the case, for controllers specifically, we recommend using class-based proxying. This is typically the default choice with controllers. However if a controller must implement an interface that is not a Spring Context callback (e.g. InitializingBean, *Aware, etc), you may need to explicitly configure class-based proxying. For example with <tx:annotation-driven />, change to <tx:annotation-driven proxy-target-class="true" />.

在一些情况控制器可能需要被描述使用AOP代理在运行时。一个例子是如果你选择@Transactional注解在控制器上。当这种情况对于指定的控制器,我们建议使用基于类的代理。这通常是默认的选择对于控制器。然而你如果一个控制器必须实现一个接口而不是spring上下文的回调(例如,InitializingBeanAware等等),你可能需要明确配置基于类的代理。例如使用<tx:annotation-driven />改为<tx:annotation-driven proxy-target-class="true" />

 

26.4.5 Sending Messages

发送消息

 

What if you want to send messages to connected clients from any part of the application? Any application component can send messages to the "brokerChannel". The easiest way to do that is to have a SimpMessagingTemplate injected, and use it to send messages. Typically it should be easy to have it injected by type, for example:

如果你希望发送消息给已经连接的客户端来自应用的任意部分改怎么做?任何应用的组件可以发送消息给"brokerChannel"。最简单的实现方式是注入SimpMessagingTemplate并且使用它来发送消息。通常可以简单的注入类型,例如:

 

@Controller

public class GreetingController {

 

    private SimpMessagingTemplate template;

 

    @Autowired

    public GreetingController(SimpMessagingTemplate template) {

        this.template = template;

    }

 

    @RequestMapping(path="/greetings", method=POST)

    public void greet(String greeting) {

        String text = "[" + getTimestamp() + "]:" + greeting;

        this.template.convertAndSend("/topic/greetings", text);

    }

 

}

 

But it can also be qualified by its name "brokerMessagingTemplate" if another bean of the same type exists.

但是也可以定义名字"brokerMessagingTemplate"如果另一个相同类型的bean存在的话。

 

26.4.6 Simple Broker

简单的消息代理

 

The built-in, simple message broker handles subscription requests from clients, stores them in memory, and broadcasts messages to connected clients with matching destinations. The broker supports path-like destinations, including subscriptions to Ant-style destination patterns.

内置的简单的消息代理处理来自客户端的订阅请求,存储在内存中并且广播消息给已经连接的客户端对于匹配的目的地。消息代理支持类似路径的目的地,包括订阅Ant风格的目的地格式。

 

[Note]

注意

 

Applications can also use dot-separated destinations (vs slash). See Section 26.4.9,Using Dot as Separator in @MessageMapping Destinations.

应用可以使用点分隔的目的地(也可以使用斜线)。见章节26.4.9,“使用点作为分隔符在@MessageMapping目的地”。

 

26.4.7 Full-Featured Broker

全功能的消息代理

 

The simple broker is great for getting started but supports only a subset of STOMP commands (e.g. no acks, receipts, etc.), relies on a simple message sending loop, and is not suitable for clustering. As an alternative, applications can upgrade to using a full-featured message broker.

简单的消息代理用于启动支持STOMP命令的子集(例如,acksreceipts等等),依赖于简单的消息发送循环,并且不适合于集群。作为代替,应用可以更新来使用一个全功能的消息代理。

 

Check the STOMP documentation for your message broker of choice (e.g. RabbitMQ, ActiveMQ, etc.), install the broker, and run it with STOMP support enabled. Then enable the STOMP broker relay in the Spring configuration instead of the simple broker.

查看STOMP的文档对于你的消息代理的选择(例如,RabbitMQActiveMQ等等),安装消息代理并且在STOMP支持启用的前提下运行。然后启用STOMP消息代理依赖于spring的配置代理简单的消息代理。

 

Below is example configuration that enables a full-featured broker:

下面是一个样例的配置来允许全功能的消息代理:

 

@Configuration

@EnableWebSocketMessageBroker

public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {

 

    @Override

    public void registerStompEndpoints(StompEndpointRegistry registry) {

        registry.addEndpoint("/portfolio").withSockJS();

    }

 

    @Override

    public void configureMessageBroker(MessageBrokerRegistry registry) {

        registry.enableStompBrokerRelay("/topic", "/queue");

        registry.setApplicationDestinationPrefixes("/app");

    }

 

}

 

XML configuration equivalent:

相当于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:message-broker application-destination-prefix="/app">

        <websocket:stomp-endpoint path="/portfolio" />

            <websocket:sockjs/>

        </websocket:stomp-endpoint>

        <websocket:stomp-broker-relay prefix="/topic,/queue" />

    </websocket:message-broker>

 

</beans>

 

The "STOMP broker relay" in the above configuration is a Spring MessageHandler that handles messages by forwarding them to an external message broker. To do so it establishes TCP connections to the broker, forwards all messages to it, and then forwards all messages received from the broker to clients through their WebSocket sessions. Essentially it acts as a "relay" that forwards messages in both directions.

上面配置中的"STOMP broker relay"springMessageHandler处理请求通过转发他们给外部的消息代理。为了这么做需要建立TCP连接到消息代理,转发所有的消息给代理并且转发所有的接收到的来自消息代理发送给客户端的内容通过她们的WebSocket会话。本质上他扮演了一个“传播者”对于传递消息在两个目的地之间。

 

[Note]

注意

 

Please add a dependency on org.projectreactor:reactor-net for TCP connection management.

添加一个依赖对于org.projectreactor:reactor-net用于TCP连接的管理。

 

Furthermore, application components (e.g. HTTP request handling methods, business services, etc.) can also send messages to the broker relay, as described in Section 26.4.5,Sending Messages, in order to broadcast messages to subscribed WebSocket clients.

此外,应用组件(例如HTTP请求处理方法,业务服务等)也可以发送消息给消息代理,描述在章节26.4.5,“发送消息”用于广播消息给订阅的WebSocket客户端。

 

In effect, the broker relay enables robust and scalable message broadcasting.

因此,消息代理允许强壮和大范围的消息广播。

 

26.4.8 Connections To Full-Featured Broker

连接到全功能的消息代理

 

A STOMP broker relay maintains a single "system" TCP connection to the broker. This connection is used for messages originating from the server-side application only, not for receiving messages. You can configure the STOMP credentials for this connection, i.e. the STOMP frame login and passcode headers. This is exposed in both the XML namespace and the Java config as the systemLogin/systemPasscode properties with default values guest/guest.

STOMP的消息代理保持单一的系统的TCP连接对于消息代理。连接被使用用于消息来自服务端应用,不是为接收消息。你可以配置STOMP用于这个连接,例如STOMP帧来登录和密码的头信息。暴露在XML命名空间中并且Java配置作为systemLogin/systemPasscode属性且默认值是guest/guest

 

The STOMP broker relay also creates a separate TCP connection for every connected WebSocket client. You can configure the STOMP credentials to use for all TCP connections created on behalf of clients. This is exposed in both the XML namespace and the Java config as the clientLogin/clientPasscode properties with default values guest/guest.

STOMP消息代理也可以创建分离的TCP连接对于每个已经连接的WebSocket客户端。你可以配置STOMP证书用于使用在所有的TCP连接创建基于客户端的行为。暴露在xml命名空间和java配置中作为clientLogin/clientPasscode属性且默认值是guest/guest

 

[Note]

注意

 

The STOMP broker relay always sets the login and passcode headers on every CONNECT frame that it forwards to the broker on behalf of clients. Therefore WebSocket clients need not set those headers; they will be ignored. As the following section explains, instead WebSocket clients should rely on HTTP authentication to protect the WebSocket endpoint and establish the client identity.

STOMP消息代理设置登录和密码头信息在每一个CONNECT帧中因此他转发给客户端行为的消息代理。因此WebSocket客户端不需要设置这些头信息,他们将被忽略。由于下面的章节的解释,WebSocket客户端将依赖于HTTP授权来保护WebSocket端点和建立客户端认证。

 

The STOMP broker relay also sends and receives heartbeats to and from the message broker over the "system" TCP connection. You can configure the intervals for sending and receiving heartbeats (10 seconds each by default). If connectivity to the broker is lost, the broker relay will continue to try to reconnect, every 5 seconds, until it succeeds.

STOMP消息代理发送和接收心跳和systemTCP连接的消息代理。你可以配置发送和接收心跳的间隔(默认是每10秒)。如果消息代理的连接丢失了,消息代理将继续尝试重新连接,每隔5秒钟直到成功为止。

 

[Note]

注意

 

A Spring bean can implement ApplicationListener<BrokerAvailabilityEvent> in order to receive notifications when the "system" connection to the broker is lost and re-established. For example a Stock Quote service broadcasting stock quotes can stop trying to send messages when there is no active "system" connection.

springbean可以实现ApplicationListener<BrokerAvailabilityEvent>用于接收system连接的通知对于消息代理丢失和重新连接的时候。例如股票服务广播股票价格试图发送消息当没有激活的system连接时。

 

The STOMP broker relay can also be configured with a virtualHost property. The value of this property will be set as the host header of every CONNECT frame and may be useful for example in a cloud environment where the actual host to which the TCP connection is established is different from the host providing the cloud-based STOMP service.

STOMP的消息代理可以配置一个virtualHost属性。这个属性的值将被设置为每个CONNECT帧的主机头信息并且在云环境中是十分有用的当实际的TCP连接被建立并且和基于云的STOMP服务不一样的时候。

 

26.4.9 Using Dot as Separator in @MessageMapping Destinations

使用点作为@MessageMapping目的地的分隔符

 

Although slash-separated path patterns are familiar to web developers, in messaging it is common to use a "." as the separator, for example in the names of topics, queues, exchanges, etc. Applications can also switch to using "." (dot) instead of "/" (slash) as the separator in @MessageMapping mappings by configuring a custom AntPathMatcher.

尽管路径分隔符模式对于web开发者来说比较熟悉,在消息中通常使用点作为分隔符,例如在topicqueueexchange的名字中等等。应用也可以交换使用点和斜线作为分隔符在@MessageMapping的匹配中通过使用自定义的AntPathMatcher

 

In Java config:

Java配置中:

 

@Configuration

@EnableWebSocketMessageBroker

public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {

 

  // ...

 

  @Override

  public void configureMessageBroker(MessageBrokerRegistry registry) {

    registry.enableStompBrokerRelay("/queue/", "/topic/");

    registry.setApplicationDestinationPrefixes("/app");

    registry.setPathMatcher(new AntPathMatcher("."));

  }

 

}

 

In XML config:

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:message-broker application-destination-prefix="/app" path-matcher="pathMatcher">

    <websocket:stomp-endpoint path="/stomp" />

    <websocket:simple-broker prefix="/topic, /queue"/>

  </websocket:message-broker>

 

  <bean id="pathMatcher" class="org.springframework.util.AntPathMatcher">

    <constructor-arg index="0" value="." />

  </bean>

 

</beans>

 

And below is a simple example to illustrate a controller with "." separator:

下面是一个简答的例子庸官声明一个控制器使用点作为分隔符:

 

@Controller

@MessageMapping("foo")

public class FooController {

 

  @MessageMapping("bar.{baz}")

  public void handleBaz(@DestinationVariable String baz) {

  }

 

}

 

If the application prefix is set to "/app" then the foo method is effectively mapped to "/app/foo.bar.{baz}".

如果应用的前缀设置为"/app"然后foo方法就匹配为"/app/foo.bar.{baz}"

 

26.4.10 Authentication

验证

 

In a WebSocket-style application it is often useful to know who sent a message. Therefore some form of authentication is needed to establish the user identity and associate it with the current session.

WebSocket风格的应用中,发送一个消息是很有用的,一些形式的严重是需要建立用户身份和当前会话的连接的。

 

Existing Web applications already use HTTP based authentication. For example Spring Security can secure the HTTP URLs of the application as usual. Since a WebSocket session begins with an HTTP handshake, that means URLs mapped to STOMP/WebSocket are already automatically protected and require authentication. Moreover the page that opens the WebSocket connection is itself likely protected and so by the time of the actual handshake, the user should have been authenticated.

已有的Web应用已经使用HTTP基本的验证。例如spring Security可以保证HTTPURL是正常的。WebSocket会话使用HTTP握手协议作为开始,意味着URL匹配STOMP/WebSocker已经自动被保护和要求验证了。此外页面由WebSocket连接打开本身也是受到保护的并且在实际握手协议中已经实现了,用户应当是被授权访问的。

 

When a WebSocket handshake is made and a new WebSocket session is created, Springs WebSocket support automatically propagates the java.security.Principal from the HTTP request to the WebSocket session. After that every message flowing through the application on that WebSocket session is enriched with the user information. Its present in the message as a header. Controller methods can access the current user by adding a method argument of type javax.security.Principal.

当一个WebSocket握手协议已经完成并且一个新的WebSocket会话被创建,springWebSocket支持自动传播java.security.Principal来自HTTP的请求对于WebSocket的会话。在每个消息经过应用之后对于WebSocket会话是丰富了用户的信息。表现在消息的头部。控制器方法可以访问当前用户通过添加javax.security.Principal类型的参数。

 

Note that even though the STOMP CONNECT frame has "login" and "passcode" headers that can be used for authentication, Springs STOMP WebSocket support ignores them and currently expects users to have been authenticated already via HTTP.

注意即使STOMPCONNECT的帧有loginpasscode的头信息可以被验证使用,springSTOMPWebSocket支持忽略他们并且期望用户已经通过了HTTP的验证。

 

In some cases it may be useful to assign an identity to a WebSocket session even when the user has not been formally authenticated. For example, a mobile app might assign some identity to anonymous users, perhaps based on geographical location. The do that currently, an application can sub-class DefaultHandshakeHandler and override the determineUser method. The custom handshake handler can then be plugged in (see examples in Section 26.2.4,Deployment Considerations).

在一些情况下是有用的关于定义一个身份对于WebSocket会话当用户没有被正常授权时。例如,一个移动的app可以指定一些授权给匿名的用户,通常基于地理的位置。他们这么做是正确的,一个应用可以继承DefaultHandshakeHandler并且覆盖determineUser方法。自定义握手协议处理器可以被插件化使用(将章节26.2.4中的案例,“开发中的考虑”)。

 

26.4.11 User Destinations

用户的目的地

 

An application can send messages targeting a specific user, and Springs STOMP support recognizes destinations prefixed with "/user/" for this purpose. For example, a client might subscribe to the destination "/user/queue/position-updates". This destination will be handled by the UserDestinationMessageHandler and transformed into a destination unique to the user session, e.g. "/queue/position-updates-user123". This provides the convenience of subscribing to a generically named destination while at the same time ensuring no collisions with other users subscribing to the same destination so that each user can receive unique stock position updates.

应用可以发送消息目标给特定的用户,并且springSTOMP支持识别目的前缀使用"/user/"。例如,一个客户端可以订阅"/user/queue/position-updates"。这些destination将被处理通过UserDestinationMessageHandler并且转换为destination对于用户的会话,例如,"/queue/position-updates-user123"。这个订阅的方便提供对于命名的destination当不允许其他用户的订阅对于相同的destination因此每个用户可以收到唯一的股票位置更新。

 

On the sending side messages can be sent to a destination such as "/user/{username}/queue/position-updates", which in turn will be translated by the UserDestinationMessageHandler into one or more destinations, one for each session associated with the user. This allows any component within the application to send messages targeting a specific user without necessarily knowing anything more than their name and the generic destination. This is also supported through an annotation as well as a messaging template.

对于发送消息方面可以发送destination例如"/user/{username}/queue/position-updates",因此将被UserDestinationMessageHandler翻译为一个或多个destination,其中每一个是连接用户的会话。这允许任意组件在应用中发送消息给目标用户而不需要知道他们的名字和通用的destination。通常支持注解和消息模板。

 

For example, a message-handling method can send messages to the user associated with the message being handled through the @SendToUser annotation (also supported on the class-level to share a common destination):

例如,一个消息处理方法可以发送消息给用户并且处理通过@SendToUser注解(也支持类级别的注解来共享通用的destination):

 

@Controller

public class PortfolioController {

 

    @MessageMapping("/trade")

    @SendToUser("/queue/position-updates")

    public TradeResult executeTrade(Trade trade, Principal principal) {

        // ...

        return tradeResult;

    }

}

 

If the user has more than one session, by default all of the sessions subscribed to the given destination are targeted. However sometimes, it may be necessary to target only the session that sent the message being handled. This can be done by setting the broadcast attribute to false, for example:

如果用户有多个会话,默认的所有的会话订阅了给定的destination。然而有时有必要只有一个发送的消息被处理。这可以通过设置broadcast属性为false来实现,例如:

 

@Controller

public class MyController {

 

    @MessageMapping("/action")

    public void handleAction() throws Exception{

        // raise MyBusinessException here

    }

 

    @MessageExceptionHandler

    @SendToUser(destinations="/queue/errors", broadcast=false)

    public ApplicationError handleException(MyBusinessException exception) {

        // ...

        return appError;

    }

}

 

[Note]

注意

 

While user destinations generally imply an authenticated user, it isnt required strictly. A WebSocket session that is not associated with an authenticated user can subscribe to a user destination. In such cases the @SendToUser annotation will behave exactly the same as with broadcast=false, i.e. targeting only the session that sent the message being handled.

当用户的destination通常应用于授权用户,他不是必须的。一个WebSocket会话没有和授权用户连接可以订阅用户的destination。在这种情况下@SendToUser注解将和broadcast=false的表现是相同的,例如,只有目标的发送消息的会话会被处理。

 

It is also possible to send a message to user destinations from any application component by injecting the SimpMessagingTemplate created by the Java config or XML namespace, for example (the bean name is "brokerMessagingTemplate" if required for qualification with @Qualifier):

这是重要的有关发送消息给用户的destination来自被注入的用户组建通过java配置或xml命名空间来创建,例如,(bean的名字是"brokerMessagingTemplate"如果需要@Qualifier来用于资格)。

 

@Service

public class TradeServiceImpl implements TradeService {

 

private final SimpMessagingTemplate messagingTemplate;

 

@Autowired

public TradeServiceImpl(SimpMessagingTemplate messagingTemplate) {

this.messagingTemplate = messagingTemplate;

}

 

// ...

 

public void afterTradeExecuted(Trade trade) {

this.messagingTemplate.convertAndSendToUser(

trade.getUserName(), "/queue/position-updates", trade.getResult());

}

}

 

[Note]

注意

 

When using user destinations with an external message broker, check the broker documentation on how to manage inactive queues, so that when the user session is over, all unique user queues are removed. For example, RabbitMQ creates auto-delete queues when destinations like /exchange/amq.direct/position-updates are used. So in that case the client could subscribe to /user/exchange/amq.direct/position-updates. Similarly, ActiveMQ has configuration options for purging inactive destinations.

当使用用户destination配合一个外部的消息代理,检查代理的文档有关如何管理未激活的队列,因此当用户会话关闭后,所有唯一的用户队列被移除。例如,RabbitMQ创建自动删除队列当destination类似于使用/exchange/amq.direct/position-updates。因此客户端可以订阅/user/exchange/amq.direct/position-updates。同样的,ActiveMQ可以配置选项用于非激活的destination

 

In a multi-application server scenario a user destination may remain unresolved because the user is connected to a different server. In such cases you can configure a destination to broadcast unresolved messages to so that other servers have a chance to try. This can be done through the userDestinationBroadcast property of the MessageBrokerRegistry in Java config and the user-destination-broadcast attribute of the message-broker element in XML.

在一个多应用的服务场景中一个用户destination可以保留未解决因为用户被连接到不同的服务器。在这种情况你可以配置destination用于广播未处理的消息因此服务器可以有机会尝试。这可以通过设置MessageBrokerRegistry中的userDestinationBroadcast属性和xmlmessage-broker元素的user-destination-broadcast属性来实现。

 

26.4.12 Listening To ApplicationContext Events and Intercepting Messages

监听应用事件和中断消息

 

Several ApplicationContext events (listed below) are published and can be received by implementing Springs ApplicationListener interface.

一些应用上下文事件(列在下面)可以被发布并且可以被接收通过实现springApplicationListener接口。

 

    BrokerAvailabilityEvent — indicates when the broker becomes available/unavailable. While the "simple" broker becomes available immediately on startup and remains so while the application is running, the STOMP "broker relay" may lose its connection to the full featured broker, for example if the broker is restarted. The broker relay has reconnect logic and will re-establish the "system" connection to the broker when it comes back, hence this event is published whenever the state changes from connected to disconnected and vice versa. Components using the SimpMessagingTemplate should subscribe to this event and avoid sending messages at times when the broker is not available. In any case they should be prepared to handle MessageDeliveryException when sending a message.

BrokerAvailabilityEvent————当消息代理可用/不可用,当代理启动和暂停当应用运行的时候,STOMP消息代理可能丢失连接对于全特性的消息代理,例如如果消息代理被重启。消息代理依赖可以重新连接并且将重新建立system连接对于消息代理当其重启后,因此事件被发布来通知状态改变对于连接和断开连接。组件使用SimpMessagingTemplate应当订阅这个时间并且避免同时发送消息当消息代理不可用的时候。在任何情况他应当被准备用于处理MessageDeliveryException当发送一个消息的时候。

    SessionConnectEvent — published when a new STOMP CONNECT is received indicating the start of a new client session. The event contains the message representing the connect including the session id, user information (if any), and any custom headers the client may have sent. This is useful for tracking client sessions. Components subscribed to this event can wrap the contained message using SimpMessageHeaderAccessor or StompMessageHeaderAccessor.

SessionConnectEvent————发布事件当一个新的STOMPCONNECT收到新客户端会话的启动消息。这个事件包括消息表现的连接包括会话id、用户信息和自定义头信息。这对于追踪客户端会话是有用的。组件订阅这个时间可以处理消息通过使用SimpMessageHeaderAccessorStompMessageHeaderAccessor

    SessionConnectedEvent — published shortly after a SessionConnectEvent when the broker has sent a STOMP CONNECTED frame in response to the CONNECT. At this point the STOMP session can be considered fully established.

SessionConnectedEvent————事件发布在SessionConnectEvent事件之后,当消息代理发送一个STOMPCONNECTED帧对于CONNECT的返回。在这个时候STOMP会话可以被认为已经完全建立了。

    SessionSubscribeEvent — published when a new STOMP SUBSCRIBE is received.

SessionSubscribeEvent————事件被发布当收到一个新的STOMPSUBSCRIBE之后。

    SessionUnsubscribeEvent — published when a new STOMP UNSUBSCRIBE is received.

SessionUnsubscribeEvent————事件被发布当收到一个新的STOMPUNSUBSCRIBE之后。

    SessionDisconnectEvent — published when a STOMP session ends. The DISCONNECT may have been sent from the client, or it may also be automatically generated when the WebSocket session is closed. In some cases this event may be published more than once per session. Components should be idempotent with regard to multiple disconnect events.

SessionDisconnectEvent————当STOMP会话结束后被发布。DISCONNECT可以来自客户端的发送,或可以自动生成当WebSocket会话被关闭的时候。在一些情况中这个时间可以被发布多次对于每个会话。组件应当幂等的处理多个断开连接事件。

 

[Note]

注意

 

When using a full-featured broker, the STOMP "broker relay" automatically reconnects the "system" connection in case the broker becomes temporarily unavailable. Client connections however are not automatically reconnected. Assuming heartbeats are enabled, the client will typically notice the broker is not responding within 10 seconds. Clients need to implement their own reconnect logic.

当使用一个全功能的消息代理,STOMP的消息依赖自动连接system连接因此消息代理临时不可以用。客户端连接没有自动重连。假设心跳被允许,客户端通常意识到消息代理没有返回在10秒以内。客户端需要实现他自己的重连逻辑。

 

Furthermore, an application can directly intercept every incoming and outgoing message by registering a ChannelInterceptor on the respective message channel. For example to intercept inbound messages:

此外,一个应用可以直接打断输入和输出消息通过注册ChannelInterceptor在消息渠道中。例如打断收到的消息如下:

 

@Configuration

@EnableWebSocketMessageBroker

public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {

 

  @Override

  public void configureClientInboundChannel(ChannelRegistration registration) {

    registration.setInterceptors(new MyChannelInterceptor());

  }

}

 

A custom ChannelInterceptor can extend the empty method base class ChannelInterceptorAdapter and use StompHeaderAccessor or SimpMessageHeaderAccessor to access information about the message.

一个自定义的ChannelInterceptor可以继承空的方法基类ChannelInterceptorAdapter并使用StompHeaderAccessorSimpMessageHeaderAccessor来访问信息中的信息。

 

public class MyChannelInterceptor extends ChannelInterceptorAdapter {

 

  @Override

  public Message<?> preSend(Message<?> message, MessageChannel channel) {

    StompHeaderAccessor accessor = StompHeaderAccessor.wrap(message);

    StompCommand command = accessor.getStompCommand();

    // ...

    return message;

  }

}

 

26.4.13 STOMP Client

STOMP客户端

 

Spring provides a STOMP over WebSocket client and a STOMP over TCP client.

spring提供了STOMPWebSocket客户端之上和STOMPTCP客户端上。

 

To begin create and configure WebSocketStompClient:

开始创建和配置WebSocketStompClient

 

WebSocketClient webSocketClient = new StandardWebSocketClient();

WebSocketStompClient stompClient = new WebSocketStompClient(webSocketClient);

stompClient.setMessageConverter(new StringMessageConverter());

stompClient.setTaskScheduler(taskScheduler); // for heartbeats

 

In the above example StandardWebSocketClient could be replaced with SockJsClient since that is also an implementation of WebSocketClient. The SockJsClient can use WebSocket or HTTP-based transport as a fallback. For more details see Section 26.3.7,SockJS Client.

在上面的例子中StandardWebSocketClient可以被替代通过使用SockJsClient,并且也是一个WebSocketClient的实现。SockJsClient可以使用WebSocket或基于HTTP的连接作为回调。有关更多的信息见章节26.3.7,“SockJS客户端”。

 

Next establish a connection and provide a handler for the STOMP session:

下一步建立一个连接并且提供一个处理器对于STOMP的会话:

 

String url = "ws://127.0.0.1:8080/endpoint";

StompSessionHandler sessionHandler = new MyStompSessionHandler();

stompClient.connect(url, sessionHandler);

 

When the session is ready for use the handler is notified:

当会话已经准备好使用处理器:

 

public class MyStompSessionHandler extends StompSessionHandlerAdapter {

 

    @Override

    public void afterConnected(StompSession session, StompHeaders connectedHeaders) {

        // ...

    }

}

 

Once the session is established any payload can be sent and that will be serialized with the configured MessageConverter:

当会话被建立可以发送将被序列化通过已经配置的MessageConverter

 

session.send("/topic/foo", "payload");

 

You can also subscribe to destinations. The subscribe methods require a handler for messages on the subscription and return a Subscription handle that can be used to unsubscribe. For each received message the handler can specify the target Object type the payload should be deserialized to:

你也可以订阅destination。订阅方法要求一个消息的处理器并且返回Subscription处理可以被使用来取消订阅。对于每个收到的消息处理器可以定义模板object类型用于反序列化:

 

session.subscribe("/topic/foo", new StompFrameHandler() {

 

    @Override

    public Type getPayloadType(StompHeaders headers) {

        return String.class;

    }

 

    @Override

    public void handleFrame(StompHeaders headers, Object payload) {

        // ...

    }

 

});

 

To enable STOMP heartbeat configure WebSocketStompClient with a TaskScheduler and optionally customize the heartbeat intervals, 10 seconds for write inactivity which causes a heartbeat to be sent and 10 seconds for read inactivity which closes the connection.

为了保证STOMP的心跳配置WebSocketStompClient使用了TaskScheduler和自定义选择心跳间隔,10秒用于静止不发送心跳并且读取的时候不激活心跳当关闭连接的时候。

 

[Note]

注意

 

When using WebSocketStompClient for performance tests to simulate thousands of clients from the same machine consider turning off heartbeats since each connection schedules its own heartbeat tasks and thats not optimized for a a large number of clients running on the same machine.

当使用WebSocketStompClient用于性能测试来模仿上千个客户端来自相同的机器则考虑关闭心跳对于每个连接有自己的心跳任务并且对于大量的客户端运行在相同的机器上不是很好的方式。

 

The STOMP protocol also supports receipts where the client must add a "receipt" header to which the server responds with a RECEIPT frame after the send or subscribe are processed. To support this the StompSession offers setAutoReceipt(boolean) that causes a "receipt" header to be added on every subsequent send or subscribe. Alternatively you can also manually add a "receipt" header to the StompHeaders. Both send and subscribe return an instance of Receiptable that can be used to register for receipt success and failure callbacks. For this feature the client must be configured with a TaskScheduler and the amount of time before a receipt expires (15 seconds by default).

STOMP协议也支持收据当客户端必须添加receipt头信息对于服务器返回的RECEIPT帧在发送了订阅被处理之后。为了支持StompSession提供了setAutoReceipt(boolean)使得receipt头被添加对于每一个后续的订阅。作为替代你也可以手动添加receipt头对于StompHeaders。发送和订阅消息返回一个Receiptable的实例可以被使用对于注册和失败时的回调。由于这个特性客户端必须配置TaskScheduler和一定的时间在receipt过期之前(默认是15秒)。

 

Note that StompSessionHandler itself is a StompFrameHandler which allows it to handle ERROR frames in addition to the handleException callback for exceptions from the handling of messages, and handleTransportError for transport-level errors including ConnectionLostException.

注意StompSessionHandler本身是一个StompFrameHandler允许他来处理ERROR帧此外handleException回调函数对于来自消息的异常处理和handleTransportError用于传输级别的包含ConnectionLostException

 

26.4.14 WebSocket Scope

WebSocket范围

 

Each WebSocket session has a map of attributes. The map is attached as a header to inbound client messages and may be accessed from a controller method, for example:

每一个WebSocket会话有一个属性map。这个map作为一个头信息绑定客户端信息并且可以被访问来自于控制器方法,例如:

 

@Controller

public class MyController {

 

    @MessageMapping("/action")

    public void handle(SimpMessageHeaderAccessor headerAccessor) {

        Map<String, Object> attrs = headerAccessor.getSessionAttributes();

        // ...

    }

}

 

It is also possible to declare a Spring-managed bean in the websocket scope. WebSocket-scoped beans can be injected into controllers and any channel interceptors registered on the "clientInboundChannel". Those are typically singletons and live longer than any individual WebSocket session. Therefore you will need to use a scope proxy mode for WebSocket-scoped beans:

可以定义一个spring管理的beanWebSocket范围。WebSocket范围的bean可以被注入到控制器并且任何渠道拦截器注册在"clientInboundChannel"上。这些通常是单例的并且生存时间较长比其他独立的WebSocket会话。此外你需要使用一个scope属性用于WebSocket范围的bean

 

@Component

@Scope(scopeName = "websocket", proxyMode = ScopedProxyMode.TARGET_CLASS)

public class MyBean {

 

    @PostConstruct

    public void init() {

        // Invoked after dependencies injected

    }

 

    // ...

 

    @PreDestroy

    public void destroy() {

        // Invoked when the WebSocket session ends

    }

}

 

@Controller

public class MyController {

 

    private final MyBean myBean;

 

    @Autowired

    public MyController(MyBean myBean) {

        this.myBean = myBean;

    }

 

    @MessageMapping("/action")

    public void handle() {

        // this.myBean from the current WebSocket session

    }

}

 

As with any custom scope, Spring initializes a new MyBean instance the first time it is accessed from the controller and stores the instance in the WebSocket session attributes. The same instance is returned subsequently until the session ends. WebSocket-scoped beans will have all Spring lifecycle methods invoked as shown in the examples above.

对于自定义的范围,spring首先初始化一个新的MyBean实例并且接收来自控制器的访问并且存储实例在WebSocket的会话属性中。相同的实例被返回直到会话结束。WebSocket范围的bean将拥有整个spring的生命周期方法并且展示在上面的例子中。

 

26.4.15 Configuration and Performance

配置和性能

 

There is no silver bullet when it comes to performance. Many factors may affect it including the size of messages, the volume, whether application methods perform work that requires blocking, as well as external factors such as network speed and others. The goal of this section is to provide an overview of the available configuration options along with some thoughts on how to reason about scaling.

没有做好的配置性能的方法。许多因素会影响性能包括消息的大小、分卷、应用方法执行以及是否阻塞,作为外部因素例如网络速度等等。这一章节的目的是提供一个大概的可用的配置选项对于如何查找原因。

 

In a messaging application messages are passed through channels for asynchronous executions backed by thread pools. Configuring such an application requires good knowledge of the channels and the flow of messages. Therefore it is recommended to review Section 26.4.3, Flow of Messages.

在消息应用消息被传播通过异步执行的线程池渠道。配置这些应用需要对于channel和消息流有一定的知识要求。此外建议回顾章节26.4.3,“消息流”

 

The obvious place to start is to configure the thread pools backing the "clientInboundChannel" and the "clientOutboundChannel". By default both are configured at twice the number of available processors.

从最明显的位置开始配置线程池有关"clientInboundChannel""clientOutboundChannel"。默认的需要配置为可用处理器的两倍。

 

If the handling of messages in annotated methods is mainly CPU bound then the number of threads for the "clientInboundChannel" should remain close to the number of processors. If the work they do is more IO bound and requires blocking or waiting on a database or other external system then the thread pool size will need to be increased.

如果处理消息在声明的方法中主要有关CPU绑定和线程数用于"clientInboundChannel"应当接近处理器的数量。如果他们进行过多的IO操作并且要求等待数据库或其他外部的系统则线程池的大小需要被扩大。

 

[Note]

注意

 

ThreadPoolExecutor has 3 important properties. Those are the core and the max thread pool size as well as the capacity for the queue to store tasks for which there are no available threads.

ThreadPoolExecutor有三个重要的属性。他们是核心的并且最大线程池大小和队列用于存储任务而没有多余的线程的数目是一样的。

 

A common point of confusion is that configuring the core pool size (e.g. 10) and max pool size (e.g. 20) results in a thread pool with 10 to 20 threads. In fact if the capacity is left at its default value of Integer.MAX_VALUE then the thread pool will never increase beyond the core pool size since all additional tasks will be queued.

一个常见的困惑是配置核心池的大小(例如10)和最大池的大小(例如20)的结果是这个线程池中的线程数目是1020之间。此外如果容量有剩余,实际上如果他的值小于Integer.MAX_VALUE则线程将不会增多超过核心池的数目直到所有的任务都在队列中。

 

Please review the Javadoc of ThreadPoolExecutor to learn how these properties work and understand the various queuing strategies.

请回顾有关ThreadPoolExecutorJavadoc来了解这些属性是如何工作的和了解不同的队列策略。

 

On the "clientOutboundChannel" side it is all about sending messages to WebSocket clients. If clients are on a fast network then the number of threads should remain close to the number of available processors. If they are slow or on low bandwidth they will take longer to consume messages and put a burden on the thread pool. Therefore increasing the thread pool size will be necessary.

对于"clientOutboundChannel"方面有关发送消息给WebSocket客户端。如果客户端在一个快速的网络中则线程的数目应当接近可用的处理器的数目。如果他们在慢速的网络中则将花费更长的时间来消费消息并且对于线程池来说是一个负担。因此增加线程池的大小是有必要的。

 

While the workload for the "clientInboundChannel" is possible to predict — after all it is based on what the application does — how to configure the "clientOutboundChannel" is harder as it is based on factors beyond the control of the application. For this reason there are two additional properties related to the sending of messages. Those are the "sendTimeLimit" and the "sendBufferSizeLimit". Those are used to configure how long a send is allowed to take and how much data can be buffered when sending messages to a client.

当对于"clientInboundChannel"的工作量是可以预测的————他基于应用的处理————如何配置"clientOutboundChannel"是困难的由于他存在应用的影响因素。处于这些原因有两个属性依赖于消息的发送。他们是"sendTimeLimit""sendBufferSizeLimit"。他们被用于配置多长时间进行一个发送和客户端发送数据量的大小。

 

The general idea is that at any given time only a single thread may be used to send to a client. All additional messages meanwhile get buffered and you can use these properties to decide how long sending a message is allowed to take and how much data can be buffered in the mean time. Please review the Javadoc and documentation of the XML schema for this configuration for important additional details.

通常只有一个线程可以用于发送消息。所有其他的消息同时放在缓存中并且你可以使用这些属性来决定多久间隔发送一个消息和发送消息的数据量。请参考javadoc和文档有关xmlschema有关这些配置的细节内容。

 

Here is example configuration:

这里有一些样例的配置:

 

@Configuration

@EnableWebSocketMessageBroker

public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {

 

    @Override

    public void configureWebSocketTransport(WebSocketTransportRegistration registration) {

        registration.setSendTimeLimit(15 * 1000).setSendBufferSizeLimit(512 * 1024);

    }

 

    // ...

 

}

 

<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:message-broker>

        <websocket:transport send-timeout="15000" send-buffer-size="524288" />

        <!-- ... -->

    </websocket:message-broker>

 

</beans>

 

The WebSocket transport configuration shown above can also be used to configure the maximum allowed size for incoming STOMP messages. Although in theory a WebSocket message can be almost unlimited in size, in practice WebSocket servers impose limits — for example, 8K on Tomcat and 64K on Jetty. For this reason STOMP clients such as stomp.js split larger STOMP messages at 16K boundaries and send them as multiple WebSocket messages thus requiring the server to buffer and re-assemble.

WebSocket传输配置展示如上也可以被使用来配置最大的值来接收STOMP消息。尽管在WebSocket信息中可以不限制大小,在一些WebSocket的服务器存在现在————例如,在Tomcat上是8k并且在Jetty上是64k。出于这些原因STOMP客户端例如stomp.js切分大的STOMP消息为16k并且发送通过多个WebSocket消息来请求服务器缓存和处理。

 

Springs STOMP over WebSocket support does this so applications can configure the maximum size for STOMP messages irrespective of WebSocket server specific message sizes. Do keep in mind that the WebSocket message size will be automatically adjusted if necessary to ensure they can carry 16K WebSocket messages at a minimum.

springSTOMPWebSocket支持使得应用可以配置STOMP消息的最大值而不在考虑WebSocket服务器特定的消息大小。记住WebSocket的消息大小将被自动适应调整为最小包含16k大小的消息。

 

Here is example configuration:

这是一个样例的配置:

 

@Configuration

@EnableWebSocketMessageBroker

public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {

 

    @Override

    public void configureWebSocketTransport(WebSocketTransportRegistration registration) {

        registration.setMessageSizeLimit(128 * 1024);

    }

 

    // ...

 

}

 

<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:message-broker>

        <websocket:transport message-size="131072" />

        <!-- ... -->

    </websocket:message-broker>

 

</beans>

 

An important point about scaling is using multiple application instances. Currently it is not possible to do that with the simple broker. However when using a full-featured broker such as RabbitMQ, each application instance connects to the broker and messages broadcast from one application instance can be broadcast through the broker to WebSocket clients connected through any other application instances.

有关使用多个应用实例的重点是。当前不能适用于简单的消息代理。然而当使用一个全功能的消息代理例如RabbitMQ,每个应用实例连接消息代理并且消息广播来自一个应用的实例可以通过WebSocket客户端的消息代理来处理连接其他的应用实例。

 

26.4.16 Runtime Monitoring

运行时监控

 

When using @EnableWebSocketMessageBroker or <websocket:message-broker> key infrastructure components automatically gather stats and counters that provide important insight into the internal state of the application. The configuration also declares a bean of type WebSocketMessageBrokerStats that gathers all available information in one place and by default logs it at INFO level once every 30 minutes. This bean can be exported to JMX through Springs MBeanExporter for viewing at runtime, for example through JDKs jconsole. Below is a summary of the available information.

当使用@EnableWebSocketMessageBroker<websocket:message-broker>关键基础设施自动抓取状态和提供重要的依据对于应用的内部状态。配置也可以定义WebSocketMessageBrokerStats类型的bean来收集所有可用的消息并且默认记录为INFO级别每隔30分钟。这个bean可以被暴露给JMX通过springMBeanExporter用于运行时,例如通过JDKjconsole。下面是一个可用信息的总结。

 

Client WebSocket Sessions

客户端WebSocket会话

 

    Current

当前

        indicates how many client sessions there are currently with the count further broken down by WebSocket vs HTTP streaming and polling SockJS sessions.

指示当前有多少客户端会话被关闭通过WebSocketHTTP流和SockJS会话。

    Total

总共

        indicates how many total sessions have been established.

指示有多少会话被建立。

    Abnormally Closed

非正常关闭

 

        Connect Failures

连接失败

            these are sessions that got established but were closed after not having received any messages within 60 seconds. This is usually an indication of proxy or network issues.

这些会话已经被连接但是在60秒内没有收到消息后被关闭了。这通常暗示了网络或代理方面的问题。

        Send Limit Exceeded

发送限制

            sessions closed after exceeding the configured send timeout or the send buffer limits which can occur with slow clients (see previous section).

会话被关闭超过了发送时长或发送消息大小限制可以发生在客户端(见之前的章节)

        Transport Errors

传输错误

            sessions closed after a transport error such as failure to read or write to a WebSocket connection or HTTP request/response.

会话被关闭由于传输错误例如未能读取或写入一个WebSocket连接或HTTP请求或响应。

 

    STOMP Frames

STOMP

        the total number of CONNECT, CONNECTED, and DISCONNECT frames processed indicating how many clients connected on the STOMP level. Note that the DISCONNECT count may be lower when sessions get closed abnormally or when clients close without sending a DISCONNECT frame.

CONNECTCONNECTEDDISCONNECT帧的总数目指示有多少客户端连接在STOMP级别。注意DISCONNECT技术可能较少当会话非正常关闭或客户端关闭但是没有发送DISCONNECT的帧。

 

STOMP Broker Relay

STOMP消息代理回复

 

    TCP Connections

TCP连接数

        indicates how many TCP connections on behalf of client WebSocket sessions are established to the broker. This should be equal to the number of client WebSocket sessions + 1 additional shared "system" connection for sending messages from within the application.

指示有多少TCP连接用于客户端的WebSocket会话被建立对于消息代理。这和客户端WebSocket会话数加一相同此外共享system连接用于发送消息也包括在应用中。

    STOMP Frames

STOMP

        the total number of CONNECT, CONNECTED, and DISCONNECT frames forwarded to or received from the broker on behalf of clients. Note that a DISCONNECT frame is sent to the broker regardless of how the client WebSocket session was closed. Therefore a lower DISCONNECT frame count is an indication that the broker is pro-actively closing connections, may be because of a heartbeat that didnt arrive in time, an invalid input frame, or other.

CONNECTCONNECTEDDISCONNECT帧的总数有关发送或接收来自消息代理。注意DISCONNECT的帧作为客户端WebSocket会话被关闭时的数据。此外一个较低的DISCONNECT帧指示了消息代理提前关闭了连接,可能是由于心跳、一个独立的帧或等等其他的原因。

 

Client Inbound Channel

客户端输入channel

    stats from thread pool backing the "clientInboundChannel" providing insight into the health of incoming message processing. Tasks queueing up here is an indication the application may be too slow to handle messages. If there I/O bound tasks (e.g. slow database query, HTTP request to 3rd party REST API, etc) consider increasing the thread pool size.

来自线程池的状态指示"clientInboundChannel"提供有关输入消息的处理程度。任务队列指示应用可能对于消息的处理较慢。如果是IO型的任务(例如慢数据库查询、HTTP请求对于第三方的RESTAPI等等)则应该考虑增加线程池的大小。

Client Outbound Channel

客户端输出channel

    stats from the thread pool backing the "clientOutboundChannel" providing insight into the health of broadcasting messages to clients. Tasks queueing up here is an indication clients are too slow to consume messages. One way to address this is to increase the thread pool size to accommodate the number of concurrent slow clients expected. Another option is to reduce the send timeout and send buffer size limits (see the previous section).

来自线程池的状态指示clientOutboundChannel提供有关给客户端的广播信息。任务队列指示客户端消费消息的速度过于缓慢。一种处理方式是增加线程池的数据来调整慢客户端的并发。另一种方法是减少发送延迟和发送缓存大小限制(见之前的章节内容)。

SockJS Task Scheduler

SockJS的任务计划

    stats from thread pool of the SockJS task scheduler which is used to send heartbeats. Note that when heartbeats are negotiated on the STOMP level the SockJS heartbeats are disabled.

来自SockJS任务计划线程池的状态用于发送心跳。注意当心跳被忽略在STOMP的级别则SockJS的心跳被取消。

26.4.17 Testing Annotated Controller Methods

测试注解控制器方法

 

There are two main approaches to testing applications using Springs STOMP over WebSocket support. The first is to write server-side tests verifying the functionality of controllers and their annotated message handling methods. The second is to write full end-to-end tests that involve running a client and a server.

这里有两个主要的方法来测试应用使用springSTOMPWebSocket的支持之上。首先书写服务端的测试来验证控制器的功能和他们注解的消息处理方法。其次是书写点到点的测试来调用客户端和服务器。

 

The two approaches are not mutually exclusive. On the contrary each has a place in an overall test strategy. Server-side tests are more focused and easier to write and maintain. End-to-end integration tests on the other hand are more complete and test much more, but theyre also more involved to write and maintain.

这两种方式不是互相排斥的。正好相反每一种都有综合的测试策略。服务端的测试更加关注于写入和保持。点到点的集成测试在另一方面比较复杂并且会测试更多但是他们会调用更多的写和维持。

 

The simplest form of server-side tests is to write controller unit tests. However this is not useful enough since much of what a controller does depends on its annotations. Pure unit tests simply cant test that.

最简单的形式有关服务端测试是书写单元控制器测试。然而这不是有用的因为控制器通常依赖他的注解。单纯的单元测试是无法完成的。

 

Ideally controllers under test should be invoked as they are at runtime, much like the approach to testing controllers handling HTTP requests using the Spring MVC Test framework. i.e. without running a Servlet container but relying on the Spring Framework to invoke the annotated controllers. Just like with Spring MVC Test here there are two two possible alternatives, either using a "context-based" or "standalone" setup:

测试中的控制器应当被调用像他们运行时那样,用于测试控制器的方法处理HTTP请求使用springmvc框架,例如不需要运行一个Servlet容器但是依赖于spring的框架来调用注解控制器。就像springmvc的测试她们有两种可选的方式,使用"context-based""standalone"的设置。

 

    Load the actual Spring configuration with the help of the Spring TestContext framework, inject "clientInboundChannel" as a test field, and use it to send messages to be handled by controller methods.

加载实际的spring配置可以借助springTestContext的框架,注入"clientInboundChannel"作为一个测试域并且使用它来发送应当被控制器方法来处理的消息。

    Manually set up the minimum Spring framework infrastructure required to invoke controllers (namely the SimpAnnotationMethodMessageHandler) and pass messages for controllers directly to it.

手动设置最小的spring框架基础设施要求调用控制器(名字是SimpAnnotationMethodMessageHandler)并且直接发送消息给控制器。

 

Both of these setup scenarios are demonstrated in the tests for the stock portfolio sample application.

这些设置场景用于测试股票投资组合的简单应用。

 

The second approach is to create end-to-end integration tests. For that you will need to run a WebSocket server in embedded mode and connect to it as a WebSocket client sending WebSocket messages containing STOMP frames. The tests for the stock portfolio sample application also demonstrates this approach using Tomcat as the embedded WebSocket server and a simple STOMP client for test purposes.

第二种方法是创建点到点的集成测试。你将需要运行一个WebSocket服务器以集成的方式并且连接作为一个WebSocket客户端来发送WebSocket消息包含STOMP帧。测试用于股票投资组合的简单应用也可以使用Tomcat作为内置的WebSocket服务器和一个简单的STOMP客户端用于测试的目的。

 

 

阅读全文
0 0