MINA源码简易剖析(一)

MINA框架是一个基于NIO的网络层框架,有着高并发,高扩展性的特点。最近刚好一个项目再用它,本着学习的目的,研究一下MINA框架的源码。

MINA在网络层对服务端以及客户端都进行了封装,本文主要从服务端开始学习下整个流程的搭建过程。

  • 一、NIO服务端实现

在服务器端,一个简单的NIO实现如下:

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;

public class NIOServerImpl {

    public static void main(String[] args){
        try {
            //服务端通道注册
            ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
            serverSocketChannel.configureBlocking(false);
            serverSocketChannel.bind(new InetSocketAddress(5222));      
            Selector selector = Selector.open();
            serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);

            //开始监听
            SocketChannel socketChannel = null;
            ByteBuffer readBuffer = ByteBuffer.allocate(1024);
            while(true){
                int readyCount = selector.select();
                if(readyCount == 0)
                    continue;
                Iterator<SelectionKey> it = selector.selectedKeys().iterator();
                while(it.hasNext()){
                    SelectionKey selectionKey = it.next();
                    if(selectionKey.isAcceptable()){
                        serverSocketChannel = (ServerSocketChannel)selectionKey.channel();
                        socketChannel = serverSocketChannel.accept();
                        socketChannel.configureBlocking(false);
                        socketChannel.register(selector, SelectionKey.OP_READ);

                    } else if(selectionKey.isReadable()){
                        socketChannel = (SocketChannel)selectionKey.channel();
                        readBuffer.clear();

                        ByteArrayOutputStream baos = new ByteArrayOutputStream();

                        int byteCount = socketChannel.read(readBuffer);
                        while(byteCount != -1){
                            readBuffer.flip();
                            byte[] bytes = new byte[1024];
                            while(readBuffer.hasRemaining()){
                                readBuffer.get(bytes, 0, bytes.length);
                                baos.write(bytes);
                            }
                            readBuffer.clear();
                            byteCount = socketChannel.read(readBuffer);

                        }
                    } else {
                        if(socketChannel != null)
                            socketChannel.close();
                    }
                    it.remove();
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

首先进行通道注册,然后开始进行监听。每当有新的事件过来时,通过Selector返回就绪的通道数,进行对应事件的操作(接收请求或者进行读写请求)。

  • 二、MINA框架服务端流程

    AbstractIoService类实现IoService接口提供基本的IO服务以及Session的管理。在AbstractIoService类中有一个Executor的实例,用于建立客户端以及服务端的线程池(监听或者连接),当服务端进行监听时,则启动以一个线程去执行监听任务。
    NioSocketAcceptor类通过使用NIO的API完成服务端监听的建立。当创建一个NioSocketAcceptor实例时,完成IoProcessor实例以及Session会话的配置

 public NioSocketAcceptor() {
        super(new DefaultSocketSessionConfig(), NioProcessor.class);
        ((DefaultSocketSessionConfig) getSessionConfig()).init(this);
    }

如果没有提供Executor的实例,则使用父类提供的Executor实例。

 protected AbstractIoService(IoSessionConfig sessionConfig, Executor executor){
    ......
        if (executor == null) {
            this.executor = Executors.newCachedThreadPool();
            createdExecutor = true;
        } else {
            this.executor = executor;
            createdExecutor = false;
        }
  }

NioSocketAcceptor提供了一个Selector,服务于服务端通道的选择,初始化同样在NioSocketAcceptor实例创建的时候。
在父类AbstractPollingIoAcceptor构造时调用init方法

private AbstractPollingIoAcceptor(IoSessionConfig sessionConfig, Executor executor, IoProcessor<S> processor,boolean createdProcessor, SelectorProvider selectorProvider){
    ......
     try {
            // Initialize the selector
            init(selectorProvider);

            // The selector is now ready, we can switch the
            // flag to true so that incoming connection can be accepted
            selectable = true;
        } catch (RuntimeException e) {
            throw e;
        } catch (Exception e) {
            throw new RuntimeIoException("Failed to initialize.", e);
        } finally {
            if (!selectable) {
                try {
                    destroy();
                } catch (Exception e) {
                    ExceptionMonitor.getInstance().exceptionCaught(e);
                }
            }
        }
}
 protected void init(SelectorProvider selectorProvider) throws Exception {
        this.selectorProvider = selectorProvider;

        if (selectorProvider == null) {
            selector = Selector.open();
        } else {
            selector = selectorProvider.openSelector();
        }
    }

完成Selector实例的创建后,则开始进行Channel的创建以及注册。
Channel创建以及注册的方法在NioSocketAcceptor中实现:

 protected ServerSocketChannel open(SocketAddress localAddress) throws Exception {
        // Creates the listening ServerSocket

        ServerSocketChannel channel = null;

        if (selectorProvider != null) {
            channel = selectorProvider.openServerSocketChannel();
        } else {
            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.
            try {
                socket.bind(localAddress, getBacklog());
            } catch (IOException ioe) {
                // Add some info regarding the address we try to bind to the
                // message
                String newMessage = "Error while binding on " + localAddress + "\n" + "original message : "
                        + ioe.getMessage();
                Exception e = new IOException(newMessage);
                e.initCause(ioe.getCause());

                // And close the channel
                channel.close();

                throw e;
            }

            // Register the channel within the selector for ACCEPT event
            channel.register(selector, SelectionKey.OP_ACCEPT);
            success = true;
        } finally {
            if (!success) {
                close(channel);
            }
        }
        return channel;
    }

上述方法完成ServerSocketChannel的建立并进行注册。MINA可以注册多个通道,也就是说可以同时绑定多个IP和端口。

 protected NioSession accept(IoProcessor<NioSession> processor, ServerSocketChannel handle) throws Exception {

        SelectionKey key = null;

        if (handle != null) {
            key = handle.keyFor(selector);
        }

        if ((key == null) || (!key.isValid()) || (!key.isAcceptable())) {
            return null;
        }

        // accept the connection from the client
        SocketChannel ch = handle.accept();

        if (ch == null) {
            return null;
        }

        return new NioSocketSession(this, processor, ch);
    }

MINA使用bind方法完成上述过程,NioSocketAcceptor没有此方法,主要通过继承父类AbstractIoAcceptor的bind方式来实现。bing过程中,首先将地址传递进去,然后调用AbstractIoAcceptor的bindInternal方法进行下一步操作。
bindInternal方法的实现在AbstractPollingIoAcceptor类中,

 protected final Set<SocketAddress> bindInternal(List<? extends SocketAddress> localAddresses) throws Exception {

        AcceptorOperationFuture request = new AcceptorOperationFuture(localAddresses);

       //将要进行注册的地址加入到并发队列中
        registerQueue.add(request);

        //开始进行通道注册以及监听
        startupAcceptor();

        ......
        Set<SocketAddress> newLocalAddresses = new HashSet<SocketAddress>();

        for (H handle : boundHandles.values()) {
            newLocalAddresses.add(localAddress(handle));
        }
        return newLocalAddresses;
    }

在AbstractPollingIoAcceptor中,有一个Acceptor的内部类,用于监听客户端连接,同时,当没有任何Channel进行注册时,关闭监听服务。

 private class Acceptor implements Runnable {
        public void run() {
            assert (acceptorRef.get() == this);

            int nHandles = 0;

        ......

            while (selectable) {
                try {
                    //有多少通道就绪
                    int selected = select();

                    //服务端监听通道注册
                    nHandles += registerHandles();

                  ......
                    if (selected > 0) {
                        //处理客户端的连接请求
                        processHandles(selectedHandles());
                    }

                    // 关闭未注册的通道
                    nHandles -= unregisterHandles();
                } catch (ClosedSelectorException cse) {

                    ExceptionMonitor.getInstance().exceptionCaught(cse);
                    break;
                } catch (Exception e) {
                    ExceptionMonitor.getInstance().exceptionCaught(e);

                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e1) {
                        ExceptionMonitor.getInstance().exceptionCaught(e1);
                    }
                }
            }

          //停止服务
            if (selectable && isDisposing()) {
                selectable = false;
                try {
                    if (createdProcessor) {
                        processor.dispose();
                    }
                } finally {
                    try {
                        synchronized (disposalLock) {
                            if (isDisposing()) {
                                destroy();
                            }
                        }
                    } catch (Exception e) {
                        ExceptionMonitor.getInstance().exceptionCaught(e);
                    } finally {
                        disposalFuture.setDone();
                    }
                }
            }
        }

以上run方法里面实现的流程和NIO实现的监听流程一样,只是在后续的处理过程中,MINA加入Processor以及Session来处理连接建立成功后的通道。

MINA服务器端的建立流程大致如上。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值