淺談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)    熱愛生活的架構師 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM