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); } } }
疑惑:
- 为什么要
synchronized (decoderOut)
每一个session都被指定的processpr执行,不应该出现多线程的问题啊? - IoService 中的线程池是做什么的
- Mina中的多线程问题
下一篇博客内容
- 知道了主要的Mina流程,下一篇我将比较详细介绍一下各个组件以及之间的关系
- Mina在实现应用层协议时应该注意哪些问题
参考资料
系列介绍
Mina官方文档
资料
资料
Mina实现自定义应用层协议
Java NIO基础
多selector介绍
- Mina主体逻辑流程
- Mybatis操作主体流程
- MINA工作流程
- Apache mina流程分析
- Apache mina -Processor线程逻辑
- 微信小程序:MINA逻辑层
- 4.1.3 main函数主体流程
- SystemUI启动流程及主体布局介绍
- SystemUI启动流程及主体布局介绍
- SystemUI启动流程及主体布局介绍
- 主体
- 游戏任务模块的前段主体实现逻辑示例
- Mina框架数据发送流程
- 缓存操作流程逻辑
- C语言逻辑流程
- Mina运行流程解析(一)
- Mina运行流程解析(三)
- Mina运行流程解析(二)
- HDOJ 2008 数值统计
- AsyncTask源码分析
- HOG+SVM
- GDOI2017狗带记
- Android(安卓):res之shape的创建并制作
- Mina主体逻辑流程
- C++--图算法之DFS,BFS,Dijstra
- Machine Learning第六讲[应用机器学习的建议] --(四)处理不平衡类
- Android播放器进度条
- Datawindow通过Json接口实现数据操作时(三层)PHP5.2 json_encode时发现汉字转换成了\u表示的处理方法
- 使用dom4j把java对象转XML并存为xml文件
- 在线小说网站的设计与实现(附源码)
- CSS3 3D转换
- ThinkPHP微信自定义分享Demo