先轉發,加關注後私信“netty源碼”即可免費獲取源碼
基於netty4,protoBuff的netty心跳服務器支持鑑權、斷線重連、其他業務模塊開發、支持圖形化實時監控,可直接投入生產
Netty心跳服務器
Springboot2.0.8集成 netty4 ,使用protobuf作為ping的數據交換,比json更加的小巧,佔用數據量更小,可用於任何第三方應用做心跳監控
應用場景
比如公司可以使用的一種業務場景是這樣的: 現有多臺服務器,有a業務的 有b業務的 有c業務的 有d業務項目等等,有時候用戶量大或者其他因素,這些服務器down掉了,大多數情況都是基於運維監控報警的,但是由於項目都是多臺機器部署同一個項目,項目掛了後centos主機並不能很快檢查出來,一般時間會比較長在半小時之內,甚至有的時候是用戶來打電話告知項目down掉了,這對公司來說影響多麼的大。 掛上了項目心跳後,可以實時看到100多臺機器的實時ping值是否正常,當然你也可以通過定時請求http接口來判斷是否應用正常,熟知http接口的請求耗費資源,使用本項目的ping或者其他鑑權,protobuff基本上消耗網路開銷非常的小,響應快,netty的長連接上線下立馬感知,性能穩定好。
開發環境
- JDK8+
- IntelliJ IDEA
- springboot 2.0.8.RELEASE
- Netty4.1.6.Final
- protobuf-java3.3.0
- Redis
- Thymeleaf模板
- Echarts圖表
- Tomcat8.5
項目說明
- 已完成功能
- 客戶端授權驗證(基於protoBuff)
- 心跳檢測(基於protoBuff)
- 斷線重連
- 計算ping值(支持到微秒)
- 其他子業務模塊(架子已搭進去)
- 為了方便測試,項目已經支持跨域訪問
說明
Netty是一個NIO客戶端服務器框架,可以快速輕鬆地開發協議服務器和客戶端等網絡應用程序。它極大地簡化並簡化了TCP和UDP套接字服務器等網絡編程。 “快速簡便”並不意味著最終的應用程序會受到可維護性或性能問題的影響。Netty經過精心設計,具有豐富的協議,如FTP,SMTP,HTTP以及各種二進制和基於文本的傳統協議。因此,Netty成功地找到了一種在不妥協的情況下實現易於開發,性能,穩定性和靈活性的方法。4x(推薦,穩定,jdk1.6+)
幾個常見的類
- ServerBootstrap
netty服務器的幫助類
- NioEventLoopGroup
處理I/O操作的多線程事件循環相當於一組線程池,服務器一般需要指定兩個NioEventLoopGroup,一個作為監控tcp連接的,一個作為處理io事件的,前者默認1個線程就可以,後者最好是cpu核心數的2倍 客戶端用一個就夠了
- NioServerSocketChannel
主要是server端接收建立SocketChannel用的
- NioSocketChannel
主要是客戶端接收SocketChannel用的
- ChannelInboundHandlerAdapter
可以重寫的各種事件處理程序方法,包括channelRead()、exceptionCaught()等方法
- SimpleChannelInboundHandler
可以重寫的各種事件處理程序方法,包括channelRead0()方法 這個可以跟指定的消息類型比上面的,如果自定義的消息類型用這個稍微多一點
- ChannelPipeline
存放各種處理器,包括解碼器,編碼器等自定義處理器,idleStateHandler一定要放在第一個,傳送數據時,編碼器和解碼器一定要放在前面,這個加載是分順序的
- ChannelHandlerContext
ChannelHandlerContext的writeAndFlush和ChannelHandlerContext.channel().writeAndFlush()是有區別的 前者是從當前hanler從後往前找OutputboundHandler,然後交給它執行的,後者是從最後一個開始執行handler的,最常見就是寫錯pipeline中的順序後,客戶端或服務器發消息就收不到了
- ChannelInitializer
客戶端和服務器都要用這個的SocketChannel,初始化的時候,加載ChannelPipeline
- Bootstrap
客戶端的連接幫助類
- idleStateHandler
Netty 可以使用 IdleStateHandler 來實現連接管理,當連接空閒時間太長(沒有發送、接收消息)時則會觸發一個事件,我們便可在該事件中實現心跳機制
基本以上的幾個類就可以滿足日常的80%需求了,剩下就是書寫各自的業務
心跳服務器實現詳細過程
- 客戶端網絡空閒5秒沒有進行寫操作是,進行發送一次ping心跳給服務端;用自定義的消息格式類,其中包含驗證auth、ping、pong等類型來代表不同的業務,首次連接時候在ChannelInboundHandlerAdapter中可以建立連接,客戶端會發送一次auth類型的授權信息(用戶名+鹽值密碼),服務器收到後作出校驗響應,只有收到服務器的auth_ack響應客戶端才能做出連接,否則斷開當前鏈接,釋放鏈路;
- 客戶端如果在下一個發送ping心跳週期來臨時,還沒有收到服務端pong的心跳應答,則失敗心跳計數器加1;
- 每當客戶端收到服務端的pong心跳應答後,失敗心跳計數器清零;
- 如果連續超過3次沒有收到服務端的心跳回復,則斷開當前連接,在5秒後進行重連操作,直到重連成功,否則每隔5秒又會進行重連;
- 服務端網絡空閒狀態到達10秒後,服務端心跳失敗計數器加1;
- 只要收到客戶端的ping消息,服務端心跳失敗計數器清零;
- 服務端連續3次沒有收到客戶端的ping消息後,將關閉鏈路,釋放資源,等待客戶端重連;
心跳相關驗證
當客戶端超過三次沒有收到服務器pong,客戶端主動斷開當前channel連接,重連服務器
當服務器超過三次沒有收到客戶端ping,服務器主動斷開當前客戶端的channel連接
ping值設計過程
ping值是基於客戶端來的,即客戶端發送ping之後,會先記錄當前的時間(默認單位是ms),支持微秒的方法,當客戶端再次收到響應後,記錄當前時間a,因為數據傳輸雙方時間是相同的,所以拿當前時間b-a,再除以2就是ping值了。然後把這個ping值,記錄到redis中去。這裡使用的是 System.currentTimeMillis(),用new Date().getTime()時間會出錯,不知道為啥?看源碼是一樣的邏輯不大理解
起初是基於服務器計時的,但是服務器和客戶端不可能在一臺主機上,導致時間存在偏差,ping值就不準確了。 存到redis主要是為了使echarts訪問最近幾次的ping狀態。起初想做成websocket的,但是這樣的話,第一次進來當前頁面數據會不全,而且不好日後做歷史ping的統計
物聯網定製開發
先轉發,加關注後私信“netty源碼”即可免費獲取源碼
閱讀更多 大數據java架構師 的文章