WebSocket 是一种网络通信协议,提供了在单个TCP连接上进行全双工通信的能力。它允许服务器和客户端之间建立一个持久的连接,并通过这个连接实时地发送和接收数据。
WebSocket 工作流程大致可以分为以下几个步骤:
1.客户端发起请求:客户端通过HTTP发送一个带有特殊头部的请求到服务器,请求升级到WebSocket协议。
2.服务器接收请求:服务器接收到客户端的请求后,检查是否支持WebSocket。
3.握手过程:如果服务器支持WebSocket,它将发送一个握手响应,包含Sec-WebSocket-Accept头部,这是对客户端Sec-WebSocket-Key的加密确认。
4.连接建立:客户端验证服务器的Sec-WebSocket-Accept头部,如果验证成功,握手完成,WebSocket连接建立。
5.数据传输:连接建立后,客户端和服务器可以开始发送和接收数据帧。
6.帧编码:数据在发送前被编码,可能包括掩码和压缩。
7.帧传输:编码后的数据帧通过网络传输。
8.帧解码:接收端收到数据帧后,进行解码,去除掩码和解压缩。
9.数据处理:解码后的数据帧被传递给应用程序进行处理。
10.关闭连接:任何一方都可以通过发送关闭帧来关闭WebSocket连接。
11.连接关闭:一旦关闭帧被发送并确认,WebSocket连接就正式关闭。
特性 | Netty | T-IO |
---|---|---|
社区活跃度 | 高,拥有广泛的用户基础和开发者社区 | 相对较低,但提供了丰富的示例和文档 |
API设计 | 功能丰富,但相对复杂 | 易懂,尽量避免引入自创概念,降低学习成本 |
性能 | 高性能,尤其在处理大量并发连接时表现出色 | 性能良好,但在某些测试中可能不如Netty |
资源管理 | 需要手动管理资源 | 内置业务数据管理能力,简化资源绑定与释放 |
协议支持 | 支持多种协议,包括HTTP、WebSocket、FTP、SMTP等 | 官方提供的协议支持较少,主要为HTTP和WebSocket |
线程模型 | 异步非阻塞,基于Reactor模式 | 同步安全线程池,简化内部调度线程 |
内存管理 | 可能需要更细致的内存管理 | 自创同步安全线程池,减少内存管理复杂性 |
易用性 | 功能强大,但学习曲线较陡 | 易学,API设计让工程师用起来舒服 |
文档和示例 | 文档丰富,但可能难以理解 | 提供了生产级别的showcase示范工程 |
跨平台 | 支持多种操作系统 | 未明确说明,但通常Java框架具有跨平台特性 |
基于以上比对,需要高性能,文档完善,功能丰富的组件,因此我们选择使用Netty作为我们的WebSocket服务器框架。
Netty WebSocket的工作流程可以分为以下几个步骤:
1.初始化服务器:创建ServerBootstrap实例,配置线程模型、通道类型等。
2.设置线程池:通过group方法设置事件循环组,通常使用NioEventLoopGroup类型,分为Boss线程组和Worker线程组。
3.配置通道和处理器:调用channel方法指定通道类型,如NioServerSocketChannel,并通过childHandler方法设置通道处理器(ChannelInitializer),用于初始化新连接的Channel,并添加业务逻辑处理器(ChannelHandler)。
4.绑定端口:通过bind方法绑定监听端口,并调用sync方法等待绑定完成。
5.处理连接:一旦有客户端连接到服务器端口,Boss线程会接收到连接事件,创建新的SocketChannel并将其注册到Worker线程组的事件循环中。
6.事件循环处理:Worker线程会不断循环从注册的SocketChannel中读取数据,并将数据交给ChannelPipeline中的ChannelHandler处理。处理完成后,可以向客户端发送响应数据。
7.WebSocket握手:在ChannelInitializer中添加HttpServerCodec和WebSocketServerProtocolHandler,用于处理WebSocket握手和消息传输。
8.数据传输:握手成功后,客户端和服务器之间可以开始发送和接收WebSocket消息。
9.关闭连接:任何一方都可以通过发送关闭帧来关闭WebSocket连接。
10.优雅关闭:调用shutdownGracefully方法优雅地关闭EventLoopGroup,释放所有的资源。
这个流程涵盖了从服务器初始化到WebSocket连接建立、数据传输以及连接关闭的全过程。通过Netty的事件驱动模型,可以高效地处理大量并发WebSocket连接。
1.customer1通过ng链接任意一台server, 如server1. agent1连接server2
2.保存客户端和服务端的连接信息到 redis, uid <--> hostip。保存 uid <--> channel 本地缓存(使用BiMap实现)。持久化消息成功后返回发送成功,否则返回发送失败,客户端重发该消息。
3.customer1 <-> server1 保持长连接,customer1 开启心跳,上报活动状态。服务端也开启心跳,剔除长时间没有心跳的客户端。
4.customer1发送msg1到server1。
5.服务端根据消息toId找到agent1,判断agent1的长连接是否存在
存在则通过本地BiMap获取对应channel, 通过channel把消息写出去。
不存在则通过redis获取对应host, 通过host + url 把消息转发到服务端上server2。
6.server2消费请求,通过BiMap获取agnet1的channel, 写收到的消息进该channel.
1.发送消息时先生成msgId, msgId采用数据库主键
2.server收到消息后直接推送对应的聊天对象,
3.发送该消息到mq系统
4.普通消息通过发送消息触发投递。
5.客户端收到消息后,发送ack消息。
6.服务端收到ack消息,记录该消息已读。
7.每台实例消费mq消息,判断是否已读。如未读重新投递消息。
8.重复#6
WebSocket 协议的全双工特性允许客户端和服务器实时、双向地交换数据,这使得它非常适合需要实时交互的应用,如在线游戏、聊天应用、股票行情更新等。
不吃鱼的猫,信也科技后端研发