关注公众号:锅外的大佬

每日推送国外优秀的技术翻译文章,励志帮助国内的开发者更好地成长!

WebSocket协议是应用程序处理实时消息的方法之一。最常见的替代方案是长轮询(long polling)和服务器推送事件(server-sent events)。这些解决方案中的每个都有其优缺点。在本文中,我将向您展示如何使用Spring Boot实现WebSocket。我将介绍服务器端和客户端设置,使用WebSocket协议之上的STOMP进行相互通信。

服务器端将完全用Java编码。但是,就客户端而言,我将展示用JavaJavaScript(SockJS)编写的片段,因为通常,WebSocket客户端嵌入在前端应用程序中。代码示例将演示如何使用pub-sub模型向多个用户广播消息以及如何仅向单个用户发送消息。在本文的另一部分中,我将简要讨论WebSocket安全问题以及如何确保即使环境不支持WebSocket协议,基于WebSocket的解决方案也能运行。

注意,WebSocket安全话题仅在此处简要介绍,因为这是一个非常复杂的问题,可以单独撰写一篇文章。由于这个原因,以及我在文章最后一节WebSocket in production?中提及的因素,我建议在生产中先对安全设置进行修改,直到生产就绪,安全措施到位为止。

1.WebSocket和STOMP协议

WebSocket协议允许应用程序之间实现双向通信。重要的是要知道HTTP仅用于初始握手。初次握手之后,HTTP连接将升级为被WebSocket使用的新TCP/IP连接。

WebSocket协议是一种相当低级的协议。它定义了如何将字节流转换为帧。帧可以包含文本或二进制消息。由于消息本身不提供有关如何路由或处理它的任何其他信息,因此很难在不编写其他代码的情况下实现更复杂的应用程序。幸运的是,WebSocket规范允许在更高的应用程序级别上使用子协议。STOMP是其中之一,由Spring Framework支持。

STOMP是一种简单的基于文本的消息传递协议,最初是为RubyPythonPerl等脚本语言创建的,用于连接企业级消息代理。由于STOMP,使不同语言开发的客户端和代理可以相互发送和接收消息。WebSocket协议有时称为Web TCP。以此类推,STOMP被称为Web HTTP。它定义了一些映射到WebSocket帧的帧类型,例如CONNECTSUBSCRIBEUNSUBSCRIBEACKSEND。一方面,这些命令非常便于管理通信,另一方面,它们允许我们实现具有更复杂功能的解决方案,如消息确认。

2.服务端:Spring Boot和WebSocket

为了构建WebSocket服务器端,我们将利用Spring Boot框架,该框架使得在Java中开发独立程序和Web应用程序更快。 Spring Boot包含spring-WebSocket模块,该模块与Java WebSocket API标准(JSR-356)兼容。

使用Spring Boot实现WebSocket服务器端并不是一项非常复杂的任务,只包含几个步骤,我们将逐一介绍。

*步骤1:*首先,添加WebSocket库依赖项。

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>

如果计划使用JSON格式传输消息,则可能还需要包含GSONJackson依赖项。您还可能还需要一个安全框架,例如Spring Security

*步骤2:*然后,可以配置Spring启用WebSocketSTOMP消息传递。

Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer { @Override
public void registerStompEndpoints(StompEndpointRegistry
registry) {
registry.addEndpoint("/mywebsockets")
.setAllowedOrigins("mydomain.com").withSockJS();
} @Override
public void configureMessageBroker(MessageBrokerRegistry config){
config.enableSimpleBroker("/topic/", "/queue/");
config.setApplicationDestinationPrefixes("/app");
}
}

configureMessageBroker主要做两件事情:

  • 创建内存中的消息代理,其中包含一个或多个用于发送和接收消息的目标。在上面的示例中,定义了两个目标地址前缀:topicqueue。它们遵循以下惯例:通过pub-sub模型将以topic为前缀的消息传递到所有订阅客户端的目标地址。另一方面,私有消息的目标地址通常以queue为前缀。
  • 定义前缀app,用于过滤目标地址,这些地址在Controller中被@MessageMapping修饰的方法处理。

image

服务器端如何处理消息

回到上面的代码段 - 可能你已经注意到对方法withSockJS()的调用——它启用了SockJS后备选项。简而言之,即使互联网浏览器不支持WebSocket协议,它也会让我们的WebSockets工作。我将进一步详细讨论这个主题。

还有一件事需要澄清——为什么我们在端点上调用setAllowedOrigins()方法。一般是必需的,因为WebSocketSockJS的默认行为是仅接受同源请求。因此,如果客户端和服务端处于不同的域,则需要调用此方法允许它们之间的通信。

*步骤3:*实现处理用户请求的控制器
它将向订阅特定主题的所有用户广播收到的消息。

这是一个将消息发送到目标地址/topic/news的示例方法。

@MessageMapping("/news")
@SendTo("/topic/news")
public void broadcastNews(@Payload String message) {
return message;
}

也可以使用SimpMessagingTemplate而不是注解@SendTo,您可以在控制器内自动装配(Autowired)。

@MessageMapping("/news")
public void broadcastNews(@Payload String message) {
this.simpMessagingTemplate.convertAndSend("/topic/news", message)
}

在后面的步骤中,可能需要添加一些其他类来保护端点,例如Spring Security框架中的ResourceServerConfigurerAdapterWebSecurityConfigurerAdapter。此外,实现消息模型通常是有益的,这样传输的JSON可以映射成对象。

3.WebSocket客户端构建

客户端实现是一项更简单的任务。

*步骤1:*装配Spring STOMP客户端

@Autowired
private WebSocketStompClient stompClient;

步骤2: 打开连接

StompSessionHandler sessionHandler = new CustmStompSessionHandler();

StompSession stompSession = stompClient.connect(loggerServerQueueUrl,
sessionHandler).get();

此操作完成后,可以将消息发送到目的地。该消息将发送给所有订阅主题的用户。

stompSession.send("topic/greetings", "Hello new user");

以下方法也可以订阅消息

session.subscribe("topic/greetings", this);

@Override
public void handleFrame(StompHeaders headers, Object payload) {
Message msg = (Message) payload;
logger.info("Received : " + msg.getText()+ " from : " +
msg.getFrom());
}

有时需要向特定用户发送消息(例如,在实现聊天时)。然后,客户端和服务器端必须使用专用于此私人会话的单独目标地址。可以通过将唯一标识符附加到通用地址来创建目标地址的名称,例如/queue/chat-user123HTTP会话或STOMP会话标识符可用于此目的。

Spring使发送私人消息变得更加容易。我们只需要使用@SendToUser注释Controller的方法。然后,目标地址将由UserDestinationMessageHandler处理,它依赖于会话标识符。在客户端,当客户端订阅以/user为前缀的目标地址时,此目标地址将转换为此用户唯一的目标地址。在服务器端,根据用户的Principal解析用户目标地址。

服务器端使用@SendToUser注解示例代码:

@MessageMapping("/greetings")
@SendToUser("/queue/greetings")
public String reply(@Payload String message,
Principal user) {
return "Hello " + message;
}

或者可以使用SimpMessagingTemplate

String username = ...
this.simpMessagingTemplate.convertAndSendToUser();
username, "/queue/greetings", "Hello " + username);

现在让我们看看如何实现一个能够接收私有消息的JavaScript(SockJS)客户端,该客户端可以接收上面的示例中的Java代码发送的消息。值得一提的是,WebSocketsHTML5规范的一部分,并且受到大多数现代浏览器的支持(从版本10开始,Internet Explorer支持它们)。

function connect() {
var socket = new SockJS('/greetings');
stompClient = Stomp.over(socket);
stompClient.connect({}, function (frame) {
stompClient.subscribe('/user/queue/greetings', function (greeting) {
showGreeting(JSON.parse(greeting.body).name);
});
});
} function sendName() {
stompClient.send("/app/greetings", {}, $("#name").val());
}

您可能已经注意到,要接收私人消息,客户端需要订阅前缀为/user的目标地址/queue/greetings。它不必担心任何唯一标识符。但是,在客户端登录应用程序之前,服务器端必须初始化Principal对象。

4.WebSocket安全

许多Web应用程序使用基于cookie的身份验证,例如,我们可以使用Spring Security限制已登录的用户访问某些页面或控制器限制。然后,通过基于cookie的HTTP会话维护用户上下文安全,该会话稍后与为该用户创建的WebSocketSockJS会话相关联。 WebSocket端点可以像任何其他请求一样受到保护,例如,在SpringWebSecurityConfigurerAdapter中的实现。

如今,Web应用程序通常使用REST API作为后端,使用OAuth/JWT令牌进行用户身份验证和授权。 WebSocket协议未描述服务器在HTTP握手期间如何对客户端进行身份验证。实际上,标准HTTP头(例如,授权)用于此目的。不幸的是,并非所有STOMP客户端都支持它。 SpringSTOMP客户端允许为握手设置标头:

WebSocketHttpHeaders handshakeHeaders = new WebSocketHttpHeaders();
handshakeHeaders.add(principalRequestHeader, principalRequestValue);

但是SockJS的JavaScript客户端不支持使用SockJS请求发送授权请求头(Authorization)。但是,它允许发送可用于传递令牌的查询参数。此方法需要在服务器端编写自定义代码,该代码将从查询参数中读取令牌并对其进行验证。特别重要的是确保令牌不与请求一起记录(或日志受到良好保护),因为这可能会导致严重的安全违规。

5.SockJS后备选项

WebSocket的集成可能并不总是尽如人意。某些浏览器(例如,IE 9)不支持WebSocket。更重要的是,限制性代理可能使HTTP升级变得不可能,或者它们切断了打开太久的连接。在这种情况下,SockJS就会伸出援手。

SockJS传输分为三大类:WebSocketHTTP StreamingHTTP Long Polling。通信从SockJS发送GET /info以从服务器获取基本信息开始。SockJS根据响应决定使用的哪种传输方式。第一个选择是WebSocket。如果不支持,则尽可能使用Streaming。如果Streaming也不可用,则选择轮询作为传输方法。

6.生产中使用WebSocket

虽然这种设置有效,但它并不是“最佳”。Spring Boot允许您使用任何具有STOMP协议的完整消息系统(例如,ActiveMQ,RabbitMQ),并且外部代理可以支持更多STOMP操作(例如,确认,租借)而不是我们使用的简单代理。 STOMP Over WebSocket提供有关WebSocketSTOMP协议的信息。它列出了处理STOMP协议的消息传递系统,可能是在生产中使用的更好的解决方案。特别是由于请求数量很大,消息代理需要进行集群(Spring的简单消息代理不适合集群)。然后,不需要在WebSocketConfig中启用简单代理,而是需要启用Stomp代理中继,该中继将消息转发到外部消息代理和从外部消息代理转发消息。总而言之,外部消息代理可以帮助您构建更具伸缩性和可靠性的解决方案。

作者:Tomasz Dąbrowski

译者:Emma

Spring Boot实现STOMP协议的WebSocket的更多相关文章

  1. Spring Boot开发HTTPS协议的REST接口

    Spring Boot开发HTTP的REST接口流程在前文中已经描述过,见<SpringBoot开发REST接口>. 如需要支持HTTPS,只需要在如上基础上进行设置.修改/resourc ...

  2. spring boot websocket stomp 实现广播通信和一对一通信聊天

    一.前言 玩.net的时候,在asp.net下有一个叫 SignalR 的框架,可以在ASP .NET的Web项目中实现实时通信.刚接触java寻找相关替代品,发现 java 体系中有一套基于stom ...

  3. spring+rabbitmq+stomp搭建websocket消息推送(非spring boot方式)

    前言: 两年前做过spring+activemq+stomp的ws推送,那个做起来很简单,但现在公司用的mq中间件是rabbitmq,因此需要通过rabbitmq去做ws通信.仔细搜了搜百度/谷歌,网 ...

  4. Spring Boot WebSocket从入门到放弃

    在构建Spring boot项目时已经提供webSocket依赖的勾选.webSocket是TCP之上的一个非常薄的轻量级层 ,webSocket主要的应用场景离不开即时通讯与消息推送,但只要应用程序 ...

  5. springboot+websocket+sockjs进行消息推送【基于STOMP协议】

    springboot+websocket+sockjs进行消息推送[基于STOMP协议] WebSocket是在HTML5基础上单个TCP连接上进行全双工通讯的协议,只要浏览器和服务器进行一次握手,就 ...

  6. 在spring boot中使用webSocket组件(一)

    最近在项目中使用到了spring的webSocket组件,在这里和大家分享下,如有错误,欢迎大家指正. 在这里我使用的IDE工具是Intellij idea,框架是spring boot.spring ...

  7. Spring Boot -- 认识Spring Boot

    在前面我们已经学习过Srping MVC框架,我们需要配置web.xml.spring mvc配置文件,tomcat,是不是感觉配置较为繁琐.那我们今天不妨来试试使用Spring Boot,Sprin ...

  8. Nginx 使用自签名证书实现 https 反代 Spring Boot 中碰到的页面跳转问题

    问题一:页面自动跳转到 80 端口 问题描述 最近在使用Nginx反代一个Spring Boot项目中碰到了一个问题,使用 Spring Boot 中的 redirect: 进行页面跳转的时候,通过 ...

  9. 在Spring Boot框架下使用WebSocket实现消息推送

    Spring Boot的学习持续进行中.前面两篇博客我们介绍了如何使用Spring Boot容器搭建Web项目(使用Spring Boot开发Web项目)以及怎样为我们的Project添加HTTPS的 ...

随机推荐

  1. JavaScript阻止冒泡和取消事件默认行为

    //功能:停止事件冒泡 function stopBubble(e) { if ( e && e.stopPropagation ) { e.stopPropagation(); } ...

  2. 刷题总结——解方程(NOIP2014)

    题目: 题目描述 已知多项式方程: a0+a1x+a2x2+…+anxn=0 求这个方程在[1,m]内的整数解(n 和 m 均为正整数). 输入格式 输入共 n+2 行. 第一行包含 2 个整数 n. ...

  3. xp局域网共享访问没权限处理

    参考: http://www.360doc.com/content/11/0114/00/905007_86380468.shtml

  4. dubbo面试题之dubbo-client 和dubbo-server同时配置超时时间,是以哪个为准;

    根据官网表示: 以 timeout 为例,下图显示了配置的查找顺序,其它 retries, loadbalance, actives 等类似: 方法级优先,接口级次之,全局配置再次之. 如果级别一样, ...

  5. 解决vue项目route使用history模式,tomcat部署刷新url 404问题

    在webapps/项目名 创建WEB-INF ,创建web.xml文件 文件内容如下: <?xml version="1.0" encoding="UTF-8&qu ...

  6. Introduction to the WinPcap Networking Libraries

    Introduction to the WinPcap Networking Libraries use wire shark or fiddler

  7. 在 NetBeans 中开发一般 Java 应用程序时配置 Allatori 进行代码混淆

    要在 NetBeans 中开发一般 Java 应用程序时利用 Allatori 进行代码混淆,设置比 IntelliJ IDEA 稍微简单一点,首先在 NetBeans 项目所在硬盘目录内创建一个名为 ...

  8. win10 升级导致找不到SQL Server配置管理器

    1.背景 SQL Server配置管理器可用来管理与SQL Server相关联的服务.配置SQL Server使用的网络协议以及从SQL Server客户端计算机管理网络连接配置.但是win10从17 ...

  9. Babel6.x的安装过程

    1.首先安装babel-cli(用于在终端使用babel) npm install -g babel-cli 2.然后安装babel-preset-es2015插件 npm install --sav ...

  10. 2016-2017 ACM-ICPC Southeastern European Regional Programming Contest (SEERC 2016)

    题目链接  Codefores_Gym_101164 Solved  6/11 Penalty Problem A Problem B Problem C Problem D Problem E Pr ...