《netty入门与实战》笔记-02:服务端启动流程
為什么80%的碼農都做不了架構師?>>> ??
1.服務端啟動流程
這一小節,我們來學習一下如何使用 Netty 來啟動一個服務端應用程序,以下是服務端啟動的一個非常精簡的 Demo:
NettyServer.java
public class NettyServer {public static void main(String[] args) {NioEventLoopGroup bossGroup = new NioEventLoopGroup();NioEventLoopGroup workerGroup = new NioEventLoopGroup();ServerBootstrap serverBootstrap = new ServerBootstrap();serverBootstrap.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class).childHandler(new ChannelInitializer<NioSocketChannel>() {protected void initChannel(NioSocketChannel ch) {}});serverBootstrap.bind(8000);} }- 首先看到,我們創建了兩個NioEventLoopGroup,這兩個對象可以看做是傳統IO編程模型的兩大線程組,bossGroup表示監聽端口,accept 新連接的線程組,workerGroup表示處理每一條連接的數據讀寫的線程組。用生活中的例子來講就是,一個工廠要運作,必然要有一個老板負責從外面接活,然后有很多員工,負責具體干活,老板就是bossGroup,員工們就是workerGroup,bossGroup接收完連接,扔給workerGroup去處理。
- 接下來 我們創建了一個引導類 ServerBootstrap,這個類將引導我們進行服務端的啟動工作,直接new出來開搞。
- 我們通過.group(bossGroup, workerGroup)給引導類配置兩大線程組,這個引導類的線程模型也就定型了。
- 然后,我們指定我們服務端的 IO 模型為NIO,我們通過.channel(NioServerSocketChannel.class)來指定 IO 模型,當然,這里也有其他的選擇,如果你想指定 IO 模型為 BIO,那么這里配置上OioServerSocketChannel.class類型即可,當然通常我們也不會這么做,因為Netty的優勢就在于NIO。
- 接著,我們調用childHandler()方法,給這個引導類創建一個ChannelInitializer,這里主要就是定義后續每條連接的數據讀寫,業務處理邏輯,不理解沒關系,在后面我們會詳細分析。ChannelInitializer這個類中,我們注意到有一個泛型參數NioSocketChannel,這個類呢,就是 Netty 對 NIO 類型的連接的抽象,而我們前面NioServerSocketChannel也是對 NIO 類型的連接的抽象,NioServerSocketChannel和NioSocketChannel的概念可以和 BIO 編程模型中的ServerSocket以及Socket兩個概念對應上
我們的最小化參數配置到這里就完成了,我們總結一下就是,要啟動一個Netty服務端,必須要指定三類屬性,分別是線程模型、IO 模型、連接讀寫處理邏輯,有了這三者,之后在調用bind(8000),我們就可以在本地綁定一個 8000 端口啟動起來,以上這段代碼讀者可以直接拷貝到你的 IDE 中運行。
2.自動綁定遞增端口
在上面代碼中我們綁定了 8000 端口,接下來我們實現一個稍微復雜一點的邏輯,我們指定一個起始端口號,比如 1000,然后呢,我們從1000號端口往上找一個端口,直到這個端口能夠綁定成功,比如 1000 端口不可用,我們就嘗試綁定 1001,然后 1002,依次類推。
serverBootstrap.bind(8000);這個方法呢,它是一個異步的方法,調用之后是立即返回的,他的返回值是一個ChannelFuture,我們可以給這個ChannelFuture添加一個監聽器GenericFutureListener,然后我們在GenericFutureListener的operationComplete方法里面,我們可以監聽端口是否綁定成功,接下來是監測端口是否綁定成功的代碼片段
serverBootstrap.bind(8000).addListener(new GenericFutureListener<Future<? super Void>>() {public void operationComplete(Future<? super Void> future) {if (future.isSuccess()) {System.out.println("端口綁定成功!");} else {System.err.println("端口綁定失敗!");}} });我們接下來從 1000 端口號,開始往上找端口號,直到端口綁定成功,我們要做的就是在 if (future.isSuccess())的else邏輯里面重新綁定一個遞增的端口號,接下來,我們把這段綁定邏輯抽取出一個bind方法
private static void bind(final ServerBootstrap serverBootstrap, final int port) {serverBootstrap.bind(port).addListener(new GenericFutureListener<Future<? super Void>>() {public void operationComplete(Future<? super Void> future) {if (future.isSuccess()) {System.out.println("端口[" + port + "]綁定成功!");} else {System.err.println("端口[" + port + "]綁定失敗!");bind(serverBootstrap, port + 1);}}}); }然后呢,以上代碼中最關鍵的就是在端口綁定失敗之后,重新調用自身方法,并且把端口號加一,然后,在我們的主流程里面,我們就可以直接調用
bind(serverBootstrap, 1000)端口成功綁定了在1024,從 1000 開始到 1023,端口均綁定失敗了,這是因為在我的 MAC 系統下,1023 以下的端口號都是被系統保留了,需要 ROOT 權限才能綁定。
以上就是自動綁定遞增端口的邏輯,接下來,我們來一起學習一下,服務端啟動,我們的引導類ServerBootstrap除了指定線程模型,IO 模型,連接讀寫處理邏輯之外,他還可以干哪些事情?
3.服務端啟動其他方法
handler() 方法
serverBootstrap.handler(new ChannelInitializer<NioServerSocketChannel>() {protected void initChannel(NioServerSocketChannel ch) {System.out.println("服務端啟動中");} })handler()方法呢,可以和我們前面分析的childHandler()方法對應起來,childHandler()用于指定處理新連接數據的讀寫處理邏輯,handler()用于指定在服務端啟動過程中的一些邏輯,通常情況下呢,我們用不著這個方法。
attr() 方法
serverBootstrap.attr(AttributeKey.newInstance("serverName"), "nettyServer")attr()方法可以給服務端的 channel,也就是NioServerSocketChannel指定一些自定義屬性,然后我們可以通過channel.attr()取出這個屬性,比如,上面的代碼我們指定我們服務端channel的一個serverName屬性,屬性值為nettyServer,其實說白了就是給NioServerSocketChannel維護一個map而已,通常情況下,我們也用不上這個方法。
那么,當然,除了可以給服務端 channel NioServerSocketChannel指定一些自定義屬性之外,我們還可以給每一條連接指定自定義屬性
childAttr() 方法
serverBootstrap.childAttr(AttributeKey.newInstance("clientKey"), "clientValue")上面的childAttr可以給每一條連接指定自定義屬性,然后后續我們可以通過channel.attr()取出該屬性。
childOption() 方法
serverBootstrap.childOption(ChannelOption.SO_KEEPALIVE, true).childOption(ChannelOption.TCP_NODELAY, true)childOption()可以給每條連接設置一些TCP底層相關的屬性,比如上面,我們設置了兩種TCP屬性,其中
- ChannelOption.SO_KEEPALIVE表示是否開啟TCP底層心跳機制,true為開啟
- ChannelOption.TCP_NODELAY表示是否開始Nagle算法,true表示關閉,false表示開啟,通俗地說,如果要求高實時性,有數據發送時就馬上發送,就關閉,如果需要減少發送次數減少網絡交互,就開啟。
其他的參數這里就不一一講解,有興趣的同學可以去這個類里面自行研究。
option() 方法
除了給每個連接設置這一系列屬性之外,我們還可以給服務端channel設置一些屬性,最常見的就是so_backlog,如下設置
serverBootstrap.option(ChannelOption.SO_BACKLOG, 1024)表示系統用于臨時存放已完成三次握手的請求的隊列的最大長度,如果連接建立頻繁,服務器處理創建新連接較慢,可以適當調大這個參數
4.總結
- 本文中,我們首先學習了 Netty 服務端啟動的流程,一句話來說就是:創建一個引導類,然后給他指定線程模型,IO模型,連接讀寫處理邏輯,綁定端口之后,服務端就啟動起來了。
- 然后,我們學習到 bind 方法是異步的,我們可以通過這個異步機制來實現端口遞增綁定。
- 最后呢,我們討論了 Netty 服務端啟動額外的參數,主要包括給服務端 Channel 或者客戶端 Channel 設置屬性值,設置底層 TCP 參數。
以上內容來源于掘金小冊《Netty 入門與實戰:仿寫微信 IM 即時通訊系統》,若想獲得更多,更詳細的內容,請用微信掃碼訂閱:
轉載于:https://my.oschina.net/funcy/blog/2242215
總結
以上是生活随笔為你收集整理的《netty入门与实战》笔记-02:服务端启动流程的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 梦到隔壁着火是什么意思
- 下一篇: 做梦梦到狗吃蛇是什么意思