版權(quán)聲明:本文為博主原創(chuàng)文章敌完,未經(jīng)博主允許不得轉(zhuǎn)載。
摘要
該系列文章主要是分析Netty源碼4.1.16.Final,了解Netty框架的設(shè)計(jì)和其中各種組件的優(yōu)化手段。
這篇文章作為系列的第一篇文章吗氏,以最簡(jiǎn)單的EchoServer為例,從ServerBootstrap開(kāi)始跟蹤其初始化和啟動(dòng)流程雷逆,讓讀者在直觀上對(duì)Netty框架有大概的認(rèn)識(shí)弦讽。
EchoServer
我們從Netty提供的EchoServer開(kāi)始,重要的代碼片段如下:
// Configure the server.
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.option(ChannelOption.SO_BACKLOG, 100)
.handler(new LoggingHandler(LogLevel.INFO))
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline p = ch.pipeline();
if (sslCtx != null) {
p.addLast(sslCtx.newHandler(ch.alloc()));
}
//p.addLast(new LoggingHandler(LogLevel.INFO));
p.addLast(new EchoServerHandler());
}
});
// Start the server.
ChannelFuture f = b.bind(PORT).sync();
// Wait until the server socket is closed.
f.channel().closeFuture().sync();
} finally {
// Shut down all event loops to terminate all threads.
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
ServerBootstrap
ServerBootstrap作為一個(gè)啟動(dòng)輔助類(lèi)关面,通過(guò)他可以很方便的創(chuàng)建一個(gè)Netty服務(wù)端坦袍。從EchoServer可以看出,實(shí)現(xiàn)一個(gè)Server大概分為這樣幾個(gè)步驟:
- 設(shè)置bossGroup和workGroup等太。這里可以先理解為兩個(gè)線(xiàn)程池捂齐,bossGroup設(shè)置一個(gè)線(xiàn)程,用于處理連接請(qǐng)求和建立連接缩抡,而workGroup線(xiàn)程池大小默認(rèn)值2*CPU核數(shù)奠宜,在連接建立之后處理IO請(qǐng)求。EventLoopGroup體現(xiàn)了Netty對(duì)線(xiàn)程模型的抽象設(shè)計(jì)瞻想,之后會(huì)獨(dú)立開(kāi)篇介紹压真。
- 指定使用NioServerSocketChannel來(lái)處理連接請(qǐng)求。
channel(NioServerSocketChannel.class)
這段代碼實(shí)際上在設(shè)置channelFactory蘑险,而ServerBootstrap會(huì)通過(guò)channelFactory.newChannel來(lái)生產(chǎn)channel滴肿。這里channelFactory是一個(gè)ReflectiveChannelFactory
,顧名思義這個(gè)工廠類(lèi)以反射的方式來(lái)構(gòu)建channel實(shí)例佃迄,而實(shí)例的類(lèi)型就是我們指定的NioServerSocketChannel泼差。
public T newChannel() {
...
return clazz.getConstructor().newInstance();
...
}
- 配置TCP參數(shù)。
- 配置handler和childHandler呵俏,數(shù)據(jù)處理器堆缘。
- ServerBootstrap啟動(dòng)服務(wù)器。
真正的啟動(dòng)過(guò)程由ChannelFuture f = b.bind(PORT).sync();
開(kāi)始觸發(fā)普碎。調(diào)用鏈路:ServerBootstrap.bind → AbstractBootstrap.bind → AbstractBootstrap.doBind
吼肥,重點(diǎn)分析一下doBind:
private ChannelFuture doBind(final SocketAddress localAddress) {
final ChannelFuture regFuture = initAndRegister();
final Channel channel = regFuture.channel();
...
ChannelPromise promise = channel.newPromise();
doBind0(regFuture, channel, localAddress, promise);
return promise;
...
}
這個(gè)過(guò)程分為兩個(gè)關(guān)鍵步驟:
- initAndRegister - 包括Channel的創(chuàng)建、初始化和注冊(cè)三個(gè)子步驟
- doBind0 - Channel綁定到監(jiān)聽(tīng)端口
1.1 NioServerSocketChannel創(chuàng)建
final ChannelFuture initAndRegister() {
...
channel = channelFactory.newChannel();
init(channel);
...
ChannelFuture regFuture = config().group().register(channel);
...
return regFuture;
}
這里channelFactory就是上面提到的ReflectiveChannelFactory
麻车,他通過(guò)反射來(lái)創(chuàng)建NioServerSocketChannel實(shí)例缀皱,繼續(xù)跟進(jìn)NioServerSocketChannel的構(gòu)造方法:
private static final SelectorProvider DEFAULT_SELECTOR_PROVIDER = SelectorProvider.provider();
public NioServerSocketChannel() {
this(newSocket(DEFAULT_SELECTOR_PROVIDER));
}
private static ServerSocketChannel newSocket(SelectorProvider provider) {
...
return provider.openServerSocketChannel();
...
}
public NioServerSocketChannel(ServerSocketChannel channel) {
super(null, channel, SelectionKey.OP_ACCEPT);
config = new NioServerSocketChannelConfig(this, javaChannel().socket());
}
因?yàn)楂@取provider的過(guò)程包含同步訪(fǎng)問(wèn)的消耗,所以NioServerSocketChannel緩存了provider并使用provider.openServerSocketChannel()
來(lái)創(chuàng)建ServerSocketChannel實(shí)例动猬。
除了創(chuàng)建ServerSocketChannel外唆鸡,還要?jiǎng)?chuàng)建NioServerSocketChannelConfig。ChannelConfig的職責(zé)是統(tǒng)一保存channel配置枣察,并提供讀取和設(shè)置的接口。不同的ChannelConfig具體實(shí)現(xiàn)不一樣,例如NioServerSocketChannelConfig提供的setReuseAddress接口序目,是為ServerSocketChannel設(shè)置useAddress參數(shù)臂痕。
NioServerSocketChannel的構(gòu)造過(guò)程并沒(méi)有這么簡(jiǎn)單,還要繼續(xù)向上看看父類(lèi)的構(gòu)造函數(shù)猿涨。
先來(lái)看AbstractNioChannel的構(gòu)造方法:
protected AbstractNioChannel(Channel parent, SelectableChannel ch, int readInterestOp) {
super(parent);
this.ch = ch;
this.readInterestOp = readInterestOp;
...
//設(shè)置channel非阻塞模式
ch.configureBlocking(false);
...
}
}
接下來(lái)是AbstractChannel類(lèi)的構(gòu)造方法:
protected AbstractChannel(Channel parent) {
this.parent = parent;
id = newId();
unsafe = newUnsafe();
pipeline = newChannelPipeline();
}
代碼清楚的交代了3件事情:
- 為當(dāng)前channel生成ID(由機(jī)器名握童,進(jìn)程名,序列號(hào)叛赚,進(jìn)程號(hào)和隨機(jī)數(shù)組成)澡绩。
- 創(chuàng)建Unsafe實(shí)例(NioMessageUnSafe)。所有的UnSafe類(lèi)都主要用于網(wǎng)絡(luò)方面的操作俺附,比如read肥卡,write,close等事镣,Channel關(guān)于網(wǎng)絡(luò)的操作都會(huì)委托給該unsafe來(lái)完成步鉴。
- 創(chuàng)建DefaultChannelPipeline,他就是大名鼎鼎的Pipeline機(jī)制璃哟。先從DefaultChannelPipeline構(gòu)造函數(shù)入手:
protected DefaultChannelPipeline(Channel channel) {
this.channel = ObjectUtil.checkNotNull(channel, "channel");
...
tail = new TailContext(this);
head = new HeadContext(this);
head.next = tail;
tail.prev = head;
}
pipeline內(nèi)部會(huì)組織一個(gè)ChannelHandlerContext的鏈表氛琢,上下行的事件都會(huì)經(jīng)過(guò)這個(gè)鏈表依次處理。但是在構(gòu)造Pipeline時(shí)随闪,就只有兩個(gè)ChannelHandlerContext:HeadContext主要用于處理輸出事件阳似,而TailContext用于處理輸入事件。用戶(hù)添加的Handler也會(huì)被封裝在新的Context然后插入到鏈表中铐伴,參與事件的處理撮奏,這一點(diǎn)后面會(huì)再提到。
先來(lái)看看TailContext構(gòu)造函數(shù):
TailContext(DefaultChannelPipeline pipeline) {
super(pipeline, null, TAIL_NAME, true, false);
...
}
AbstractChannelHandlerContext(DefaultChannelPipeline pipeline, EventExecutor executor, String name,
boolean inbound, boolean outbound) {
this.name = ObjectUtil.checkNotNull(name, "name");
this.pipeline = pipeline;
this.executor = executor;
this.inbound = inbound;
this.outbound = outbound;
ordered = executor == null || executor instanceof OrderedEventExecutor;
}
值得注意的是inbound和outbound兩個(gè)屬性盛杰,TailContext(inbound=true挽荡,outbound=false),而HeadContext剛好相反(inbound=false即供,outbound=true)定拟,這兩個(gè)屬性由于標(biāo)識(shí)該Context是用于處理輸入事件還是輸出事件的,當(dāng)事件處理時(shí)會(huì)根據(jù)是事件輸入還是輸出而選擇相應(yīng)的Context來(lái)處理逗嫡。
再來(lái)看HeadContext:
HeadContext(DefaultChannelPipeline pipeline) {
super(pipeline, null, HEAD_NAME, false, true);
unsafe = pipeline.channel().unsafe();
...
}
與TailContext
相比青自,HeadContext多了unsafe屬性,是從pipeline中的channel中獲取的驱证,即NioSocketChannel構(gòu)造函數(shù)中提到的unsafe對(duì)象延窜。TailContext實(shí)現(xiàn)的很多方法都是空的,不做任何處理抹锄;而HeadContext實(shí)現(xiàn)的方法就比較多了逆瑞,包括bind荠藤,connect,disconnect获高,read哈肖,write等,都是依賴(lài)unsafe來(lái)完成的念秧。
到這里NioServerSocketChannel的創(chuàng)建過(guò)程才算結(jié)束淤井,總結(jié)一下這個(gè)過(guò)程都包含了那些步驟:
- ServerBootStrap通過(guò)ReflectiveChannelFactory創(chuàng)建NioServerSocketChannel實(shí)例。
- NioServerSocketChannel實(shí)例內(nèi)部創(chuàng)建了ServerSocketChannel摊趾。
- 每個(gè)Channel(AbstractChannel)內(nèi)部都會(huì)創(chuàng)建ChannelID币狠,unsafe以及pipeline。
1.2 NioServerSocketChannel初始化
回到AbstractBootstrap.initAndRegister
:
final ChannelFuture initAndRegister() {
...
channel = channelFactory.newChannel();
init(channel);
...
ChannelFuture regFuture = config().group().register(channel);
...
return regFuture;
}
上一節(jié)介紹了channel的創(chuàng)建過(guò)程砾层,接下來(lái)再看ServerBootstrap.init
:
void init(Channel channel) throws Exception {
final Map<ChannelOption<?>, Object> options = options0();
synchronized (options) {
setChannelOptions(channel, options, logger);
}
final Map<AttributeKey<?>, Object> attrs = attrs0();
synchronized (attrs) {
for (Entry<AttributeKey<?>, Object> e: attrs.entrySet()) {
@SuppressWarnings("unchecked")
AttributeKey<Object> key = (AttributeKey<Object>) e.getKey();
channel.attr(key).set(e.getValue());
}
}
ChannelPipeline p = channel.pipeline();
...
synchronized (childOptions) {
currentChildOptions = childOptions.entrySet().toArray(newOptionArray(childOptions.size()));
}
synchronized (childAttrs) {
currentChildAttrs = childAttrs.entrySet().toArray(newAttrArray(childAttrs.size()));
}
p.addLast(new ChannelInitializer<Channel>() {
@Override
public void initChannel(final Channel ch) throws Exception {
final ChannelPipeline pipeline = ch.pipeline();
ChannelHandler handler = config.handler();
if (handler != null) {
pipeline.addLast(handler);
}
ch.eventLoop().execute(new Runnable() {
@Override
public void run() {
pipeline.addLast(new ServerBootstrapAcceptor(
ch, currentChildGroup, currentChildHandler, currentChildOptions, currentChildAttrs));
}
});
}
});
}
該初始化過(guò)程可以劃分為兩個(gè)部分:
- 設(shè)置channel的配置參數(shù)和附屬屬性漩绵。
- 向channel的pipeline添加handler。這里的handler是通過(guò)
ServerBootstrap.handler
設(shè)置的梢为,是在連接建立后的通用handler渐行。
重點(diǎn)來(lái)看最后提到的ServerBootstrapAcceptor是什么?首先他是一個(gè)ChannelHandler铸董,也會(huì)參與到事件的處理中祟印,其次事件處理過(guò)程channelRead
值得注意:
public void channelRead(ChannelHandlerContext ctx, Object msg) {
final Channel child = (Channel) msg;
child.pipeline().addLast(childHandler);
...
childGroup.register(child).addListener(new ChannelFutureListener() {
...
});
...
}
當(dāng)新的連接SocketChannel建立后,會(huì)被包裝在一個(gè)新建的NioSocketChannel中粟害,之后開(kāi)始觸發(fā)pipeline.fireChannelRead
蕴忆,最終經(jīng)過(guò)ServerBootstrapAcceptor.channelRead
。在這里開(kāi)始為這個(gè)NioSocketChannel添加childHandler悲幅,而childHandler則是我們?cè)赟erverBootstrap中指定的childHandler套鹅。接著在childGroup(也就是我們配置的workerGroup)中注冊(cè)