netty启动过程 server
演示案例
public static void main(String[] args) {
--服务端
NioEventLoopGroup serverGroup = new NioEventLoopGroup();
NioEventLoopGroup workerGroup = new NioEventLoopGroup();
try{
ServerBootstrap bootstrap = new ServerBootstrap();
//serverGroup赋值给group workerGroup赋值给childGroup
ChannelFuture channelFuture = bootstrap.group(serverGroup, workerGroup)
//指定channelFactory为ReflectiveChannelFactory() 通过反射创建Channel
.channel(NioServerSocketChannel.class)
//表示服务端启动时会经过那些工序 这里添加了一个自定义的handler,后续会在异步线程中添加到pipeline中
.handler(new SimpleServerHandler())
//为每个连接设置一些tcp参数,这里代表禁用Nagle算法,有数据就马上发送
//其他还有childAttr()给每个连接指定自定义属性
.childOption(ChannelOption.TCP_NODELAY, true)
//主要用于设置一系列handler来处理每个连接的数据
.childHandler(new ChannelInitializer<NioSocketChannel>() {
@Override
protected void initChannel(NioSocketChannel ch) throws Exception {
ch.pipeline().addLast(SimpleClientHandler.INSTANCE);
ch.pipeline().addLast(new StringDecoder());
ch.pipeline().addLast(new SimpleChannelInboundHandler<String>() {
@Override
protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
System.out.println("msg = " + msg);
}
});
}
}).bind(8000);
channelFuture.channel().closeFuture()
//等待服务端关闭端口绑定 其实就是让程序不退出
.sync();
}finally {
serverGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
public static void main(String[] args) throws InterruptedException {
Bootstrap bootstrap = new Bootstrap();
NioEventLoopGroup group = new NioEventLoopGroup();
bootstrap.group(group)
.channel(NioSocketChannel.class)
.handler(new ChannelInitializer<Channel>() {
@Override
protected void initChannel(Channel ch) throws Exception {
ch.pipeline().addLast(new StringEncoder());
}
});
Channel channel = bootstrap.connect("127.0.0.1", 8000).channel();
while (true) {
channel.writeAndFlush(new Date() + ":hello");
Thread.sleep(2000);
}
}
public class SimpleServerHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
System.out.println("----server channelActive----");
super.channelActive(ctx);
}
@Override
public void channelRegistered(ChannelHandlerContext ctx) throws Exception {
System.out.println("----server channelRegistered----");
super.channelRegistered(ctx);
}
@Override
public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
System.out.println("----server handlerAdded----");
super.handlerAdded(ctx);
}
}
控制台打印:
----server handlerAdded----
----server channelRegistered----
----server channelActive----
...
----client handlerAdded----
----client channelRegistered----
----client channelActive----
----client channelRead----
msg = Tue Jul 05 11:45:12 CST 2022:hello
----client channelReadComplete----
...
----client channelInactive----
----client channelUnregistered----
----client handlerRemoved----
1.类图关系
Bootstrap
通过类图得知,ServerBootstrap和Bootstrap共同享有父类AbstractBootstrap带来的group,options,attrs属性,ServerBootstrap 同时还扩展了childGroup,childOptions,childOptuibs,childHandler属性。
EventLoopGroup
从这个类图可以看到NioEventLoopGroup和 NioEventLoop都实现了ExecutorService接口,这代表他们都是一个线程池,无非就是多线程还是单线程的关系了。
Channel ChannelConfig
这两个放在一起来看是因为NioSocketChannel和NioServerSocketChannel中各自维护了一个config对象,用
来辅助存放jdk中的SocketChannel,netty中的SocketChannel底层的调用依赖于jdk中的SocketChannel
Pipeline ChannelHandlerContext ChannelHandler
pipeline基于责任链模式,由一系列ChannelHandlerContext组成,ChannelHandlerContext包裹着ChannelHandler,基于ChannelHandlerContext辅助调用ChannelHandler实现
2. NioEventLoopGroup
构造方法
public NioEventLoopGroup() {
this(0);
}
层层调用
//executor传参为null
protected MultithreadEventLoopGroup(int nThreads, Executor executor, Object... args) {
//Math.max(1, SystemPropertyUtil.getInt("io.netty.eventLoopThreads",
Runtime.getRuntime().availableProcessors() * 2));
//基于配置或者两倍cpu核数
super(nThreads == 0 ? DEFAULT_EVENT_LOOP_THREADS : nThreads, executor, args);
}
protected MultithreadEventExecutorGroup(int nThreads, Executor executor, Object... args) {
//INSTANCE就是该类自己的实例 DefaultEventExecutorChooserFactory
this(nThreads, executor, DefaultEventExecutorChooserFactory.INSTANCE, args);
}
最终调用至MultithreadEventExecutorGroup构造方法
protected MultithreadEventExecutorGroup(int nThreads, Executor executor,
EventExecutorChooserFactory chooserFactory, Object... args) {
...
if (executor == null) {
//2.1 创建线程池 ThreadPerTaskExecutor实现了Executor接口,
//它的调用其实就是通过传入的ThreadFactory,每次都new一个线程
executor = new ThreadPerTaskExecutor(newDefaultThreadFactory());
}
//在类图中可以看到 NioEvenyloop是EventExecutor的实现
children = new EventExecutor[nThreads];
for (int i = 0; i < nThreads; i ++) {
boolean success = false;
try {
//2.2 创建 NioEventLoop
children[i] = newChild(executor, args);
success = true;
} catch (Exception e) {
...
} finally {
...
}
}
2.3 获取线程选择器
chooser = chooserFactory.newChooser(children);
...
}
2.1创建线程池 newDefaultThreadFactory
MultithreadEventLoopGroup#newDefaultThreadFactory
protected ThreadFactory newDefaultThreadFactory() {
//getClass也就是NioEventLoopGroup了 权重设置为5
return new DefaultThreadFactory(getClass(), Thread.MAX_PRIORITY);
}
public DefaultThreadFactory(Class<?> poolType, boolean daemon, int priority) {
//取首字母小写 也就是nioEventLoopGroup ; 非守护线程
this(toPoolName(poolType), daemon, priority);
}
public DefaultThreadFactory(String poolName, boolean daemon, int priority, ThreadGroup threadGroup) {
...这也就是为什么netty的线程都是 nioEventLoopGroup-2-1这样
prefix = poolName + '-' + poolId.incrementAndGet() + '-';
this.daemon = daemon;
this.priority = priority;
this.threadGroup = threadGroup;
}
2.2创建 NioEventLoop newChild
NioEventLoopGroup#newChild
protected EventLoop newChild(Executor executor, Object... args) throws Exception {
// NioEventLoop了, 拒绝策略无法加入到工作队列时抛出异常
return new NioEventLoop(this, executor, (SelectorProvider) args[0],
((SelectStrategyFactory) args[1]).newSelectStrategy(), (RejectedExecutionHandler) args[2]);
}
NioEventLoop(NioEventLoopGroup parent, Executor executor, SelectorProvider selectorProvider,
SelectStrategy strategy, RejectedExecutionHandler rejectedExecutionHandler) {
//下方SingleThreadEventExecutor addTaskWakesUp设置为false ,最大任务数根据配置 如果没有配置就是Interger.MAX了
super(parent, executor, false, DEFAULT_MAX_PENDING_TASKS, rejectedExecutionHandler);
...
provider = selectorProvider;
// 2.2.1 获取Selector(jdk)
selector = openSelector();
selectStrategy = strategy;
}
protected SingleThreadEventExecutor(EventExecutorGroup parent, Executor executor,
boolean addTaskWakesUp, int maxPendingTasks, RejectedExecutionHandler rejectedHandler) {
//保存NioEventLoopGroup的引用
super(parent);
this.addTaskWakesUp = addTaskWakesUp;
this.maxPendingTasks = Math.max(16, maxPendingTasks);
//前面创建的DefaultThreadFactory
this.executor = ObjectUtil.checkNotNull(executor, "executor");
//Mpsc队列 多生产者单消费者,单消费者指的是该NioEventloop对应的线程,多生产者对应的是之外的线程,
//通常就是我们的业务线程。比如writeAndFlush不用考虑线程安全,随意调用。
//这是普通任务队列 ,
//对应的还有的定时任务队列 scheduledTaskQueue(PriorityQueue 根据定时任务的到期时间进行排序)
taskQueue = newTaskQueue(this.maxPendingTasks);
rejectedExecutionHandler = ObjectUtil.checkNotNull(rejectedHandler, "rejectedHandler");
}
定时任务比如:
ctx.channel().eventloop().schedule(()->{ xxxxx,60,TimeUnit.SECONDS);
每个NioEventloop中都维护有一个Selector 和 MPSC队列
2.2.1 获取Selector(jdk) openSelector
NioEventLoop#openSelector
private Selector openSelector() {
final Selector selector;
selector = provider.openSelector();
...
final SelectedSelectionKeySet selectedKeySet = new SelectedSelectionKeySet();
...
final Class<?> selectorImplClass = Class.forName(
"sun.nio.ch.SelectorImpl")
Field selectedKeysField = selectorImplClass.getDeclaredField("selectedKeys");
Field publicSelectedKeysField = selectorImplClass.getDeclaredField("publicSelectedKeys");
selectedKeysField.setAccessible(true);
publicSelectedKeysField.setAccessible(true);
selectedKeysField.set(selector, selectedKeySet);
publicSelectedKeysField.set(selector, selectedKeySet);
//赋值给NioEventloop
selectedKeys = selectedKeySet;
...
return selector;
}
大致的意思就是用SelectedSelectionKeySet这个类替换掉 SelectorImpl中的 selectedKeys,publicSelectedKeys,
因为SelectedSelectionKeySet也继承AbstractSet了,所以可以直接替换,但替换肯定是有原因的
SelectedSelectionKeySet
final class SelectedSelectionKeySet extends AbstractSet<SelectionKey> {
private SelectionKey[] keysA;
private int keysASize;
private SelectionKey[] keysB;
private int keysBSize;
private boolean isA = true;
SelectedSelectionKeySet() {
keysA = new SelectionKey[1024];
keysB = keysA.clone();
}
@Override
public boolean add(SelectionKey o) {
if (o == null) {
return false;
}
if (isA) {
int size = keysASize;
//直接添加到数据的末尾
keysA[size ++] = o;
keysASize = size;
if (size == keysA.length) {
//扩容
doubleCapacityA();
}
} else {
int size = keysBSize;
keysB[size ++] = o;
keysBSize = size;
if (size == keysB.length) {
doubleCapacityB();
}
}
return true;
}
}
这里底层使用了两个数组 交替使用充当set,在4.1.9Final中变为一个数组,因为是等效的,我们就默认只有一个数组就好,
它的实质就是每次完成select操作完成后,如果有io事件发生,需要向selectedKeys中调用add方法填充数据时,
操纵的是一个全新的数组,但是只要每次把下标置为0,用一个数组是等效的。
当后续的代码中 遍历selectedKeys 发现这样的代码
if (selectedKeys[i] == null) {
break;
}
其实就是到达了边界,后续不用看了。
使用数组的好处就是遍历的时候非常高效,add的复杂度在不扩容的情况下永远是O(1)
2.3 获取线程选择器 newChooser
DefaultEventExecutorChooserFactory#newChooser
public EventExecutorChooser newChooser(EventExecutor[] executors)
// (val & -val) == val 基于位运算判断 线程数是否为2的幂
if (isPowerOfTwo(executors.length)) {
//idx.getAndIncrement() & executors.length - 1 位运算 性能更佳
return new PowerOfTowEventExecutorChooser(executors);
} else {
//Math.abs(idx.getAndIncrement() % executors.length) 取模
return new GenericEventExecutorChooser(executors);
}
不管何种方式,最终效果都是从第一个NioEventLoop遍历到最后一个,反复如此
}
这就是上述NioEventloopGroup与NioEventloop的关系
3.doBind()
案例中 bind()方法 ,其内部又调用了doBind方法
AbstractBootstrap#doBind
private ChannelFuture doBind(final SocketAddress localAddress) {
//3.1 初始化channel 并注册到 selector
final ChannelFuture regFuture = initAndRegister();
final Channel channel = regFuture.channel();
if (regFuture.cause() != null) {
return regFuture;
}
//register操作异步线程实现的 只有当register0方法结束 regFuture才算完成
if (regFuture.isDone()) {
ChannelPromise promise = channel.newPromise();
doBind0(regFuture, channel, localAddress, promise);
return promise;
} else {
final PendingRegistrationPromise promise = new PendingRegistrationPromise(channel);
//还未执行完register操作,添加listner,待register执行完毕后 触发回调
regFuture.addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture future) throws Exception {
Throwable cause = future.cause();
if (cause != null) {
promise.setFailure(cause);
} else {
promise.registered();
//3.2 绑定端口
doBind0(regFuture, channel, localAddress, promise);
}
}
});
return promise;
}
}
3.1初始化并注册 AbstractBootstrap#initAndRegister
final ChannelFuture initAndRegister() {
Channel channel = null;
try {
//3.1.1 基于反射创建channel,示例中传入了NioServerSocketChannel.class
channel = channelFactory.newChannel();
//3.1.2 初始化
init(channel);
} catch (Throwable t) {
...
return new DefaultChannelPromise(channel, GlobalEventExecutor.INSTANCE).setFailure(t);
}
//3.1.3 注册
ChannelFuture regFuture = config().group().register(channel);
if (regFuture.cause() != null) {
...
}
return regFuture;
}
3.1.1 反射创建 NioServerSocketChannel实例
无参构造调用到这里, ServerSocketChannel 是jdk中的类 使用 SelectorProvider.openServerSocketChannel()获得
public NioServerSocketChannel(ServerSocketChannel channel) {
//感兴趣的ops为 OP_ACCEPT,代表我只关心 客户端的连入操作
super(null, channel, SelectionKey.OP_ACCEPT);
//保存channel ,ServerSocket
config = new NioServerSocketChannelConfig(this, javaChannel().socket());
}
父类构造
protected AbstractNioChannel(Channel parent, SelectableChannel ch, int readInterestOp) {
//根据前文传参parent设置为null,生成随机id编号,
//初始化unsafe设置为NioMessageUnsafe,这个unsafe也是netty自定义的接口,非jdk,后续jdk中的类用(jdk)表示
//初始化pipeline为DefaultChannelPipeline
super(parent);
//保存jdk中的channel
this.ch = ch;
//保存感兴趣的操作 这里是16(1<<4) SelectionKey.OP_ACCEPT
this.readInterestOp = readInterestOp;
try {
//设置为非阻塞
ch.configureBlocking(false);
} catch (IOException e) {
.....
}
}
DefaultChannelPipeline构造
protected DefaultChannelPipeline(Channel channel) {
this.channel = ObjectUtil.checkNotNull(channel, "channel");
succeededFuture = new SucceededChannelFuture(channel, null);
voidPromise = new VoidChannelPromise(channel, true);
//初始化头尾节点
tail = new TailContext(this);
head = new HeadContext(this);
//双向链表 互相指向
head.next = tail;
tail.prev = head;
}
Unsafe
netty的中的Unsafe在Channel中定义,与Chnnel密切相关
从它的方法列表可以得知,它主要负责
- 分配内存
- Socket四元组信息
- 注册到selector
- 绑定端口
- Socket的连接和关闭
- Socket的读写
NioMessageUnsafe
与新连接建立操作相关
NioByteUnsafe
与连接的字节数据读写相关
总结: 示例中"channel(NioServerSocketChannel.class)" 其实就是在声明服务端的类型。启动时,反射创建NioServerSocketChannel,内部会维护ServerSocketChannel(通过SelectorProvider.openServerSocketChannel()创建而来),并将其设置为非阻塞,保留SelectionKey.OP_ACCEPT为感兴趣的操作(与Selector交互时用到,此时暂时还未注册),实例化NioMessageUnsafe,DefaultChannelPipeline。最后自身的属性NioServerSocketChannelConfig会持有this(NioServerSocketChannel)和ServerSocket(jdk)的引用
3.1.2 初始化 ServerBootstrap#init
void init(Channel channel) throws Exception {
//我们传给bootStrap的options 示例中没有进行配置
final Map<ChannelOption<?>, Object> options = options0();
synchronized (options) {
// channel.config()中也就是NioServerSocketChannelConfig中有很多属性,
//这里将options的挨个提取并按条件赋值
channel.config().setOptions(options);
}
final Map<AttributeKey<?>, Object> attrs = attrs0();
synchronized (attrs) {
for (Entry<AttributeKey<?>, Object> e: attrs.entrySet()) {
//设置服务端 attr属性值
AttributeKey<Object> key = (AttributeKey<Object>) e.getKey();
channel.attr(key).set(e.getValue());
}
}
//提取 childOptions childAttrs 此时只有NioServerSocketChannel一个channel。
//ServerSocketChannel(jdk)与NioServerSocketChannel对应。那么SocketChannel(jdk)肯定也会和某个类对应。
....
//3.1.2.1 添加用户自定义的handler 上述的类图里可以看到 ChannelInitializer也属于ChannelHandler
//p是 NioServerSocketChannel构造中实例化的pipepline
p.addLast(new ChannelInitializer<Channel>() {
@Override
public void initChannel(Channel ch) throws Exception {
final ChannelPipeline pipeline = ch.pipeline();
//config.handler() 其实就是我们传给ServerBootStrap的自定义handler
ChannelHandler handler = config.handler();
if (handler != null) {
pipeline.addLast(handler);
}
ch.eventLoop().execute(new Runnable() {
@Override
public void run() {
//添加一个接入器 用于接收新请求
//currentChildOptions currentChildAttrs 也就是上面提取的childOptions childAttrs
pipeline.addLast(new ServerBootstrapAcceptor(
currentChildGroup, currentChildHandler, currentChildOptions, currentChildAttrs));
}
});
}
});
}
options:
ServerBootstrap 设置 Channel 属性有option和childOption两个方法,option 主要负责设置 Boss 线程组,而 childOption 对应的是 Worker 线程组。
- SO_KEEPALIVE: 设置为 true 代表启用了 TCP SO_KEEPALIVE 属性,TCP 会主动探测连接状态,即连接保活
- SO_BACKLOG:已完成三次握手的请求队列最大长度,同一时刻服务端可能会处理多个连接,在高并发海量连接的场景下,该参数应适当调大
- TCP_NODELAY Netty 默认是 true,表示立即发送数据。如果设置为 false 表示启用 Nagle 算法,该算法会将 TCP 网络数据包累积到一定量才会发送,虽然可以减少报文发送的数量,但是会造成一定的数据延迟。Netty 为了最小化数据传输的延迟,默认禁用了 Nagle 算法
- SO_SNDBUF:TCP 数据发送缓冲区大小
- SO_RCVBUF:TCP数据接收缓冲区大小,TCP数据接收缓冲区大小
- SO_LINGER:设置延迟关闭的时间,等待缓冲区中的数据发送完成
- CONNECT_TIMEOUT_MILLIS :建立连接的超时时间
3.1.2.1 添加用户自定义的handler
DefaultChannelPipeline#addLast
public final ChannelPipeline addLast(EventExecutorGroup group, String name, ChannelHandler handler) {
final AbstractChannelHandlerContext newCtx;
synchronized (this) {
checkMultiplicity(handler);
//包装为 DefaultChannelHandlerContext
newCtx = newContext(group, filterName(name, handler), handler);
//加入双向链表 头尾之间
addLast0(newCtx);
//还未完成注册 注册的动作在3.1.3中
if (!registered) {
newCtx.setAddPending();
//将ctx包装为PendingHandlerAddedTask , 等待后续调用ctx.handler().handlerAdded(ctx);
//也就是下方3.1.3.2 register0中注册完毕后完成的回调
callHandlerCallbackLater(newCtx, true);
return this;
}
//已经完成注册 我们自定义的handler逻辑进入这里
EventExecutor executor = newCtx.executor();
...
}
//回调handlerAdded方法 也就是我们实例中的 打印 ----server handlerAdded----
callHandlerAdded0(newCtx);
return this;
}
总结:创建完NioServerSocketChannel需要进行初始化,我们传入的的options和attrs分别有channel的config属性和自身维护。将子类的childOptions childAttrs封装到一个ServerBootstrapAcceptor类中(此类后续会加入到pipeline中)。向自身的pipeline中加入一个ChannelInitializer用于辅助添加我们定义的handler,此时由于尚未完成注册操作,ChannelInitializer会被包装成PendingHandlerAddedTask等待后续回调。
3.1.3 注册 register
config().group().register(channel);
MultithreadEventLoopGroup#register
public ChannelFuture register(Channel channel) {
//next()就是调用chooser线程选择器获取EventExecutor[]数组中的一个也就是NioEventLoop
return next().register(channel);
}
SingleThreadEventLoop#register
public ChannelFuture register(Channel channel) {
//promise中维护有 channel 和 EventExecutor(当前为NioEventLoop)
return register(new DefaultChannelPromise(channel, this));
}
public ChannelFuture register(final ChannelPromise promise) {
//基于unsafe进行注册
promise.channel().unsafe().register(this, promise);
return promise;
}
AbstractChannel.AbstractUnsafe#register
public final void register(EventLoop eventLoop, final ChannelPromise promise) {
...
//将当前的EventLoop 也就是NioEventLoop的引用指向给NioServerSocketChannel
AbstractChannel.this.eventLoop = eventLoop;
//判断 Thread.currentThread() 是不是等于 当前NioEventLoop自己维护的thread(当前为null)
if (eventLoop.inEventLoop()) {
register0(promise);
} else {
try {
//3.1.3.1 NioEventLoop execute 执行任务
eventLoop.execute(new Runnable() {
@Override
public void run() {
//3.1.3.2 真正的注册逻辑
register0(promise);
}
});
} catch (Throwable t) {
...
}
}
}
3.1.3.1 NioEventLoop execute 执行任务
public void execute(Runnable task) {
boolean inEventLoop = inEventLoop();
if (inEventLoop) {
//是同一个线程操作就直接添加任务到队列中
addTask(task);
} else {
//判断是否需要创建线程
//第一次进入这里 因为thread引用还为空 激活Thread 内部调用 doStartThread方法,此方法只会触发一次
startThread();
//任务添加到 taskQueue中
addTask(task);
if (isShutdown() && removeTask(task)) {
reject();
}
}
//判断任务是不是NonWakeupRunnable 不是就需要唤醒selector ,只有WriteTask不需要
if (!addTaskWakesUp && wakesUpForTask(task)) {
//内部还会再判断,inEventLoop为false才会执行,代表此时是其他线程提交了新的任务。
//并且wakenUp状态为false才会唤醒,执行select操作时会置为false,代表此时线程阻塞住,需要被唤醒
wakeup(inEventLoop);
}
}
SingleThreadEventExecutor#doStartThread
private void doStartThread() {
assert thread == null;
//executor是 2.1中创建的ThreadPerTaskExecutor,内部持有的是DefaultThreadFactory
//这里调用的是 是DefaultThreadFactory.newThread方法,创建一个 FastThreadLocalThread线程并传入任务 开启线程
executor.execute(new Runnable() {
@Override
public void run() {
//将NioEventLoop持有的thread引用指向 上面创建的FastThreadLocalThread
thread = Thread.currentThread();
if (interrupted) {
thread.interrupt();
}
boolean success = false;
updateLastExecutionTime();
try {
//NioEventloop就绪 开始 死循环执行任务了
SingleThreadEventExecutor.this.run();
success = true;
} catch (Throwable t) {
logger.warn("Unexpected exception from an event executor: ", t);
} finally {
清除任务 关闭selector
}
}
});
}
3.1.3.2 真正的注册逻辑 register0
AbstractChannel.AbstractUnsafe#register0
private void register0(ChannelPromise promise) {
try {
...
boolean firstRegistration = neverRegistered;
//兜兜转转 又回到了 NioServerSocketChannel 调用jdk的注册方法
doRegister();
neverRegistered = false;
registered = true;
//调用PendingHandlerAddedTask内部的channelHandler#handlerAdded,
//ChannelInitializer的handlerAdded内部继续调用initChannel方法方法
//也就是会回调3.1.2.1 中 添加的ChannelInitializer.initChannel,回调的时候最终会将自身从pipeline中移除
//这也就是为什么我们可以通过
//childHandler 方法传入 ChannelInitializer,initChannel方法 反复调用 ch.pipeline().addLast(xxx);
//来添加多个handler
pipeline.invokeHandlerAddedIfNeeded();
//标记注册完成 同时回到listner doBind0绑定端口操作要么是在register结束后主线程中执行
//或者 在此处回调listner执行
safeSetSuccess(promise);
//遍历执行pipeline中的ChannelInboundHandler类型的 channelHandler#channelRegistered方法
//head- >我们自定义的handler -> tail
pipeline.fireChannelRegistered();
//判断 javaChannel().socket().isBound() 此时流程还未走到将ServerSocket绑定到address上
//这里我们注册的是NioServerSocketChannel NioSocketChannel也会调用这个方法进行注册,
//它的判断依据是SocketChannel(jdk)是否保持连接,所以会立即执行
if (isActive()) {
if (firstRegistration) {
pipeline.fireChannelActive();
} else if (config().isAutoRead()) {
beginRead();
}
}
} catch (Throwable t) {
...
}
}
AbstractNioChannel#doRegister 调用jdk的注册方法
protected void doRegister() throws Exception {
boolean selected = false;
for (;;) {
try {
//eventLoop() 是 3.1.3 中AbstractChannel.AbstractUnsafe#register赋值的,
//也就是ServerBootStrap的group中 children第一个NioEventloop
//本质上就是调用jdk 注册方法,并将自身NioServerSocketChannel作为attachment绑定起来
selectionKey = javaChannel().register(eventLoop().selector, 0, this);
return;
} catch (CancelledKeyException e) {
...
}
}
}
总结:注册完成的逻辑比较多。不仅是将jdk的channel注册到selector上,同时有可能回调bind操作,做端口绑定。
- NioServerSocketChannel完成初始化后,需要完成register操作。NioEventloopGroup从自身维护的NioEventloop[]数组中取出一个,将 AbstractUnsafe#register0方法交给NioEventloop处理(加到taskQuene中),同时将NioEventloop指向给NioServerSocketChannel.此时NioEventloop内部持有的线程为null,调用DefaultThreadFactory创建一个线程,将线程指向给NioEventloop.线程内部开始执行NioEventloop中的run方法,其中就有真正的register操作。
- 那么回到AbstractUnsafe#register0,调用SelectableChannel#register 注册方法,将ServerSocketChannel(jdk)注册到selector,同时将NioSercerSocketChannel作为attachment绑定起来。
- 之后回调上文提到的PendingHandlerAddedTask,即ChannelInitializer.initChannel方法,此方法会将我们自定义的handler加入到pipeline中,同时还会将添加ServerBootstrapAcceptor这样一个handler到pipeline的任务加入到taskQuene中,等待后续执行。
- 标记注册完成,此时可能会处理bind操作的回调方法,之所以是可能,因为bind操作时在主线程,有可能此线程执行完毕,主线程尚未走到bind操作,也就没有回调,届时bind操作在主线程完成,否则在此处回调 无论如何,bind操作都是将真正的任务放到taskQuene中,等待后续异步执行
- 调用pipeline.fireChannelRegistered();
- NioServerSocketChannel和NioSocketChannel register操作都是调用的这个方法。在接下来的这一步NioServerSocketChannel直接返回了,因为bind操作还未完成。而NioSocketChannel的判断依据是SocketChannel(jdk)是否保持连接,所以会立即执行,调用pipeline.fireChannelActive();这个方法后续会有介绍,其实就是将感兴趣的操作注册到selector上。
3.2 绑定端口 doBind0
AbstractBootstrap#doBind0
private static void doBind0(
final ChannelFuture regFuture, final Channel channel,
final SocketAddress localAddress, final ChannelPromise promise) {
//再度往taskQueue中提交任务 前面还有两次操作 register0 和 添加ServerBootstrapAcceptor
channel.eventLoop().execute(new Runnable() {
@Override
public void run() {
if (regFuture.isSuccess()) {
//内部其实是调用pipeline 从tail节点开始向前查找outbound类型的节点,
//最后查找到head节点调用其bind方法.内部实际上调用的是unsafe.bind
channel.bind(localAddress, promise)
//添加一个回调 如果绑定失败 就关闭channel
.addListener(ChannelFutureListener.CLOSE_ON_FAILURE);
} else {
promise.setFailure(regFuture.cause());
}
}
});
}
AbstractChannel.AbstractUnsafe#bind
public final void bind(final SocketAddress localAddress, final ChannelPromise promise) {
... 判断是否已经绑定端口
boolean wasActive = isActive();
try {
//基于java版本 7及以上使用sockectChannel 否则使用 使用socket
doBind(localAddress);
} catch (Throwable t) {
safeSetFailure(promise, t);
closeIfClosed();
return;
}
if (!wasActive && isActive()) {
//依然是 向NioServerSocketChannel绑定的NioEventLoop中 taskQuene提交任务
invokeLater(new Runnable() {
@Override
public void run() {
//回调active并开启监听
pipeline.fireChannelActive();
}
});
}
//设置状态 回调listner
safeSetSuccess(promise);
}
3.2.1 回调active并开启监听 fireChannelActive
DefaultChannelPipeline#fireChannelActive
public void channelActive(ChannelHandlerContext ctx) throws Exception {
//从head节点开始,其channelActive方法 查找下一个Inbound节点,回调其 channelActive方法,
/这也就是为什么我们要在示例中 channelActive方法末尾要添加 super.channelActive(ctx); 传播active事件
ctx.fireChannelActive();
//默认从tail节点向上查找outbound类型节点,找到head,调用read方法,内部又调用unsafe.beginRead(),
//最终调用到 下方 AbstractNioChannel#doBeginRead 方法
readIfIsAutoRead();
}
AbstractNioChannel#doBeginRead
protected void doBeginRead() throws Exception {
final SelectionKey selectionKey = this.selectionKey;
readPending = true;
//这里interestOps 为0 因为我们还没有把将SelectionKey.OP_ACCEPT绑定上去
//每轮读完成后也就是 channelRead方法完成后,传播channelReadComplete事件后,head节点还会调用此方法 当然
//此时已经注册了OP_ACCEPT事件,if不成立,不会反复注册
final int interestOps = selectionKey.interestOps();
if ((interestOps & readInterestOp) == 0) {
//注册 SelectionKey.OP_ACCEPT 至此 就绪完毕 socket可以检测新连接的接入了
selectionKey.interestOps(interestOps | readInterestOp);
}
}
总结:bind操作是异步执行的,因为不知何时完成register操作。bind操作主要就是绑定端口,内部会判断java版本, 7及以上使用sockectChannel 否则使用 使用socket 。绑定端口之后,调用pipeline.fireChannelActive();将感兴趣的操作注册到selector上, ServerSocketChannel监听连接事件,SocketChannel监听读事件,至此,channel就绪完毕,可以处理连接请求了。(SocketChannel可以处理请求数据了)
总结
时序图就不画了,大纲其实看目录的结构就知道了。
-
创建EventloopGroup其实就是在创建Reactor线程池,我们创建了两个,使用的是主从多线程模型,一个负责处理连接,一个负责处理各个连接的读写事件。每个reactor线程都有独立的 Selector 对象。
-
NioServerSocketChannel,NioSocketChannel对应的就是jdk的ServerSocketChannel和SocketChannel.每个channel都维护有pipeline,netty使用了pipeline可以让开发者加入自定义的handler,来处理请求的各个阶段。
在下一章,我们看看netty的主从reactor线程如何联动,充分发挥cpu的效能。以及reactor线程如何兼顾事件的监听,处理io事件和定时任务。