在以前的 SpringBoot 整合長鏈接心跳機制 一文中認識了 Netty。java
但其實只是能用,爲何要用 Netty?它有哪些優點?這些其實都不清楚。git
本文就來從歷史源頭說道說道。github
在 Netty 以及 NIO 出現以前,咱們寫 IO 應用其實用的都是用 java.io.*
下所提供的包。 bootstrap
好比下面的僞代碼:多線程
ServeSocket serverSocket = new ServeSocket(8080); Socket socket = serverSocket.accept() ; BufferReader in = .... ; String request ; while((request = in.readLine()) != null){ new Thread(new Task()).start() }
<!--more-->併發
大概是這樣,其實主要想表達的是:這樣一個線程只能處理一個鏈接。異步
若是是 100 個客戶端鏈接那就得開 100 個線程,1000 那就得 1000 個線程。socket
要知道線程資源很是寶貴,每次的建立都會帶來消耗,並且每一個線程還得爲它分配對應的棧內存。高併發
即使是咱們給 JVM 足夠的內存,大量線程所帶來的上下文切換也是受不了的。oop
而且傳統 IO 是阻塞模式,每一次的響應必須的是發起 IO 請求,處理請求完成再同時返回,直接的結果就是性能差,吞吐量低。
所以業界經常使用的高性能 IO 模型是 Reactor
。
它是一種異步、非阻塞的事件驅動模型。
一般也表現爲如下三種方式:
從圖中能夠看出:
它是由一個線程來接收客戶端的鏈接,並將該請求分發到對應的事件處理 handler 中,整個過程徹底是異步非阻塞的;而且徹底不存在共享資源的問題。因此理論上來講吞吐量也還不錯。
但因爲是一個線程,對多核 CPU 利用率不高,一旦有大量的客戶端鏈接上來性能必然降低,甚至會有大量請求沒法響應。
最壞的狀況是一旦這個線程哪裏沒有處理好進入了死循環那整個服務都將不可用!
所以產生了多線程模型。
其實最大的改進就是將原有的事件處理改成了多線程。
能夠基於 Java 自身的線程池實現,這樣在大量請求的處理上性能提示是巨大的。
雖然如此,但理論上來講依然有一個地方是單點的;那就是處理客戶端鏈接的線程。
由於大多數服務端應用或多或少在鏈接時都會處理一些業務,如鑑權之類的,當鏈接的客戶端愈來愈多時這一個線程依然會存在性能問題。
因而又有了下面的線程模型。
該模型將客戶端鏈接那一塊的線程也改成多線程,稱爲主線程。
同時也是多個子線程來處理事件響應,這樣不管是鏈接仍是事件都是高性能的。
以上談了這麼多其實 Netty 的線程模型與之的相似。
咱們回到以前 SpringBoot 整合長鏈接心跳機制TCP-Heartbeat/) 中的服務端代碼:
private EventLoopGroup boss = new NioEventLoopGroup(); private EventLoopGroup work = new NioEventLoopGroup(); /** * 啓動 Netty * * @return * @throws InterruptedException */ @PostConstruct public void start() throws InterruptedException { ServerBootstrap bootstrap = new ServerBootstrap() .group(boss, work) .channel(NioServerSocketChannel.class) .localAddress(new InetSocketAddress(nettyPort)) //保持長鏈接 .childOption(ChannelOption.SO_KEEPALIVE, true) .childHandler(new HeartbeatInitializer()); ChannelFuture future = bootstrap.bind().sync(); if (future.isSuccess()) { LOGGER.info("啓動 Netty 成功"); } }
其實這裏的 boss 就至關於 Reactor 模型中處理客戶端鏈接的線程池。
work 天然就是處理事件的線程池了。
那麼如何來實現上文的三種模式呢?其實也很簡單:
單線程模型:
private EventLoopGroup group = new NioEventLoopGroup(); ServerBootstrap bootstrap = new ServerBootstrap() .group(group) .childHandler(new HeartbeatInitializer());
多線程模型:
private EventLoopGroup boss = new NioEventLoopGroup(1); private EventLoopGroup work = new NioEventLoopGroup(); ServerBootstrap bootstrap = new ServerBootstrap() .group(boss,work) .childHandler(new HeartbeatInitializer());
主從多線程:
private EventLoopGroup boss = new NioEventLoopGroup(); private EventLoopGroup work = new NioEventLoopGroup(); ServerBootstrap bootstrap = new ServerBootstrap() .group(boss,work) .childHandler(new HeartbeatInitializer());
相信你們一看也明白。
其實看過了 Netty 的線程模型以後可否對咱們平時作高性能應用帶來點啓發呢?
我認爲是能夠的:
無非也就是這些,只是作了這些以後就會帶來其餘問題:
這就是一個博弈的過程,想要作到一個儘可能高效的應用是須要不斷磨合試錯的。
上文相關的代碼:
https://github.com/crossoverJie/netty-action
歡迎關注公衆號一塊兒交流: