Mina Nio processor

Mina Io processor abstract implementation: http://donald-draper.iteye.com/blog/2377663
Introduction:
In the last article, we saw the abstract implementation of the Io processor, let's review it first:
     AbstractPollingIoProcessor, the main Several key internal variables are the selection operation timeout time SELECT_TIMEOUT, which is used to free up time to process idle sessions; the internal executor of the executor processor, which is used to run the internal processor Processor; the threadIds that store the maximum thread id of threads such as the Io processor ( Map); create session queue newSessions to store newly created sessions; remove session queue removingSessions to store sessions removed from the processor; refresh session queue flushingSessions to store sessions to send write requests; order control session queue trafficControllingSessions is used to store the session to be read and written; the Io processor thread refers to processorRef.
     Adding a session First add a session to the Io processor's create session queue, and start the processor thread Processor. The actual work of the processor, try 10 nbTries selection operations, during each selection operation, first perform a timeout selection operation, and then check whether the Io processor is disconnected, and whether the number of attempts nbTries is zero If it is 0, register a new The selector; then traverse the create session queue, pull the session from the queue, initialize the session if the session is not null, build the session filter chain (inherited from IoService), trigger the session creation and session open events of the session filter chain, and record new The number of sessions created is nSessions; the session status is also known. This process is to obtain the session from the session order control queue, check the session status, if the status is OPENED, update the read and write status of the session, if it is OPENING, put it back into the sequence control session queue; if the operation is selected The value of the returned SELECTKey is greater than 0, that is, there are related interest operation events (read and write events), traverse the session that is ready for read and write operations after selection, if the session is readable, read the session buffer data to the buffer, triggering the filter chain The message receiving event MessageReceive, after receiving the message, if the session input stream is closed, the filter chain fireInputClosed event is triggered. If an exception occurs during this process, the filter chain exception event ExceptionCaught is triggered. If the session is writable, add the session to the refresh session queue. ; Traverse and refresh the session queue, send the session data according to whether the session write request message type is IoBuffer or FileRegion, after sending the session data, if there are still some requests in the session, add the session to the queue, if there is an exception in this process, add the session to the Session removal queue; traverse the session removal queue, if the session is closed, try to close the session and clear the session write request queue, if the session data has been sent, trigger the session filter chain message sending event fireMessageSent; update the processor session counter nSessions; traverse all sessions of the processor, trigger the session filter session idle time fireSessionIdle; if in this process, the processor session counter nSessions is 0, clear the processor reference; if the Io processor is closing, add all sessions to the mobile Remove the session queue and release the resources that the Io processor closed first.
     The abstract Io processor AbstractPollingIoProcessor mainly handles the IoProcessor associated session message* events, and all the work is done through the processor thread Processor. Whenever a session is added to the IoProcessor, a processor thread Processor is started to process the read and write operations and related events of the session. Even the release of IoProcessor resources is handled by the processor thread Processor. When the IoProcessor is closed, the processor is now associated with the session, adding and removing the session queue, and the actual work is done by the doDispose method of the subclass of IoProcessor.
Today, let's look at a specific implementation of the Io processor, NioProcessor:
/**
 * A processor for incoming and outgoing data get and written on a TCP socket.
 *
 * @author [url=http://mina.apache.org]Apache MINA Project[/url]
 */
public final class NioProcessor extends AbstractPollingIoProcessor<NioSession> {
    /** The selector associated with this processor */
    private Selector selector;//Selector
    /** A lock used to protect concurent access to the selector */
    private ReadWriteLock selectorLock = new ReentrantReadWriteLock();
    private SelectorProvider selectorProvider = null;//Selector provider
 }

look at the construction method
  
/**
     *
     * Creates a new instance of NioProcessor.
     *
     * @param executor The executor to use
     */
    public NioProcessor(Executor executor) {
        super(executor);

        try {
            // Open a new selector
            selector = Selector.open();
        } catch (IOException e) {
            throw new RuntimeIoException("Failed to open a selector.", e);
        }
    }
    /**
     *
     * Creates a new instance of NioProcessor.
     *
     * @param executor The executor to use
     * @param selectorProvider The Selector provider to use
     */
    public NioProcessor(Executor executor, SelectorProvider selectorProvider) {
        super(executor);

        try {
            // Open a new selector
            if (selectorProvider == null) {
                selector = Selector.open();
            } else {
                this.selectorProvider = selectorProvider;
                selector = selectorProvider.openSelector();
            }
        } catch (IOException e) {
            throw new RuntimeIoException("Failed to open a selector.", e);
        }
}

As can be seen from the constructor, NioProcessor is mainly to initialize thread executors and selectors.
Then choose an action:
@Override
    protected int select(long timeout) throws Exception {
        selectorLock.readLock().lock();
        
        try {
            return selector.select(timeout);
        } finally {
            selectorLock.readLock().unlock();
        }
    }

    @Override
    protected int select() throws Exception {
        selectorLock.readLock().lock();
        
        try {
            return selector.select();
        } finally {
            selectorLock.readLock().unlock();
        }
    }

From the above, the selection operation of the Nio processor is actually completed through the internal selector.
 
  @Override
    protected boolean isSelectorEmpty() {
        selectorLock.readLock().lock();
        
        try {
            return selector.keys().isEmpty();
        } finally {
            selectorLock.readLock().unlock();
        }
    }
    @Override
    protected void wakeup() {
        wakeupCalled.getAndSet(true);
        selectorLock.readLock().lock();
        
        try {
            selector.wakeup();
        } finally {
            selectorLock.readLock().unlock();
        }
    }
    @Override
    protected Iterator<NioSession> allSessions() {
        selectorLock.readLock().lock();
        
        try {
            return new IoSessionIterator(selector.keys());
        } finally {
            selectorLock.readLock().unlock();
        }
    }
    @SuppressWarnings("synthetic-access")
    @Override
    protected Iterator<NioSession> selectedSessions() {
        return new IoSessionIterator(selector.selectedKeys());
    }
 
   //Initialize the session, mainly to configure the session channel as non-blocking mode, register the session channel read event to the selector
    @Override
    protected void init(NioSession session) throws Exception {
        SelectableChannel ch = (SelectableChannel) session.getChannel();
        ch.configureBlocking(false);
        selectorLock.readLock().lock();
        
        try {
            session.setSelectionKey(ch.register(selector, SelectionKey.OP_READ, session));
        } finally {
            selectorLock.readLock().unlock();
        }
    }
    
    @Override
    //Close the byte channel associated with the session and select the key
    protected void destroy(NioSession session) throws Exception {
        ByteChannel ch = session.getChannel();
        SelectionKey key = session.getSelectionKey();
        if (key != null) {
            key.cancel();
        }
        if ( ch.isOpen() ) {
            ch.close();
        }
    }

Let's look at registering a new selector
  
/**
     * In the case we are using the java select() method, this method is used to
     * trash the buggy selector and create a new one, registering all the
     * sockets on it.
     */
    @Override
    protected void registerNewSelector() throws IOException {
        selectorLock.writeLock().lock();
        
        try {
	    //Get the selector selection key set
            Set<SelectionKey> keys = selector.keys();
            Selector newSelector;
	    //create a new selector
            // Open a new selector
            if (selectorProvider == null) {
                newSelector = Selector.open();
            } else {
                newSelector = selectorProvider.openSelector();
            }
            // Register the session, channel, and channel interest event set associated with the selection key of the old selector to the new selector.
            // Loop on all the registered keys, and register them on the new selector
            for (SelectionKey key : keys) {
                SelectableChannel ch = key.channel();

                // Don't forget to attache the session, and back !
                NioSession session = (NioSession) key.attachment();
                SelectionKey newKey = ch.register(newSelector, key.interestOps(), session);
                session.setSelectionKey(newKey);
            }

            // Now we can close the old selector and switch it
            selector.close();
            selector = newSelector;
        } finally {
            selectorLock.writeLock().unlock();
        }

    }

From the above, you can look at registering a new selector, mainly to register the session, channel, and channel interest event set associated with the selection key (set) of the old selector to the new selector; the session is attached to the Attachment of the channel selection key.
see other operations
/**
 * {@inheritDoc}
 */
@Override
//To determine whether the processor is closed, mainly to see whether the channel associated with the selection key registered to the selector is disconnected,
//There is a disconnection, then the processor disconnects
protected boolean isBrokenConnection() throws IOException {
    // A flag set to true if we find a broken session
    boolean brokenSession = false;
    selectorLock.readLock().lock();
    try {
        // Get the selector keys
        Set<SelectionKey> keys = selector.keys();
        // Loop on all the keys to see if one of them
        // has a closed channel
        for (SelectionKey key : keys) {
            SelectableChannel channel = key.channel();
            if (((channel instanceof DatagramChannel) && !((DatagramChannel) channel).isConnected())
                    || ((channel instanceof SocketChannel) && !((SocketChannel) channel).isConnected())) {
                // The channel is not connected anymore. Cancel
                // the associated key then.
                key.cancel();

                // Set the flag to true to avoid a selector switch
                brokenSession = true;
            }
        }
    } finally {
        selectorLock.readLock().unlock();
    }
    return brokenSession;
}

/**
 * {@inheritDoc}
 If the selection key associated with the session is valid, that is, the session state is open, if it is null, it is being opened, otherwise the session is closed.
 */
@Override
protected SessionState getState(NioSession session) {
    SelectionKey key = session.getSelectionKey();
    if (key == null) {
        // The channel is not yet registred to a selector
        return SessionState.OPENING;
    }
    if (key.isValid()) {
        // The session is opened
        return SessionState.OPENED;
    } else {
        // The session still as to be closed
        return SessionState.CLOSING;
    }
}
//whether the session is readable
@Override
protected boolean isReadable(NioSession session) {
    SelectionKey key = session.getSelectionKey();
    return (key != null) && key.isValid() && key.isReadable();
}
//whether the session is writable
@Override
protected boolean isWritable(NioSession session) {
    SelectionKey key = session.getSelectionKey();
    return (key != null) && key.isValid() && key.isWritable();
}
//whether the session is readable
@Override
protected boolean isInterestedInRead(NioSession session) {
    SelectionKey key = session.getSelectionKey();
    return (key != null) && key.isValid() && ((key.interestOps() & SelectionKey.OP_READ) != 0);
}
//Whether to pay attention to the write event
@Override
protected boolean isInterestedInWrite(NioSession session) {
    SelectionKey key = session.getSelectionKey();

    return (key != null) && key.isValid() && ((key.interestOps() & SelectionKey.OP_WRITE) != 0);
}
/**
 * {@inheritDoc}
 Set read event as session interest event
 */
@Override
protected void setInterestedInRead(NioSession session, boolean isInterested) throws Exception {
    SelectionKey key = session.getSelectionKey();

    if ((key == null) || !key.isValid()) {
        return;
    }
    int oldInterestOps = key.interestOps();
    int newInterestOps = oldInterestOps;
    if (isInterested) {
        newInterestOps |= SelectionKey.OP_READ;
    } else {
        newInterestOps &= ~SelectionKey.OP_READ;
    }
    if (oldInterestOps != newInterestOps) {
        key.interestOps(newInterestOps);
    }
}

/**
 * {@inheritDoc}
 Set write event as session interest event
 */
@Override
protected void setInterestedInWrite(NioSession session, boolean isInterested) throws Exception {
    SelectionKey key = session.getSelectionKey();

    if ((key == null) || !key.isValid()) {
        return;
    }
    int newInterestOps = key.interestOps();
    if (isInterested) {
        newInterestOps |= SelectionKey.OP_WRITE;
    } else {
        newInterestOps &= ~SelectionKey.OP_WRITE;
    }
    key.interestOps(newInterestOps);
}

Let's look at read and write operations
@Override
protected int read(NioSession session, IoBuffer buf) throws Exception {
    ByteChannel channel = session.getChannel();
    //Delegate to the session associated channel
    return channel.read(buf.buf());
}

@Override
//Delegate to the session associated channel
protected int write(NioSession session, IoBuffer buf, int length) throws IOException {
    if (buf.remaining() <= length) {
        return session.getChannel().write(buf.buf());
    }
    int oldLimit = buf.limit();
    buf.limit(buf.position() + length);
    try {
        return session.getChannel().write(buf.buf());
    } finally {
        buf.limit(oldLimit);
    }
}
@Override
protected int transferFile(NioSession session, FileRegion region, int length) throws Exception {
    try {
        return (int) region.getFileChannel().transferTo(region.getPosition(), length, session.getChannel());
    } catch (IOException e) {
        // Check to see if the IOException is being thrown due to
        // http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=5103988
        String message = e.getMessage();
        if ((message != null) && message.contains("temporarily unavailable")) {
            return 0;
        }
        throw e;
    }
}

From the above point of view, the processor handles session read and write operations, mainly through the channel associated with the session.
@Override
protected void doDispose() throws Exception {
    selectorLock.readLock().lock();
    try {
        selector.close();//Close the selector
    } finally {
        selectorLock.readLock().unlock();
    }
}

Below we post the code of NioSession in order to understand the Nio processor,
public abstract class NioSession extends AbstractIoSession
{
    protected final IoProcessor processor;//Io processor
    protected final Channel channel;//Select the channel
    private SelectionKey key;//选择key
    private final IoFilterChain filterChain = new DefaultIoFilterChain(this);//过滤链
    protected NioSession(IoProcessor processor, IoService service, Channel channel)
    {
        super(service);
        this.channel = channel;
        this.processor = processor;
    }
    abstract ByteChannel getChannel();
    public IoFilterChain getFilterChain ()
    {
        return filterChain;
    }
    SelectionKey getSelectionKey()
    {
        return key;
    }
    void setSelectionKey(SelectionKey key)
    {
        this.key = key;
    }
    public IoProcessor getProcessor()
    {
        return processor;
    }
    public final boolean isActive()
    {
        return key.isValid();
    }
}

As can be seen from the definition of NioSession, the Nio session is associated with an Io processor IoProcessor, selects the channel Channel, selects the key (SelectionKey) and a filter chain IoFilterChain. In fact, I personally feel that NioProcessor and NioSession can be understood as the selector Selector and the selection channel Channel in Java Nio.
Summary:
     NioProcessor has a selector Selector inside, a reentrant read-write lock is used to control selector-related operations, and the construction is mainly to initialize thread executors and selectors. The selection operation, wake-up and other operations of the Nio processor are actually completed through the internal selector. Initialize the session, mainly configure the session channel to non-blocking mode, register the session channel read event to the selector. Registering a new selector is mainly to register the session, channel, and channel interest event set associated with the selection key (set) of the old selector to the new selector; the session is attached to the Attachment of the channel selection key. The processor handles session read and write operations, mainly through the channel associated with the session. Closing a session is mainly to close the byte channel associated with the session and cancel the session association selection key.
Attached:
//IoSessionIterator
/**
     * An encapsulating iterator around the {@link Selector#selectedKeys()} or
     * the {@link Selector#keys()} iterator;
     */
    protected static class IoSessionIterator<NioSession> implements Iterator<NioSession> {
        private final Iterator<SelectionKey> iterator;

        /**
         * Create this iterator as a wrapper on top of the selectionKey Set.
         *
         * @param keys
         *            The set of selected sessions
         */
        private IoSessionIterator(Set<SelectionKey> keys) {
            iterator = keys.iterator();
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public boolean hasNext() {
            return iterator.hasNext();
        }
        /**
         * {@inheritDoc}
         */
        @Override
        public NioSession next() {
            SelectionKey key = iterator.next();
            
            return (NioSession) key.attachment();
        }
        /**
         * {@inheritDoc}
         */
        @Override
        public void remove() {
            iterator.remove();
        }
    }
}

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=326503172&siteId=291194637