Spring Chapter4 WebSocket 胡乱翻译 (一)

4. WebSocket

包含了Servlet stack,原生WebSocket交互,通过SockJS模拟,并且通过STOMP在WebSocket之上订阅、发布消息。

4.1 简介

不扯了,看到这个地方的都已经知道WebSocket是干啥的,用在啥地方。

4.2 WebSocket API

Spring框架提供了WebSocket的API,可以用来写处理WebSocket消息的client和server的程序。

4.2.1 WebSocketHandler

创建一个WebSocket服务器很简单,实现WebSockHandler接口就完事了。或者更具体点,继承TextWebSocketHandler或者BinaryWebSocketHandler:

import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.TextMessage;
public class MyHandler extends TextWebSocketHandler {   @Override   public void handleTextMessage(WebSocketSession session, TextMessage message) {   // ...   } }

 通过下面的java代码将上面的Handler配置到具体的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();   } }

 

在SpringMVC应用里,上面的配置可以可以包含在DispatcherServlet的配置中。然而,Spring WebSocket可以独立于SpringMVC。通过WebSocketHttpRequestHandler可以把WebSocketHandler集成到其他的HTTP服务环境中。

4.2.2 WebSocket握手

自定义初始HTTP WebSocket握手请求的最简单的方法是通过HandShakeInterceptor,它提供了“before”和“after”握手方法。这些拦截器可以用来阻止握手或者让WebSocketSession得到一下属性。例如,有一个内置的拦截器,可以用来向WebSocket的session传递HTTP的session的属性。

@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {
  @Override   
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {     registry.addHandler(new MyHandler(), "/myHandler")     .addInterceptors(new HttpSessionHandshakeInterceptor());   } }

 

更高级的选项是继承DefaultHandshakeHandler,它执行WebSocket握手步骤,包括验证客户端来源,协商子协议等。如果应用程序需要配置自定义RequestUpgradeStrategy以适应WebSocket服务器引擎和尚不支持的版本,则应用程序可能还需要使用此选项(有关此主题的更多信息,请参阅部署)。 

 4.2.3 部署

Spring WebSocket API易于集成到Spring MVC应用程序中,其中DispatcherServlet同时提供HTTP WebSocket握手以及其他HTTP请求。 通过调用WebSocketHttpRequestHandler,也可以轻松地集成到其他HTTP处理场景中。 这很方便易懂。 但是,JSR-356 runtimes有些特殊注意事项。

Java WebSocket API(JSR-356)提供了两种部署机制。

  • 第一个涉及启动时的Servlet容器classpath扫描(Servlet 3功能);
  • 另一个是在Servlet容器初始化时使用的注册API。

这些机制都不能使用单个“前端控制器”进行所有HTTP处理 - 包括WebSocket握手和所有其他HTTP请求 - 例如Spring MVC的DispatcherServlet。

这是JSR-356的一个重要限制,Spring的WebSocket支持即使在JSR-356运行时运行时也能解决特定于服务器的RequestUpgradeStrategy问题。 Tomcat,Jetty,GlassFish,WebLogic,WebSphere和Undertow(以及WildFly)目前存在这样的策略。 (这段没看太明白,WEBSOCKET_SPEC-211可以解决这个问题)

第二个考虑因素是具有JSR-356支持的Servlet容器应该执行ServletContainerInitializer(SCI)扫描,这可能会减慢应用程序启动速度,在某些情况下会显着降低。 如果在升级到支持JSR-356的Servlet容器版本后观察到重大影响,则应该可以通过在web.xml中使用<absolute-ordering />元素来选择性地启用或禁用Web片段(和SCI扫描).

4.2.4 Server配置

每个底层WebSocket引擎都公开控制运行时特性的配置属性,例如消息缓冲区大小,空闲超时等。

对于Tomcat,WildFly和GlassFish,将ServletServerContainerFactoryBean添加到WebSocket Java配置中:

@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {

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

 

对于client端,使用ContainerProvider.getWebSocketContainer()来配置。(XML中用WebSocketContainerFactoryBean)

4.2.5 Allowed origins

hmmm,暂时没啥用,用的时候网上一搜一大堆~~

4.3 SockJS方案

模拟器,在原生WebSocket API不能用的情况下,通过SockJS继续实现功能。

4.3.1 概要

就那回事吧,不写了。

4.3.2 启用SockJS

很简单,通过java代码即可:

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

 

在浏览器里,程序可以使用sockjs-client来搞事情。

4.3.3 IE8,9

跳过这节,现在用Ubuntu呢:)

4.3.4 心跳

SockJS需要服务器发送心跳信号来防止连接挂掉。Spring SockJS配置有个属性叫做heartbeatTime,可以用来自定义心跳频率。默认在没有通讯25秒之后跳。

Spring SockJS也支持配置一个TaskScheduler来调度心跳任务。

4.3.5 Client disconnects

客户端断开了,但是服务器不知道。得注意。

4.3.6 SockJS和CORS

这个暂时也用不上。

4.3.7 SockJS Client

可以提供测试。一个Java的SockJS的Client。

4.4 STOMP

WebSocket只定义了2种消息:Test和Binary,但是没定义它们的内容。这是和STOMP出现了,定义了消息的种类,格式和内容。

4.4.1 概述

客户端可以使用SEND或SUBSCRIBE命令发送或订阅消息以及“目标”标头,该标头描述消息的内容以及应由谁接收消息。这启用了一个简单的发布 - 订阅机制,可用于通过代理将消息发送到其他连接的客户端,或者向服务器发送消息以请求执行某些工作。

使用Spring的STOMP支持时,Spring WebSocket应用程序充当客户端的STOMP代理。消息被路由到@Controller消息处理方法或者路由到一个简单的内存代理,该代理跟踪订阅并向订阅用户广播消息。您还可以将Spring配置为使用专用的STOMP代理(例如RabbitMQ,ActiveMQ等)来实现消息的实际广播。在这种情况下,Spring维护与代理的TCP连接,向其中继消息,并将消息从其传递到连接的WebSocket客户端。因此,Spring Web应用程序可以依赖于基于HTTP的统一安全性,通用验证以及熟悉的编程模型消息处理工作。

这是订阅接收股票报价的客户的示例,服务器可以周期性地发出该报价。通过计划任务通过SimpMessagingTemplate将消息发送到代理:

SUBSCRIBE
id:sub-1
destination:/topic/price.stock.*
^@

 

 以下是客户端发送交易请求的示例,服务器可以通过@MessageMapping方法处理该交易请求,稍后在执行后,向客户端广播交易确认消息和详细信息:

SEND
destination:/queue/trade
content-type:application/json
content-length:44

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

在STOMP规范中故意将目的地的含义保持不透明。 它可以是任何字符串,完全取决于STOMP服务器,以定义它们支持的目标语义和语法。 然而,很常见的是,目标是类似路径的字符串,其中“/ topic / ..”表示发布 - 订阅(一对多),“/ queue /”表示点对点(一对一) -one)消息交换。

使用MESSAGE来广播消息到所有客户端:

MESSAGE
message-id:nxahklf6-1
subscription:sub-1
destination:/topic/price.stock.MMM

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

 

知道服务器无法发送未经请求的消息非常重要。 来自服务器的所有消息必须是特定的客户端订阅的响应,并且服务器消息的“subscription-id”头必须与客户端订阅的“id”头匹配。 以上概述旨在提供对STOMP协议的最基本的了解。 建议完整地查看协议规范。

4.4.2 好处

不用多说了,不用再造轮子了,直接开干。

4.4.3 启用STMOP

spring-messaging和spring-websocket模块里有STOMP的支持。你可以使用STOMP的endpoint:

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"); //③   } }

 

客户端,可以使用JSteunou/webstomp-client:

var socket = new SockJS("/spring-websocket-portfolio/portfolio");
var stompClient = webstomp.over(socket);
stompClient.connect({},
function(frame) { }

 

或者通过WebSocket(没有SockJS)

var socket = new WebSocket("/spring-websocket-portfolio/portfolio");
var stompClient = Stomp.over(socket);

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

 

posted on 2018-09-27 21:22  zhsmtp  阅读(378)  评论(0编辑  收藏  举报