摘要:水文监测系统是一个提供水文部门监测各类水文信息的平台。业务功能比较丰富,主要包括遥测站的定位、状态的查询、实时信息以及往年水文信息的查询等。水文监测系统需要异步处理各个遥测站发来的消息,包括信息的收集、分析与存储以及网页端的实时信息的查询。传统的水文监测服务端多采用BIO的方式接受处理数据,该文在比较传统BIO技术的基础上,采用NIO的方式处理水文类数据。通过对NIO框架Netty源码的封装与扩展,提供能接受处理客户端异步请求的通用的可重用的水文监测服务端的设计思路与具体实现。 

  关键词:水文监测;NIO;Netty框架;异步  

  1 背景 

  水文监测系统主要应用与水文部门对各个地区的降水量、河道、水库(湖泊)以及地下水等的监测与统计,从而制定出相应的对策。传统的水文监测普遍采用人工监测和有线传播等方式进行,这种方式存在很严重的弊端,人工监测的成本高,信息采集的准确度不高,并且信息的实时性也不高,这样给水文部门制定相应的策略的时候造成很大的干扰。随着科学技术的不断发展,现如今的水文监测技术已经发展的比较成熟,其中主要涉及传感器技术、嵌入式技术、通信技术、存储技术、信息处理技术以及人工智能等多种高新信息技术[1]。 

  网络编程的基本模型是Client/Server模型,也就是两个进程之间进行相互通信,其中服务端提供位置信息(绑定的IP地址和监听端口),客户端通过连接操作向服务端监听的地址发起连接请求,通过三次握手建立连接,如果连接成功,双方就可以通过网络套接字(Socket)进行通信[2]。 

  2 BIO与NIO的比较 

  传统的网络服务器使用BIO(阻塞IO)开发,多采用一连接一线程(One thread per connection)的线程模型,即每接受一个连接请求则产生一个子线程处理该请求。如图1所示,Acceptor负责监听各个Client的连接请求,当接收到Client的连接请求之后为每个Client创建一个新的线程进行处理,处理完成之后再通过输出流返回应答消息给每个Client,之后线程销毁。 

  该架构最大的问题就是不具备弹性伸缩能力,当客户端数量增加后,服务端的线程个数和并发访问数成线性正比,由于线程是JAVA虚拟机非常宝贵的系统资源,当线程数膨胀之后,系统的性能急剧下降,随着并发量的继续增加,可能会发生句柄溢出、线程堆栈溢出等问题,并导致服务器最终宕机[3]。 

  3 Netty原理及其概述 

  NIO类库和API繁杂,使用麻烦工作量和难度都非常大,并且JDK NIO本身存在BUG,例如epoll bug,它会导致Selector空轮询,最终导致CPU100%[2]。目前主流的NIO框架包括Mina、Netty、Grizzly等。Mina是Apache组织的一个开源的项目,Netty从某种程度上来说是Mina的延伸和扩展,对Mina的设计上进行了一些优化。而Grizzly由sun公司开发,专门解决多客户端访问服务器时产生的各种问题。本文通过调研,决定采用Netty作为底层源码实现服务端。 

  Netty是一款NIO的客户端服务器框架,能够快速而简单的开发出高性能的、可维护的网络应用比如协议服务器和客户端[6]。Netty 可以通过调整参数灵活配置成Reactor 单线程、多线程和主从多线程模型,用少量的线程即可以处理上万条TCP 连接,同时Netty 中集成了主流的编解码框架和灵活的自定义编解码器实现,能轻松实现私有的协议栈[5]。   同时Netty具有以下优点[2]: 

  1)对原生的NIO类库进行封装,并且预置了多种编解码器,便于使用; 

  2)同时支持阻塞以及非阻塞的socket; 

  3)支持多种主流的协议,如TCP,UDP,HTTP,SMTP等; 

  4)可扩展性高,可以通过自定义的ChannelHandler对框架进行灵活的扩展; 

  5)与其他主流NIO框架相比,性能最优。 

  4 设计与分析 

  4.1 总体设计 

  本文是以Netty[6]为底层框架,将Netty对私有协议编解码的支持封装,提供可以解析符合水利行业标准信息报文的通用型水文监测服务端。其硬件部署图如图3所示,遥测站监测数据,通过无线通信网络发送到远端中心站进行处理。 

  4.2 详细设计 

  水文监测服务端的主要任�帐谴�理与遥测站之间的数据交互,这里主要指的是各类水文信息报文。水文信息报文帧结构如表1所示,采用中华人民共和国水利部规定的水文监测数据通信规约[7] ,报文正文主要由两类报文构成,一是遥测站主动发送给服务端的报文,包括链路维持报,测试报,均匀时段水文信息报,遥测站定时报,遥测站小时报等;二是中心站查询信息报文,包括中心站查询遥测站实时数据,中心站查询遥测站时段数据,中心站查询遥测站指定要素实时数据,中心站修改遥测站基本配置数据,中心站查询遥测站状态和报警信息等。 

  4.2.1 TCP粘包/拆包 

  服务端从网络中接收到的是一系列二进制的数据,这些数据在未处理之前,用户是无法识别的,因此,对这些数据的解析就显得尤为重要。 

  数据传输的过程中,TCP底层并不了解上层的业务,在包划分时,只会根据自身缓冲区进行,因此,不能保证接收到的消息为整包消息。现假设客户端向服务端发送两个数据包B1,B2,则服务端在接收客户端数据时可能存在以下几种情况[2]: 

  1)服务端分两次收到了两个完整的独立的数据包; 

  2)服务端一次收到了两个粘在一起的包,即TCP粘包; 

  3)服务端分两次收到两个数据包,第一次的包是完整的B1和部分B2,第二次的包是余下的B2; 

  4)服务端分两次收到两个数据包,第一次是部分B1,第二次是余下的B1和完整的B2; 

  5)当数据包比较大而缓冲区比较小的时候,服务端分多次才能将B1,B2接收完全。 

  针对TCP粘包问题提出以下拆包策略: 

  6)根据特定的分隔符对报文进行分割 

  7)在包尾增加回车换行符进行分割; 

  8)消息长度固定; 

  9)根据消息中的长度字段解析报文。 

  4.2.2 业务数据编码/解码 

  ObjectEncoder和ObjectDecoder是Netty提供的一组用于POJO的序列化及反序列化即编码和解码的处理类。对象序列化是对象持久化的一种实现方法,它是将一个对象的属性和方法转化为一种序列化的格式以用于存储和传输,反序列化就是根据这些保存的信息重建对象的过程[8]。 

  当服务端向客户端发送消息时,需要对业务消息进行编码,即将实体类对象序列化为ByteBuf,不需要与TCP层打交道,也就不存在粘包问题。此时只需考虑编码问题,ObjectEncoder是Java序列化编码器,它负责将实现Serializable接口的对象序列化为byte [],然后写入到ByteBuf中用于消息的跨网络传输。编码过后的数据结构如图7所示,经过ObjectEncoder编码之后的数据包会默认在包头添加length字段默认为4Byte,用来标识数据包正文部分的长度。 

  4.2.3 业务数据处理 

  经过ObjectDecoder解码过后的数据包是一个完整的包,对应一个业务消息,需要做进一步的业务处理。MyServerHandler是继承自ChannelHandlerAdapter的一个业务处理类,并覆盖了其messageReceived、exceptionCaught等方法,用来对业务数据进行处理。MyServerHandler基于事件驱动机制,当接收到消息时,会激活messageReceived方法,获取功能码,由此来判断消息类型,然后获取校验码,做循环冗余校验,经过校验无误之后,存储到相应的数据库表中供整个系统其他模块的使用。 

  5 实现与测试 

  5.1 实现步骤 

  1)创建一组EventLoopGroup,分别用来负责接收客户端和处理客户端连接,并创建一个ServerBootstrap实例; 

  2)设置channel属性:ChannelOption.SO_BACKLOG使消息立即发出去,不用等到一定的数据量才发出去,ChannelOption.SO_KEEPALIVE保持长连接; 

  3)定制自己的ChannelPipeline,加入相应的ChannelHandler; 

  4)绑定端口,并监听在该端口上的客户端的请求并异步的处理; 

  5)开启存数据库与发送消息线程; 

  6)编写自己的ChannelHandler。 

  5.2 测试结果 

  按照实现步骤开启服务端,通过一台PC机模拟客户端,PC机的配置为:32位Win7操作系统,2GB内存,Inter Core i3处理器,通过该PC机网服务端发送消息,服务端接收结果如图9所示。 

  6 结束语 

  本文设计并实现了基于Netty的水文监测服务端,借鉴了许多开源软件的设计思想,始终保持高内聚低耦合的设计理念,为今后程序的扩展提供了方便。 

  虽然,本文所设计的服务端已经能基本满足项目的需求,但是功能上也有待进一步完善。下一步所要完善的方向为如何在多客户端连接的同时,服务端向指定客户端的消息推送问题以及网页客户端如何查询指定遥测站客户端信息的问题,同时系统的性能还需进一步优化以满足更多客户端的接入。 

  参考文献: 

  [1] 陈威, 郭书普. 中国农业信息化技术发展现状及存在的问题[J]. 农业工程学报, 2013(22): 196-204. 

  [2] 李林峰. Netty权威指南[M]. 北京: 电子工业出版社, 2014. 

  [3] 李林峰. NIO框架Netty解析[EB/OL]. (2016-04-11).http://www.jiagoushuo.com/article/1000126.html. 

  [4] 李林峰. Netty 系列之Netty 线程模型[EB/OL].( 2014-07-11)http://www.infoq.com/cn/articles/netty-threading- model. 

  [5] 代超, 邓中亮. 基于Netty的面向移动终端的推送服务设计[J]. 软件, 2015, 36(12): 01-04. 

  [6] JBoss. Netty project[CP/OL]. http://netty.io/,2012-4-1 

  [7] 中华人民共和国水利部. 水文监测数据通信规约[M]. 北京: 中国水利水电出版社, 2014. 

  [8] 郭荷清, 王增勋. XML数据绑定及对象序列化的应用研究[J]. 计算机应用与软件, 2006, 23(5). 

  [9] Norman Maurer. Netty in Action[M]. 5th ed.Connecticut: Manning Publications Co., 2013. 

  [10] Alan Bateman. New I /O in JDK 7[R]. Java-One, 2008.