浅谈HP-Socket在物联网的应用

原文链接:https://my.oschina.net/chrisforbt/blog/1669746

一、应用背景

    去年公司成立了个项目——《智慧用电安全隐患监管服务平台》,计划是开发一款设备,能够安装在电箱里面,用于实时监控电线的温度、漏电、电流、烟雾等信息。如果检测到有问题,那么就马上发送推送到手机和PC web,同时也有短信通知和电话语音通知。当时听到这个项目,笔者是觉得十分有意义的,毕竟没有比人命更珍贵的东西了。如果能够做出这样一个系统,让人们不用再担心生活中的火灾威胁,那绝对是功德无量。

二、架构设计

   仔细分析了这个系统之后,服务器需要实现的功能如下:

    1、接收前端设备发送过来的数据;

    2、解析采集数据,根据数据状态做不同的处理(如存储、报警、推送等等);

    3、提供APP和WEB PC 相应的数据API;

    4、接受APP和WEB PC端的控制指令,往相应的设备发送指令。

    由于设备采集和业务逻辑两部分需要的硬件条件不一样:采集端的业务简单,不需要耗那么多内存和CPU,但会比较耗端口号;业务逻辑需要耗内存和CPU,端口号问题可以通过负载均衡解决。所以避免互相干扰,就决定把这两部分分开来。

    基于第4点,需要APP和PC WEB实时控制设备。用UDP协议的话可能会丢包,而且需要设备时不时唤醒一下,更新下状态,避免IP变了控制不到。那么要用UDP来保证控制的实时性,可能比较高难度。于是便选用TCP。

    那么架构设计目前就是:TCP服务器接收前端采集数据,通过内网发送给业务逻辑服务器处理。APP和PC WEB发送控制指令给业务逻辑服务器,先找到对应的设备TCP连接,然后通知TCP服务器将控制指令下发给设备。

二、选择框架

    由于市面上有很多现成框架,所以笔者也不打算重复造轮子(之前造过,性能不怎么高,凑合能用)。基于前期一直从事C/C++开发,脚本语言也只会lua、shell。所以这次选择框架,首先就从C/C++入手。那么为什么会选择HP-Socket呢?当时Google出来是一堆的,唯独HP-Socket是被贴上了“国产优秀网络通信框架”和“国内最火的开源项目”,接着便去研究了下。经过一个星期的实验,发现实在是非常强大,便决定用HP-Socket来做TCP服务器了。

三、开发环境

    硬件环境:双核2.36GHz CPU/4G内存/50G硬盘/4M带宽 PC机

    软件环境:64位Windows Server2012R2、VS2017、HP-Socket 4.3.1

四、技术细节

    1、数据解析

        前端采集数据将数据打包为C结构体,然后通过2G网络发送给服务器。TCP接收数据的时候,无可避免的一个问题便是粘包。网络传输过程中,数据包超过某长度会MTU分片(具体可参考:https://blog.csdn.net/singular2611/article/details/52513406)。那么服务器接收到的一个包,可能是一个完整包、半个包或者一个半包,还好贴心HP-Socket作者已经帮我们考虑好,有3种接收模型可选:PACK、PULL和PUSH模型(全自动、半自动和手工解决粘包问题)。笔者的通信数据结构有多种,于是便选择了PULL(半自动)模型。每次先 PEEK,如果够一个完整包便提取出来,不够则等待下次OnReceive触发。

    2、数据处理

        基本上所有业务逻辑都靠OnReceive这个事件触发,所以OnReceive里面如果有耗时操作,那么将会严重影响整个系统的性能。笔者之前曾经在OnReceive发起了Http请求,当并发量到200时,直接就不能用了。

        所以这里推荐业务逻辑尽量另外开线程处理,也尽量不要在OnReceive中使用公共变量(锁会降低效率)。因为HP-Socket给每个连接分配了个extraData,利用这个特性可以解决很多问题了。业务线程的触发,笔者用的是无锁队列。OnReceive负责生产数据,然后往无锁队列中填充,业务线程读取无锁队列时,如果没数据则阻塞,有数据则一次性取出(最好也限制下大小,那样CPU不会忽高忽低)。

      从无锁队列取出的数据就可以处理后发送给业务逻辑服务器了。

    3、数据发送

        数据发送部分,笔者刚开始是使用http协议,直接把数据POST给业务逻辑服务器。HP-Socket框架自带的http协议接口,当时笔者没有使用对,导致很多问题,尤其是内网连接不上,于是便换成libcurl了(http client的杠把子,十分给力)。但这样会造成一个问题,便是每次http请求都是短链接,系统不可避免地产生了很多TIME_WAIT。当TIME_WAIT个数达到16384时,整个系统就瘫痪了,得要等2ML后才恢复(随后又堵)。所以这个方案只能撑一时,不能撑一世。

        由于笔者的业务逻辑服务器是Linux系统,刚刚好HP-Socket的Linux版面世,马上拿来写了个数据接收服务器,作为采集端和业务逻辑端的缓冲层。采集服务器的数据直接通过TCP发送给缓冲层,那么TIME_WAIT问题就不复存在了。缓冲层将数据存储在内存数据库中,并通知业务逻辑服务器去处理,稳稳的幸福。

五、困难与解决过程

    1、HP-Socket入门问题

        估计这个问题是大多数人都会遇到的,文档比较粗糙,群里面的大牛面对新手们无限重复的简单问题也懒得回答,怪兽大大喜欢发吴莫愁,Demo只有MFC版本,编译不过去等等问题。笔者当时也花了几天时间才编译过去,毕竟之前一直用vim,都是用gcc和makefile,VS2017不熟悉,所以会出现这样那样的问题。

        文档虽然粗糙,但是确实还是挺有用的,尤其是最后的QA,笔者看了6遍后才成功把自己想要的信息提取出来,相信HP-Socket的普及面广了之后会出份详细文档。

        新手们提问题时建议把问题捋好,如“HP-Socket怎么编译”之类的问题就不要问了,不是大牛不回答,是很难回答,大家都要工作生活,时间真的不多。

        除了文档,Demo代码也是很有参考价值,笔者没有开发过MFC,有的Demo也没编译过去,但是要相信作者们发布出来肯定是正确的(出了问题肯定是自己没用对),看思路就好。

    2、服务器性能问题

        HP-Socket的性能是毋庸置疑的了,如果服务器性能不高,建议从以下几点入手:

        a)、OnReceive里面是否有阻塞操作;

        b)、HP-Socket是IOCP服务器,所以一般CPU的使用率都很低,跑简单逻辑估计就3~5%的使用率。如果CPU使用率很高,就要看看代码复杂部分是否有bug;

        c)、如果服务器有HTTP client的逻辑,要看看TIME_WAIT的数量(Windows指令:netstat -ano | findstr "TIME_WAIT" | find /C "端口号"),一般系统TIME_WAIT到达16384个就满了,没有多余的端口能够往该端口号发送数据;

        d)、如果内存涨得比较快,而且不降,那么就要考虑内存泄露问题了,但良好的编程习惯一般不会导致内存泄露问题。这时候要检查代码中的慢I/O操作(也就是耗时操作),慢I/O操作处理不善,容易导致生产速度大于消费速度,那么内存就会一直堆上去了。

六、总结

        使用HP-Socket这半年多时间,除了刚刚开头入门的难度高,后面给笔者带来的是无尽的惊喜,其效率与稳定性完全适合物联网之类的项目。比如说在今年春节,公司尚未招到运营,那么维护服务器的工作就落在笔者身上,而作为一名热爱生活的开发者,春节肯定选择去嗨。晾了服务器一个月,怀着忐忑不安的心理看了下服务器监控,只能用“稳如狗”来形容了。CPU一直在10%~15%间波动,内存维持在1500Mb,内网传输曲线与外网如带宽的一致,如下图所示:

 

                                        

                                                              图1 7天CPU监控图

                    

                                                            图2  7天内存监控图

                      图3 7天内网带宽监控图                                      图4 7天外网带宽监控图

        笔者没有发布各种服务器性能测试之类的报告,因为这些一般人都很难懂,不利于服务器推广,只能用最简单的言语表达下笔者对HP-Socket的喜爱,如有不妥之处,求轻拍。

                                                                          ——蔡剑彬(caijianbin668943@163.com)    热爱生活的架构师 

posted @ 2019-06-04 11:58  DarJeely  阅读(3900)  评论(0编辑  收藏  举报