Netty实战解决订单详情页面无法实时刷新问题

1 问题背景

问题:如下图所示是订单详情页面。餐饮商在其小程序应用中的该页面编辑商品数量和价格,需要供货商在其小程序应用中的该页面实时显示商品的数量和价格。
在这里插入图片描述

这个问题属于实时通信范畴,通常有三种解决方案:

  1. ajax轮询
  2. long pull
  3. websocket

由于ajax轮询和long pull这两种解决方案资源开销大,在客户端和服务器通信时每次都要建立HTTP连接,并且对服务器端并行处理要求高,因此本文采用websocket方案[1]。

2 Netty简介

Netty是由JBOSS提供的一个java开源框架。Netty提供异步的、事件驱动的网络应用程序框架和工具,用以快速开发高性能、高可靠性的网络服务器和客户端程序。Netty 是一个基于NIO的客户、服务器端编程框架,使用Netty 可以确保你快速和简单的开发出一个网络应用[2]。

Netty有如下几个特性:

  1. 高并发
  2. 零拷贝
  3. 简单易用

下面首先介绍NIO模型,体现Netty的高并发特性;再讲到零拷贝以此说明Netty如何减少内存拷贝,突出其资源消耗少、快速的特点;最后讲解reactor主从多线程模型结构。

2.1 NIO简介
  1. BIO 就是传统的 java.io 包,它是基于流模型实现的,交互的方式是同步、阻塞方式,也就是说在读入输入流或者输出流时,在读写动作完成之前,线程会一直阻塞在那里,它们之间的调用时可靠的线性顺序。它的有点就是代码比较简单、直观;缺点就是 IO 的效率和扩展性很低,容易成为应用性能瓶颈。
  2. NIO 是 Java 1.4 引入的 java.nio 包,提供了 Channel、Selector、Buffer 等新的抽象,可以构建多路复用的、同步非阻塞 IO 程序,同时提供了更接近操作系统底层高性能的数据操作方式。
  3. AIO 是 Java 1.7 之后引入的包,是 NIO 的升级版本,提供了异步非堵塞的 IO 操作方式,所以人们叫它 AIO(Asynchronous IO),异步 IO 是基于事件和回调机制实现的,也就是应用操作之后会直接返回,不会堵塞在那里,当后台处理完成,操作系统会通知相应的线程进行后续的操作[3]。

在这里插入图片描述

2.2 零拷贝

“零拷贝”是指计算机操作的过程中,CPU不需要为数据在内存之间的拷贝消耗资源。而它通常是指计算机在网络上发送文件时,不需要将文件内容拷贝到用户空间(User Space)而直接在内核空间(Kernel Space)中传输到网络的方式[4]。

2.3 reactor主从多线程模型结构

主从Reactor线程模型的特点是:服务端用于接收客户端连接的不再是个1个单独的NIO线程,而是一个独立的NIO线程池。Acceptor接收到客户端TCP连接请求处理完成后(可能包含接入认证等),将新创建的SocketChannel注册到IO线程池(sub reactor线程池)的某个IO线程上,由它负责SocketChannel的读写和编解码工作。Acceptor线程池仅仅只用于客户端的登陆、握手和安全认证,一旦链路建立成功,就将链路注册到后端subReactor线程池的IO线程上,由IO线程负责后续的IO操作[5]。

3 业务架构

3.1 服务器端初始化流程图

在这里插入图片描述

3.2 业务流程介绍
3.2.1 创建websocket连接

客户端发送建立websocket连接请求,服务器端接收请求并处理握手动作。

3.2.2 客户端发送心跳

客户端每隔20秒发一次心跳请求。

3.2.3 断开websocket连接

客户端发送断开websocket连接请求,服务器端接收请求并断开连接,移除用户信息。

3.2.4 客户端发送修改订单项数量的消息

用户在订单详情页面编辑订单数量时,给服务器发送订单修改信息。服务器接收该消息,查询该订单对应的供货商是否在线,如果在线,转发该消息;如果不在线,给供货商发送微信消息。

3.2.5 客户端发送修改订单项价格的消息

用户在订单详情页面编辑订单价格时,给服务器发送订单修改信息。服务器接收该消息,查询该订单对应的供货商是否在线,如果在线,转发该消息;如果不在线,给供货商发送微信消息。

3.2.6 客户端发送修改订单项备注的消息

用户在订单详情页面编辑订单备注时,给服务器发送订单修改信息。服务器接收该消息,查询该订单对应的供货商是否在线,如果在线,转发该消息;如果不在线,给供货商发送微信消息。

3.2.7 客户端发送确认订单的消息

用户在订单详情页确认收货时,给服务器发送订单确认消息。服务器接收该消息,查询该订单对应的供货商是否在线,如果在线,转发该消息;如果不在线,给供货商发送微信消息。

3.3 实战开发
3.3.1 服务器端开发
  1. pom文件引入netty jar包。
			<dependency>
				<groupId>io.netty</groupId>
				<artifactId>netty-all</artifactId>
				<version>4.1.25.Final</version>
			</dependency>
  1. 编写websocket server,使用主从Reactor线程模型。
@Component
public class WSServer {

    private static class SingletionWSServer {
        static final WSServer instance = new WSServer();
    }

    public static WSServer getInstance() {
        return SingletionWSServer.instance;
    }

    private EventLoopGroup mainGroup;
    private EventLoopGroup subGroup;
    private ServerBootstrap server;
    private ChannelFuture future;

    public WSServer() {
        mainGroup = new NioEventLoopGroup();
        subGroup = new NioEventLoopGroup();
        server = new ServerBootstrap();
        server.group(mainGroup, subGroup).channel(NioServerSocketChannel.class).childHandler(new WSServerInitialzer());
    }

    public void start() {
        this.future = server.bind(8088);
        LoggerManager.info("netty websocket server 启动完毕...");
    }
}

  1. 编写WSServerInitialzer,用于定义websocket使用的http协议;心跳支持;业务处理的注册

  2. 编写心跳连接的HeartBeatHandler,判断是读空闲、写空闲还是channel关闭

  3. 编写业务处理的OrderHandler。参考3.3.2小节。

  4. 编写启动netty的NettyBooter,在服务器端启动时启动netty。

3.3.2 OrderHandler
  1. 定义消息数据结构。
  2. 连接消息。用于绑定用户、订单sn与channel。
  3. 修改订单消息。接收到消息,保存数据库;查找在线的(供货商,订单),存在则发送消息;不存在发送微信消息。
  4. 心跳消息
3.3.3 小程序端开发
  1. 定义与后端同样的数据结构
  2. 创建一个websocket连接
  3. 微信监听websocket连接事件
  4. 向服务器端发生该用户以及订单在线的消息
  5. 心跳连接
  6. 向服务器端发生修改订单的消息
  7. 接收服务器端的消息(订单变化内容)
  8. 如果websocket没有建立连接,采用http请求发送订单修改信息。(未采用此方式,原因是后端代码改动量较大。现在的消息只是回写到前端实时展示)
3.4 技术细节
3.4.1 集成wss
  • 什么是WS/WSS
    WS是Web Socket的缩写
    WebSocket 是 HTML5 一种新的协议。它实现了浏览器与服务器全双工通信,能更好的节省服务器资源和带宽并达到实时通讯,它建立在 TCP 之上,同 HTTP 一样通过 TCP 来传输数据,但是它和 HTTP 最大不同是:
    WebSocket 是一种双向通信协议,在建立连接后,WebSocket 服务器和 Browser/Client Agent 都能主动的向对方发送或接收数据,就像 Socket 一样;WebSocket 需要类似 TCP 的客户端和服务器端通过握手连接,连接成功后才能相互通信。
    WSS是Web Socket Secure的缩写即WebSocket加密版本。
  • 为何使用WS/WSS
    随着互联网的蓬勃发展,各种类型的WEB应用层出不穷,很多应用要求服务端有能力进行实时推送能力(比如直播间聊天室),以往很多网站为了实现推送技术,所用的技术都是轮询。轮询是在特定的的时间间隔(如每1秒),由浏览器对服务器发出HTTP请求,然后由服务器返回最新的数据给客户端的浏览器。这种传统的模式带来很明显的缺点,即浏览器需要不断的向服务器发出请求,然而HTTP请求可能包含较长的头部,其中真正有效的数据可能只是很小的一部分,显然这样会浪费很多的带宽等资源。
    在这种情况下,HTML5定义了WebSocket协议,能更好的节省服务器资源和带宽,并且能够更实时地进行通讯。
    WebSocket实现了浏览器与服务器全双工(full-duplex)通信—允许服务器主动发送信息给客户端。

WebSocket协议的交互过程如下:
在这里插入图片描述

  • 如何在阿里云负载均衡SLB上启用WS/WSS支持
    无需配置,当选用HTTP监听时,默认支持无加密版本WebSocket协议;当选择HTTPS监听时,默认支持加密版本的WebSocket协议,即WSS;
    在这里插入图片描述

  • 限制于约束
    负载均衡与ECS后端服务的连接采用HTTP/1.1,建议后端服务器采用支持HTTP/1.1的WebServer
    若负载均衡与后端服务超过60秒无消息交互,会主动断开连接,如需要维持连接一直不中断,需要主动实现保活机制,每60秒内进行一次报文交互

  • wss 服务器端采用https,因此直接配置小程序端为wss协议即可。以上资料参考[6][7]。

  1. netty服务端并发量测试-未完待续

4 参考文章

  1. 浅谈Websocket、Ajax轮询和长连接(long pull)
  2. Netty百度百科介绍
  3. Java核心(五)深入理解BIO、NIO、AIO
  4. 理解Netty中的零拷贝(Zero-Copy)机制
  5. Netty Reactor模型
  6. http/https与websocket的ws/wss的关系
  7. 阿里云负载均衡支持HTTP/2、WSS协议(现已全地域覆盖)
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值