Netty Bootstrap 客戶端端啟動類
1. 前言
上一節(jié),我們主要講解了一個Netty入門案例,其中無論是客戶端還是服務(wù)端的代碼編寫,都是分為 2 個核心步驟,分別是:啟動類模板代碼 + 自定義業(yè)務(wù) Handler,其中 Handler 可以根據(jù)不同的業(yè)務(wù)定義多個。
本節(jié)主要介紹 Bootstrap 客戶端啟動類的代碼含義。
2. Bootstrap 流程
客戶端啟動類的寫法都是固定模板的寫法,需要掌握幾個核心的流程,有助于理解模板代碼,具體如下:
- 指定線程模型: 通過
.group(group)
給引導(dǎo)類配置線程組,這個引導(dǎo)類的線程模型也就定型了。每個通道,也就是 Channel 都需要綁定一個線程,該線程是線程池分配的線程,專門負(fù)責(zé)處理相關(guān) Handler; - 指定 IO 模型: 我們通過
.channel(NioServerSocketChannel.class)
來指定 NIO 模型。如果指定 IO 模型為 BIO,那么這里配置上OioServerSocketChannel.class
類型即可,通常都是使用 NIO,因為 Netty 的優(yōu)勢就在于 NIO; - 指定處理邏輯器: 通過 childHandler () 方法,給這個引導(dǎo)類創(chuàng)建一個 ChannelInitializer,這里主要就是管理自定義 Handler,最終把這些 Handler 組裝成一條雙向鏈表,Channel 有事件時則觸發(fā)鏈表進(jìn)行業(yè)務(wù)處理邏輯;
- 連接服務(wù)端:,調(diào)用 connect () 連接服務(wù)端,需要傳遞兩個參數(shù),分別是服務(wù)端的 IP 地址和端口號。
3. 核心方法
方法 | 說明 |
---|---|
group() | 指定線程模型 |
channel() | 指定 IO 模型 |
attr() | 給客戶端 Channel 設(shè)置自定義屬性 |
handler() | 給客戶端 Channel 指定處理邏輯 Handler |
option() | 給客戶端 Channel 設(shè)置底層 TCP 的屬性 |
connect() | 連接服務(wù)端,返回 ChannelFuture |
需要指定每個方法的功能是什么,下面將講解其具體使用。
4. 核心方法詳解
4.1 connect()
connect () 用來連接服務(wù)端,常見的運(yùn)用場景主要有三點(diǎn),分別是①監(jiān)聽連接結(jié)果;②失敗重連;③斷開重連。
4.1.1 連接監(jiān)聽
connect () 方法返回的是 ChannelFuture,也就是說不需要等待連接成功或失敗才往下執(zhí)行代碼,后期可以監(jiān)聽連接結(jié)果。
實例:
//1.連接Netty服務(wù)端
ChannelFuture future=bootstrap.connect("127.0.0.1",80);
//2.監(jiān)聽連接結(jié)果
future.addListener(future -> {
if (future.isSuccess()) {
System.out.println("連接成功!");
} else {
System.err.println("連接失敗!");
}
});
總結(jié),這種模式的好處是,連接是異步的,無需等待連接響應(yīng)代碼才會往下執(zhí)行。
4.1.2 失敗重連
在網(wǎng)絡(luò)情況差的情況下,客戶端第一次連接可能會連接失敗,這個時候我們可能會嘗試重新連接,具體實現(xiàn)如下:
方案一: 通過 ChannelFuture 的返回狀態(tài)來監(jiān)聽連接是否成功。
實例:
private static void connect(Bootstrap bootstrap, String host, int port) {
bootstrap.connect(host, port).addListener(future -> {
if (future.isSuccess()) {
System.out.println("連接成功!");
} else {
System.err.println("連接失敗,開始重連");
//遞歸調(diào)用連接方法
connect(bootstrap, host, port);
}
});
}
方案二: 避免短時間內(nèi)頻繁的請求連接,可以使用定時線程池來每隔 n 秒重連一次。
實例:
private static void connect(Bootstrap bootstrap, String host, int port) {
bootstrap.connect(host, port).addListener(future -> {
if (future.isSuccess()) {
System.out.println("連接成功!");
} else {
//獲取EventLoopGroup
EventLoopGroup thread=bootstrap.config().group();
//每隔5秒鐘重連一次
thread.schedule(new Runnable() {
public void run() {
connect(bootstrap, host, port)
}
}, 5, TimeUnit.SECONDS);
}
});
}
代碼說明:
bootstrap.config().group()
獲取的 EventLoopGroup,它是一個線程池,線程池里面有一個叫定時線程池。
4.2 attr()
attr () 方法可以給客戶端 Channel 初始屬性,也就是 NioSocketChannel 綁定自定義屬性,然后我們可以通過 channel.attr()
取出這個屬性。其實就是給 NioSocketChannel 維護(hù)一個 map。
給 Channel 綁定屬性。
實例:
//省略其它代碼,只保留核心部分
bootstrap.attr(AttributeKey.newInstance("token"), "123");
取出 Channel 所綁定的屬性。
實例:
public class NettyClientHandler extends ChannelInboundHandlerAdapter {
//客戶端啟動的時候,觸發(fā)事件
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
//獲取Channel的綁定的屬性值
AttributeKey<String> key=AttributeKey.valueOf("token");
String value=ctx.channel().attr(key).get();
}
}
通過以上代碼,我們介紹了 attr () 如何綁定屬性和取出屬性。
4.3 option()
可以通過 option () 方法可以給連接設(shè)置一些 TCP 底層相關(guān)的屬性,以下是常見的三種 TCP 屬性設(shè)置。
實例:
//省略其它代碼,只保留核心部分
bootstrap
.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 5000)
.option(ChannelOption.SO_KEEPALIVE, true)
.option(ChannelOption.TCP_NODELAY, true)
代碼說明:
ChannelOption.CONNECT_TIMEOUT_MILLIS
表示連接的超時時間,超過這個時間還是建立不上的話則代表連接失??;ChannelOption.SO_KEEPALIVE
表示是否開啟 TCP 底層心跳機(jī)制,true 為開啟;ChannelOption.TCP_NODELAY
表示是否開始 Nagle 算法,true 表示關(guān)閉,false 表示開啟,通俗地說,如果要求高實時性,有數(shù)據(jù)發(fā)送時就馬上發(fā)送,就設(shè)置為 true 關(guān)閉,如果需要減少發(fā)送次數(shù)減少網(wǎng)絡(luò)交互,就設(shè)置為 false 開啟。
5. 小結(jié)
本節(jié)的學(xué)習(xí),主要介紹了 Netty 客戶端啟動類 Bootstrap 的使用講解,需要掌握的核心知識點(diǎn)如下:
- 了解 Bootstrap 的核心步驟,分別是是①指定線程模型;②指定 IO 模型;③指定業(yè)務(wù)邏輯處理器;④連接服務(wù)器;
- 掌握 Bootstrap 的幾個核心方法的功能及用法。