Netty NiO starts the whole process

Source: Internet
Author: User

Netty NiO starts the whole process 1. The relationship between the components


Description: Eventloopgroup Similar thread pool, EventLoop is single threaded, each eventloop associated with a nio Selector, used to register the channel, forming a eventloop is common to multiple channel. The EventLoop will perform channel IO selection operations, as well as non-IO tasks. The pipeline is created after the channel is initialized and is the handler list structure.

2. Server-side VS client-initiated
Server Start Private channelfuture dobind (final socketaddress localaddress) {final Channelfuture regfuture = Initandregiste    R ();        Final Channel channel = Regfuture.channel ();    if (regfuture.cause () = null) {return regfuture;        if (Regfuture.isdone ()) {//AT-this-point we know the registration is complete and successful.        Channelpromise promise = Channel.newpromise ();        DoBind0 (Regfuture, channel, localaddress, Promise);    return promise;        } else {//registration almost always fulfilled already and just in case it's not.        Final Pendingregistrationpromise promise = new Pendingregistrationpromise (channel); Regfuture.addlistener (New Channelfuturelistener () {@Override public void Operationcomplete (channelf                Uture future) throws Exception {throwable cause = Future.cause (); if (cause! = NULL) {//Registration on the EventLoop failed so FAIl the channelpromise directly to not cause a//illegalstateexception once we try to access the Event                    Loop of the Channel.                Promise.setfailure (cause);                    } else {//registration is successful, so set the correct executor to use.                    See https://github.com/netty/netty/issues/2586 promise.registered ();                DoBind0 (Regfuture, channel, localaddress, Promise);        }            }        });    return promise; }}
Client launches private Channelfuture Doresolveandconnect (final socketaddress remoteaddress, final socketaddress localaddress)    {final Channelfuture regfuture = Initandregister ();    Final Channel channel = Regfuture.channel ();        if (Regfuture.isdone ()) {if (!regfuture.issuccess ()) {return regfuture;    } return DoResolveAndConnect0 (channel, Remoteaddress, LocalAddress, Channel.newpromise ());        } else {//registration almost always fulfilled already and just in case it's not.        Final Pendingregistrationpromise promise = new Pendingregistrationpromise (channel); Regfuture.addlistener (New Channelfuturelistener () {@Override public void Operationcomplete (channelf Uture future) throws Exception {//Directly obtain the cause and does a null check so we only need one Volat                Ile read in case of A//failure.                Throwable cause = Future.cause (); if (cause! = NULL) {//Registration on the EventLoop failed so fail the channelpromise directly to not cause an                    IllegalStateException once we try to access the eventloop of the Channel.                Promise.setfailure (cause);                    } else {//registration is successful, so set the correct executor to use.                    See https://github.com/netty/netty/issues/2586 promise.registered ();                DoResolveAndConnect0 (channel, Remoteaddress, localaddress, Promise);        }            }        });    return promise; }}

Word, first do the initial channel and channel registration operations, and then the server initiates the binding operation, the client initiates the connection operation. The initialization of channel and channel registrations is accomplished through Initandregister (). Maximize code reuse.

3. Initialize create channel and channel Register 3.1 template method creation channel--------channel registration
Final Channelfuture Initandregister () {channel channel = NULL;     try {//create channels channel = Channelfactory.newchannel ();    Initialize channel init (channels); } catch (Throwable t) {if (channel! = NULL) {//channel can be NULL if Newchannel crashed (eg Socketex            Ception ("Too Many open files") Channel.unsafe (). closeforcibly (); As the Channel isn't registered yet we need to force the usage of the Globaleventexecutor return new Defaul        Tchannelpromise (channel, globaleventexecutor.instance). Setfailure (t); }//As the Channel is not registered yet we need to force the usage of the Globaleventexecutor return new D    Efaultchannelpromise (New Failedchannel (), globaleventexecutor.instance). Setfailure (t);    }//Channel registration Channelfuture regfuture = config (). Group (). Register (channel);        if (regfuture.cause () = null) {if (channel.isregistered ()) {channel.close ();            } else {Channel.unsafe (). closeforcibly (); }} return regfuture;}
3.2 Creating a Channel

    1. Structure Channel
protected AbstractNioChannel(Channel parent, SelectableChannel ch, int readInterestOp) {    super(parent);    this.ch = ch;    this.readInterestOp = readInterestOp;    try {        ch.configureBlocking(false);    } catch (IOException e) {        try {            ch.close();        } catch (IOException e2) {            if (logger.isWarnEnabled()) {                logger.warn(                        "Failed to close a partially initialized socket.", e2);            }        }        throw new ChannelException("Failed to enter non-blocking mode.", e);    }}
protected AbstractChannel(Channel parent) {    this.parent = parent;    id = newId();    unsafe = newUnsafe();    pipeline = newChannelPipeline();}

Niochannel has packaged Java Selectablechannel and added pipeline and unsafe operations, and the default pipeline is a doubly linked list structure that contains only head and tail two nodes.

    1. Initialize Channel
      For the client, add the handler of the builder method directly to pipeline, and the common properties of some NIO operations, and for server-side creation, add only one initialized handler, in addition to some basic NIO properties
// 客户端创建ChannelPipeline p = channel.pipeline();p.addLast(config.handler());
//服务端创建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));            }        });    }});

Note: The Initchannnel of Channelinitializer will be called after successful registration, so that the dynamic extension can be implemented.
The client is created with no channelinitializer in pipeline and needs to be added by itself.

    1. Channel Registration
      Primarily binds the channel to EventLoop and then performs the registration operation in the EventLoop single thread
@Overridepublic final void Register (EventLoop eventloop, Final channelpromise Promise) {if (EventLoop = = null) {    throw new NullPointerException ("EventLoop");        } if (isregistered ()) {promise.setfailure (New IllegalStateException ("registered to an event loop already"));    Return } if (!iscompatible (EventLoop)) {promise.setfailure (New IllegalStateException ("Incompatible Eve        NT Loop Type: "+ Eventloop.getclass (). GetName ()));    Return    } AbstractChannel.this.eventLoop = EventLoop;    At this point in the main thread, I do not know if (Eventloop.ineventloop ()) {Register0 (Promise) in the pool of eventloop threads; } else {try {eventloop.execute (new Runnable () {@Override public void Ru                N () {Register0 (Promise);        }            }); } catch (Throwable t) {Logger.warn ("force-closing a channel whose registration task was no T accepted by an event loop: {} ", abstractchannel.this, T);            Closeforcibly ();            Closefuture.setclosed ();        Safesetfailure (Promise, T); }    }}

Register0 main Dry three things, registration, call Channelinitializer initchannnel complete add handler-> Register Channel Care Operations
3.1 Java Channel Registration, 0 means registration only, no action is taken

selectionKey = javaChannel().register(eventLoop().unwrappedSelector(), 0, this);

3.2 pipeline.firechannelregistered ()
At this point, Pipeline contains three handler, one of which is channelinitializer.

public final void channelRegistered(ChannelHandlerContext ctx) throws Exception {    if (initChannel(ctx)) {        ctx.pipeline().fireChannelRegistered();    } else {        ctx.fireChannelRegistered();    }}

3.2 BeginRead ();

@Overrideprotected void doBeginRead() throws Exception {    // Channel.read() or ChannelHandlerContext.read() was called    final SelectionKey selectionKey = this.selectionKey;    if (!selectionKey.isValid()) {        return;    }    readPending = true;    final int interestOps = selectionKey.interestOps();    if ((interestOps & readInterestOp) == 0) {        selectionKey.interestOps(interestOps | readInterestOp);    }}

Note that this is the event of a real registration relationship at this time, for the service side is the accept, the client is created, is connect

public NioServerSocketChannel(ServerSocketChannel channel) {     super(null, channel, SelectionKey.OP_ACCEPT);     config = new NioServerSocketChannelConfig(this, javaChannel().socket()); }
protected AbstractNioByteChannel(Channel parent, SelectableChannel ch) {     super(parent, ch, SelectionKey.OP_READ); }

At this point, the client and the server finish initializing the channel and registering the channel operation.

4. Server bound to the specified port
private static void doBind0(        final ChannelFuture regFuture, final Channel channel,        final SocketAddress localAddress, final ChannelPromise promise) {    channel.eventLoop().execute(new Runnable() {        @Override        public void run() {            if (regFuture.isSuccess()) {                channel.bind(localAddress, promise).addListener(ChannelFutureListener.CLOSE_ON_FAILURE);            } else {                promise.setFailure(regFuture.cause());            }        }    });}

To perform a bind port operation in EventLoop

@Overridepublic void bind(        ChannelHandlerContext ctx, SocketAddress localAddress, ChannelPromise promise)        throws Exception {    unsafe.bind(localAddress, promise);}

Finally, the Bind method will call unsafe to complete the port binding operation.

5. Client connection to remote server
private static void doConnect(        final SocketAddress remoteAddress, final SocketAddress localAddress, final ChannelPromise connectPromise) {    final Channel channel = connectPromise.channel();    channel.eventLoop().execute(new Runnable() {        @Override        public void run() {            if (localAddress == null) {                channel.connect(remoteAddress, connectPromise);            } else {                channel.connect(remoteAddress, localAddress, connectPromise);            }            connectPromise.addListener(ChannelFutureListener.CLOSE_ON_FAILURE);        }    });}

The connection server is ultimately executed in EventLoop, and finally calls the unsafe connect method.

protected boolean doConnect(SocketAddress remoteAddress, SocketAddress localAddress) throws Exception {    if (localAddress != null) {        doBind0(localAddress);    }    boolean success = false;    try {        boolean connected = SocketUtils.connect(javaChannel(), remoteAddress);        if (!connected) {            selectionKey().interestOps(SelectionKey.OP_CONNECT);        }        success = true;        return connected;    } finally {        if (!success) {            doClose();        }    }}

CONNECT has three results, succeeds, directly returns true, fails to temporarily know the result, detects op_connect, and abnormally closes the link directly.
It is worth noting that the JDK does not support connection timeouts by default, Netty adds a timeout mechanism: Adds a timeout task in EventLoop, closes the connection after the timeout is triggered, and deletes the timeout task when the connection succeeds.

Schedule Connect timeout.int connecttimeoutmillis = Config (). Getconnecttimeoutmillis (); if (Connecttimeoutmillis            > 0) {connecttimeoutfuture = EventLoop (). Schedule (new Runnable () {@Override public void run () {            Channelpromise connectpromise = AbstractNioChannel.this.connectPromise;            Connecttimeoutexception cause = new Connecttimeoutexception ("Connection timed out:" + remoteaddress);            if (connectpromise! = null && connectpromise.tryfailure (cause)) {Close (voidpromise ()); }}}, Connecttimeoutmillis, timeunit.milliseconds);} Promise.addlistener (New Channelfuturelistener () {@Override public void Operationcomplete (Channelfuture the Future) thro WS Exception {if (future.iscancelled ()) {if (connecttimeoutfuture! = null) {Connecttim            Eoutfuture.cancel (FALSE);            } connectpromise = null;      Close (Voidpromise ());  }    }}); 
6.EventLooop Processing IO Events
if ((readyOps & SelectionKey.OP_CONNECT) != 0) {    // remove OP_CONNECT as otherwise Selector.select(..) will always return without blocking    // See https://github.com/netty/netty/issues/924    int ops = k.interestOps();    ops &= ~SelectionKey.OP_CONNECT;    k.interestOps(ops);    unsafe.finishConnect();}// Process OP_WRITE first as we may be able to write some queued buffers and so free memory.if ((readyOps & SelectionKey.OP_WRITE) != 0) {    // Call forceFlush which will also take care of clear the OP_WRITE once there is nothing left to write    ch.unsafe().forceFlush();}// Also check for readOps of 0 to workaround possible JDK bug which may otherwise lead// to a spin loopif ((readyOps & (SelectionKey.OP_READ | SelectionKey.OP_ACCEPT)) != 0 || readyOps == 0) {    unsafe.read();}

Netty NiO starts the whole process

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.