Apache Mina IoAcceptor与IoConnector

简介

Apache mina是一个介于应用程序与网络之间的NIO框架,它使程序员从繁琐的网络操作中解脱出来,花更多的时间在业务处理上。

如下图所示,mina分为三层

1、IOService层:处理IO操作

2、IOFilter层:过滤器链,日志处理、字节变换、对象转换等操作

3、IOHandler层:真正的处理业务逻辑的地方

Apache Mina IoAcceptor与IoConnector_第1张图片

IOService层根据不同的角色又分为IOAcceptor和IOConnector,分别用于接受连接与请求连接操作。

IOAcceptor

Apache Mina IoAcceptor与IoConnector_第2张图片

上图是IOAcceptor的类图,IOAcceptor相当于是对ServerSocketChannel的封装,最重要的两个操作是绑定与接受连接,IOService接口中有多个重载的bind方法

public interface IoAcceptor extends IoService {        
    void bind() throws IOException;   
    void bind(SocketAddress localAddress) throws IOException;       
    void bind(SocketAddress firstLocalAddress, SocketAddress... addresses) throws IOException;   
    void bind(Iterable<? extends SocketAddress> localAddresses) throws IOException;
}   
其方法的实现在抽象类AbstractIOAcceptor的bind方法中,这个方法在做了参数检查等操作后,将真正的绑定操作交给抽象方法bindInternal来完成。对于bindInternal有基于TCP/IP,UDP/IP,VMPipe三种实现,以TCP/IP为例来看绑定过程
   protected final Set<SocketAddress> bindInternal(
            List<? extends SocketAddress> localAddresses) throws Exception {
        // Create a bind request as a Future operation. When the selector
        // have handled the registration, it will signal this future.
        AcceptorOperationFuture request = new AcceptorOperationFuture(
                localAddresses);

        // adds the Registration request to the queue for the Workers
        // to handle
        registerQueue.add(request);

        // creates the Acceptor instance and has the local
        // executor kick it off.
        startupAcceptor();
        
        // As we just started the acceptor, we have to unblock the select()
        // in order to process the bind request we just have added to the 
        // registerQueue.
        wakeup();
        
        // Now, we wait until this request is completed.
        request.awaitUninterruptibly();

        if (request.getException() != null) {
            throw request.getException();
        }

        // Update the local addresses.
        // setLocalAddresses() shouldn't be called from the worker thread
        // because of deadlock.
        Set<SocketAddress> newLocalAddresses = new HashSet<SocketAddress>();
        
        for (H handle:boundHandles.values()) {
            newLocalAddresses.add(localAddress(handle));
        }

        return newLocalAddresses;
    }

主要干了以下几件事情:

1、将绑定请求放入registerQueue中

2、启动Acceptor,从Acceptor类的run方法可以看到,这一步会阻塞在Acceptor选择器的选择操作中

3、调用wakeup让选择器返回

4、等待请求处理完成,这一步会阻塞在ready变量中,当ready变量为true时才会返回,当接受连接后ready会被设置为true.

现在重点看一下Acceptor的run方法

public void run() {
		......
    while (selectable) {
        try {
            int selected = select();
            nHandles += registerHandles();
            if (nHandles == 0) {
                acceptorRef.set(null);

                if (registerQueue.isEmpty() && cancelQueue.isEmpty()) {
                    assert (acceptorRef.get() != this);
                    break;
                }
                
                if (!acceptorRef.compareAndSet(null, this)) {
                    assert (acceptorRef.get() != this);
                    break;
                }
                
                assert (acceptorRef.get() == this);
            }

            if (selected > 0) {
                processHandles(selectedHandles());
            }

            // check to see if any cancellation request has been made.
            nHandles -= unregisterHandles();
        } catch (...) {
       	...
    }

    // Cleanup all the processors, and shutdown the acceptor. set ready=true
}
(1)、selector被wakeup唤醒后,调用registerHandles方法从registerQueue中取出请求依次调用open方法
 protected ServerSocketChannel open(SocketAddress localAddress)
            throws Exception {
        // Creates the listening ServerSocket
        ServerSocketChannel channel = ServerSocketChannel.open();
        
        boolean success = false;
        
        try {
            // This is a non blocking socket channel
            channel.configureBlocking(false);
        
            // Configure the server socket,
            ServerSocket socket = channel.socket();
            
            // Set the reuseAddress flag accordingly with the setting
            socket.setReuseAddress(isReuseAddress());
            
            // and bind.
            socket.bind(localAddress, getBacklog());
            
            // Register the channel within the selector for ACCEPT event
            channel.register(selector, SelectionKey.OP_ACCEPT);
            success = true;
        } finally {
            if (!success) {
                close(channel);
            }
        }
        return channel;
    }
open方法完成了ServerSocket的绑定和注册
(2)、从(1)中可以知道selector上注册了ServerSocketChannel的OP_ACCEPT键,注册后nHandles==0,selected==0,进行下一次循环,同样是阻塞在select方法上

(3)、当连接到来时,select方法返回,selected>0,执行processHandles方法

private void processHandles(Iterator<H> handles) throws Exception {
            while (handles.hasNext()) {
                H handle = handles.next();
                handles.remove();

                // Associates a new created connection to a processor,
                // and get back a session
                S session = accept(processor, handle);
                
                if (session == null) {
                    break;
                }

                initSession(session, null, null);

                // add the session to the SocketIoProcessor
                session.getProcessor().add(session);
            }
        }
该方法在完成真正的接受连接操作后,创建session并扔到processor中,后续的工作交给processor来完成。每个session中其实有一个SocketChannel,这个socketChannel实际上是被注册到了processor的selector上。注册代码在NioProcessor类中可以找到
    protected void init(NioSession session) throws Exception {
        SelectableChannel ch = (SelectableChannel) session.getChannel();
        ch.configureBlocking(false);
        session.setSelectionKey(ch.register(selector, SelectionKey.OP_READ,
                session));
    }

整个Acceptor的实现就讲解完了,总结一下:Acceptor线程专门负责接受连接,在其上有一个selector,轮询是否有连接建立上来,当有连接建立上来,调用ServerSocketChannel.accept方法来接受连接,这个方法返回一个session对象,然后将这个session对象加入processor中,由processor遍历每个session来完成真正的IO操作。processor上也有一个selector与一个Processor线程,selector用于轮询session,Processor线程处理每个session的IO操作。

IOConnector

IOConnector的类图如下:

Apache Mina IoAcceptor与IoConnector_第3张图片

IOConnector的设计与IOAcceptor几乎完全一样,唯一不同的是与Acceptor线程对应的是Connector线程,在完成连接操作后也是扔了一个session对象到Processor中。

关于Processor与Session后续再分析......


你可能感兴趣的:(Apache Mina IoAcceptor与IoConnector)