1 前端不能成功解析后端信息造成消息阻塞问题
文章主要以问题场景复现、问题相关概念剖析、错误点的定位、最终总结为切入点,尽详细介绍此次问题由来与解决。
1.1 问题记录
1.1.1 问题场景复现
初步怀疑:WebSocket与Spring Taskd或者HTTP的冲突
1.1.1.1:问题场景:使用WebSocket让客户端,播报订单相关语音信息时,一句语音总是不能报放完且每隔一段时间一直循环。客户端控制台,一直重复提交数据。
1.1.2 问题业务开发情况
1.1.2.1:此次业务目的:在客户端实现用户下单号,语音播报“ 有用户下单,请及时处理 ”
1.1.2.2:为确保开发过程中,代码的健壮性,前期使用模拟业务对WS协议中,客户端与服务器交互进行验证,验证代码如下:
@Autowired
private WebSocketServer webSocketServer;/** * 通过WebSocket每隔5秒向客户端发送消息 */ @Scheduled(cron = "0/5 * * * * ?") public void sendMessageToClient(){ webSocketServer.sendToAllClient("这是来自服务端的消息:" + DateTimeFormatter.ofPattern("HH:mm:ss").form>at(LocalDateTime.now())); }
1.1.2.2:之后controller中常规书写,崽业务层实现类中。代码如下:
public void paySuccess(OrdersPaymentDTO ordersPaymentDTO) { //取出前端传参订单number String orderNumber = ordersPaymentDTO.getOrderNumber(); //根据订单number查询订单详细 Orders orders = orderMapper.getByNumber(orderNumber); //构建订单状态,支付方式,支付状态,结账时间 orders = Orders.builder() .id(orders.getId()) .amount(orders.getAmount()) .payStatus(Orders.PAID) .checkoutTime(LocalDateTime.now()) .number(orderNumber) >.status(Orders.TO_BE_CONFIRMED).build(); //根据订单id更新订单状态 orderMapper.update(orders); log.info("模拟支付完毕"); Map map = new HashMap<>(); map.put("type", 1); map.put("orderId", orders.getId()); map.put("content", "订单号:" + orderNumber); //map.put("content0", "订单号:" + orderNumber);//content >webSocketServer.sendToAllClient(JSON.toJSONString(map)); }
最终用户完成下单,客户端,发生语音播报循环。
1.2 问题剖析
1.2.1 WebSocket协议
1.2.1.1 WS协议介绍
WebSocket :是基于 TCP 的新网络协议。它实现了浏览器与服务器全双工通信——浏览器和服务器只需要完成一次握手,两者间就可以创建持久性的连接, 并进行双向数据传输。
全双工通信
:又称双向同时通信
,即通信双方可以同时发送和接收信息的信息交互方式。
1.2.1.2 WebSocket缺点
1.服务器长期维护长连接需要一定的成本各个浏览器支持程度不一
2.WebSocket 是长连接,受网络限制比较大,需要处理好重连
1.2.2 HTTP协议
1.2.2.1 HTTP协议介绍
介绍:HTTP协议(超文本传输协议)是一种网络通信协议,允许将超文本标记语言(HTML)文档从Web服务器传送到客户端的浏览器。默认端口:80
1.2.2.2 HTTP协议和WebSocket协议对比
HTTP 协议 | WebSocket 协议 |
---|---|
HTTP是短连接 | WebSocket是长连接 |
HTTP通信是单向 的,基于请求响应模式 | WebSocket支持双向 通信 |
HTTP和WebSocket底层都是TCP连接
1.2.3 Spring Taskd
1.2.3.1 Spring Taskd介绍
介绍:Spring Taskd是Spring框架提供的任务调度工具,可以按照约定时间
自动执行
某个代码逻辑。
1.2.3.2 Spring Taskd中cron表达式
cron表达式就是一个字符串,通过cron表达式可以
定义任务触发时间
构成规则:分为6或7个域,由空格分隔开,每个域代表一个含义
每个域的含义分别为:秒
、分
、时
、日
、月
、周
、年(可选)
例如:cron = "6/6 * * * * ? " //测试:从6开始每6s.执行一次
参考:cron表达式在线生成器
1.3 错误点的定位
1.3.1 controller层debug断点观察小程序端支付接口传参
接收参数无误
1.3.2 同时管理员浏览器客户端观察控制台输出日志
发现,后端服务一直再给前端响应信息,而我的支付接口还没响应完毕(断点没放行)
1.3.3 前后端错误总结
可以观察到管理员端,每隔5s中一直再响应数据中,所以定位到后端Task定时任务错误,其次根据日志中信息,得到准确错误处,最终注释cron定义的任务时间,禁止其向客户端响应数据,完成bug修复
1.4 观点总结
直接原因
: 同一个webSocketServer中的sendToAllClient方法被调用多次,这是直接导致此次bug出现的原因直接原因
:其次后端使用webStocketServer协议传递的信息前后端已经约定好,当传递前端自定义信息“这是来自服务端的消息时”和语音信息时,@Scheduled(cron = "0/5 * * * * ?")
,先是在0~5s
解析自定义信息,前端无法正常解析,导致前端控制台报错
,其次在5s~10s
进行语音播报,但是语音长度>5s
,直接导致两次任务方法循环运行,本次bug出现。深层次
:WS协议中无多路复用可以考虑,此篇不再深入,如关注后继,烦请三连关注。