Mina主体逻辑流程

来源:互联网 发布:淘宝联盟导购id 编辑:程序博客网 时间:2024/05/14 11:28

Mina也是一个one loop per thread的Reactor框架,关于这部分的知识可以看看《muduo网络库》这本书,Mina的优化什么的我看的不是很仔细,而且很多看不懂。这一篇博客主要从上层代码走一下Mina的主要逻辑流程。

简单介绍

Mina有几个主要的组件,分别是IoService,IoBuffer,IoFilter,IoHandler,IoSession,IoFuture(这部分简要介绍了参考资料中关于Mina的资料,可详细阅读参考资料

Mina的命名规范都是一个IoXXX接口,然后AbstractIoXXX定义主要逻辑过程的抽象类,然后就是具体的实现一般是NIOXXX

  • IoBuffer是一个抽象类,基本是对Java NIO的Buffer的封装使用,基本上只有setAllocator的一些实现AbstractIoBuffer 是IoBuffer的具体实现类,包含了expand和shrink以及put和get的方法

    • IoBufferAllocator是一个接口,具体实现有两个,内部类中包含了AbstractIoBuffer 的具体实现:

    SimpleBufferAllocator最基本的使用

    CachedBuffer如果频繁的扩充,那么就会多机器有压力,所以提前以2的幂去存储一堆已经生成的Buffer Map,存到ThreadLocal中,然后直接调用

  • IoService 用来管理各种IO服务,在mina中,这些服务可以包括session、filter、handler等

    AbstractIoService 负责初始化组建,关闭,初始化session(使用IoService中的属性最后去初始化AttributeMap),以及初始化一些IoService中包含的一些属性。

整体代码流程

public class MinaAcceptorThread implements Runnable {    @Override    public void run() {        MinaClientHandler handler = new MinaClientHandler();        NioSocketAcceptor acceptor = new NioSocketAcceptor();        acceptor.setReuseAddress(true);        acceptor.getFilterChain().addLast("protocol", new ProtocolCodecFilter(new IMCodeFactory(false)));        acceptor.setDefaultLocalAddress(new InetSocketAddress(PathConstant.PORT));        acceptor.setHandler(handler);        try {            acceptor.bind();        } catch (IOException e) {            e.printStackTrace();        }    }}

NioSocketAcceptor会在初始化时调用

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

NioSocketAcceptor中的selector只负责接收新的连接,具体的one loop per thread是由SimpleIoProcessorPool实现的。

SimpleIoProcessorPool中默认的线程池this.executor = Executors.newCachedThreadPool();SimpleIoProcessorPool中的pool是提前生成的CPU个数+1的NIOProcessor

NioSocketAcceptor接收到新的连接时执行

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

初始化session的过程没有细看

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

加入SimpleIoProcessorPool的时候会将session与processor绑定,也就是连接绑定processor。

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;    }

SimpleIoProcessorPool中的pool是提前生成的CPU个数+1的NIOProcessor

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();    }

接下来就是执行AbstractPollingIoProcessor中内部的Processor类,也就是在线程池中

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();    }

这部分也就是每个NIO具体在select分配过来的网络连接

nSessions += handleNewSessions();//创建FilterChain过程....//处理过程  if (selected > 0) {    process();}

具体处理数据的过程

private void process(S session) {        // Process Reads        if (isReadable(session) && !session.isReadSuspended()) {            read(session);        }        // Process writes        if (isWritable(session) && !session.isWriteSuspended()) {            // add the session to the queue, if it's not already there            if (session.setScheduledForFlush(true)) {                flushingSessions.add(session);            }        }    }

以read为例

private void read(S session) {        ...            if (readBytes > 0) {                IoFilterChain filterChain = session.getFilterChain();                filterChain.fireMessageReceived(buf);                buf = null;                if (hasFragmentation) {                    if (readBytes << 1 < config.getReadBufferSize()) {                        session.decreaseReadBufferSize();                    } else if (readBytes == config.getReadBufferSize()) {                        session.increaseReadBufferSize();                    }                }            }...

调用IoFilterChain就开始了对数据解析以及最终用户通过IoHandler得到具体的网络通信数据。FilterChain的流程在DefaultIoFilterChain中定义

public void fireMessageReceived(Object message) {        if (message instanceof IoBuffer) {            session.increaseReadBytes(((IoBuffer) message).remaining(), System                    .currentTimeMillis());        }        Entry head = this.head;        callNextMessageReceived(head, session, message);    }    private void callNextMessageReceived(Entry entry, IoSession session,            Object message) {        try {            IoFilter filter = entry.getFilter();            NextFilter nextFilter = entry.getNextFilter();            filter.messageReceived(nextFilter, session,                    message);        } catch (Throwable e) {            fireExceptionCaught(e);        }    }

CumulativeProtocolDecoder可以帮助你积累未收完的一条消息,doDecode返回false会使用IoBuffer缓存该消息。

public void messageReceived(NextFilter nextFilter, IoSession session,            Object message) throws Exception {        LOGGER.debug( "Processing a MESSAGE_RECEIVED for session {}", session.getId() );        if (!(message instanceof IoBuffer)) {            nextFilter.messageReceived(session, message);            return;        }        IoBuffer in = (IoBuffer) message;        ProtocolDecoder decoder = factory.getDecoder(session);        ProtocolDecoderOutput decoderOut = getDecoderOut(session, nextFilter);        // Loop until we don't have anymore byte in the buffer,        // or until the decoder throws an unrecoverable exception or         // can't decoder a message, because there are not enough         // data in the buffer        while (in.hasRemaining()) {            int oldPos = in.position();            try {                synchronized (decoderOut) {                    // Call the decoder with the read bytes                    decoder.decode(session, in, decoderOut);                }                // Finish decoding if no exception was thrown.                decoderOut.flush(nextFilter, session);            } catch (Throwable t) {              ...

最后看看在DefaultIoFilterChain中如何实现IoFilterChain与IoHandler结合成一个链条。DefaultIoFilterChain中的Entry最后由DefaultIoFilterChainImpl中buildFilterChain把Entry插入IoFilterChain。Entry中包含了IoFilter,Entry是IoFilterChain的包装

接上面创建FilterChain过程

nSessions += handleNewSessions();

handlerNewSessions()->addNow()会将初始化时addLast的IoFilterChain(也就是一开始代码中的acceptor.getFilterChain().addLast(“protocol”, new ProtocolCodecFilter(new IMCodeFactory(false)));)

通过bulider生成DefaultIoFilterChain中Entry的链子

public void buildFilterChain(IoFilterChain chain) throws Exception {        for (Entry e : entries) {            chain.addLast(e.getName(), e.getFilter());        }    }

DefaultIoFilterChain在初始化时会

public DefaultIoFilterChain(AbstractIoSession session) {        if (session == null) {            throw new IllegalArgumentException("session");        }        this.session = session;        head = new EntryImpl(null, null, "head", new HeadFilter());        tail = new EntryImpl(head, null, "tail", new TailFilter());        head.nextEntry = tail;    }

Head主要是flush发送缓冲区(具体关于send的过程没有详细阅读)

if (!s.isWriteSuspended()) {    s.getProcessor().flush(s);}

Tail是IoHandler,看一眼就明白了

private static class TailFilter extends IoFilterAdapter {        @Override        public void sessionCreated(NextFilter nextFilter, IoSession session)                throws Exception {            try {                session.getHandler().sessionCreated(session);            } finally {                // Notify the related future.                ConnectFuture future = (ConnectFuture) session                        .removeAttribute(SESSION_CREATED_FUTURE);                if (future != null) {                    future.setSession(session);                }            }        }

疑惑:

  1. 为什么要synchronized (decoderOut)每一个session都被指定的processpr执行,不应该出现多线程的问题啊?
  2. IoService 中的线程池是做什么的
  3. Mina中的多线程问题

下一篇博客内容

  • 知道了主要的Mina流程,下一篇我将比较详细介绍一下各个组件以及之间的关系
  • Mina在实现应用层协议时应该注意哪些问题

参考资料

系列介绍

Mina官方文档

资料

资料

Mina实现自定义应用层协议

Java NIO基础

多selector介绍

0 0