Mina源码学习(一)mina的NIO机制

本文详细介绍了如何使用MINA框架实现NIO机制的socket服务程序。从普通IO机制的局限性出发,逐步深入到NIO机制的核心原理,包括Accepter与Processor的工作流程、Session的创建与管理等内容。

  为项目开发socket服务程序,一开始使用的是普通的IO机制,socket服务端监听线程每收到一个链接,即启动一个线程来收发处理链接报文,在连接数较少时,此种处理方式的线程数不多,数据量也不大,带来的CPU和资源的负荷较小,程序还能稳定的工作。但是随着链接的增大,数据量的增多,简单的IO机制的服务程序无论从性能还是稳定性上都不能满足系统的需要。在这个时候开始接触NIO机制,用来作为服务程序的框架,于是接触了MINA框架。

  MINA框架的使用比较简单,在绑定套接字前,只需要定义部分参数、编解码处理器及过滤器即可,简单示例如下:

1 IoAcceptor acceptor = new NioSocketAcceptor();
2 acceptor.getFilterChain().addLast("logger", new LoggingFilter());
3 acceptor.getFilterChain().addLast("codec",new          ProtocolCodecFilter(new DemoCodecFactory()));
4 acceptor.setHandler(new DemoServerHandler());
5 acceptor.getSessionConfig().setReadBufferSize(13);
6         acceptor.getSessionConfig().setIdleTime(IdleStatus.READER_IDLE, 60);
7 acceptor.bind(new InetSocketAddress(PORT));

  此次主要学习MINA的NIO机制,所以只涉及到上述代码中的第一行及最后一行作为入口。

IoAcceptor acceptor = new NioSocketAcceptor();

  代码中使用的是NioSocketAcceptor的无参构造函数,查看NioSocketAcceptor的源代码,可以看到

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

  调用的是父类AbstractPollingIoAcceptor中的

protected AbstractPollingIoAcceptor(IoSessionConfig sessionConfig, Class<? extends IoProcessor<S>> processorClass) {
        this(sessionConfig, null, new SimpleIoProcessorPool<S>(processorClass), true, null);
}

  此处需要注意的是new SimpleIoProcessorPool<S>(processorClass)这个参数。SimpleIoProcessorPool既继承了IoProcessor接口同时又是IoProcessor的一个集合。IoProcessor即是报文读取处理的类,这一块我们下面介绍。也就是说定义无参Accepter时自动构建了一个处理器的池子,通过下面的代码

public SimpleIoProcessorPool(Class<? extends IoProcessor<S>> processorType) {
        this(processorType, null, DEFAULT_SIZE, null);
    }

我们可以看出来,处理器池的默认大小DEFAULT_SIZE是Runtime.getRuntime().availableProcessors() + 1,也就是CPU个数+1。通过初始化的代码我们可以看出来,Accepter初始化时构建了一个处理器池,然后我们看一下Accepter和Processor是怎么工作的。

 

public final void bind(Iterable<? extends SocketAddress> localAddresses)

  通过上述绑定套接字的方法,可以看出MINA可以监听多个端口,忽略掉验证的内容,查看内部实现我们可以看到:

private class Acceptor implements Runnable {
        public void run() {
            ...

            while (selectable) {
                try {
                    // Detect if we have some keys ready to be processed
                    // The select() will be woke up if some new connection
                    // have occurred, or if the selector has been explicitly
                    // woke up
                    int selected = select();

                    // this actually sets the selector to OP_ACCEPT,
                    // and binds to the port on which this class will
                    // listen on
                    nHandles += registerHandles();

                    // Now, if the number of registred handles is 0, we can
                    // quit the loop: we don't have any socket listening
                    // for incoming connection.
                    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) {
                        // We have some connection request, let's process
                        // them here.
                        processHandles(selectedHandles());
                    }

                    // check to see if any cancellation request has been made.
                    nHandles -= unregisterHandles();
                } catch (ClosedSelectorException cse) {
                    // If the selector has been closed, we can exit the loop
                    ExceptionMonitor.getInstance().exceptionCaught(cse);
                    break;
                } catch (Exception e) {
                    ExceptionMonitor.getInstance().exceptionCaught(e);

                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e1) {
                        ExceptionMonitor.getInstance().exceptionCaught(e1);
                    }
                }
            }
...
        }
private int registerHandles() {
        for (;;) {
         ......
            try {
                // Process all the addresses
                for (SocketAddress a : localAddresses) {
                    H handle = open(a);
                    newHandles.put(localAddress(handle), handle);
                }
        ......
    }    
@Override
    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;
    }

直到此时,我们发现Accepter就是将各个channel的SelectionKey.OP_ACCEPT事件注册到了Accepter的selector上。

  下一步我们就是分析链接成功后是如何进行数据读取的,当然了上面我们提到过,数据读取处理是通过Processor来完成的。我们还是回到Accepter的处理逻辑中,请注意下面的代码:

if (selected > 0) {
    // We have some connection request, let's process
    // them here.
    processHandles(selectedHandles());
}

我们看一下是怎么处理的:

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) {
                    continue;
                }

                initSession(session, null, null);

                // add the session to the SocketIoProcessor
                session.getProcessor().add(session);
            }
        }
accept(processor, handle)为新连接创建了一个Session,Session中保存了该链接的相关信息,生命周期将伴随整个链接的处理过程。通过session.getProcessor()我们可以发现,session中已经包含了Processor的信息,我们看看是怎么为该session分配的Processor。需要注意的是,此时的参数IoProcessor<NioSession> processor仍然是我们初始化时的Processor池。通过类SimpleIoProcessorPool中的方法getProcessor我们可以看到
private IoProcessor<S> getProcessor(S session) {
        IoProcessor<S> processor = (IoProcessor<S>) session.getAttribute(PROCESSOR);

        if (processor == null) {
            if (disposed || disposing) {
                throw new IllegalStateException("A disposed processor cannot be accessed.");
            }

            processor = pool[Math.abs((int) session.getId()) % pool.length];

            if (processor == null) {
                throw new IllegalStateException("A disposed processor cannot be accessed.");
            }

            session.setAttributeIfAbsent(PROCESSOR, processor);
        }

        return processor;
    }

Processor池最终通过简单的算法为我们提供了唯一的一个IoProcessor对象,最后我们来看一下IoProcessor的add方法做了什么操作

public final void add(S session) {
        if (disposed || disposing) {
            throw new IllegalStateException("Already disposed.");
        }

        // Adds the session to the newSession queue and starts the worker
        newSessions.add(session);
        startupProcessor();
}
private void startupProcessor() {
        Processor processor = processorRef.get();

        if (processor == null) {
            processor = new Processor();

            if (processorRef.compareAndSet(null, processor)) {
                executor.execute(new NamePreservingRunnable(processor, threadName));
            }
        }

        // Just stop the select() and start it again, so that the processor
        // can be activated immediately.
        wakeup();
    }

在Processor内部线程类中,我们可以看到这一句:nSessions += handleNewSessions();,最终我们可以看到在addNew的init方法中完成了读事件的Selector注册

@Override
    protected void init(NioSession session) throws Exception {
        SelectableChannel ch = (SelectableChannel) session.getChannel();
        ch.configureBlocking(false);
        session.setSelectionKey(ch.register(selector, SelectionKey.OP_READ, session));
    }

至此,我们捋顺了socket从建立连接到监听读数据的NIO结构,下面的章节我们将针对数据的读取及处理来进行学习。


 


 

转载于:https://www.cnblogs.com/fordreaming/p/5489291.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值