在java界,netty無疑是開發網絡應用的拿手菜。你不須要太多關注複雜的nio模型和底層網絡的細節,使用其豐富的接口,能夠很容易的實現複雜的通信功能。java
和golang的網絡模塊相比,netty仍是太過臃腫。不過java類框架就是這樣,屬於那種離了IDE就沒法存活的編碼語言。mysql
最新的netty版本將模塊分的很是細,若是不清楚每一個模塊都有什麼內容,直接使用netty-all
便可。linux
單純從使用方面來講,netty是很是簡單的,掌握ByteBuf、Channel、Pipeline、Event模型等,就能夠進行開發了。你會發現面試netty相關知識,沒得聊。但Netty與其餘開發模式很大不一樣,最主要的就是其異步化。異步化形成的後果就是編程模型的不一樣,同時有調試上的困難,對編碼的要求比較高,由於bug的代價與業務代碼的bug代價不可同日而語。golang
但從項目來講,麻雀雖小五臟俱全,從業務層到服務網關,以及各類技術保障,包括監控和配置,都是須要考慮的因素。netty自己佔比很小。 本文將說明使用netty開發,都關注哪些通用的內容,而後附上單機支持100w鏈接的linux配置。本文並不關注netty的基礎知識。面試
網絡開發中最重要的就是其通信格式,協議。咱們常見的protobuf、json、avro、mqtt等,都屬於此列。協議有語法、語義、時序三個要素。 redis
我見過不少中間件應用,採用的是redis協議,然後端落地的倒是mysql;也見過更多的採用mysql協議實現的各類自定義存儲系統,好比proxy端的分庫分表中間件、tidb等。sql
咱們經常使用的redis,使用的是文本協議;mysql等實現的是二進制協議。放在netty中也是同樣,實現一套codec便可(繼承Decoder或Encoder系列)。netty默認實現了dns、haproxy、http、http二、memcache、mqtt、redis、smtp、socks、stomp、xml等協議,能夠說是很全了,直接拿來用很爽。編程
一個可能的產品結構會是這樣的,對外提供一致的外觀,核心存儲卻不一樣: 文本協議在調試起來是比較直觀和容易的,但安全性欠佳;而二進制協議就須要依賴日誌、wireshark等其餘方式進行分析,增長了開發難度。傳說中的粘包拆包,就在這裏處理。而形成粘包的緣由,主要是因爲緩衝區的介入,因此須要約定雙方的傳輸概要等信息,netty在必定程度上解決了這個問題。json
每個想要開發網絡應用的同窗,內心都埋了一顆從新設計協議的夢想種子。但協議的設計能夠說是很是困難了,要深耕相應業務,還要考慮其擴展性。如沒有特別的必要,建議使用現有的協議。後端
作Netty開發,鏈接管理功能是很是重要的。通訊質量、系統狀態,以及一些黑科技功能,都是依賴鏈接管理功能。 不管是做爲服務端仍是客戶端,netty在建立鏈接以後,都會獲得一個叫作Channel
的對象。咱們所要作的,就是對它的管理,我習慣給它起名叫作ConnectionManager
。
管理類會經過緩存一些內存對象,用來統計運行中的數據。好比面向鏈接的功能:包發送、接收數量;包發送、接收速率;錯誤計數;鏈接重連次數;調用延遲;鏈接狀態等。這會頻繁用到java中concurrent包
的相關類,每每也是bug集中地。
但咱們還須要更多,管理類會給予每一個鏈接更多的功能。好比,鏈接建立後,想要預熱一些功能,那這些狀態就能夠參與路由的決策。一般狀況下,將用戶或其餘元信息也attach到鏈接上,可以多維度的根據條件篩選一些鏈接,進行批量操做,好比灰度、過載保護等,是一個很是重要的功能。
管理後臺能夠看到每一個鏈接的信息,篩選到一個或多個鏈接後,可以開啓對這些鏈接的流量錄製、信息監控、斷點調試,你能體驗到掌控一切的感受。
管理功能還可以看到系統的整個運行狀態,及時調整負載均衡策略;同時對擴容、縮容提供數據依據。
應用協議層的心跳是必須的,它和tcp keepalive是徹底不一樣的概念。
應用層協議層的心跳檢測的是鏈接雙方的存活性,兼而鏈接質量,而keepalive
檢測的是鏈接自己的存活性。並且後者的超時時間默認過長,徹底不能適應現代的網絡環境。 心跳就是靠輪訓,不管是服務端,仍是客戶端好比GCM
等。保活機制會在不一樣的應用場景進行動態的切換,好比程序喚起和在後臺,輪訓的策略是不同的。
Netty內置經過增長IdleStateHandler
產生IDLE
事件進行便捷的心跳控制。你要處理的,就是心跳超時的邏輯,好比延遲重連。但它的輪訓時間是固定的,沒法動態修改,高級功能須要本身定製。
在一些客戶端好比Android,頻繁心跳的喚起會浪費大量的網絡和電量,它的心跳策略會更加複雜一些。
Java的優雅停機一般經過註冊JDK ShutdownHook來實現。
Runtime.getRuntime().addShutdownHook();
通常經過kill -15
進行java進程的關閉,以便在進程死亡以前進行一些清理工做。
注意:kill -9 會立馬殺死進程,不給遺言的機會,比較危險。
雖然netty作了不少優雅退出的工做,經過EventLoopGroup
的shutdownGracefully
方法對nio進行了一些狀態設置,但在不少狀況下,這還不夠多。它只負責單機環境的優雅關閉。
流量可能還會經過外層的路由持續進入,形成無效請求。個人一般作法是首先在外層路由進行一次本地實例的摘除,把流量截斷,而後再進行netty自己的優雅關閉。這種設計很是簡單,即便沒有重試機制也會運行的很好,前提是在路由層須要提早暴露相關接口。
netty因爲其異步化的開發方式,以及其事件機制,在異常處理方面就顯得異常重要。爲了保證鏈接的高可靠性,許多異常須要靜悄悄的忽略,或者在用戶態沒有感知。
netty的異常會經過pipeline進行傳播,因此在任何一層進行處理都是可行的,但編程習慣上,習慣性拋到最外層集中處理。
爲了最大限度的區別異常信息,一般會定義大量的異常類,不一樣的錯誤會拋出不一樣的異常。發生異常後,能夠根據不一樣的類型選擇斷線重連(好比一些二進制協議的編解碼紊亂問題),或者調度到其餘節點。
網絡應用就該幹網絡應用的事,任何通信都是昂貴的。在《Linux之《荒島餘生》(五)網絡篇》中,咱們談到百萬鏈接的服務器,廣播一個1kb消息,就須要1000M的帶寬,因此並非什麼均可以放在網絡應用裏的。
一個大型網絡應用的合理的思路就是值發送相關指令
。客戶端在收到指令之後,經過其餘方式,好比http,進行大型文件到獲取。不少IM的設計思路就是如此。
指令模式還會讓通信系統的擴展性和穩定性獲得保證。增長指令能夠是配置式的,當即生效,服務端不須要編碼重啓。
網絡應用的流量通常都是很是大的,並不適合全量日誌的開啓。應用應該只關注主要事件的日誌,關注異常狀況下的處理流程,日誌要打印有度。
網絡應用也不適合調用其餘緩慢的api,或者任何阻塞I/O的接口。一些實時的事件,也不該該經過調用接口吐出數據,能夠走高速mq等其餘異步通道。
緩存多是網絡應用裏用的最多的組件。jvm內緩存能夠存儲一些單機的統計數據,redis等存儲一些全局性的統計和中間態數據。 網絡應用中會大量使用redis、kv、高吞吐的mq,用來快速響應用戶請求。總之,儘可能保持通信層的清爽,你會省去不少憂慮。
單機支持100萬鏈接是可行的,但帶寬問題會成爲顯著的瓶頸。啓用壓縮的二進制協議會節省部分帶寬,但開發難度增長。
和《LWP進程資源耗盡,Resource temporarily unavailable》中提到的ES配置同樣,優化都有相似的思路。這份配置,能夠節省你幾天的時間,請收下!
更改進程最大文件句柄數
ulimit -n 1048576
修改單個進程可分配的最大文件數
echo 2097152 > /proc/sys/fs/nr_open
修改/etc/security/limits.conf文件
* soft nofile 1048576 * hard nofile 1048576 * soft nproc unlimited root soft nproc unlimited
記得清理掉/etc/security/limits.d/*下的配置
打開/etc/sysctl.conf
,添加配置 而後執行,使用sysctl
生效
#單個進程可分配的最大文件數 fs.nr_open=2097152 #系統最大文件句柄數 fs.file-max = 1048576 #backlog 設置 net.core.somaxconn=32768 net.ipv4.tcp_max_syn_backlog=16384 net.core.netdev_max_backlog=16384 #可用知名端口範圍配置 net.ipv4.ip_local_port_range='1000 65535' #TCP Socket 讀寫 Buffer 設置 net.core.rmem_default=262144 net.core.wmem_default=262144 net.core.rmem_max=16777216 net.core.wmem_max=16777216 net.core.optmem_max=16777216 net.ipv4.tcp_rmem='1024 4096 16777216' net.ipv4.tcp_wmem='1024 4096 16777216' #TCP 鏈接追蹤設置 net.nf_conntrack_max=1000000 net.netfilter.nf_conntrack_max=1000000 net.netfilter.nf_conntrack_tcp_timeout_time_wait=30 #TIME-WAIT Socket 最大數量、回收與重用設置 net.ipv4.tcp_max_tw_buckets=1048576 # FIN-WAIT-2 Socket 超時設置 net.ipv4.tcp_fin_timeout = 15
netty的開發工做並不集中在netty自己,更多體如今保證服務的高可靠性和穩定性上。同時有大量的工做集中在監控和調試,減小bug修復的成本。
深刻了解netty是在系統遇到疑難問題時可以深刻挖掘進行排查,或者對苛刻的性能進行提高。但對於廣大應用開發者來講,netty的上手成本小,死挖底層並不會產生太多收益。
它只是個工具,你還能讓它怎樣啊。 0.jpeg