After a rough introduction to Netty in the last article, this article will cover the startup process of netty’s server in detail.
ServerBootstrap
For those of you who have seen the previous example, ServerBootstrap plays an important role in Netty server startup. AbstractBootstrap is a server-side bootstrap class provided by Netty, derived from AbstractBootstrap. ServerBootstrap consists of two parts: bossGroup and workerGroup. BossGroup is used to bind ports and receive requests from clients. After receiving requests, bossGroup sends these requests to workGroup for processing. Just like the boss and employee in real life, they set up their own company (binding port), go outside to receive work (receiving requests), and ask employees to do work (letting workers handle it).
Port binding
Before port binding, check whether the bossGroup and workerGroup of the bootstrap class (ServerBootstrap) are set and then call doBind.
private ChannelFuture doBind(final SocketAddress localAddress) {
// Initialize and register a channel and return chanelFuture
final ChannelFuture regFuture = initAndRegister();
// Get the actual channel (initialization and registration may not be complete)
final Channel channel = regFuture.channel();
// If an exception occurs, return directly
if(regFuture.cause() ! =null) {
return regFuture;
}
// When the chanel related processing has been completed
if (regFuture.isDone()) {
// The channel is registered successfully
ChannelPromise promise = channel.newPromise();
// Perform related binding operations
doBind0(regFuture, channel, localAddress, promise);
return promise;
} else {
// Registration is usually completed at this point, just in case
final PendingRegistrationPromise promise = new PendingRegistrationPromise(channel);
// Add a listener
regFuture.addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture future) throws Exception {
Throwable cause = future.cause();
if(cause ! =null) {
promise.setFailure(cause);
} else {
/ / modify registration status to success (when registration is not in the use of global executor, use the channel their, see https://github.com/netty/netty/issues/2586).
promise.registered();
// Perform related binding operationsdoBind0(regFuture, channel, localAddress, promise); }}});returnpromise; }}Copy the code
The above code has two main parts: initializing and registering a channel and binding port.
Initialize and register a channel
final ChannelFuture initAndRegister() { Channel channel = null; Try {/ / production of the new channel channel. = channelFactory newChannel (); // Initialize channel init(channel); } catch (Throwable t) {if(channel ! // Forcibly close channel.unsafe(). CloseForcibly (); // Force GlobalEventExecutor because channel is not registered yetreturn new DefaultChannelPromise(channel, GlobalEventExecutor.INSTANCE).setFailure(t);
}
returnnew DefaultChannelPromise(new FailedChannel(), GlobalEventExecutor.INSTANCE).setFailure(t); } // Register channel ChannelFuture regFuture = config().group().register(channel);if(regFuture.cause() ! = null) {if (channel.isRegistered()) {
channel.close();
} else{ channel.unsafe().closeForcibly(); }}return regFuture;
}
Copy the code
Channel initialization:
Void init(Channel Channel) throws Exception {// Obtain the optional Map of bossChannel final Map<ChannelOption<? >, Object> options = options0(); synchronized (options) {setChannelOptions(channel, options, logger); } // Obtain bossChannel's attribute Map 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(); Final EventLoopGroup currentChildGroup = childGroup; final ChannelHandler currentChildHandler = childHandler; final Entry<ChannelOption<? >, Object>[] currentChildOptions; final Entry<AttributeKey<? >, Object>[] currentChildAttrs; 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(); // Add handler to pipeline ChannelHandler handler = config.handler();if(handler ! = null) { pipeline.addLast(handler); } // Add ServerBootstrapAcceptor to the pipeline via EventLoop, ensuring that it is the last handler ch.eventLoop().execute(new)Runnable() {
@Override
public void run() { pipeline.addLast(new ServerBootstrapAcceptor( ch, currentChildGroup, currentChildHandler, currentChildOptions, currentChildAttrs)); }}); }}); }Copy the code
The registration method of a channel is finally called doRegister. Different channels have different methods. Nio is used as an example below:
protected void doRegister() throws Exception {
boolean selected = false;
for(; ;) SelectionKey = javaChannel().register(eventLoop().unwrappedSelector(), 0, this);return;
} catch (CancelledKeyException e) {
if(! selected) { eventLoop().selectNow(); selected =true;
} else{ throw e; }}}}Copy the code
Binding port
The final call is the doBind method of NioServerSocketChannel.
protected void doBind(SocketAddress localAddress) throws Exception {
if (PlatformDependent.javaVersion() >= 7) {
javaChannel().bind(localAddress, config.getBacklog());
} else {
javaChannel().socket().bind(localAddress, config.getBacklog()); }}Copy the code
This completes the entire startup process of the Netty server.
The code comments for the post are all at github.com/KAMIJYOUDOU… , interested in children’s shoes can pay attention to.