Spring Boot+STOMP解决消息乱序问题
当我们使用Spring Boot+websocket进行前后端进行通信时,我们需要注意:服务器可以随时向客户端发送消息。默认的情况下,不保证:服务器发送的消息与到达客户端的消息的顺序是一致的。可能先发送的消息后到,后发送的消息先到。(注意:两个消息发送的时间差不多,不能相差太多,不然就是顺序的了。一般一秒以下都会造成乱序)。
如果你的需求需要服务器向客户端发送的消息是按照顺序的。请你往下看,如果你的需求不要求服务器向客户端发送的消息是顺序的。就没有必要看了。
我们先说以下造成的原因:就是websocket发送消息采用的是多线程发送消息的,造成有的先到的消息在一个线程中在阻塞,而后到的消息使用其他的线程发送出去了。就造成了乱序。
然后我们再来看一个实例,自己去体验一把最好不过了。
一、我们先导入websocket包。
<!--这个是websocket通信的JAR文件-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
<dependency>
<groupId>org.webjars</groupId>
<artifactId>webjars-locator-core</artifactId>
</dependency>
<!--这个是客户端连接服务器需要的文件-->
<dependency>
<groupId>org.webjars</groupId>
<artifactId>sockjs-client</artifactId>
<version>1.0.2</version>
</dependency>
<dependency>
<groupId>org.webjars</groupId>
<artifactId>stomp-websocket</artifactId>
<version>2.3.3</version>
</dependency>
<!--页面布局的文件-->
<dependency>
<groupId>org.webjars</groupId>
<artifactId>bootstrap</artifactId>
<version>3.3.7</version>
</dependency>
<dependency>
<groupId>org.webjars</groupId>
<artifactId>jquery</artifactId>
<version>3.1.0</version>
</dependency>
这个Jar包会将所有的SpringBoot的JAR文件都导入进去。
二、添加websocket的配置文件,进行websocket的通信。我们这里使用sockJS进行通信,他是websocket的一个改进,当浏览器不支持websocket的通信时,就会使用SockJS进行连接。
@Configuration
@EnableWebSocketMessageBroker
public class WebsocketConfig implements WebSocketMessageBrokerConfigurer {
//设置心跳的时间间隔
private static long HEART_BEAT=50000;
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
//设置连接websocket的地址, 注意使用 “/项目名/ws-start” 进行连接websocket。
//setAllowedOrigins("*")是设置所有请求都可以访问,即允许跨域的问题,或者自己设置允许访问的域名。
//withSockJS() 在不支持websocket的浏览器中,使用sockJs备选作为连接。
registry.addEndpoint("/ws-start").setAllowedOrigins("*").withSockJS();
}
@Override
public void configureMessageBroker(MessageBrokerRegistry registry) {
//设置简单的消息代理器,它使用Memory(内存)作为消息代理器,
//其中/user和/topic都是我们发送到前台的数据前缀。前端必须订阅以/user开始的消息(.subscribe()进行监听)。
//setHeartbeatValue设置后台向前台发送的心跳,
//注意:setHeartbeatValue这个不能单独设置,不然不起作用,要配合后面setTaskScheduler才可以生效。
//对应的解决方法的网址:https://stackoverflow.com/questions/39220647/spring-stomp-over-websockets-not-scheduling-heartbeats
registry.enableSimpleBroker("/user","/topic").setHeartbeatValue(new long[]{HEART_BEAT,HEART_BEAT}).setTaskScheduler(new DefaultManagedTaskScheduler());
//设置我们前端发送:websocket请求的前缀地址。即client.send("/ws-send")作为前缀,然后再加上对应的@MessageMapping后面的地址
registry.setApplicationDestinationPrefixes("/ws-send");
}
}
三、创建控制器,客户端将消息发送到哪里。
@Controller
public class StompSequenceTest {
/**
* 这个就是用于向客户端发送消息的类
*/
@Autowired
private SimpMessagingTemplate simpMessagingTemplate;
@MessageMapping("/sequence")
public void sequence(){
int index=0;
while(index<200){
//用于向客户端发送前200个数字,我们希望树循序接收到0到199这200个数字
simpMessagingTemplate.convertAndSend("/topic/sequence",index++);
try {
//我们睡眠1毫秒,1毫秒对于发送数据已经很大了。CPU执行都是纳米级的
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
然后添加HTML页面,CSS文件,JS文件,注意要放在resource下的static文件夹中。
然后在浏览器中打开:localhost:8088.
具体的项目在:https://gitee.com/chang_501/springboot_stomp_all/tree/master/springboot_stomp_sequence中下载,并使用idea打开,然后运行。在浏览器中执行localhost:8088。就会显示下面的图片:
当我们单击send按钮之后,出现下面这个图片:
这个是因为我们使用默认的发送器,默认的发送器是采用多线程发送的。
下面就是解决方案:就是修改默认的发送器的线程个数为1,就可以了。
解决方案:
我们只要在WebsocketConfig类中添加3行代码。
/**
* 这是用于配置服务器向浏览器发送的消息。
* clientOut就表示出出口。还有一个inBoundChannel用于处理浏览器向服务器发送的消息
* @param registration
*/
@Override
public void configureClientOutboundChannel(ChannelRegistration registration) {
registration.taskExecutor().corePoolSize(1).maxPoolSize(1);
}
最终的文件:
package com.kmust.springboot_stomp_sequence.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.messaging.simp.config.ChannelRegistration;
import org.springframework.messaging.simp.config.MessageBrokerRegistry;
import org.springframework.scheduling.concurrent.DefaultManagedTaskScheduler;
import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer;
/**
* 作者: 常伟
* 创建时间: 2018/7/5 17:21
* 邮箱: kmustchang@qq.com
* 功能:
*/
@Configuration
@EnableWebSocketMessageBroker
public class WebsocketConfig implements WebSocketMessageBrokerConfigurer {
//设置心跳的时间间隔
private static long HEART_BEAT=50000;
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
//设置连接websocket的地址, 注意使用 “/项目名/ws-start” 进行连接websocket。
//setAllowedOrigins("*")是设置所有请求都可以访问,即允许跨域的问题,或者自己设置允许访问的域名。
//withSockJS() 在不支持websocket的浏览器中,使用sockJs备选作为连接。
registry.addEndpoint("/ws-start").setAllowedOrigins("*").withSockJS();
}
/**
* 这是用于配置服务器向浏览器发送的消息。
* clientOut就表示出出口。还有一个inBoundChannel用于处理浏览器向服务器发送的消息
* @param registration
*/
@Override
public void configureClientOutboundChannel(ChannelRegistration registration) {
registration.taskExecutor().corePoolSize(1).maxPoolSize(1);
}
@Override
public void configureMessageBroker(MessageBrokerRegistry registry) {
//设置简单的消息代理器,它使用Memory(内存)作为消息代理器,
//其中/user和/topic都是我们发送到前台的数据前缀。前端必须订阅以/user开始的消息(.subscribe()进行监听)。
//setHeartbeatValue设置后台向前台发送的心跳,
//注意:setHeartbeatValue这个不能单独设置,不然不起作用,要配合后面setTaskScheduler才可以生效。
//对应的解决方法的网址:https://stackoverflow.com/questions/39220647/spring-stomp-over-websockets-not-scheduling-heartbeats
registry.enableSimpleBroker("/user","/topic").setHeartbeatValue(new long[]{HEART_BEAT,HEART_BEAT}).setTaskScheduler(new DefaultManagedTaskScheduler());
//设置我们前端发送:websocket请求的前缀地址。即client.send("/ws-send")作为前缀,然后再加上对应的@MessageMapping后面的地址
registry.setApplicationDestinationPrefixes("/ws-send");
}
}
然后再次运行。然后访问localhost:8088.就会发现发送的消息都是顺序的了,不管实验多少次。
Spring Boot+STOMP解决消息乱序问题的更多相关文章
- Spring Boot 2 (七):Spring Boot 如何解决项目启动时初始化资源
Spring Boot 2 (七):Spring Boot 如何解决项目启动时初始化资源 在项目启动的时候需要做一些初始化的操作,比如初始化线程池,提前加载好加密证书等.今天就给大家介绍一个 Spri ...
- spring boot下WebSocket消息推送
WebSocket协议 WebSocket是一种在单个TCP连接上进行全双工通讯的协议.WebSocket通信协议于2011年被IETF定为标准RFC 6455,并由RFC7936补充规范.WebSo ...
- Spring Boot (25) RabbitMQ消息队列
MQ全程(Message Queue)又名消息队列,是一种异步通讯的中间件.可以理解为邮局,发送者将消息投递到邮局,然后邮局帮我们发送给具体的接收者,具体发送过程和时间与我们无关,常见的MQ又kafk ...
- Spring Kafka和Spring Boot整合实现消息发送与消费简单案例
本文主要分享下Spring Boot和Spring Kafka如何配置整合,实现发送和接收来自Spring Kafka的消息. 先前我已经分享了Kafka的基本介绍与集群环境搭建方法.关于Kafka的 ...
- (转)Spring Boot 2 (七):Spring Boot 如何解决项目启动时初始化资源
http://www.ityouknow.com/springboot/2018/05/03/spring-boot-commandLineRunner.html 在我们实际工作中,总会遇到这样需求, ...
- Spring Boot 2.0(七):Spring Boot 如何解决项目启动时初始化资源
在我们实际工作中,总会遇到这样需求,在项目启动的时候需要做一些初始化的操作,比如初始化线程池,提前加载好加密证书等.今天就给大家介绍一个 Spring Boot 神器,专门帮助大家解决项目启动初始化资 ...
- Spring Boot RabbitMQ 延迟消息实现完整版
概述 曾经去网易面试的时候,面试官问了我一个问题,说 下完订单后,如果用户未支付,需要取消订单,可以怎么做 我当时的回答是,用定时任务扫描DB表即可.面试官不是很满意,提出: 用定时任务无法做到准实时 ...
- 76. Spring Boot完美解决(406)Could not find acceptable representation原因及解决方法
[原创文章] 使用Spring Boot的Web项目,处理/login请求的控制器方法(该方法会返回JSON格式的数据).此时如果访问localhost:8080/login.html, ...
- Spring Boot 如何解决项目启动时初始化资源
在我们实际工作中,总会遇到这样需求,在项目启动的时候需要做一些初始化的操作,比如初始化线程池,提前加载好加密证书等.今天就给大家介绍一个 Spring Boot 神器,专门帮助大家解决项目启动初始化资 ...
随机推荐
- preg_quote
$p = 'http://'; $p = '/'.preg_quote($p, '/').'/'; echo $p; /http\:\/\//
- ServletRequest与HttpServletRequest
ServletRequest 解析:代表来自客户端的请求.当Servlet容器接收到客户端的要求访问特定Servlet的请求时,容器先解析客户端的原始请求数据,把它包装成一个ServletReques ...
- 又一神器框架:linq.js
1.支持jQuery插件的方式.jquery.linq.min.js. 2.也可以像普通js方法一样使用.linq.min.js. 3.当然用习惯VS的童鞋肯定希望有个良好的智能感知,是的,它支持. ...
- 【Tools】Myeclise-2018.12.0 最新破解文件
Myeclise-2018.12.0 最新破解文件. 最近在写android app登录块,需要用到这个工具,顺手就拿到了,发现资源太少.这里分享给大家. 有币高富帅打赏下载地址: https://d ...
- Android Tcp操作
Tcp是基于传输层的面向连接的可靠通讯协议,其优点是基于连接,使得服务端和客户端可以实现双向通信,且实时性高,在需要服务端主动向客户端推送数据的应用场景中,使用TCP协议是一种很好的方式. 初学And ...
- 最新 北森java校招面经 (含整理过的面试题大全)
从6月到10月,经过4个月努力和坚持,自己有幸拿到了网易雷火.京东.去哪儿.北森等10家互联网公司的校招Offer,因为某些自身原因最终选择了北森.6.7月主要是做系统复习.项目复盘.LeetCode ...
- 启动3ds Max报 d3dx9_43.dll丢失 解决方法
d3dx9_43.dll文件与Microsoft DirectX关联.负责启动基于Windows的游戏和高级图形程序. 在360安全卫士上搜索了 DirectX 下载了这个修复工具 运行应用 点击修复 ...
- 【转帖】MIPS构架之:我和龙芯有个约会
MIPS构架之:我和龙芯有个约会 https://www.eefocus.com/mcu-dsp/364490 <处理器史话>之十二 2016-06-24 12:21 作者:付丽华预计 1 ...
- 2019秋季PAT甲级_C++题解
2019 秋季 PAT (Advanced Level) C++题解 考试拿到了满分但受考场状态和知识水平所限可能方法不够简洁,此处保留记录,仍需多加学习.备考总结(笔记目录)在这里 7-1 Fore ...
- kubernetes 实践五:Service详解
Service 是 k8s 的核心概念,通过创建Service,可以为一组具有相同功能的容器应用提供一个统一的入口地址,并且将请求负载分发到后端的各个容器应用上. Service 的定义 Servic ...