Netty源码解析-基本组件架构&启动过程(一)

之前读过Netty3的源码,没有相关记录,这次再来看Netty4的时候发现其结构变化挺大,另外也记录下方便以后自己查看

目录:

  1. Netty基本组件
  2. Netty运作流程
  3. Netty服务端Demo
  4. 跟踪Netty服务端启动关键代码解析

1. Netty基本组件

整个Nettty核心组件主要由以上这些核心类构成。NioEventLoop、Channel、Pipeline、ChannelHandler、ByteBuf。

其中NioEventLoop负责处理客户端的连接,Channel处理客户端的读写,Pipeline逻辑链,ChannelHandler最终的逻辑处理实现类,ByteBuf数据缓冲区。

2.Netty服务端Demo&运行流程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
package com.jh.socket.netty;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;

/**
* @program:
* @description:
* @author: lishijia
* @create: 2019-07-26 10:28
**/
public class Server {

public static void main(String args[]) throws InterruptedException {

EventLoopGroup bossGroup = new NioEventLoopGroup(1);
EventLoopGroup workerGroup = new NioEventLoopGroup();

try {
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
//BACKLOG用于构造服务端套接字ServerSocket对象,标识当服务器请求处理线程全满时,
//用于临时存放已完成三次握手的请求的队列的最大长度。如果未设置或所设置的值小于1,Java将使用默认值50。
.childOption(ChannelOption.SO_BACKLOG, 100)
//设置这样做好的好处就是禁用nagle算法 nagle是尽可能的把小包合并发送一个大包,最大延迟500ms
.childOption(ChannelOption.TCP_NODELAY, true)
.handler(new ServerHandler())
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
System.out.println("init Channel");
}
});

ChannelFuture channelFuture = b.bind(6379).sync();
channelFuture.channel().closeFuture().sync();

}finally {
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}

}

}
  • 创建ServerBootStrap实例
  • 设置Reactor线程池:EventLoopGroup、EventLoop就处理所有注册到本线程的Selector上面的Channel
  • 设置绑定服务端Channel
  • 创建处理网络事件的ChannelPipeline和Handler
  • 绑定并启动监听端口
  • 当轮询到准备就绪的channel后,由Reactor线程:NioEventLoop执行Pipeline中的方法,最终调用channelHandler

4.跟踪Netty服务端启动关键代码解析

根据上面的Server启动类,可以跟踪到如下的代码逻辑,主要包含两大块

  • 初始化实例化Channel并且把Channel绑定到Selector上
  • Server端启动绑定端口逻辑并且监听Accept事件
1
2
3
4
-->bind(int inetPort)--AbstractBootstrap,Netty Server端启动绑定
-->doBind(final SocketAddress localAddress)--AbstractBootstrap,Netty Server端启动绑定
-->initAndRegister()--AbstractBootstrap,初始化Channel与注册channel
-->doBind0(final ChannelFuture regFuture, final Channel channel,final SocketAddress localAddress, final ChannelPromise promise)Netty Server端启动绑定端口逻辑并且监听Accept事件

初始化实例化注册Channel

1
2
3
-->newChannel()--BootstrapChannelFactory这个工厂类在启动之前就已经实例化好了
-->init(Channel channel)--ServerBootstrap这里里面也没干啥,主要就是把上面的channel再次初始化,绑定一些相关属性,绑定Options,attribute,
-->register(Channel channel)--EventLoopGroup即bossGroup>MultithreadEventLoopGroup

实例化Channel

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
-->newChannel()--BootstrapChannelFactory这个工厂类在启动之前就已经实例化好了
-->clazz.newInstance()-->BootstrapChannelFactory,通过反射实例化channel(NioServerSocketChannel)
-->NioServerSocketChannel()
-->newSocket(SelectorProvider provider)--NioServerSocketChannel
->return provider.openServerSocketChannel(); 最终返回ServerSocketChannel,jdk的ServerSocketChannel类
-->NioServerSocketChannel(ServerSocketChannel channel)
-->super(null, channel, SelectionKey.OP_ACCEPT)--主要把channel往上传递,另外需要传递的是一个Accept这么一个事件
-->AbstractNioMessageChannel(Channel parent, SelectableChannel ch, int readInterestOp)--AbstractNioMessageChannel
-->super(parent, ch, readInterestOp)--AbstractNioMessageChannel
--AbstractNioChannel(Channel parent, SelectableChannel ch, int readInterestOp)
-->super(parent)
-->AbstractChannel(Channel parent)--AbstractChannel
-->this.parent = parent;绑定jdk的serverSocketchannel
-->unsafe = newUnsafe();初始化它的Unsafe操作类
-->pipeline = new DefaultChannelPipeline(this);初始化默认的Pipeline
-->this.ch = ch;绑定jdk的ServerSocket
-->this.readInterestOp = readInterestOp;初始化Accept事件属性
-->ch.configureBlocking(false);设置为非阻塞 到这里ServerSocketChannel也就是基本上实例化完了

初始化

1
2
-->init(Channel channel)--ServerBootstrap这里里面也没干啥,主要就是把上面的channel再次初始化,绑定一些相关属性,绑定Options,attribute,
-->ch.pipeline().addLast(new ServerBootstrapAcceptor(currentChildGroup, currentChildHandler, currentChildOptions, currentChildAttrs)) 这个算是最终要的,添加一个连接处理器,用于处理新的连接

注册Channel至Selector

1
2
3
4
5
6
7
8
9
10
11
12
13
14
-->register(Channel channel)--EventLoopGroup即bossGroup>MultithreadEventLoopGroup
-->next() 获取一个选择器处理 MultithreadEventLoopGroup MultithreadEventLoopGroup
--next() MultithreadEventExecutorGroup
-->PowerOfTwoEventExecutorChooser 优化过的通过&计算得到一个线程池去注册
-->GenericEventExecutorChooser 普通的一个算法得到一个线程去注册
-->next() 获取一个NioEventLoop
-->register(Channel channel) NioEventLoop->SingleThreadEventLoop>SingleThreadEventExecutor>EventLoop继承关系
-->register(final SelectableChannel ch, final int interestOps, final NioTask<?> task)
-->channel.unsafe().register(this, promise)
-->register(EventLoop eventLoop, ChannelPromise promise)--DefaultServerUnsafe->AbstractUnsafe
-->register0(ChannelPromise promise)--DefaultServerUnsafe->AbstractUnsafe
-->doRegister()--AbstractNioChannel
-->selectionKey = javaChannel().register(eventLoop().selector, 0, this);
-->绑定注册监听事件0,这个时候标识它不关心任何事件,这里仅仅只是把我们的ServerSocketChannel绑定到Selector上面去。后续会再来绑定对应的Accept事件

绑定端口并且监听Accept事件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
-->doBind0(final ChannelFuture regFuture, final Channel channel,final SocketAddress localAddress, final ChannelPromise promise)--AbstractBootstrap,Netty Server端启动绑定端口逻辑
-->bind(SocketAddress localAddress, ChannelPromise promise)--NioServerSocketChannel->AbstractChannel->Channel
-->bind(SocketAddress localAddress, ChannelPromise promise) DefaultChannelPipeline
-->bind(final SocketAddress localAddress, final ChannelPromise promise) DefaultChannelPipeline->AbstractChannelHandlerContext
-->invokeBind(SocketAddress localAddress, ChannelPromise promise) NioEventLoop取出实际的线程处理
-->bind(ChannelHandlerContext ctx, SocketAddress localAddress, ChannelPromise promise)一个bind链路直到head HeadContext>ChannelHandler
-->bind(final SocketAddress localAddress, final ChannelPromise promise)--DefaultServerUnsafe->AbstractUnsafe
-->doBind(SocketAddress localAddress) NioServerSocketChannel
-->javaChannel().socket().bind(localAddress, config.getBacklog());
以上基本上端口绑定就完成了,接下来就需要发起一个传播事件autoRead去监听 Accept
-->isActive()
-->pipeline.fireChannelActive() 一个fireChannelActive传播事件链路直到head HeadContext>ChannelHandler
-->channel.read()
-->pipeline.read(); 再次发起一个read传播事件
-->next.invokeRead();
-->read(ChannelHandlerContext ctx)
-->unsafe.beginRead();
-->super.beginRead();
-->doBeginRead()
-->selectionKey.interestOps(0 | 16);

以上这段代码的分析仅仅只是Netty的大致启动过程。从Netty初始化实例化Channel,到注册Selector。然后绑定服务端端口,以及最终监听Accept事件

分享到 评论