“This article has participated in the call for good writing activities, click to view: the back end, the big front end double track submission, 20,000 yuan prize pool waiting for you to challenge!”
In this chapter, we will continue to analyze NioServerSocketChannel. NioServerSocketChannel is a channel object that is encapsulated by Netty. SocketChannel is used to replace or wrap JDK native SocketChannel objects. How does it relate NioServerSocketChannel to JDK nio-related code?
One, source code entry search
The main source method we analyzed last class is initAndRegister method, in fact, from the name can be seen, here is to do the channel initialization in the register, we continue to return to this method, the method to find, refer to the previous chapter section:
AbstractBootstrap#initAndRegister
Let’s skip the code we analyzed in the last lesson and go straight to the registration logic:
ChannelFuture regFuture = config().group().register(channel);
Copy the code
We analyze it methodically:
config()
Copy the code
Now we create ServerBootstrap, so I won’t go into why we chose this one:
private final ServerBootstrapConfig config = new ServerBootstrapConfig(this);
Copy the code
We can see that he returns the object that was created automatically when he created ServerBootstrap. We can see that he has a “this” in his constructor, which means that he has a ServerBootstrap reference. Get all properties and methods in ServerBootstrap! So what do you do when you get this class?
config().group()
Copy the code
It is estimated that many of you have guessed, let’s directly click into the group to verify:
@SuppressWarnings("deprecation")
public final EventLoopGroup group(a) {
return bootstrap.group();
}
Copy the code
This code is the bossGroup object that we set when we built ServerBootstrap. If you are interested, you can follow it. It is relatively simple here, so we will not do too much explanation.
config().group().register(channel);
Copy the code
NioEventLoopGroup = NioEventLoopGroup = NioEventLoopGroup = NioEventLoopGroup
We found that there was no NioEventLoopGroup, but through the study of our previous chapters, we know that NioEventLoopGroup MultithreadEventLoopGroup subclasses, so we couldn’t find the parent class, subclass We enter into MultithreadEventLoopGroup source code:
@Override
public ChannelFuture register(Channel channel) {
// Generally speaking, NioEventLoop is inherited from SingleThreadEventLoop
return next().register(channel);
}
Copy the code
Here, we see the code we analyzed earlier, next(), which calls chooser.next(); Chooser is an executor selector created when we build the NioEventLoopGroup. The next method returns a thread executor: NioEventLoop! If you don’t remember, go back to the NioEventLoopGroup initialization source code parsing chapter!
Now that we know from previous chapters that the next() method returns a NioEventLoop class, we go to the register() method to check:
NioEventLoop’s parent class is SingleThreadEventLoop. NioEventLoop’s parent is SingleThreadEventLoop. So we go to SingleThreadEventLoop#register(io.net ty.channel.channel) :
@Override
public ChannelFuture register(Channel channel) {
// Call its own registration method
return register(new DefaultChannelPromise(channel, this));
}
// Nothing more to say
@Override
public ChannelFuture register(final ChannelPromise promise) {
ObjectUtil.checkNotNull(promise, "promise");
promise.channel().unsafe().register(this, promise);
return promise;
}
Copy the code
Broadening ().unsafe().register(this, promise); broadening (), unsafe().
Unsafe is NioMessageUnsafe, as we examined in the previous chapter, but register does not have its implementation:
We still need to the parent class after, into the io.net ty. Channel. AbstractChannel. AbstractUnsafe# register (this, promise) :
Let’s focus on the parameters:
What NioEventLoop is it passing in, that is, an actuator
Promise: wrapper object for NioServerSocketChannel
Let’s go into the register method and analyze the main code:
@Override
public final void register(EventLoop eventLoop, final ChannelPromise promise) {... Temporarily ignore unnecessary code... AbstractChannel.this.eventLoop = eventLoop;
// Thread = null so return false
if (eventLoop.inEventLoop()) {
// The actual registry selector fires the handlerAdded event and the channelRegistered event
register0(promise);
} else{... Ignore unnecessary code for now...................... }}Copy the code
AbstractChannel.this.eventLoop = eventLoop;
Copy the code
First we save the actuator obtained in the previous step in a NioServerSocketChannel! This line of code strongly demonstrates that each Channel is bound to a NioEventLoop object!
if (eventLoop.inEventLoop()) {
// The actual registry selector fires the handlerAdded event and the channelRegistered event
register0(promise);
}
Copy the code
Note: I need to clarify here, and the real debugging process, this branch will not go, but will go else branch asynchronous to register, here in order to more convenient you understand, I will in accordance with the if branch source code analysis, there is no too big change, are called register0 methods to register, just an asynchronous, synchronous a about asynchronous, It is Netty in and its important a knowledge point, I will be put behind alone open a chapter to undertake explain!
Let’s go to the register0 source code:
private void register0(ChannelPromise promise) {
try {
..............忽略代码..................
]
// The actual registration calls the underlying data registry selector in the JDK
// Call the underlying JDK register() to register
//io.netty.channel.nio.AbstractNioChannel.doRegister
doRegister();
neverRegistered = false;
registered = true;
// The notification pipe propagates the handlerAdded event
// Trigger handlerAdded Triggers the task add event
pipeline.invokeHandlerAddedIfNeeded();
safeSetSuccess(promise);
// Notify the pipe to propagate channelRegistered events
// Trigger channelRegistered event
pipeline.fireChannelRegistered();
// Only channelActive is triggered if the channel has never been registered.
// Multiple channels are active if the channel is unregistered and re-registered.
/ / isActive () returns false
// The Channel has not registered its binding address yet, so it is inactive
if(isActive()) { .................... Ignore unnecessary code.................. }}catch (Throwable t) {
// Close the channel directly to avoid FD leakage.closeForcibly(); closeFuture.setClosed(); safeSetFailure(promise, t); }}Copy the code
Two, source code analysis
doRegister();
doRegister();
Copy the code
Real registration method, this method is the Netty itself NioServerSocket and JDK to connect the most important class!
selectionKey = javaChannel().register(eventLoop().unwrappedSelector(), 0.this);
Copy the code
The javaChannel() method returns the JDK’s native SocketChannel, which was saved when NioServerSocketChannel was initialized. Remember how NIO developed sockets
Let’s focus on the javaChannel().register argument:
EventLoop ().unwrappedSelector() : When NioEventLoop is created, it will save two selectors, one is the original JDK selector, the other is Netty wrapped selector, this returns the native selector!
0: do not pay attention to any events
This: This represents the current class, which is of NioServerSocketChannel type. It binds an NioServerSocketChannel object to a JDK native selector, and then it just goes through selectionKey.Attachment (), NioServerSocketChannel can be obtained, and a NioServerSocketChannel contains a JDK native Channel object, you can be based on the JDK native Channel to carry out various read and write operations!
Now that we’ve done NIO’s binding of channels to selectors in the JDK, let’s go back to the previous step:
pipeline.invokeHandlerAddedIfNeeded
pipeline.invokeHandlerAddedIfNeeded();
Copy the code
Start adding custom events to the pipeline channel:
final void invokeHandlerAddedIfNeeded(a) {
assert channel.eventLoop().inEventLoop();
if (firstRegistration) {
firstRegistration = false;
// We are now registered with EventLoop. Now it's time to call the ChannelHandler callback,
// The content that was added before the registration was completed.callHandlerAddedForAllHandlers(); }}//callHandlerAddedForAllHandlers
private void callHandlerAddedForAllHandlers(a) {
//task = PendingHandlerAddedTask
PendingHandlerCallback task = pendingHandlerCallbackHead;
while(task ! =null) { task.execute(); task = task.next; }}Copy the code
Note that the PendingHandlerCallback task is of type PendingHandlerAddedTask. When was it loaded? AddLast: socketChannel: socketChannel: socketChannel: SocketChannel: SocketChannel: SocketChannel: SocketChannel: SocketChannel: SocketChannel: SocketChannel: SocketChannel: SocketChannel: SocketChannel: SocketChannel
if (executor.inEventLoop()) {
callHandlerAdded0(ctx);
}
// Go to the callHandlerAdded0 source logic
private void callHandlerAdded0(final AbstractChannelHandlerContext ctx) {
try{ ctx.callHandlerAdded(); }... }// Go to ctx.callHandlerAdded();
final void callHandlerAdded(a) throws Exception {
if (setAddComplete()) {
handler().handlerAdded(this); }}Copy the code
Handler () added a ChannelInitializer to the pipeline and returned that ChannelInitializer! We go to the ChannelInitializer#handlerAdded method:
@Override
public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
if (ctx.channel().isRegistered()) {
if(initChannel(ctx)) { removeState(ctx); }}}Copy the code
First we focus on an initChannel(CTX),
private boolean initChannel(ChannelHandlerContext ctx) throws Exception {
// Prevent re-entry.
if (initMap.add(ctx)) {
try {
// Call the initChannel() method implemented by ChannelInitializer
initChannel((C) ctx.channel());
} catch (Throwable cause) {
................................
} finally {
ChannelPipeline pipeline = ctx.pipeline();
if (pipeline.context(this) != null) {
// Remove ChannelInitializer itself from the Pipeline
pipeline.remove(this); }}return true;
}
return false;
}
Copy the code
initChannel((C) ctx.channel());
Copy the code
This method calls back to the ChannelInitializer abstract method initChannel. The abstract method is done when we initialize it. We need to find where to implement this abstract method. io.netty.bootstrap.ServerBootstrap#init
void init(Channel channel) {... ; p.addLast(new ChannelInitializer<Channel>() {
@Override
public void initChannel(final Channel ch) {
final ChannelPipeline pipeline = ch.pipeline();
// Add user-defined handlers to the pipeline handler is the handler passed in when ServerBootStr is built
ChannelHandler handler = config.handler();
if(handler ! =null) {
pipeline.addLast(handler);
}
ch.eventLoop().execute(() -> {
pipeline.addLast(newServerBootstrapAcceptor( ch, currentChildGroup, currentChildHandler, currentChildOptions, currentChildAttrs)); }); }}Copy the code
We skipped this logic in the last class, saying that we would add a ChannelInitializer implementation to the channel, and now call back its initChannel method:
ChannelHandler handler = config.handler();
if(handler ! =null) {
pipeline.addLast(handler);
}
Copy the code
This code will add the handler passed in when the client builds ServerBootstrap again to the channel. We assume that the user has not set the handler, so this handler will not pass and we will skip it.
ch.eventLoop().execute(() -> {
pipeline.addLast(new ServerBootstrapAcceptor(
ch, currentChildGroup, currentChildHandler, currentChildOptions, currentChildAttrs));
});
Copy the code
This is done asynchronously by registering a default Handler to the pipeline stream. We’ll leave it at that. We’ll call it synchronous.
The purpose of ServerBootstrapAcceptor is specifically for new connections.
ServerBootstrapAcceptor(
finalChannel channel, EventLoopGroup childGroup, ChannelHandler childHandler, Entry<ChannelOption<? >, Object>[] childOptions, Entry<AttributeKey<? >, Object>[] childAttrs) {this.childGroup = childGroup;
this.childHandler = childHandler;
this.childOptions = childOptions;
this.childAttrs = childAttrs;
enableAutoReadTask = new Runnable() {
@Override
public void run(a) {
channel.config().setAutoRead(true); }}; }Copy the code
As you can see, it saves a number of parameters, including WorkGroup, childHandler, childOptions, and childAttrs, that were passed in when we created serverBootstrap. This also proves that, These parameters apply to client Socket connections!
We’ll do a more detailed analysis of ServerBootstrapAcceptor, and then we’ll say that we need to focus on the addLast method,
@Override
public final ChannelPipeline addLast(EventExecutorGroup group, String name, ChannelHandler handler) {... Ignore...// Notification add method callback
callHandlerAdded0(newCtx);
return this;
}
Copy the code
When adding, it calls back to the handlerAdded method generated internally. Remember the business channel section where we introduced the basic architecture of Netty?
When addLast is called again, the method is called back!
Now that all methods are registered, let’s go back to the ChannelInitializer#handlerAdded method, after **initChannel(CTX)** is called:
private boolean initChannel(ChannelHandlerContext ctx) throws Exception {
// Prevent re-entry.
if (initMap.add(ctx)) {
try {
// Call the initChannel() method implemented by ChannelInitializer
initChannel((C) ctx.channel());
} catch (Throwable cause) {
................................
} finally {
ChannelPipeline pipeline = ctx.pipeline();
if (pipeline.context(this) != null) {
// Remove ChannelInitializer itself from the Pipeline
pipeline.remove(this); }}return true;
}
return false;
}
Copy the code
Finally, we will delete the current class. The current class is ChannelInitializer, so after the deletion, the structure of the pipeline object will look like the following:
At this point invokeHandlerAddedIfNeeded parsed
pipeline.fireChannelRegistered();
@Override
public final ChannelPipeline fireChannelRegistered(a) {
AbstractChannelHandlerContext.invokeChannelRegistered(head);
return this;
}
Copy the code
The channelRegistered method is propagated from the HeadContext node:
So far, NioServerSocketChannel registration has been basically analyzed.
if (isActive()) {
if (firstRegistration) {
// The channelActive event is emitted when the current state of Channel is active
pipeline.fireChannelActive();
} else if (config().isAutoRead()) {
// The channel is registered and autoRead () is set. That means we need to start reading
// Again, so that we can process the inbound data.
//
// See https://github.com/netty/netty/issues/4805
// Start reading eventsbeginRead(); }}Copy the code
This code will not be called when the channel is started for the first time, because the channel has not yet started the binding port, so isActive will return false.
Third, summary
- Netty calls the JDK’s underlying registration method and binds its own NioServerSocketChannel to the selection event as att!
- The handlerAdded method is called back when the registration is complete
- Netty calls the Channelinitialization registered during the initialization of NioServerSocketChannel, adds a new connection access ServerBootstrapAcceptor, and deletes itself!
- The Channelregistered method is called back when registration is complete