- 浏览: 980063 次
文章分类
- 全部博客 (428)
- Hadoop (2)
- HBase (1)
- ELK (1)
- ActiveMQ (13)
- Kafka (5)
- Redis (14)
- Dubbo (1)
- Memcached (5)
- Netty (56)
- Mina (34)
- NIO (51)
- JUC (53)
- Spring (13)
- Mybatis (17)
- MySQL (21)
- JDBC (12)
- C3P0 (5)
- Tomcat (13)
- SLF4J-log4j (9)
- P6Spy (4)
- Quartz (12)
- Zabbix (7)
- JAVA (9)
- Linux (15)
- HTML (9)
- Lucene (0)
- JS (2)
- WebService (1)
- Maven (4)
- Oracle&MSSQL (14)
- iText (11)
- Development Tools (8)
- UTILS (4)
- LIFE (8)
最新评论
-
Donald_Draper:
Donald_Draper 写道刘落落cici 写道能给我发一 ...
DatagramChannelImpl 解析三(多播) -
Donald_Draper:
刘落落cici 写道能给我发一份这个类的源码吗Datagram ...
DatagramChannelImpl 解析三(多播) -
lyfyouyun:
请问楼主,执行消息发送的时候,报错:Transport sch ...
ActiveMQ连接工厂、连接详解 -
ezlhq:
关于 PollArrayWrapper 状态含义猜测:参考 S ...
WindowsSelectorImpl解析一(FdMap,PollArrayWrapper) -
flyfeifei66:
打算使用xmemcache作为memcache的客户端,由于x ...
Memcached分布式客户端(Xmemcached)
Mina 过滤链抽象实现:http://donald-draper.iteye.com/blog/2376335
Mina Socket与报文过滤链:http://donald-draper.iteye.com/blog/2376440
在上面这篇文章中,当会话发送消息后,消息被过滤链上的过滤器过滤,从链尾到链头,过程如下:
//消息发送,Iohanlder-》从链尾到链头(这是会话事件,只是在handler的方法中使用会话发送消息,handler并不处理会话事件)
再来看一下过滤链头HeadFilter
//HeadFilter
从HeadFilter的定义来看,HeadFilter触发IoHandler和IoSession事件时,将事件传递给后继过滤器;
有两个方法有所不同:
//HeadFilter
//AbstractIoFilterChain,待子类扩展
再来看SocketFilterChain
从上面可以看出会话发送消息最后由会话IoProcessor处理,下面来看一下IoProcessor接口的定义:
下面来看Io处理器的抽象实现AbstractPollingIoProcessor
从上面可以看出抽象Io处理器,主要几个关键内部变量为选择操作超时时间SELECT_TIMEOUT,用于腾出时间,处理空闲的会话;executor处理器内部执行器,用于运行内部处理器Processor;存储Io处理器等线程最大线程id的threadIds(Map);创建会话队列newSessions用于存储新创建的会话;移除会话队列removingSessions用于存放从处理器移除的会话;刷新会话队列flushingSessions,用于存放要发送写请求的会话;次序控制会话队列trafficControllingSessions用于存放会话暂定读写的会话;;正在处理进来消息的处理器引用processorRef;这些变量暂时这么理解,后面如果发现错误,再更正。
来看构造:
来看其他方法定义
来看添加会话到处理器
这个过程有两点要看
1.
2.
先来看处理器线程
1.
Io处理器,处理线程Processor的实际工作有一下几点要看
a.
b.
c.
d.
e.
f.
g.
h.
i.
j.
t
下面我们分别来看这几点
a.
b.
c.
添加会话有一下几点要看
c.1
c.2
//IoServiceListenerSupport
c.3
d.
e.
处理会话有两点要关注,
e.1
e.2
f.
方法有以下几点要关注
f.1
这一点有一下几点要看
f.1.1
f.1.2
//写文件
//写文件
f.2
f.3
g.
//移除会话
//尝试移除会话
//清除会话写请求队列
h.
//AbstractIoSession
i.
j.
t
从上面来看处理器的实际工作,尝试10次nbTries选择操作,在每次选择操作过程中,
首先进行超时选择操作,然后检查Io处理器是否断开连接,尝试次数nbTries是否为零如果为0,则注册新的选择器;然后遍历创建会话队列,从队列拉取会话,如果会话为不null,则初始化会话,构建会话过滤链(从IoService继承)触发会话过滤链的会话创建和会话打开事件,并记录新创建的会话数量nSessions;更会会话状态,此过程为从会话次序控制队列
获取会话,检查会话状态,如果状态为OPENED更新会话的读写状态,如果为OPENING放回次序控制会话队列;如果选择操作返回的SELECTKey的值大于0,即有相关的兴趣操作事件(读写事件),遍历选择后读写等操作就绪的会话,如果会话可读,则读取会话缓存区数据到buffer,触发过滤链消息接收事件MessageReceive,接收完消息后,如果会话输入流关闭则触发过滤链fireInputClosed事件,如果在这过程有异常发生,则触发过滤链异常事件ExceptionCaught,如果会话可写,则添加会话到刷新会话队列;遍历刷新会话队列,根据会话写请求消息类型为IoBuffer还是FileRegion,发送会话数据,发送会话数据后,如果会话还有些请求,则添加会话到队列,如果在这个过程中有异常,则添加会话到会话移除队列;遍历会话移除队列,
如果会话为关闭,则尝试关闭会话,并清除会话写请求队列,如果会话数据已发送完,
则触发会话过滤链消息发送事件fireMessageSent;更新处理器会话计数器nSessions;
遍历处理器所有会话,触发会话过滤器会话空闲时间fireSessionIdle;如果在这个过程中,处理器会话计数器nSessions为0,则清除处理器引用;如果Io处理器正在关闭,则添加所有会话到移除会话队列,释放Io处理器先关的资源。
再来看启动处理器方法#startupProcessor的第二点
2.
回到添加会话方法:
小节,从上面来年,添加会话首先添加会话到Io处理器的创建会话队列中,启动处理器线程Processor。处理器的实际工作,尝试10次nbTries选择操作,在每次选择操作过程中,
首先进行超时选择操作,然后检查Io处理器是否断开连接,尝试次数nbTries是否为零如果为0,则注册新的选择器;然后遍历创建会话队列,从队列拉取会话,如果会话为不null,则初始化会话,构建会话过滤链(从IoService继承)触发会话过滤链的会话创建和会话打开事件,并记录新创建的会话数量nSessions;更会会话状态,此过程为从会话次序控制队列
获取会话,检查会话状态,如果状态为OPENED更新会话的读写状态,如果为OPENING放回次序控制会话队列;如果选择操作返回的SELECTKey的值大于0,即有相关的兴趣操作事件(读写事件),遍历选择后读写等操作就绪的会话,如果会话可读,则读取会话缓存区数据到buffer,触发过滤链消息接收事件MessageReceive,接收完消息后,如果会话输入流
关闭则触发过滤链fireInputClosed事件,如果在这过程有异常发生,则触发过滤链异常事件ExceptionCaught,如果会话可写,则添加会话到刷新会话队列;遍历刷新会话队列,根据会话写请求消息类型为IoBuffer还是FileRegion,发送会话数据,发送会话数据后,如果会话还有些请求,则添加会话到队列,如果在这个过程中有异常,则添加会话到会话移除队列;遍历会话移除队列,如果会话为关闭,则尝试关闭会话,并清除会话写请求队列,如果会话数据已发送完,则触发会话过滤链消息发送事件fireMessageSent;更新处理器会话计数器nSessions;遍历处理器所有会话,触发会话过滤器会话空闲时间fireSessionIdle;
如果在这个过程中,处理器会话计数器nSessions为0,则清除处理器引用;如果Io处理器正在关闭,则添加所有会话到移除会话队列,释放Io处理器先关的资源。
再来看其他方法:
总结:
抽象Io处理器AbstractPollingIoProcessor,主要几个关键内部变量为选择操作超时时间SELECT_TIMEOUT,用于腾出时间,处理空闲的会话; executor处理器内部执行器,用于运行内部处理器Processor;存储Io处理器等线程最大线程id的threadIds(Map);创建会话队列newSessions用于存储新创建的会话;移除会话队列removingSessions用于存放从处理器移除的会话;刷新会话队列flushingSessions,用于存放要发送写请求的会话;次序控制会话队列trafficControllingSessions用于存放会话待读写的会话;Io处理器线程引用processorRef。
添加会话首先添加会话到Io处理器的创建会话队列中,启动处理器线程Processor。处理器的实际工作,尝试10次nbTries选择操作,在每次选择操作过程中,首先进行超时选择操作,然后检查Io处理器是否断开连接,尝试次数nbTries是否为零如果为0,则注册新的选择器;然后遍历创建会话队列,从队列拉取会话,如果会话为不null,则初始化会话,构建会话过滤链(从IoService继承)触发会话过滤链的会话创建和会话打开事件,并记录新创建的会话数量nSessions;更会会话状态,此过程为从会话次序控制队列获取会话,检查会话状态,如果状态为OPENED更新会话的读写状态,如果为OPENING放回次序控制会话队列;如果选择操作返回的SELECTKey的值大于0,即有相关的兴趣操作事件(读写事件),遍历选择后读写等操作就绪的会话,如果会话可读,则读取会话缓存区数据到buffer,触发过滤链消息接收事件MessageReceive,接收完消息后,如果会话输入流关闭则触发过滤链fireInputClosed事件,如果在这过程有异常发生,则触发过滤链异常事件ExceptionCaught,如果会话可写,则添加会话到刷新会话队列;遍历刷新会话队列,根据会话写请求消息类型为IoBuffer还是FileRegion,发送会话数据,发送会话数据后,如果会话还有些请求,则添加会话到队列,如果在这个过程中有异常,则添加会话到会话移除队列;遍历会话移除队列,如果会话为关闭,则尝试关闭会话,并清除会话写请求队列,如果会话数据已发送完,则触发会话过滤链消息发送事件fireMessageSent;更新处理器会话计数器nSessions;遍历处理器所有会话,触发会话过滤器会话空闲时间fireSessionIdle;如果在这个过程中,处理器会话计数器nSessions为0,则清除处理器引用;如果Io处理器正在关闭,则添加所有会话到移除会话队列,释放Io处理器先关的资源。
抽象Io处理器AbstractPollingIoProcessor主要是处理IoProcessor关联会话message*事件,而所有的工作,都是通过处理器线程Processor完成。每当有会话添加到IoProcessor,则启动一个处理器线程Processor,处理会话的读写操作及相关事件。就连IoProcessor资源的释放,也是由处理器线程Processor处理。关闭IoProcessor时,现将处理器关联会话,添加移除会话队列,实际工作由IoProcessor的子类的doDispose方法完成。
附:
//SessionState会话状态
//原子引用AtomicReference
Mina Socket与报文过滤链:http://donald-draper.iteye.com/blog/2376440
在上面这篇文章中,当会话发送消息后,消息被过滤链上的过滤器过滤,从链尾到链头,过程如下:
//消息发送,Iohanlder-》从链尾到链头(这是会话事件,只是在handler的方法中使用会话发送消息,handler并不处理会话事件)
public void fireFilterWrite(IoSession session, WriteRequest writeRequest) { Entry tail = this.tail; callPreviousFilterWrite(tail, session, writeRequest); } private void callPreviousFilterWrite(Entry entry, IoSession session, WriteRequest writeRequest) { try { entry.getFilter().filterWrite(entry.getNextFilter(), session, writeRequest); } catch (Throwable e) { writeRequest.getFuture().setWritten(false); fireExceptionCaught(session, e); } }
再来看一下过滤链头HeadFilter
//HeadFilter
private class HeadFilter extends IoFilterAdapter { ... public void filterWrite(NextFilter nextFilter, IoSession session, WriteRequest writeRequest) throws Exception { if (session.getTransportType().getEnvelopeType().isAssignableFrom( writeRequest.getMessage().getClass())) { doWrite(session, writeRequest); } else { throw new IllegalStateException( "Write requests must be transformed to " + session.getTransportType().getEnvelopeType() + ": " + writeRequest); } } ... }
从HeadFilter的定义来看,HeadFilter触发IoHandler和IoSession事件时,将事件传递给后继过滤器;
有两个方法有所不同:
//HeadFilter
//会话写操作 public void filterWrite(NextFilter nextFilter, IoSession session, WriteRequest writeRequest) throws Exception { if (session.getTransportType().getEnvelopeType().isAssignableFrom( writeRequest.getMessage().getClass())) { doWrite(session, writeRequest); } else { throw new IllegalStateException( "Write requests must be transformed to " + session.getTransportType().getEnvelopeType() + ": " + writeRequest); } }
//AbstractIoFilterChain,待子类扩展
protected abstract void doWrite(IoSession session, WriteRequest writeRequest) throws Exception;
再来看SocketFilterChain
class SocketFilterChain extends AbstractIoFilterChain { SocketFilterChain(IoSession parent) { super(parent); } protected void doWrite(IoSession session, WriteRequest writeRequest) { SocketSessionImpl s = (SocketSessionImpl) session; //获取Socket会话的的写请求队列,Queue继承于AbstractList,这个我们在后面再讲 Queue writeRequestQueue = s.getWriteRequestQueue(); // SocketIoProcessor.doFlush() will reset it after write is finished // because the buffer will be passed with messageSent event. //这里之所以要mark buffer的位置,主要是buffer要传给messageSent事件, //待消息发送完成,SocketIoProcessor.doFlush方法将会reset buffer到当前mark的位置 ByteBuffer buffer = (ByteBuffer) writeRequest.getMessage(); buffer.mark(); int remaining = buffer.remaining(); if (remaining == 0) { //BaseIoSession // private final AtomicInteger scheduledWriteRequests = new AtomicInteger(); //更新调度请求计数器+1 s.increaseScheduledWriteRequests(); } else { //BaseIoSession //private final AtomicInteger scheduledWriteBytes = new AtomicInteger(); //更新调度写字节计数器+buffer.remaining() s.increaseScheduledWriteBytes(buffer.remaining()); } synchronized (writeRequestQueue) { //将写请求添加到session写请求队列中 writeRequestQueue.push(writeRequest); } //如果session运行写操作,获取session关联的IoProcessor完成实际的消息发送工作,这个在以后在具体详说 if (session.getTrafficMask().isWritable()) { s.getIoProcessor().flush(s); } } //关闭会话 protected void doClose(IoSession session) throws IOException { SocketSessionImpl s = (SocketSessionImpl) session; s.getIoProcessor().remove(s);//委托给session关联的IoProcessor } }
从上面可以看出会话发送消息最后由会话IoProcessor处理,下面来看一下IoProcessor接口的定义:
/** * An internal interface to represent an 'I/O processor' that performs * actual I/O operations for {@link IoSession}s. It abstracts existing * reactor frameworks such as Java NIO once again to simplify transport * implementations. * * @author [url=http://mina.apache.org]Apache MINA Project[/url] * * @param <S> the type of the {@link IoSession} this processor can handle */ public interface IoProcessor<S extends IoSession> { /** * Releases any resources allocated by this processor. Please note that * the resources might not be released as long as there are any sessions * managed by this processor. Most implementations will close all sessions * immediately and release the related resources. 释放所有分配给IO处理器的资源。只要有任何会话占用资源,资源也许不会被Io处理器释放。 在大部分的实现版本中,是立刻关闭所有会话,释放相关资源。 */ void dispose(); /** * @return <tt>true</tt> if and if only {@link #dispose()} method has * been called. Please note that this method will return <tt>true</tt> * even after all the related resources are released. 如果dispose已经被调用,则返回true。即使在所有相关资源释放后,此方法仍返回true */ boolean isDisposing(); /** * @return <tt>true</tt> if and if only all resources of this processor * have been disposed. Io处理器所有资源释放完,则返回true。 */ boolean isDisposed(); /** * Adds the specified {@code session} to the I/O processor so that * the I/O processor starts to perform any I/O operations related * with the {@code session}. * 添加会话到Io处理器,以便Io处理器启动时,执行会话相关的操作。 * @param session The added session */ void add(S session); /** * Flushes the internal write request queue of the specified * {@code session}. * 刷新会话内部的写请求队列 * @param session The session we want the message to be written */ void flush(S session); /** * Writes the WriteRequest for the specified {@code session}. * 发送写请求到会话 * @param session The session we want the message to be written * @param writeRequest the WriteRequest to write */ void write(S session, WriteRequest writeRequest); /** * Controls the traffic of the specified {@code session} depending of the * {@link IoSession#isReadSuspended()} and {@link IoSession#isWriteSuspended()} * flags * 依赖于会话的IoSession#isReadSuspended/isWriteSuspended标志控制session读写请求的次序 * @param session The session to be updated */ void updateTrafficControl(S session); /** * Removes and closes the specified {@code session} from the I/O * processor so that the I/O processor closes the connection * associated with the {@code session} and releases any other related * resources. * 从Io处理器移除和关闭会话,以便Io处理器关闭连接关联的会话,释放相关的资源。 * @param session The session to be removed */ void remove(S session); }
下面来看Io处理器的抽象实现AbstractPollingIoProcessor
/** * An abstract implementation of {@link IoProcessor} which helps transport * developers to write an {@link IoProcessor} easily. This class is in charge of * active polling a set of {@link IoSession} and trigger events when some I/O * operation is possible. * @author [url=http://mina.apache.org]Apache MINA Project[/url] * @param <S> * the type of the {@link IoSession} this processor can handle */ public abstract class AbstractPollingIoProcessor<S extends AbstractIoSession> implements IoProcessor<S> { /** A logger for this class */ private static final Logger LOG = LoggerFactory.getLogger(IoProcessor.class); /** * A timeout used for the select, as we need to get out to deal with idle * sessions.选择操作超时时间,我们需要处理空闲的会话 */ private static final long SELECT_TIMEOUT = 1000L; /** A map containing the last Thread ID for each class */每个class的最后一个线程id private static final ConcurrentHashMap<Class<?>, AtomicInteger> threadIds = new ConcurrentHashMap<>(); /** This IoProcessor instance name处理器实例名 */ private final String threadName; /** The executor to use when we need to start the inner Processor */ private final Executor executor;//处理器内部执行器,用于运行内部处理器Processor /** A Session queue containing the newly created sessions */ private final Queue<S> newSessions = new ConcurrentLinkedQueue<>();//创建会话队列 /** A queue used to store the sessions to be removed */ private final Queue<S> removingSessions = new ConcurrentLinkedQueue<>();//移除会话队列 /** A queue used to store the sessions to be flushed */ private final Queue<S> flushingSessions = new ConcurrentLinkedQueue<>();//刷新会话队列 /** * A queue used to store the sessions which have a trafficControl to be * updated */ private final Queue<S> trafficControllingSessions = new ConcurrentLinkedQueue<>();//次序控制会话队列 /** The processor thread : it handles the incoming messages 处理器线程,用于处理进来的消息*/ private final AtomicReference<Processor> processorRef = new AtomicReference<>(); private long lastIdleCheckTime;//上次空闲检查时间 private final Object disposalLock = new Object();//关闭锁 private volatile boolean disposing;//是否正在关闭 private volatile boolean disposed;//是否已关闭 private final DefaultIoFuture disposalFuture = new DefaultIoFuture(null);//关闭结果 protected AtomicBoolean wakeupCalled = new AtomicBoolean(false);//这个暂时清楚,后面遇见时再说 }
从上面可以看出抽象Io处理器,主要几个关键内部变量为选择操作超时时间SELECT_TIMEOUT,用于腾出时间,处理空闲的会话;executor处理器内部执行器,用于运行内部处理器Processor;存储Io处理器等线程最大线程id的threadIds(Map);创建会话队列newSessions用于存储新创建的会话;移除会话队列removingSessions用于存放从处理器移除的会话;刷新会话队列flushingSessions,用于存放要发送写请求的会话;次序控制会话队列trafficControllingSessions用于存放会话暂定读写的会话;;正在处理进来消息的处理器引用processorRef;这些变量暂时这么理解,后面如果发现错误,再更正。
来看构造:
/** * Create an {@link AbstractPollingIoProcessor} with the given * {@link Executor} for handling I/Os events. * 根据给定的执行器,创建抽象Io处理器用于处理IO事件 * @param executor * the {@link Executor} for handling I/O events */ protected AbstractPollingIoProcessor(Executor executor) { if (executor == null) { throw new IllegalArgumentException("executor"); } this.threadName = nextThreadName();//获取处理器线程名 this.executor = executor; } /** * Compute the thread ID for this class instance. As we may have different * classes, we store the last ID number into a Map associating the class * name to the last assigned ID. * 计算类型实例最大线程id。因为我们有不同的类型,所以我们存在类型与类型实例最后一个id的映射 关系放在Map中管理。 * @return a name for the current thread, based on the class name and an * incremental value, starting at 1. */ private String nextThreadName() { Class<?> cls = getClass(); int newThreadId; //从类型最大线程id映射Map,获取Io处理器线程的最大线程id AtomicInteger threadId = threadIds.putIfAbsent(cls, new AtomicInteger(1)); if (threadId == null) { newThreadId = 1; } else { // Just increment the last ID, and get it. newThreadId = threadId.incrementAndGet(); } // Now we can compute the name for this thread return cls.getSimpleName() + '-' + newThreadId; }
来看其他方法定义
/** * poll those sessions for the given timeout * 超时选择 * @param timeout * milliseconds before the call timeout if no event appear * @return The number of session ready for read or for write * @throws Exception * if some low level IO error occurs */ protected abstract int select(long timeout) throws Exception; /** * poll those sessions forever * 选择操作 * @return The number of session ready for read or for write * @throws Exception * if some low level IO error occurs */ protected abstract int select() throws Exception; /** * Say if the list of {@link IoSession} polled by this {@link IoProcessor} * is empty * 判断处理器的会话集合是否为空 * @return <tt>true</tt> if at least a session is managed by this * {@link IoProcessor} */ protected abstract boolean isSelectorEmpty(); /** * Get an {@link Iterator} for the list of {@link IoSession} polled by this * {@link IoProcessor} * 返回处理器选择的所有会话 * @return {@link Iterator} of {@link IoSession} */ protected abstract Iterator<S> allSessions(); /** * Get an {@link Iterator} for the list of {@link IoSession} found selected * by the last call of {@link #select(long)} * 获取上次调用超时选择后的会话集 * @return {@link Iterator} of {@link IoSession} read for I/Os operation */ protected abstract Iterator<S> selectedSessions(); /** * Get the state of a session (One of OPENING, OPEN, CLOSING) * 获取会话状态 * @param session * the {@link IoSession} to inspect * @return the state of the session */ protected abstract SessionState getState(S session); /** * Tells if the session ready for writing * 判断会话是否可写 * @param session * the queried session * @return <tt>true</tt> is ready, <tt>false</tt> if not ready */ protected abstract boolean isWritable(S session); /** * Tells if the session ready for reading * 判断会话是否准备好读操作 * @param session * the queried session * @return <tt>true</tt> is ready, <tt>false</tt> if not ready */ protected abstract boolean isReadable(S session); /** * Set the session to be informed when a write event should be processed * 当有一个写事件要处理时,是否通知会话 * @param session * the session for which we want to be interested in write events * @param isInterested * <tt>true</tt> for registering, <tt>false</tt> for removing * @throws Exception * If there was a problem while registering the session */ protected abstract void setInterestedInWrite(S session, boolean isInterested) throws Exception; /** * Set the session to be informed when a read event should be processed * 当有一个读事件要处理时,是否通知会话 * @param session * the session for which we want to be interested in read events * @param isInterested * <tt>true</tt> for registering, <tt>false</tt> for removing * @throws Exception * If there was a problem while registering the session */ protected abstract void setInterestedInRead(S session, boolean isInterested) throws Exception; /** * Tells if this session is registered for reading * 判断会话是否注册读事件 * @param session * the queried session * @return <tt>true</tt> is registered for reading */ protected abstract boolean isInterestedInRead(S session); /** * Tells if this session is registered for writing * 判断会话是否注册写事件 * @param session * the queried session * @return <tt>true</tt> is registered for writing */ protected abstract boolean isInterestedInWrite(S session); /** * Initialize the polling of a session. Add it to the polling process. * 初始化会话,添加到处理器 * @param session * the {@link IoSession} to add to the polling * @throws Exception * any exception thrown by the underlying system calls */ protected abstract void init(S session) throws Exception; /** * Destroy the underlying client socket handle * 关闭底层客户端socket * @param session * the {@link IoSession} * @throws Exception * any exception thrown by the underlying system calls */ protected abstract void destroy(S session) throws Exception; /** * Reads a sequence of bytes from a {@link IoSession} into the given * {@link IoBuffer}. Is called when the session was found ready for reading. * 当会话准备好读操作是,从会话读字节序列 * @param session * the session to read * @param buf * the buffer to fill * @return the number of bytes read * @throws Exception * any exception thrown by the underlying system calls */ protected abstract int read(S session, IoBuffer buf) throws Exception; /** * Write a sequence of bytes to a {@link IoSession}, means to be called when * a session was found ready for writing. * 当会话准备好写操作是,写字节序列到会话 * @param session * the session to write * @param buf * the buffer to write * @param length * the number of bytes to write can be superior to the number of * bytes remaining in the buffer * @return the number of byte written * @throws IOException * any exception thrown by the underlying system calls */ protected abstract int write(S session, IoBuffer buf, int length) throws IOException; /** * Write a part of a file to a {@link IoSession}, if the underlying API * isn't supporting system calls like sendfile(), you can throw a * {@link UnsupportedOperationException} so the file will be send using * usual {@link #write(AbstractIoSession, IoBuffer, int)} call. * 写文件的某个Region到会话,如果底层API不支持sendfile方法,你可以抛出一个UnsupportedOperationException, 那么将调用#write(AbstractIoSession, IoBuffer, int)发送文件。 * @param session * the session to write * @param region * the file region to write * @param length * the length of the portion to send * @return the number of written bytes * @throws Exception * any exception thrown by the underlying system calls */ protected abstract int transferFile(S session, FileRegion region, int length) throws Exception;
来看添加会话到处理器
/** * {@inheritDoc} */ //会话创建时,添加回到到处理器, @Override 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 //添加会话到Io处理器的创建会话队列中 newSessions.add(session); //启动一个Io处理器线程 startupProcessor(); }
/** * Starts the inner Processor, asking the executor to pick a thread in its * pool. The Runnable will be renamed */ 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(); }
/** * Interrupt the {@link #select(long)} call. 中断选择操作 */ protected abstract void wakeup();
这个过程有两点要看
1.
//处理器为空,则创建一个 processor = new Processor();
2.
//执行处理器 executor.execute(new NamePreservingRunnable(processor, threadName));
先来看处理器线程
1.
//处理器为空,则创建一个 processor = new Processor();
//Processor /** * The main loop. This is the place in charge to poll the Selector, and to * process the active sessions. It's done in - handle the newly created * sessions - */ private class Processor implements Runnable { /** * {@inheritDoc} */ @Override public void run() { //断言Io处理器实际处理线程是否为当前Processor assert processorRef.get() == this; int nSessions = 0; lastIdleCheckTime = System.currentTimeMillis(); int nbTries = 10; for (;;) { try { // This select has a timeout so that we can manage // idle session when we get out of the select every // second. (note : this is a hack to avoid creating // a dedicated thread). //选择操作有一个超时时间,以便当选择超时时,处理空闲会话 long t0 = System.currentTimeMillis(); //超时选择,select方法待子类实现 int selected = select(SELECT_TIMEOUT); long t1 = System.currentTimeMillis(); long delta = t1 - t0; //当前这次选择操作,没有SELECTKey相关事件,没有中断,且此次选择操作耗时 //小于SELECT_TIMEOUT(1000)/nbTries(10) if (!wakeupCalled.getAndSet(false) && (selected == 0) && (delta < 100)) { // Last chance : the select() may have been // interrupted because we have had an closed channel. //在上次尝试选择操作时,可能通道关闭,选择操作可能被中断 if (isBrokenConnection()) { //通道关闭的话,仅仅输出日志 LOG.warn("Broken connection"); } else { // Ok, we are hit by the nasty epoll // spinning. // Basically, there is a race condition // which causes a closing file descriptor not to be // considered as available as a selected channel, // but // it stopped the select. The next time we will // call select(), it will exit immediately for the // same // reason, and do so forever, consuming 100% // CPU. // We have to destroy the selector, and // register all the socket on a new one. if (nbTries == 0) { //如果尝试次数用完我们,注册新的选择器 LOG.warn("Create a new selector. Selected is 0, delta = " + delta); registerNewSelector(); nbTries = 10;//恢复尝试次数 } else { //否则尝试次数自减 nbTries--; } } } else { nbTries = 10; } // Manage newly created session first //处理新会话 nSessions += handleNewSessions(); //更新会话状态 updateTrafficMask(); // Now, if we have had some incoming or outgoing events, // deal with them if (selected > 0) { // LOG.debug("Processing ..."); // This log hurts one of // the MDCFilter test... //如果选择操作返回的SELECTKey的值大于0,即有相关的兴趣操作事件 //读写事件,委托为process方法处理 process(); } // Write the pending requests //处理有些请求的会话 long currentTime = System.currentTimeMillis(); flush(currentTime); // And manage removed sessions //移除已关闭的会话 nSessions -= removeSessions(); // Last, not least, send Idle events to the idle sessions //通知会话空闲事件 notifyIdleSessions(currentTime); // Get a chance to exit the infinite loop if there are no // more sessions on this Processor //如果在这个过程中,激活会话最后为0,则清除处理器引用 if (nSessions == 0) { processorRef.set(null); if (newSessions.isEmpty() && isSelectorEmpty()) { // newSessions.add() precedes startupProcessor assert processorRef.get() != this; break; } assert processorRef.get() != this; if (!processorRef.compareAndSet(null, this)) { // startupProcessor won race, so must exit processor assert processorRef.get() != this; break; } assert processorRef.get() == this; } // Disconnect all sessions immediately if disposal has been // requested so that we exit this loop eventually. //判断Io处理器是否正在关闭,如果正在关闭断开所有会话 if (isDisposing()) { boolean hasKeys = false; //获取当前处理器管理的会话,移除会话 for (Iterator<S> i = allSessions(); i.hasNext();) { IoSession session = i.next(); if (session.isActive()) { scheduleRemove((S) session); hasKeys = true; } } if (hasKeys) { wakeup(); } } } catch (ClosedSelectorException cse) { // If the selector has been closed, we can exit the loop // But first, dump a stack trace ExceptionMonitor.getInstance().exceptionCaught(cse); break; } catch (Exception e) { ExceptionMonitor.getInstance().exceptionCaught(e); try { Thread.sleep(1000); } catch (InterruptedException e1) { ExceptionMonitor.getInstance().exceptionCaught(e1); } } } try { synchronized (disposalLock) { if (disposing) { //如果正在关闭则,完成实际关闭工作 doDispose(); } } } catch (Exception e) { //捕捉异常 ExceptionMonitor.getInstance().exceptionCaught(e); } finally { //已关闭 disposalFuture.setValue(true); } }
Io处理器,处理线程Processor的实际工作有一下几点要看
a.
if (isBrokenConnection()) { //通道关闭的话,仅仅输出日志 LOG.warn("Broken connection"); }
b.
else { // Ok, we are hit by the nasty epoll // spinning. // Basically, there is a race condition // which causes a closing file descriptor not to be // considered as available as a selected channel, // but // it stopped the select. The next time we will // call select(), it will exit immediately for the // same // reason, and do so forever, consuming 100% // CPU. // We have to destroy the selector, and // register all the socket on a new one. if (nbTries == 0) { //如果尝试次数用完我们,注册新的选择器 LOG.warn("Create a new selector. Selected is 0, delta = " + delta); registerNewSelector(); nbTries = 10;//恢复尝试次数 } else { //否则尝试次数自减 nbTries--; } }
c.
// Manage newly created session first //处理新会话 nSessions += handleNewSessions();
d.
//更新会话状态 updateTrafficMask();
e.
// Now, if we have had some incoming or outgoing events, // deal with them if (selected > 0) { // LOG.debug("Processing ..."); // This log hurts one of // the MDCFilter test... //如果选择操作返回的SELECTKey的值大于0,即有相关的兴趣操作事件 //读写事件,委托为process方法处理 process(); }
f.
// Write the pending requests //处理有些请求的会话 long currentTime = System.currentTimeMillis(); flush(currentTime);
g.
// And manage removed sessions //移除已关闭的会话 nSessions -= removeSessions();
h.
// Last, not least, send Idle events to the idle sessions //通知会话空闲事件 notifyIdleSessions(currentTime);
i.
// Disconnect all sessions immediately if disposal has been // requested so that we exit this loop eventually. //判断Io处理器是否正在关闭,如果正在关闭断开所有会话 if (isDisposing()) { boolean hasKeys = false; //获取当前处理器管理的会话,移除会话 for (Iterator<S> i = allSessions(); i.hasNext();) { IoSession session = i.next(); if (session.isActive()) { scheduleRemove((S) session); hasKeys = true; } } if (hasKeys) { wakeup(); } }
j.
t
ry { synchronized (disposalLock) { if (disposing) { //如果正在关闭,完成实际关闭工作 doDispose(); } } } catch (Exception e) { //捕捉异常 ExceptionMonitor.getInstance().exceptionCaught(e); } finally { //已关闭 disposalFuture.setValue(true); }
下面我们分别来看这几点
a.
if (isBrokenConnection()) { //通道关闭的话,仅仅输出日志 LOG.warn("Broken connection"); }
/** * Check that the select() has not exited immediately just because of a * broken connection. In this case, this is a standard case, and we just * have to loop. * 检查选择是否由于Io处理器连接断开,选择操作还没有退出 * @return <tt>true</tt> if a connection has been brutally closed. * @throws IOException * If we got an exception */ protected abstract boolean isBrokenConnection() throws IOException;
b.
else { // Ok, we are hit by the nasty epoll // spinning. // Basically, there is a race condition // which causes a closing file descriptor not to be // considered as available as a selected channel, // but // it stopped the select. The next time we will // call select(), it will exit immediately for the // same // reason, and do so forever, consuming 100% // CPU. // We have to destroy the selector, and // register all the socket on a new one. if (nbTries == 0) { //如果尝试次数用完我们,注册新的选择器 LOG.warn("Create a new selector. Selected is 0, delta = " + delta); registerNewSelector(); nbTries = 10;//恢复尝试次数 } else { //否则尝试次数自减 nbTries--; } }
/** * In the case we are using the java select() method, this method is used to * trash the buggy selector and create a new one, registring all the sockets * on it. * 丢弃旧的选择器,将所有socket注册到新的选择器上 * @throws IOException * If we got an exception */ protected abstract void registerNewSelector() throws IOException;
c.
// Manage newly created session first //处理新会话 nSessions += handleNewSessions();
/** * Loops over the new sessions blocking queue and returns the number of * sessions which are effectively created * 遍历创建会话队列,返回新建会话的数量 * @return The number of new sessions */ private int handleNewSessions() { int addedSessions = 0; for (S session = newSessions.poll(); session != null; session = newSessions.poll()) { if (addNow(session)) { // A new session has been created addedSessions++; } } return addedSessions; }
/** * Process a new session : - initialize it - create its chain - fire the * CREATED listeners if any * 处理新会话,初始化会话,创建会话过滤链,触发监听器会话创建事件 * @param session * The session to create * @return <tt>true</tt> if the session has been registered */ private boolean addNow(S session) { boolean registered = false; try { //初始化会话 init(session); registered = true; // Build the filter chain of this session. //获取会话service过滤链构建器 IoFilterChainBuilder chainBuilder = session.getService().getFilterChainBuilder(); //构建会话过滤链 chainBuilder.buildFilterChain(session.getFilterChain()); // DefaultIoFilterChain.CONNECT_FUTURE is cleared inside here // in AbstractIoFilterChain.fireSessionOpened(). // Propagate the SESSION_CREATED event up to the chain IoServiceListenerSupport listeners = ((AbstractIoService) session.getService()).getListeners(); //触发会话事件 listeners.fireSessionCreated(session); } catch (Exception e) { ExceptionMonitor.getInstance().exceptionCaught(e); try { destroy(session); } catch (Exception e1) { ExceptionMonitor.getInstance().exceptionCaught(e1); } finally { registered = false; } } return registered; }
添加会话有一下几点要看
c.1
//初始化会话 init(session);
protected abstract void init(AbstractIoSession abstractiosession) throws Exception;
c.2
//触发会话事件 listeners.fireSessionCreated(session);
//IoServiceListenerSupport
public void fireSessionCreated(IoSession session) { boolean firstSession = false; if(session.getService() instanceof IoConnector) synchronized(managedSessions) { firstSession = managedSessions.isEmpty(); } if(managedSessions.putIfAbsent(Long.valueOf(session.getId()), session) != null) return; if(firstSession) fireServiceActivated(); //触发会话过滤链会话创建和会话打开事件 IoFilterChain filterChain = session.getFilterChain(); filterChain.fireSessionCreated(); filterChain.fireSessionOpened(); ... }
c.3
destroy(session);
/** * Destroy the underlying client socket handle * 关闭底层客户端socket * @param session * the {@link IoSession} * @throws Exception * any exception thrown by the underlying system calls */ protected abstract void destroy(S session) throws Exception;
d.
//更新会话状态 updateTrafficMask();
/** * Update the trafficControl for all the session. */ private void updateTrafficMask() { int queueSize = trafficControllingSessions.size(); while (queueSize > 0) { S session = trafficControllingSessions.poll(); if (session == null) { // We are done with this queue. return; } //获取会话状态 SessionState state = getState(session); switch (state) { case OPENED: //更新会话状态 updateTrafficControl(session); break; case CLOSING: break; case OPENING: // Retry later if session is not yet fully initialized. // (In case that Session.suspend??() or session.resume??() is // called before addSession() is processed) // We just put back the session at the end of the queue. //如果正在打开,则添加到次序控制会话队列 trafficControllingSessions.add(session); break; default: throw new IllegalStateException(String.valueOf(state)); } // As we have handled one session, decrement the number of // remaining sessions. The OPENING session will be processed // with the next select(), as the queue size has been decreased, // even // if the session has been pushed at the end of the queue queueSize--; } }
/** * {@inheritDoc} */ @Override public void updateTrafficControl(S session) { // try { //通知读操作事件 setInterestedInRead(session, !session.isReadSuspended()); } catch (Exception e) { IoFilterChain filterChain = session.getFilterChain(); filterChain.fireExceptionCaught(e); } try { //通知写操作事件 setInterestedInWrite(session, !session.getWriteRequestQueue().isEmpty(session) && !session.isWriteSuspended()); } catch (Exception e) { IoFilterChain filterChain = session.getFilterChain(); filterChain.fireExceptionCaught(e); } }
/** * Set the session to be informed when a write event should be processed * 当有一个写事件要处理时,是否通知会话 * @param session * the session for which we want to be interested in write events * @param isInterested * <tt>true</tt> for registering, <tt>false</tt> for removing * @throws Exception * If there was a problem while registering the session */ protected abstract void setInterestedInWrite(S session, boolean isInterested) throws Exception;
/** * Set the session to be informed when a read event should be processed * 当有一个读事件要处理时,是否通知会话 * @param session * the session for which we want to be interested in read events * @param isInterested * <tt>true</tt> for registering, <tt>false</tt> for removing * @throws Exception * If there was a problem while registering the session */ protected abstract void setInterestedInRead(S session, boolean isInterested) throws Exception;
e.
// Now, if we have had some incoming or outgoing events, // deal with them if (selected > 0) { // LOG.debug("Processing ..."); // This log hurts one of // the MDCFilter test... //如果选择操作返回的SELECTKey的值大于0,即有相关的兴趣操作事件 //读写事件,委托为process方法处理 process(); }
private void process() throws Exception { for (Iterator<S> i = selectedSessions(); i.hasNext();) { S session = i.next(); //处理会话 process(session); i.remove(); } }
/** * Get an {@link Iterator} for the list of {@link IoSession} found selected * by the last call of {@link #select(long)} * 获取上次调用超时选择后,准备就绪会话集 * @return {@link Iterator} of {@link IoSession} read for I/Os operation */ protected abstract Iterator<S> selectedSessions();
/** * Deal with session ready for the read or write operations, or both. */ private void process(S session) { // Process Reads if (isReadable(session) && !session.isReadSuspended()) { //如果会话可读,则读会话接收到的数据 read(session); } // Process writes if (isWritable(session) && !session.isWriteSuspended() && session.setScheduledForFlush(true)) { // add the session to the queue, if it's not already there //如果会话有数据要发送,则将会话添加到刷新会话队列 flushingSessions.add(session); } } } }
处理会话有两点要关注,
e.1
// Process Reads if (isReadable(session) && !session.isReadSuspended()) { //如果会话可读,则读会话接收到的数据 read(session); }
private void read(S session) { //获取会话配置,会话配置读缓存size IoSessionConfig config = session.getConfig(); int bufferSize = config.getReadBufferSize(); IoBuffer buf = IoBuffer.allocate(bufferSize); final boolean hasFragmentation = session.getTransportMetadata().hasFragmentation(); try { int readBytes = 0; int ret; try { if (hasFragmentation) { //从会话读取字节序列到buffer while ((ret = read(session, buf)) > 0) { readBytes += ret; if (!buf.hasRemaining()) { break; } } } else { ret = read(session, buf); if (ret > 0) { readBytes = ret; } } } finally { buf.flip(); } if (readBytes > 0) { //获取会话过滤链,触发过滤链消息接收事件MessageReceive 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(); } } } else { // release temporary buffer when read nothing buf.free(); } //如果会话socket关闭,则触发过滤链fireInputClosed if (ret < 0) { IoFilterChain filterChain = session.getFilterChain(); filterChain.fireInputClosed(); } } catch (Exception e) { if ((e instanceof IOException) && (!(e instanceof PortUnreachableException) || !AbstractDatagramSessionConfig.class.isAssignableFrom(config.getClass()) || ((AbstractDatagramSessionConfig) config).isCloseOnPortUnreachable())) { scheduleRemove(session); } //触发过滤链异常事件ExceptionCaught IoFilterChain filterChain = session.getFilterChain(); filterChain.fireExceptionCaught(e); } }
e.2
// Process writes if (isWritable(session) && !session.isWriteSuspended() && session.setScheduledForFlush(true)) { // add the session to the queue, if it's not already there //如果会话有数据要发送,则将会话添加到刷新会话队列 flushingSessions.add(session); }
f.
// Write the pending requests //处理有写请求的会话 long currentTime = System.currentTimeMillis(); flush(currentTime);
/** * Write all the pending messages */ private void flush(long currentTime) { if (flushingSessions.isEmpty()) { return; } //遍历刷新会话队列 do { S session = flushingSessions.poll(); // the same one with // firstSession if (session == null) { // Just in case ... It should not happen. break; } // Reset the Schedule for flush flag for this session, // as we are flushing it now //设置会话刷新状态为未刷新 session.unscheduledForFlush(); //获取会话状态 SessionState state = getState(session); switch (state) { case OPENED: try { //会话已已打开,则委托给flushNow boolean flushedAll = flushNow(session, currentTime); if (flushedAll && !session.getWriteRequestQueue().isEmpty(session) && !session.isScheduledForFlush()) { //调度刷新会话 scheduleFlush(session); } } catch (Exception e) { scheduleRemove(session);//移除会话调度 session.closeNow();//异常立刻关闭会话 IoFilterChain filterChain = session.getFilterChain(); filterChain.fireExceptionCaught(e); } break; case CLOSING: // Skip if the channel is already closed. break; case OPENING: // Retry later if session is not yet fully initialized. // (In case that Session.write() is called before addSession() // is processed) //如果正在会话正在打开,则调度刷新会话 scheduleFlush(session); return; default: throw new IllegalStateException(String.valueOf(state)); } } while (!flushingSessions.isEmpty()); }
方法有以下几点要关注
f.1
//会话已已打开,则委托给flushNow boolean flushedAll = flushNow(session, currentTime);
private boolean flushNow(S session, long currentTime) { //如果会话失去连接,则添加会话到移除会话队列 if (!session.isConnected()) { scheduleRemove(session); return false; } final boolean hasFragmentation = session.getTransportMetadata().hasFragmentation(); //获取会话写请求队列 final WriteRequestQueue writeRequestQueue = session.getWriteRequestQueue(); // Set limitation for the number of written bytes for read-write // fairness. I used maxReadBufferSize * 3 / 2, which yields best // performance in my experience while not breaking fairness much. //写buffer最大size,经验值为maxReadBufferSize * 3 / 2 final int maxWrittenBytes = session.getConfig().getMaxReadBufferSize() + (session.getConfig().getMaxReadBufferSize() >>> 1); int writtenBytes = 0; WriteRequest req = null; try { // Clear OP_WRITE,清除会话写事件OP_WRITE标志 setInterestedInWrite(session, false); do { // Check for pending writes. //获取会话当前写情趣 req = session.getCurrentWriteRequest(); if (req == null) { req = writeRequestQueue.poll(session); if (req == null) { break; } session.setCurrentWriteRequest(req); } int localWrittenBytes; //获取写请求消息 Object message = req.getMessage(); if (message instanceof IoBuffer) { //写会话buffer localWrittenBytes = writeBuffer(session, req, hasFragmentation, maxWrittenBytes - writtenBytes, currentTime); if ((localWrittenBytes > 0) && ((IoBuffer) message).hasRemaining()) { // the buffer isn't empty, we re-interest it in writing setInterestedInWrite(session, true); return false; } } else if (message instanceof FileRegion) { 写文件 localWrittenBytes = writeFile(session, req, hasFragmentation, maxWrittenBytes - writtenBytes, currentTime); // Fix for Java bug on Linux // http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=5103988 // If there's still data to be written in the FileRegion, // return 0 indicating that we need // to pause until writing may resume. if ((localWrittenBytes > 0) && (((FileRegion) message).getRemainingBytes() > 0)) { setInterestedInWrite(session, true); return false; } } else { throw new IllegalStateException("Don't know how to handle message of type '" + message.getClass().getName() + "'. Are you missing a protocol encoder?"); } if (localWrittenBytes == 0) { // Kernel buffer is full. if (!req.equals(AbstractIoSession.MESSAGE_SENT_REQUEST)) { setInterestedInWrite(session, true); return false; } } else { writtenBytes += localWrittenBytes; if (writtenBytes >= maxWrittenBytes) { // Wrote too much scheduleFlush(session); return false; } } if (message instanceof IoBuffer) { ((IoBuffer) message).free(); } } while (writtenBytes < maxWrittenBytes); } catch (Exception e) { //写请求结果异常 if (req != null) { req.getFuture().setException(e); } IoFilterChain filterChain = session.getFilterChain(); filterChain.fireExceptionCaught(e); return false; } return true; }
这一点有一下几点要看
f.1.1
//写会话buffer localWrittenBytes = writeBuffer(session, req, hasFragmentation, maxWrittenBytes - writtenBytes, currentTime);
private int writeBuffer(S session, WriteRequest req, boolean hasFragmentation, int maxLength, long currentTime) throws Exception { IoBuffer buf = (IoBuffer) req.getMessage(); int localWrittenBytes = 0; if (buf.hasRemaining()) { int length; if (hasFragmentation) { length = Math.min(buf.remaining(), maxLength); } else { length = buf.remaining(); } try { //发送会话数据 localWrittenBytes = write(session, buf, length); } catch (IOException ioe) { // We have had an issue while trying to send data to the // peer : let's close the session. buf.free(); session.closeNow(); removeNow(session); return 0; } } session.increaseWrittenBytes(localWrittenBytes, currentTime); // Now, forward the original message if (!buf.hasRemaining() || (!hasFragmentation && (localWrittenBytes != 0))) { // Buffer has been sent, clear the current request. Object originalMessage = req.getOriginalRequest().getMessage(); if (originalMessage instanceof IoBuffer) { buf = (IoBuffer) req.getOriginalRequest().getMessage(); int pos = buf.position(); buf.reset(); fireMessageSent(session, req); // And set it back to its position buf.position(pos); } else { fireMessageSent(session, req); } } return localWrittenBytes; }
/** * Write a sequence of bytes to a {@link IoSession}, means to be called when * a session was found ready for writing. * 当会话准备好写操作是,写字节序列到会话 * @param session * the session to write * @param buf * the buffer to write * @param length * the number of bytes to write can be superior to the number of * bytes remaining in the buffer * @return the number of byte written * @throws IOException * any exception thrown by the underlying system calls */ protected abstract int write(S session, IoBuffer buf, int length) throws IOException;
f.1.2
//写文件
localWrittenBytes = writeFile(session, req, hasFragmentation, maxWrittenBytes - writtenBytes, currentTime);
//写文件
private int writeFile(S session, WriteRequest req, boolean hasFragmentation, int maxLength, long currentTime) throws Exception { int localWrittenBytes; //获取写请求文件FileRegion FileRegion region = (FileRegion) req.getMessage(); if (region.getRemainingBytes() > 0) { int length; if (hasFragmentation) { length = (int) Math.min(region.getRemainingBytes(), maxLength); } else { length = (int) Math.min(Integer.MAX_VALUE, region.getRemainingBytes()); } //委托给transferFile localWrittenBytes = transferFile(session, region, length); region.update(localWrittenBytes); } else { localWrittenBytes = 0; } session.increaseWrittenBytes(localWrittenBytes, currentTime); if ((region.getRemainingBytes() <= 0) || (!hasFragmentation && (localWrittenBytes != 0))) { //触发会话消息发送事件 fireMessageSent(session, req); } return localWrittenBytes; }
/** * Write a part of a file to a {@link IoSession}, if the underlying API * isn't supporting system calls like sendfile(), you can throw a * {@link UnsupportedOperationException} so the file will be send using * usual {@link #write(AbstractIoSession, IoBuffer, int)} call. * 写文件的某个Region到会话,如果底层API不支持sendfile方法,你可以抛出一个UnsupportedOperationException, 那么将调用#write(AbstractIoSession, IoBuffer, int)发送文件。 * @param session * the session to write * @param region * the file region to write * @param length * the length of the portion to send * @return the number of written bytes * @throws Exception * any exception thrown by the underlying system calls */ protected abstract int transferFile(S session, FileRegion region, int length) throws Exception;
//触发会话消息发送事件 private void fireMessageSent(AbstractIoSession session, WriteRequest req) { session.setCurrentWriteRequest(null); IoFilterChain filterChain = session.getFilterChain(); filterChain.fireMessageSent(req); }
f.2
//如果刷新完成,且会话写请求队列不为空,会话待调度 if (flushedAll && !session.getWriteRequestQueue().isEmpty(session) && !session.isScheduledForFlush()) { //调度刷新会话 scheduleFlush(session); }
//调度写请求会话,及添加到刷新队列 private void scheduleFlush(S session) { // add the session to the queue if it's not already // in the queue if (session.setScheduledForFlush(true)) { flushingSessions.add(session); } }
f.3
scheduleRemove(session);//移除会话调度 session.closeNow();//异常立刻关闭会话 IoFilterChain filterChain = session.getFilterChain(); filterChain.fireExceptionCaught(e);
//添加回到到移除队列 private void scheduleRemove(S session) { if (!removingSessions.contains(session)) { removingSessions.add(session); } }
g.
// And manage removed sessions //移除已关闭的会话 nSessions -= removeSessions();
//移除会话
private int removeSessions() { int removedSessions = 0; //遍历移除会话队列,如果poll的会话不为空,则获取会话状态, for (S session = removingSessions.poll(); session != null; session = removingSessions.poll()) { SessionState state = getState(session); // Now deal with the removal accordingly to the session's state switch (state) { case OPENED: // Try to remove this session //尝试移除会话 if (removeNow(session)) { removedSessions++; } break; case CLOSING: // Skip if channel is already closed // In any case, remove the session from the queue //会话关闭,则更新会话移除计数器 removedSessions++; break; case OPENING: // Remove session from the newSessions queue and // remove it //正在打开从新创建会话对垒移除会话 newSessions.remove(session); if (removeNow(session)) { removedSessions++; } break; default: throw new IllegalStateException(String.valueOf(state)); } } return removedSessions; }
//尝试移除会话
private boolean removeNow(S session) { //清除会话写请求队列 clearWriteRequestQueue(session); try { //销毁会话 destroy(session); return true; } catch (Exception e) { IoFilterChain filterChain = session.getFilterChain(); filterChain.fireExceptionCaught(e); } finally { try { clearWriteRequestQueue(session); ((AbstractIoService) session.getService()).getListeners().fireSessionDestroyed(session); } catch (Exception e) { // The session was either destroyed or not at this point. // We do not want any exception thrown from this "cleanup" code // to change // the return value by bubbling up. IoFilterChain filterChain = session.getFilterChain(); filterChain.fireExceptionCaught(e); } } return false; }
//清除会话写请求队列
private void clearWriteRequestQueue(S session) { WriteRequestQueue writeRequestQueue = session.getWriteRequestQueue(); WriteRequest req; List<WriteRequest> failedRequests = new ArrayList<>(); if ((req = writeRequestQueue.poll(session)) != null) { Object message = req.getMessage(); if (message instanceof IoBuffer) { IoBuffer buf = (IoBuffer) message; // The first unwritten empty buffer must be // forwarded to the filter chain. //如果会话写请求buffer还有数据,添加写请求到失败写请求集合 if (buf.hasRemaining()) { buf.reset(); failedRequests.add(req); } else { IoFilterChain filterChain = session.getFilterChain(); //触发会话过滤链消息发送事件fireMessageSent filterChain.fireMessageSent(req); } } else { failedRequests.add(req); } // Discard others.丢弃其余的会话写请求 while ((req = writeRequestQueue.poll(session)) != null) { failedRequests.add(req); } } // Create an exception and notify. if (!failedRequests.isEmpty()) { WriteToClosedSessionException cause = new WriteToClosedSessionException(failedRequests); //更新会话调度字节计数器 for (WriteRequest r : failedRequests) { session.decreaseScheduledBytesAndMessages(r); r.getFuture().setException(cause); } IoFilterChain filterChain = session.getFilterChain(); filterChain.fireExceptionCaught(cause); } }
/** * Destroy the underlying client socket handle * 关闭底层客户端socket * @param session * the {@link IoSession} * @throws Exception * any exception thrown by the underlying system calls */ protected abstract void destroy(S session) throws Exception;
h.
// Last, not least, send Idle events to the idle sessions //通知会话空闲事件 notifyIdleSessions(currentTime);
private void notifyIdleSessions(long currentTime) throws Exception { // process idle sessions if (currentTime - lastIdleCheckTime >= SELECT_TIMEOUT) { lastIdleCheckTime = currentTime; //通知会话空闲, AbstractIoSession.notifyIdleness(allSessions(), currentTime); } }
//AbstractIoSession
//遍历会话集,通知会话空闲 public static void notifyIdleness(Iterator sessions, long currentTime) { do { if(!sessions.hasNext()) break; IoSession session = (IoSession)sessions.next(); if(!session.getCloseFuture().isClosed()) notifyIdleSession(session, currentTime); } while(true); }
public static void notifyIdleSession(IoSession session, long currentTime) { //通知会话读写空闲 notifyIdleSession0(session, currentTime, session.getConfig().getIdleTimeInMillis(IdleStatus.BOTH_IDLE), IdleStatus.BOTH_IDLE, Math.max(session.getLastIoTime(), session.getLastIdleTime(IdleStatus.BOTH_IDLE))); notifyIdleSession0(session, currentTime, session.getConfig().getIdleTimeInMillis(IdleStatus.READER_IDLE), IdleStatus.READER_IDLE, Math.max(session.getLastReadTime(), session.getLastIdleTime(IdleStatus.READER_IDLE))); notifyIdleSession0(session, currentTime, session.getConfig().getIdleTimeInMillis(IdleStatus.WRITER_IDLE), IdleStatus.WRITER_IDLE, Math.max(session.getLastWriteTime(), session.getLastIdleTime(IdleStatus.WRITER_IDLE))); notifyWriteTimeout(session, currentTime); } //触发会话空闲状态 private static void notifyIdleSession0(IoSession session, long currentTime, long idleTime, IdleStatus status, long lastIoTime) { if(idleTime > 0L && lastIoTime != 0L && currentTime - lastIoTime >= idleTime) session.getFilterChain().fireSessionIdle(status); } //通知会话超时 private static void notifyWriteTimeout(IoSession session, long currentTime) { long writeTimeout = session.getConfig().getWriteTimeoutInMillis(); if(writeTimeout > 0L && currentTime - session.getLastWriteTime() >= writeTimeout && !session.getWriteRequestQueue().isEmpty(session)) { WriteRequest request = session.getCurrentWriteRequest(); if(request != null) { //设置会话写请求超时异常 session.setCurrentWriteRequest(null); WriteTimeoutException cause = new WriteTimeoutException(request); request.getFuture().setException(cause); session.getFilterChain().fireExceptionCaught(cause); session.closeNow(); } } }
i.
// Disconnect all sessions immediately if disposal has been // requested so that we exit this loop eventually. //判断Io处理器是否正在关闭,如果正在关闭断开所有会话 if (isDisposing()) { boolean hasKeys = false; //获取当前处理器管理的会话,移除会话 for (Iterator<S> i = allSessions(); i.hasNext();) { IoSession session = i.next(); if (session.isActive()) { scheduleRemove((S) session); hasKeys = true; } } if (hasKeys) { wakeup(); } }
j.
t
ry { synchronized (disposalLock) { if (disposing) { //如果正在关闭,完成实际关闭工作 doDispose(); } } } catch (Exception e) { //捕捉异常 ExceptionMonitor.getInstance().exceptionCaught(e); } finally { //已关闭 disposalFuture.setValue(true); }
/** * Dispose the resources used by this {@link IoProcessor} for polling the * client connections. The implementing class doDispose method will be * called. * 释放IO处理器相关的资源 * @throws Exception * if some low level IO error occurs */ protected abstract void doDispose() throws Exception;
从上面来看处理器的实际工作,尝试10次nbTries选择操作,在每次选择操作过程中,
首先进行超时选择操作,然后检查Io处理器是否断开连接,尝试次数nbTries是否为零如果为0,则注册新的选择器;然后遍历创建会话队列,从队列拉取会话,如果会话为不null,则初始化会话,构建会话过滤链(从IoService继承)触发会话过滤链的会话创建和会话打开事件,并记录新创建的会话数量nSessions;更会会话状态,此过程为从会话次序控制队列
获取会话,检查会话状态,如果状态为OPENED更新会话的读写状态,如果为OPENING放回次序控制会话队列;如果选择操作返回的SELECTKey的值大于0,即有相关的兴趣操作事件(读写事件),遍历选择后读写等操作就绪的会话,如果会话可读,则读取会话缓存区数据到buffer,触发过滤链消息接收事件MessageReceive,接收完消息后,如果会话输入流关闭则触发过滤链fireInputClosed事件,如果在这过程有异常发生,则触发过滤链异常事件ExceptionCaught,如果会话可写,则添加会话到刷新会话队列;遍历刷新会话队列,根据会话写请求消息类型为IoBuffer还是FileRegion,发送会话数据,发送会话数据后,如果会话还有些请求,则添加会话到队列,如果在这个过程中有异常,则添加会话到会话移除队列;遍历会话移除队列,
如果会话为关闭,则尝试关闭会话,并清除会话写请求队列,如果会话数据已发送完,
则触发会话过滤链消息发送事件fireMessageSent;更新处理器会话计数器nSessions;
遍历处理器所有会话,触发会话过滤器会话空闲时间fireSessionIdle;如果在这个过程中,处理器会话计数器nSessions为0,则清除处理器引用;如果Io处理器正在关闭,则添加所有会话到移除会话队列,释放Io处理器先关的资源。
再来看启动处理器方法#startupProcessor的第二点
2.
//执行处理器 executor.execute(new NamePreservingRunnable(processor, threadName));
/** * A {@link Runnable} wrapper that preserves the name of the thread after the runnable is * complete (for {@link Runnable}s that change the name of the Thread they use.) * 将Runnable包装成一个新的线程,只是线程名不同,线程运行完,恢复原始线程名。 * @author The Apache MINA Project (dev@mina.apache.org) * @version $Rev: 446581 $, $Date: 2006-09-15 11:36:12Z $, */ public class NamePreservingRunnable implements Runnable { private final Logger logger = LoggerFactory.getLogger(NamePreservingRunnable.class); private final String newName;//新线程名 private final Runnable runnable;//实际线程 public NamePreservingRunnable(Runnable runnable, String newName) { this.runnable = runnable; this.newName = newName; } public void run() { Thread currentThread = Thread.currentThread(); String oldName = currentThread.getName(); if (newName != null) { setName(currentThread, newName); } try { runnable.run(); } finally { setName(currentThread, oldName); } } /** * Wraps {@link Thread#setName(String)} to catch a possible {@link Exception}s such as * {@link SecurityException} in sandbox environments, such as applets 设置线程名 */ private void setName(Thread thread, String name) { try { thread.setName(name); } catch (Exception e) { // Probably SecurityException. if (logger.isWarnEnabled()) { logger.warn("Failed to set the thread name.", e); } } } }
回到添加会话方法:
/** * {@inheritDoc} */ //会话创建时,添加回到到处理器, @Override 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 //添加会话到Io处理器的创建会话队列中 newSessions.add(session); //启动一个Io处理器线程 startupProcessor(); }
小节,从上面来年,添加会话首先添加会话到Io处理器的创建会话队列中,启动处理器线程Processor。处理器的实际工作,尝试10次nbTries选择操作,在每次选择操作过程中,
首先进行超时选择操作,然后检查Io处理器是否断开连接,尝试次数nbTries是否为零如果为0,则注册新的选择器;然后遍历创建会话队列,从队列拉取会话,如果会话为不null,则初始化会话,构建会话过滤链(从IoService继承)触发会话过滤链的会话创建和会话打开事件,并记录新创建的会话数量nSessions;更会会话状态,此过程为从会话次序控制队列
获取会话,检查会话状态,如果状态为OPENED更新会话的读写状态,如果为OPENING放回次序控制会话队列;如果选择操作返回的SELECTKey的值大于0,即有相关的兴趣操作事件(读写事件),遍历选择后读写等操作就绪的会话,如果会话可读,则读取会话缓存区数据到buffer,触发过滤链消息接收事件MessageReceive,接收完消息后,如果会话输入流
关闭则触发过滤链fireInputClosed事件,如果在这过程有异常发生,则触发过滤链异常事件ExceptionCaught,如果会话可写,则添加会话到刷新会话队列;遍历刷新会话队列,根据会话写请求消息类型为IoBuffer还是FileRegion,发送会话数据,发送会话数据后,如果会话还有些请求,则添加会话到队列,如果在这个过程中有异常,则添加会话到会话移除队列;遍历会话移除队列,如果会话为关闭,则尝试关闭会话,并清除会话写请求队列,如果会话数据已发送完,则触发会话过滤链消息发送事件fireMessageSent;更新处理器会话计数器nSessions;遍历处理器所有会话,触发会话过滤器会话空闲时间fireSessionIdle;
如果在这个过程中,处理器会话计数器nSessions为0,则清除处理器引用;如果Io处理器正在关闭,则添加所有会话到移除会话队列,释放Io处理器先关的资源。
再来看其他方法:
/** * {@inheritDoc} 将写请求添加到会话写请求队列 */ @Override public void write(S session, WriteRequest writeRequest) { WriteRequestQueue writeRequestQueue = session.getWriteRequestQueue(); writeRequestQueue.offer(session, writeRequest); if (!session.isWriteSuspended()) { //刷新会话 this.flush(session); } } /** * {@inheritDoc} //添加会话到刷新会话队列 */ @Override public final void flush(S session) { // add the session to the queue if it's not already // in the queue, then wake up the select() //设置会话正在调度flush if (session.setScheduledForFlush(true)) { flushingSessions.add(session); wakeup(); } } /** * {@inheritDoc} 移除会话,添加会话到移除会话队列,启动处理器线程 */ @Override public final void remove(S session) { scheduleRemove(session); startupProcessor(); } /** * {@inheritDoc} //释放Io处理器资源 */ @Override public final void dispose() { if (disposed || disposing) { return; } synchronized (disposalLock) { disposing = true; startupProcessor(); } disposalFuture.awaitUninterruptibly(); disposed = true; } /** * {@inheritDoc} */ @Override public final boolean isDisposing() { return disposing; } /** * {@inheritDoc} */ @Override public final boolean isDisposed() { return disposed; }
总结:
抽象Io处理器AbstractPollingIoProcessor,主要几个关键内部变量为选择操作超时时间SELECT_TIMEOUT,用于腾出时间,处理空闲的会话; executor处理器内部执行器,用于运行内部处理器Processor;存储Io处理器等线程最大线程id的threadIds(Map);创建会话队列newSessions用于存储新创建的会话;移除会话队列removingSessions用于存放从处理器移除的会话;刷新会话队列flushingSessions,用于存放要发送写请求的会话;次序控制会话队列trafficControllingSessions用于存放会话待读写的会话;Io处理器线程引用processorRef。
添加会话首先添加会话到Io处理器的创建会话队列中,启动处理器线程Processor。处理器的实际工作,尝试10次nbTries选择操作,在每次选择操作过程中,首先进行超时选择操作,然后检查Io处理器是否断开连接,尝试次数nbTries是否为零如果为0,则注册新的选择器;然后遍历创建会话队列,从队列拉取会话,如果会话为不null,则初始化会话,构建会话过滤链(从IoService继承)触发会话过滤链的会话创建和会话打开事件,并记录新创建的会话数量nSessions;更会会话状态,此过程为从会话次序控制队列获取会话,检查会话状态,如果状态为OPENED更新会话的读写状态,如果为OPENING放回次序控制会话队列;如果选择操作返回的SELECTKey的值大于0,即有相关的兴趣操作事件(读写事件),遍历选择后读写等操作就绪的会话,如果会话可读,则读取会话缓存区数据到buffer,触发过滤链消息接收事件MessageReceive,接收完消息后,如果会话输入流关闭则触发过滤链fireInputClosed事件,如果在这过程有异常发生,则触发过滤链异常事件ExceptionCaught,如果会话可写,则添加会话到刷新会话队列;遍历刷新会话队列,根据会话写请求消息类型为IoBuffer还是FileRegion,发送会话数据,发送会话数据后,如果会话还有些请求,则添加会话到队列,如果在这个过程中有异常,则添加会话到会话移除队列;遍历会话移除队列,如果会话为关闭,则尝试关闭会话,并清除会话写请求队列,如果会话数据已发送完,则触发会话过滤链消息发送事件fireMessageSent;更新处理器会话计数器nSessions;遍历处理器所有会话,触发会话过滤器会话空闲时间fireSessionIdle;如果在这个过程中,处理器会话计数器nSessions为0,则清除处理器引用;如果Io处理器正在关闭,则添加所有会话到移除会话队列,释放Io处理器先关的资源。
抽象Io处理器AbstractPollingIoProcessor主要是处理IoProcessor关联会话message*事件,而所有的工作,都是通过处理器线程Processor完成。每当有会话添加到IoProcessor,则启动一个处理器线程Processor,处理会话的读写操作及相关事件。就连IoProcessor资源的释放,也是由处理器线程Processor处理。关闭IoProcessor时,现将处理器关联会话,添加移除会话队列,实际工作由IoProcessor的子类的doDispose方法完成。
附:
//SessionState会话状态
public final class SessionState extends Enum { public static final SessionState OPENING; public static final SessionState OPENED; public static final SessionState CLOSING; private static final SessionState $VALUES[]; private SessionState(String s, int i) { super(s, i); } static { OPENING = new SessionState("OPENING", 0); OPENED = new SessionState("OPENED", 1); CLOSING = new SessionState("CLOSING", 2); $VALUES = (new SessionState[] { OPENING, OPENED, CLOSING }); } public static SessionState[] values() { return (SessionState[])$VALUES.clone(); } public static SessionState valueOf(String name) { return (SessionState)Enum.valueOf(org/apache/mina/core/session/SessionState, name); } }
//原子引用AtomicReference
/** * An object reference that may be updated atomically. See the {@link * java.util.concurrent.atomic} package specification for description * of the properties of atomic variables. * @since 1.5 * @author Doug Lea * @param <V> The type of object referred to by this reference */ public class AtomicReference<V> implements java.io.Serializable { private static final long serialVersionUID = -1848883965231344442L; private static final Unsafe unsafe = Unsafe.getUnsafe(); private static final long valueOffset; static { try { valueOffset = unsafe.objectFieldOffset (AtomicReference.class.getDeclaredField("value")); } catch (Exception ex) { throw new Error(ex); } } private volatile V value; /** * Creates a new AtomicReference with the given initial value. * * @param initialValue the initial value */ public AtomicReference(V initialValue) { value = initialValue; } /** * Creates a new AtomicReference with null initial value. */ public AtomicReference() { } /** * Gets the current value. * * @return the current value */ public final V get() { return value; } /** * Sets to the given value. * * @param newValue the new value */ public final void set(V newValue) { value = newValue; } /** * Eventually sets to the given value. * * @param newValue the new value * @since 1.6 */ public final void lazySet(V newValue) { unsafe.putOrderedObject(this, valueOffset, newValue); } /** * Atomically sets the value to the given updated value * if the current value {@code ==} the expected value. * @param expect the expected value * @param update the new value * @return true if successful. False return indicates that * the actual value was not equal to the expected value. */ public final boolean compareAndSet(V expect, V update) { return unsafe.compareAndSwapObject(this, valueOffset, expect, update); } /** * Atomically sets the value to the given updated value * if the current value {@code ==} the expected value. * * <p>May [url=package-summary.html#Spurious]fail spuriously[/url] * and does not provide ordering guarantees, so is only rarely an * appropriate alternative to {@code compareAndSet}. * * @param expect the expected value * @param update the new value * @return true if successful. */ public final boolean weakCompareAndSet(V expect, V update) { return unsafe.compareAndSwapObject(this, valueOffset, expect, update); } /** * Atomically sets to the given value and returns the old value. * * @param newValue the new value * @return the previous value */ public final V getAndSet(V newValue) { while (true) { V x = get(); if (compareAndSet(x, newValue)) return x; } } /** * Returns the String representation of the current value. * @return the String representation of the current value. */ public String toString() { return String.valueOf(get()); } }
发表评论
-
Mina 报文连接器(NioDatagramConnector)
2017-06-14 08:46 1417Mina 抽象Polling连接器(A ... -
Mina 报文监听器NioDatagramAcceptor二(发送会话消息等)
2017-06-13 16:01 1542Mina 报文监听器NioDatagramAcceptor一( ... -
Mina 报文监听器NioDatagramAcceptor一(初始化,Io处理器)
2017-06-13 09:51 2575Mina Io监听器接口定义及抽象实现:http://dona ... -
Mina 报文通信简单示例
2017-06-12 09:01 2585MINA TCP简单通信实例:http://donald-dr ... -
Mina socket连接器(NioSocketConnector)
2017-06-12 08:37 4772Mina 抽象Polling连接器(AbstractPolli ... -
Mina 抽象Polling连接器(AbstractPollingIoConnector)
2017-06-11 21:29 1009Mina 连接器接口定义及抽象实现(IoConnector ) ... -
Mina 连接器接口定义及抽象实现(IoConnector )
2017-06-11 13:46 1831Mina IoService接口定义及抽象实现:http:// ... -
Mina socket监听器(NioSocketAcceptor)
2017-06-09 08:44 3419Mina IoService接口定义及抽象实现:http:// ... -
Mina 抽象polling监听器
2017-06-08 22:32 784Mina Io监听器接口定义及抽象实现:http://dona ... -
Mina Io监听器接口定义及抽象实现
2017-06-07 13:02 1349Mina IoService接口定义及抽象实现:http:// ... -
Mina IoService接口定义及抽象实现
2017-06-06 23:44 1196Mina IoHandler接口定义:http://donal ... -
Mina Nio会话(Socket,DataGram)
2017-06-06 12:53 1209Mina Socket会话配置:http://donald-d ... -
Mina 抽象Io会话
2017-06-05 22:45 1015Mina Io会话接口定义:http://donald-dra ... -
Mina Io会话接口定义
2017-06-04 23:15 1167Mina Nio处理器:http://donald-drape ... -
Mina Nio处理器
2017-06-04 22:19 743Mina Io处理器抽象实现:http://donald-dr ... -
Mina IoHandler接口定义
2017-06-01 21:30 1734Mina 过滤链抽象实现:http://donald-drap ... -
MINA 多路复用协议编解码器工厂二(多路复用协议解码器)
2017-06-01 12:52 2275MINA 多路复用协议编解码器工厂一(多路复用协议编码器): ... -
MINA 多路复用协议编解码器工厂一(多路复用协议编码器)
2017-05-31 22:22 1868MINA 多路分离解码器实例:http://donald-dr ... -
Mina 累计协议解码器
2017-05-31 00:09 1230MINA 编解码器实例:http://donald-drape ... -
Mina 协议编解码过滤器三(会话write与消息接收过滤)
2017-05-28 07:22 1750Mina 协议编解码过滤器一(协议编解码工厂、协议编码器): ...
相关推荐
文件"MinaSocket"可能包含了实现上述功能的详细代码,包括服务端的Acceptor配置、过滤器设置、事件处理器编写,以及客户端的Socket连接、数据发送和接收等。通过阅读和理解这些代码,你可以更好地掌握Mina与Socket...
这些事件处理器是非阻塞的,因此可以在不阻塞其他事件处理的同时进行工作,实现真正的异步并发。这样的设计对于处理大量并发连接的服务器来说至关重要,因为它可以避免因单个连接而阻塞整个服务的情况。 **mina-...
5. **IoEvent**:事件,MINA提供了多种事件类型,如连接建立、数据读取、连接关闭等,开发者可以通过实现Handler接口来处理这些事件。 6. **Asynchronous Channel**:异步通道,MINA基于NIO(Non-blocking I/O)...
4. **Event Handler**:事件处理器接口,当网络事件发生时,MINA会调用相应的事件处理器方法。例如,`messageReceived()`方法会在接收到网络数据时被调用。 5. **Transport**:传输层抽象,MINA支持多种传输方式,...
总的来说,Apache Mina提供了强大的工具和抽象,使得开发人员能够专注于业务逻辑,而不是底层的网络通信实现。在实际项目中,结合Mina进行文件和字符串的传输,可以提高代码的可维护性和性能,从而提升整个系统的...
Mina利用了Java NIO(New IO)库,提供了异步、非阻塞的I/O操作,提高了系统处理大量并发连接的能力。通过使用缓冲区(Buffer)和事件驱动模型,Mina能够有效地管理网络通信,并且降低了CPU的使用率。 "Mina-core-...
它抽象出了IO服务(IoService)、IO处理器(IoHandler)、IO过滤器链(IoFilterChain)等概念,简化了网络编程的复杂性。 ### MINA框架核心组件 - **IoAcceptor**:用于处理服务端接收新的连接请求的组件。 - **...
在"Mina文件及字符串传输"这个场景中,我们可以理解为利用Mina框架实现了一种服务,允许客户端通过发送字符串请求来获取服务器上的文件。这种功能在分布式系统、文件共享或者远程操作等场景中非常常见。下面我们将...
Mina提供了一个高级抽象层,允许开发者用类似处理Java IO的方式处理NIO(非阻塞I/O)。它简化了网络编程,使得开发者可以专注于业务逻辑,而无需关心底层的网络细节。Mina支持多种协议,如HTTP、FTP、Telnet等,同时...
7. **协议实现**:MINA支持多种协议,如HTTP、FTP、SNMP等,只需编写适当的过滤器和处理器,即可实现这些协议。 8. **异步操作**:MINA的所有I/O操作都是异步的,这意味着在等待数据到达时,程序可以继续执行其他...
Mina的核心思想是将网络编程抽象化,使得开发者可以专注于业务逻辑,而不是底层的网络通信细节。 标题中的"mina简单示例"指的是使用Apache Mina框架构建一个简单的客户端和服务端的程序。在Java开发中,Mina提供了...
Mina 提供了一种抽象层,简化了网络编程,允许开发者专注于业务逻辑,而无需关心底层协议的实现细节。本DEMO将帮助我们深入理解Mina的工作原理及其在实际应用中的使用。 ### 1. Mina 框架的核心概念 - **事件驱动...
2. **协议无关性**:MINA提供了一种抽象层,使得开发者无需关心底层使用的具体传输协议,如TCP或UDP,只需关注数据的读写和解析。 3. **过滤器链**:MINA引入了过滤器链的概念,开发者可以通过自定义过滤器来实现...
- IoBuffer 是 Mina 中用于数据存储和传输的核心类,它是对 Java NIO ByteBuffer 的封装和增强。 - **IoBuffer vs ByteBuffer**: IoBuffer 提供了更多便于操作的功能,比如自动扩容、直接内存分配等特性,使得数据...
1. **非阻塞I/O**:Mina采用了NIO(New IO)技术,允许同时处理大量并发连接,提高了系统的并发能力。 2. **事件驱动**:Mina采用事件驱动模型,当网络事件发生时,如数据接收或发送,会触发相应的处理器进行处理。 ...
Mina的架构设计使得它能够将网络通信的底层细节从应用程序中抽象出来,这样开发者只需要关注于编写处理数据和业务逻辑的代码。它将网络通信分解为几个核心组件: 1. IoService:这个接口负责网络连接的建立和维护,...
Apache Mina 2.0通过其高度抽象和模块化的架构,为开发者提供了一个强大且灵活的网络通信解决方案。借助Mina,即使是复杂的网络通信任务也能变得简单易行,大大提高了开发效率和应用性能。然而,为了充分发挥Mina的...
Mina的核心价值在于其能够将底层的网络通信细节抽象出来,使得开发者能够更专注于业务逻辑的实现。它支持多种传输方式,不仅限于TCP/IP和UDP/IP,还包括序列化服务、虚拟机管道通信等,提供了丰富的功能和高度的灵活...
在学习 Mina 2.0 前,需要具备 Java IO、NIO、Socket 编程以及 Java 线程和并发库的基础知识。Mina 提供了服务器端和客户端的封装,其API将网络通信与应用程序逻辑分离开,让开发者只需关注数据处理和业务逻辑。 在...