京东到家基于Netty的WebSocket应用实践分享(转载)

2024-01-28 14:30

本文主要是介绍京东到家基于Netty的WebSocket应用实践分享(转载),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

转载 https://blog.csdn.net/humn_chou/article/details/80036248

1、前言


在京东到家商家中心系统中,商家提出了要在 Web 端实现自动打印的需求,不再需要人工盯守点击打印,直接打印小票,以节约人工成本。为了满足商家的需求,开发团队决定立即着手实践。本文记录这次从技术选型到动手实践的过程,希望也能给您带来一定的启发。

2、相关文章


新手入门贴:史上最全Web端即时通讯技术原理详解
Web端即时通讯技术盘点:短轮询、Comet、Websocket、SSE
Comet技术详解:基于HTTP长连接的Web端实时通信技术
新手快速入门:WebSocket简明教程
WebSocket详解(一):初步认识WebSocket技术
WebSocket详解(二):技术原理、代码演示和应用案例
WebSocket详解(三):深入WebSocket通信协议细节
WebSocket详解(四):刨根问底HTTP与WebSocket的关系(上篇)
WebSocket详解(五):刨根问底HTTP与WebSocket的关系(下篇)
WebSocket详解(六):刨根问底WebSocket与Socket的关系
MobileIMSDK-Web的网络层框架为何使用的是Socket.io而不是Netty?
socket.io实现消息推送的一点实践及思路
LinkedIn的Web端即时通讯实践:实现单机几十万条长连接
Web端即时通讯技术的发展与WebSocket、Socket.io的技术实践
Web端即时通讯安全:跨站点WebSocket劫持漏洞详解(含示例代码)
开源框架Pomelo实践:搭建Web端高性能分布式IM聊天服务器
使用WebSocket和SSE技术实现Web端消息推送
详解Web端通信方式的演进:从Ajax、JSONP 到 SSE、Websocket
理论联系实际:从零理解WebSocket的通信原理、协议格式、安全性
>>  更多同类文章 ……

3、关于作者


李天翼: 软件开发工程师,任职于达达京东到家后台研发团队,负责订单流程的开发工作。

4、初步思路


关于问题的思考逻辑:

  • 第一种:想到的是可以用ajax来轮询服务端获取最新订单,也就是pull;
  • 第二种:我们是否可以用类似推送的设计来实现,也就是push。

两种思路我们评估其优缺点:

  • ajax方式实现简单,只需要定时从服务端pull数据即可,但也增加了很多次无效的轮询, 无形中增加服务端无效查询;
  • push方式实现稍复杂,需要服务端与PC端保持连接,这就需要建立长连接,最终通过长连接的方式来实现push效果。

经过讨论,我们选择了第二种,订单中心生产出的新订单,通过MQ的方式推送给web端,最终获得一个比较好的用户体验。

5、技术选型


关于长连接方案的选择,我们参考了不少帖子,最终选择使用websocket协议来实现长连接,类似场景如IM,服务端即时推送等都使用了这个协议。

接下来我们比较一下 websocket 的框架,比较主流的有netty、tomcat、 socketIO  三个框架。

基于支持websocket的容器,开发简单,例如tomcat,但在高并发的支持不是很好,连接的时候容易连接断开,还有就是依赖容器。

netty-socketIO是在netty4基础之上做了一层封装,效率如同netty一样,是一个全平台方案,友好的API,京东的logbook也是用了socketIO来传递日志,也是我们的一个备选方案。

netty是业内主流的NIO框架,netty对javaNIO做了封装,让开发者更多关注业务,降低开发成本,很多著名的RPC框架都采用了netty作为传输层,友好的API,功能强大,内置了很多编解码协议,实现websocket协议也是十分方便。

那我们横向比较一下这些框架:
京东到家基于Netty的WebSocket应用实践分享_1.jpeg 

所以在选型方面我们还是定位在socketIO 与 netty 上面,在兼顾扩展性与灵活性的同时,我们也考虑到netty可以提供http的功能,最终我们选择了使用netty,当然socketIO封装了很多功能,也是十分强大,相比较来说netty更适合我们,比较轻量。

6、netty的特性


netty具有异步非阻塞的特性,传统IO是面向流的,NIO是面向缓冲区的,这也是它的非阻塞原因所在。

netty的线程模型如图所示:
京东到家基于Netty的WebSocket应用实践分享_2.jpeg 

这种模型就是我们常说的Reactor模型, boss线程其实是一个独立的NIO线程池,用于接收client请求,默认线程池大小为1,worker线程池用于处理具体的读写操作,默认线程池大小为2*cpu个数

在上述模型中要特别注意ExecutionHandler,ExecutionHandler是运行在worker线程中的,所以 耗时的操作最好在线程池中运行, 比如IO或者计算,不然会影响整个netty的吞吐

7、具体的业务流程


了解了上节这些,我们根据自己的业务设计出流程如下图所示:

  • 步骤(1) web端请求服务端进行注册,注册成功保持长连接;
  • 步骤(2)服务端发送MQ;
  • 步骤(3)netty将收到的消息推送给web端;
  • 步骤(4)web端调用打印控件进行打印,打印控件需提前安装好(打印控件是pc上安装的一个驱动程序,用过JS方式来调用)。

如果调用JS成功,控件将把打印信息放入打印队列,如果不成功,重复步骤(4):
京东到家基于Netty的WebSocket应用实践分享_3.png 

当然现在的结构只是单机版,不满足生产条件,那将来的结构可能会演变成如下图所示:
京东到家基于Netty的WebSocket应用实践分享_4.png 

我们会在服务端与netty之间建立路由层,路由层的主要职责:

  • 第一:收集集群存活信息;
  • 第二:记录落点,就是落在哪一台机器上面;
  • 第三:接收消息与分发消息。

有了这三种能力,我们就可以轻松的指定信息分发策略。这里我们希望使用http协议来路由,所以就需要netty有http短连接接收的能力 ,所以netty整体上需要长短连接两种能力。

8、具体的代码实现


讲了这么多,还是来点干货,下面是部分代码。

netty启动类,我们通过spring来启动netty,因为netty启动会阻塞主线程,所以需要在子线程中来启动netty,下面是启动参数。
京东到家基于Netty的WebSocket应用实践分享_1.jpeg 

接着来写我们的ChannelInitializer,HttpServerCodec为编解码器,WSServerProtocolHandler为websocket协议握手,其中我们更关注业务层面自定义的两个hander,httpRequestHandler,authorizeHandler。
京东到家基于Netty的WebSocket应用实践分享_2.jpeg 

httpRequestHandler的作用是处理url是否合法,接收参数,httpRequestHandler此方法中也可以根据URI来过滤,自定义自己的短连接请求。
京东到家基于Netty的WebSocket应用实践分享_3.jpeg 

authorizeHandler的作用是校验数据是否正确,如果正确会将channel保存到map中,通过map建立起业务ID与通道之间的关系。

校验的过程我们在authorizeHandler中的channelRead展开,如果未通过,直接关闭当前channel,如果通过校验,则通过ctx.fireChannelRead(msg);方法将信息传入下一个handler去处理。

在项目里主要是以传递参数来进行数据校验的,也就是通过URL传参来实现。在httpRequestHandler中我们将URL参数set到channel的attr中,并传递给了下一个handler,也就是authorizeHandler, 所以在authorize方法中我们可以利用get()方法得到参数值,u是经过加密的数据,我们需要在这里进行解密,解密失败,可认为校验失败

当然如果有跨应用的服务,也可以通过Cookie的方式来进行加密串的读写,通过request.getHeader 是可以获取Cookie中的信息,这就看具体业务了,示例代码如下:
京东到家基于Netty的WebSocket应用实践分享_4.jpeg 

这个 map 可以理解为servlet中的session,当有信息需要传送给某个客户端时,我们调用map.get(key)方式的到当前该客户端的channel,调用writeAndFlush方法将信息发送出去,下面举例通过接收MQ消息后的处理逻辑。

接下来有人可能想到,那如果通道关闭了怎么办?map中的channel是不是就失效了呢?那其实我们还需要有一个类似心跳的机制去维护channel,间接的去维护这个map,如果是通道正常关闭,可以通过channelInactive方法来监听,如果是长时间空闲:在项目中我们使用了增加的IdleStateHandler来处理,通过覆盖userEventTriggered 方法来监听空闲channel,当某个channel到达我们设置的超时时间时,netty会回调此方法。

至此,核心部分已经处理完成,剩下的就是通过保存的channel来发送信息给客户端了。

最后在web端,我们采用了 reconnecting-websocket,它是一个小型的 JavaScript 库,封装了 WebSocket API, 提供了在连接断开时自动重连的机制,很能够帮助我们完成断开重连的操作

9、遇到的问题


1)经过测试,在ws的uri后面不能传递参数,不然在netty实现websocket协议握手的时候会出现断开连接的情况,针对这种情况在websocketHandler之前做了一层httpHander过滤,将传递参数放入channel的attr中,然后重写request的uri,并传入下一个管道中,基本上解决这个问题。

2)在读写空闲的时候尽量以发心跳包的方式维护连接,但在客户端由于网络不稳定或者是服务端重启,连接会断开,瞬间有可能接收不到订单消息,为此在客户端需要实现断开重连机制,此问题我们采用 reconnecting-websocket的js框架,此框架扩展了原生websocket的实现,做了断开重连机制,有效的防止断开后不能及时连接。

3)在测试过程中由于控件与小票机的问题,可能会出现打印异常或者小票机没纸的情况,Lodop控件其实是将打印信息放入电脑的打印队列,如果没纸了,小票机会报警,再次放入小票纸,打印机会自动打印队列中的数据。

4)出现调用控件异常偶尔发生,现在处理办法是在js中进行了的try catch 如果失败 进行重试,重试次数自定义,超过重试次数暂不做处理,此处还不太严谨,需要在进行优化。

10、本文小结


通过上面的实践,我们基本已经实现了web端的自动打印,经过长时间的内部测试,服务端与客户端通信稳定,我们将灰度商家做用户体验。

在特定的场景下,选择适当的技术会提高我们的效率,否则会适得其反。选择长连接,大家可以把握三个大原则:

  • 服务端是否需要主动推送数据到客户端以实现控制的效果;
  • 对于实时性的要求是否苛刻;
  • 对于客户端是否需要关注其在线状态的实时变化。

更多关于netty技术我们还在持续地研究中。

(原文链接: https://mp.weixin.qq.com/s/_MTLHfe52eSvc1uXBfuVbg

附录:更多技术文章


有关“为何选择Netty”的11个疑问及解答
开源NIO框架八卦——到底是先有MINA还是先有Netty?
选Netty还是Mina:深入研究与对比(一)
选Netty还是Mina:深入研究与对比(二)
NIO框架入门(一):服务端基于Netty4的UDP双向通信Demo演示
NIO框架入门(二):服务端基于MINA2的UDP双向通信Demo演示
NIO框架入门(三):iOS与MINA2、Netty4的跨平台UDP双向通信实战
NIO框架入门(四):Android与MINA2、Netty4的跨平台UDP双向通信实战
Netty 4.x学习(一):ByteBuf详解
Netty 4.x学习(二):Channel和Pipeline详解
Netty 4.x学习(三):线程模型详解
Apache Mina框架高级篇(一):IoFilter详解
Apache Mina框架高级篇(二):IoHandler详解
MINA2 线程原理总结(含简单测试实例)
Apache MINA2.0 开发指南(中文版)[附件下载]
MINA、Netty的源代码(在线阅读版)已整理发布
解决MINA数据传输中TCP的粘包、缺包问题(有源码)
解决Mina中多个同类型Filter实例共存的问题
实践总结:Netty3.x升级Netty4.x遇到的那些坑(线程篇)
实践总结:Netty3.x VS Netty4.x的线程模型
详解Netty的安全性:原理介绍、代码演示(上篇)
详解Netty的安全性:原理介绍、代码演示(下篇)
详解Netty的优雅退出机制和原理
NIO框架详解:Netty的高性能之道
Twitter:如何使用Netty 4来减少JVM的GC开销(译文)
绝对干货:基于Netty实现海量接入的推送服务技术要点
Netty干货分享:京东京麦的生产级TCP网关技术实践总结
>>  更多同类文章 ……

这篇关于京东到家基于Netty的WebSocket应用实践分享(转载)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/653901

相关文章

mac安装nvm(node.js)多版本管理实践步骤

《mac安装nvm(node.js)多版本管理实践步骤》:本文主要介绍mac安装nvm(node.js)多版本管理的相关资料,NVM是一个用于管理多个Node.js版本的命令行工具,它允许开发者在... 目录NVM功能简介MAC安装实践一、下载nvm二、安装nvm三、安装node.js总结NVM功能简介N

Python中常用的四种取整方式分享

《Python中常用的四种取整方式分享》在数据处理和数值计算中,取整操作是非常常见的需求,Python提供了多种取整方式,本文为大家整理了四种常用的方法,希望对大家有所帮助... 目录引言向零取整(Truncate)向下取整(Floor)向上取整(Ceil)四舍五入(Round)四种取整方式的对比综合示例应

Spring Boot 3 整合 Spring Cloud Gateway实践过程

《SpringBoot3整合SpringCloudGateway实践过程》本文介绍了如何使用SpringCloudAlibaba2023.0.0.0版本构建一个微服务网关,包括统一路由、限... 目录引子为什么需要微服务网关实践1.统一路由2.限流防刷3.登录鉴权小结引子当前微服务架构已成为中大型系统的标

MobaXterm远程登录工具功能与应用小结

《MobaXterm远程登录工具功能与应用小结》MobaXterm是一款功能强大的远程终端软件,主要支持SSH登录,拥有多种远程协议,实现跨平台访问,它包括多会话管理、本地命令行执行、图形化界面集成和... 目录1. 远程终端软件概述1.1 远程终端软件的定义与用途1.2 远程终端软件的关键特性2. 支持的

Java调用DeepSeek API的最佳实践及详细代码示例

《Java调用DeepSeekAPI的最佳实践及详细代码示例》:本文主要介绍如何使用Java调用DeepSeekAPI,包括获取API密钥、添加HTTP客户端依赖、创建HTTP请求、处理响应、... 目录1. 获取API密钥2. 添加HTTP客户端依赖3. 创建HTTP请求4. 处理响应5. 错误处理6.

golang内存对齐的项目实践

《golang内存对齐的项目实践》本文主要介绍了golang内存对齐的项目实践,内存对齐不仅有助于提高内存访问效率,还确保了与硬件接口的兼容性,是Go语言编程中不可忽视的重要优化手段,下面就来介绍一下... 目录一、结构体中的字段顺序与内存对齐二、内存对齐的原理与规则三、调整结构体字段顺序优化内存对齐四、内

linux进程D状态的解决思路分享

《linux进程D状态的解决思路分享》在Linux系统中,进程在内核模式下等待I/O完成时会进入不间断睡眠状态(D状态),这种状态下,进程无法通过普通方式被杀死,本文通过实验模拟了这种状态,并分析了如... 目录1. 问题描述2. 问题分析3. 实验模拟3.1 使用losetup创建一个卷作为pv的磁盘3.

JavaWeb-WebSocket浏览器服务器双向通信方式

《JavaWeb-WebSocket浏览器服务器双向通信方式》文章介绍了WebSocket协议的工作原理和应用场景,包括与HTTP的对比,接着,详细介绍了如何在Java中使用WebSocket,包括配... 目录一、概述二、入门2.1 POM依赖2.2 编写配置类2.3 编写WebSocket服务2.4 浏

MySQL8.2.0安装教程分享

《MySQL8.2.0安装教程分享》这篇文章详细介绍了如何在Windows系统上安装MySQL数据库软件,包括下载、安装、配置和设置环境变量的步骤... 目录mysql的安装图文1.python访问网址2javascript.点击3.进入Downloads向下滑动4.选择Community Server5.

CentOS系统Maven安装教程分享

《CentOS系统Maven安装教程分享》本文介绍了如何在CentOS系统中安装Maven,并提供了一个简单的实际应用案例,安装Maven需要先安装Java和设置环境变量,Maven可以自动管理项目的... 目录准备工作下载并安装Maven常见问题及解决方法实际应用案例总结Maven是一个流行的项目管理工具