Netty ServerBootstrap 服務(wù)端啟動(dòng)類
1. 前言
本節(jié)主要講解服務(wù)端主啟動(dòng)類 ServerBootstrap 的核心 API 的使用。
2. ServerBootstrap 流程
ServerBootstrap 的用法基本上都是固定的,一般對(duì)于新接觸 Netty 的同學(xué)來(lái)說(shuō),會(huì)覺(jué)得這些模板代碼比較多,難以理解。我們主要記住幾個(gè)核心配置即可。
- 指定線程模型: 通過(guò)
.group(bossGroup, workerGroup)
給引導(dǎo)類配置兩大線程組,這個(gè)引導(dǎo)類的線程模型也就定型了。其中 bossGroup 表示監(jiān)聽(tīng)端口,accept 新連接的線程組;workerGroup 表示處理每一條連接的數(shù)據(jù)讀寫的線程組; - 指定 IO 模型: 通過(guò)
.channel(NioServerSocketChannel.class)
來(lái)指定 NIO 模型。如果指定 IO 模型為 BIO,那么這里配置上OioServerSocketChannel.class
類型即可,通常都是使用 NIO,因?yàn)?Netty 的優(yōu)勢(shì)就在于 NIO; - 指定處理邏輯: 通過(guò) childHandler () 方法,給這個(gè)引導(dǎo)類創(chuàng)建一個(gè) ChannelInitializer,這里主要就是定義后續(xù)每條連接的數(shù)據(jù)讀寫,業(yè)務(wù)處理邏輯;
- 綁定端口號(hào): 調(diào)用 bind (80),端口號(hào)自定義,不要和其他應(yīng)用的端口號(hào)有沖突即可。
3. 核心方法
方法 | 說(shuō)明 |
---|---|
group() | 用來(lái)指定線程模型 |
channel() | 用來(lái)指定 IO 模型 |
handler() | 用來(lái)指定服務(wù)端通道需要處理的業(yè)務(wù)邏輯(了解) |
childHandler() | 用來(lái)指定客戶端通道需要處理的業(yè)務(wù)邏輯(掌握) |
attr() | 給服務(wù)端通道綁定自定義屬性(了解) |
childAttr() | 給客戶端通道綁定自定義屬性(掌握) |
option() | 給服務(wù)端通道設(shè)置配置(了解) |
childOption() | 給客戶端通道設(shè)置配置(了解) |
bind() | 用來(lái)綁定端口號(hào) |
說(shuō)明:客戶端和服務(wù)端連接之后,會(huì)維持一個(gè) Channel 通道,可以給其指定邏輯處理器和屬性配置;當(dāng)然,服務(wù)端啟動(dòng)的時(shí)候它也是一個(gè)特殊的 Channel 通道。
在開(kāi)發(fā)當(dāng)中,需要我們?nèi)プ远x的方法主要是 childHandler () 和 childAttr () 這兩個(gè)。childHandler () 用來(lái)綁定業(yè)務(wù)邏輯器,childAttr () 用來(lái)設(shè)置 Channel 屬性。比如:綁定用戶身份信息。其它方法的使用相對(duì)固定,了解即可。
4. 核心方法詳解
4.1 bind()
bind () 主要用來(lái)綁定本地端口號(hào)。
實(shí)例:
ChannelFuture future=serverBootstrap.bind(80);
future.addListener(new GenericFutureListener<Future<? super Void>>() {
public void operationComplete(Future<? super Void> future) {
if (future.isSuccess()) {
System.out.println("端口綁定成功!");
} else {
System.err.println("端口綁定失敗!");
}
}
});
代碼升級(jí),如果綁定的端口已經(jīng)存在,則端口號(hào)遞增。當(dāng)然,實(shí)際情況很少會(huì)去遞增端口號(hào),一般都是上線之前確定端口號(hào),否則客戶端不知道端口號(hào),無(wú)法連接。
實(shí)例:
private static void bind(ServerBootstrap serverBootstrap, final int port) {
ChannelFuture future=serverBootstrap.bind(port);
future.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 + "]綁定失敗!");
//遞歸重新綁定端口號(hào)
bind(serverBootstrap, port + 1);
}
}
});
}
4.2 attr()
attr () 方法可以給服務(wù)端的 channel,也就是 NioServerSocketChannel
指定一些自定義屬性,可以通過(guò) channel.attr()
取出這個(gè)屬性。
實(shí)例:
//省略了其它模板代碼
serverBootstrap.attr(AttributeKey.newInstance("serverName"), "nettyServer")
總結(jié),一般來(lái)說(shuō) attr () 運(yùn)用的比較少,了解即可。
4.3 childAttr()
childAttr 可以給每一條連接指定自定義屬性,可以通過(guò) channel.attr()
取出該屬性。
實(shí)例:
//省略了其它模板代碼
serverBootstrap.childAttr(AttributeKey.newInstance("clientKey"), "clientValue")
總結(jié),常見(jiàn)的運(yùn)用場(chǎng)景,客戶端登錄成功之后,給其對(duì)應(yīng)的 Channel 綁定標(biāo)識(shí),下次只需要判斷該 Channel 是否有標(biāo)識(shí)即可知道其是否已經(jīng)登錄。
4.4 handler()
handler () 用于指定在服務(wù)端啟動(dòng)過(guò)程中的一些邏輯。
實(shí)例:
//省略了其它模板代碼
serverBootstrap.handler(new ChannelInitializer<NioServerSocketChannel>() {
protected void initChannel(NioServerSocketChannel ch) {
System.out.println("服務(wù)端啟動(dòng)中");
}
});
總結(jié),可以在服務(wù)端啟動(dòng)的過(guò)程中做一些初始化方面的工作,比如,讀取數(shù)據(jù)庫(kù)的配置數(shù)據(jù)放到緩存當(dāng)中,這個(gè)作為了解即可。
4.5 childHandler()
childHandler () 用于指定處理新連接數(shù)據(jù)的讀寫處理邏輯。
實(shí)例:
//省略了其它模板代碼
serverBootstrap.childHandler(new ChannelInitializer<NioSocketChannel>() {
protected void initChannel(NioSocketChannel ch) {
//責(zé)任鏈,指定自定義處理業(yè)務(wù)的 Handler
ch.pipeline().addLast(new NettyServerHandler());
}
});
總結(jié),這個(gè)是核心,主要管理業(yè)務(wù)邏輯處理雙向鏈表,后面會(huì)具體講解
4.6 option()
option () 給服務(wù)端 channel 設(shè)置一些屬性,最常見(jiàn)的就是 so_backlog。
實(shí)例:
//省略了其它模板代碼
serverBootstrap.option(ChannelOption.SO_BACKLOG, 1024)
表示系統(tǒng)用于臨時(shí)存放已完成三次握手的請(qǐng)求的隊(duì)列的最大長(zhǎng)度,如果連接建立頻繁,服務(wù)器處理創(chuàng)建新連接較慢,可以適當(dāng)調(diào)大這個(gè)參數(shù)。其實(shí),客戶端請(qǐng)求在服務(wù)端也是排隊(duì)執(zhí)行的,服務(wù)端的兩大線程組分別監(jiān)聽(tīng)客戶端連接和處理客戶端連接,一旦并發(fā)量很高的時(shí)候,服務(wù)端處理不過(guò)來(lái),則會(huì)把等待處理的請(qǐng)求放入到臨時(shí)隊(duì)列里面,這個(gè)跟 Java 線程池的思想是一樣的。
4.7 childOption()
childOption () 給每條連接設(shè)置一些 TCP 底層相關(guān)的屬性。
實(shí)例:
//省略了其它模板代碼
serverBootstrap
.childOption(ChannelOption.SO_KEEPALIVE, true)
.childOption(ChannelOption.TCP_NODELAY, true)
代碼說(shuō)明:
ChannelOption.SO_KEEPALIVE
表示是否開(kāi)啟 TCP 底層心跳機(jī)制,true 為開(kāi)啟;ChannelOption.TCP_NODELAY
表示是否開(kāi)啟 Nagle 算法,true 表示關(guān)閉,false 表示開(kāi)啟,通俗地說(shuō),如果要求高實(shí)時(shí)性,有數(shù)據(jù)發(fā)送時(shí)就馬上發(fā)送,就關(guān)閉,如果需要減少發(fā)送次數(shù)減少網(wǎng)絡(luò)交互,就開(kāi)啟。
5. 小結(jié)
本節(jié)學(xué)習(xí)的核心知識(shí)點(diǎn)掌握,具體如下:
- 四個(gè)核心流程,分別是①設(shè)置線程組;②設(shè)置 IO 模型;③指定連接讀寫處理邏輯;④綁定端口號(hào);
- 核心方法的使用場(chǎng)景,重點(diǎn)掌握①bind ();②childAttr ();③childHandler ();④childOption () 的使用。