apache mina: 写数据过程

来源:互联网 发布:58端口多少钱一个月 编辑:程序博客网 时间:2024/05/21 22:20



NioSocketSession(AbstractIoSession).write(Object) 

调用IoSession.write(Object)方法向应用层中写入数据。


 // Now, we can write the message. First, create a future
  WriteFuture   writeFuture = new DefaultWriteFuture(this);
  WriteRequest   writeRequest = new DefaultWriteRequest(message, writeFuture, remoteAddress);


public class DefaultWriteRequest implements WriteRequest {
    /** An empty message */
    public static final byte[] EMPTY_MESSAGE = new byte[] {};

    private final Object message;

     private final WriteFuture future;

     private final SocketAddress destination;

}

然后创建一个WriteRequest 请求. WriteRequest 里面包括要写出的数据message ,写出数据的结果future 以及写出数据的目的地。

    public void write(S session, WriteRequest writeRequest) 
    {
        WriteRequestQueue writeRequestQueue = session.getWriteRequestQueue();


        writeRequestQueue.offer(session, writeRequest);


        if (!session.isWriteSuspended()) {
            this.flush(session);
        }
    }

在HeadFilter中,会获取IoSession与之相关的 WriteRequestQueue 队列,作为应用层写出数据缓冲区。 把写出的WriteRequest放到写出缓冲区队列中。

因为apache  mina 是按照SEDA架构设计,同时把要写出数据的IoSession放在 flushingSessions 队列中等待写出数据。



    private class HeadFilter extends IoFilterAdapter {        @SuppressWarnings("unchecked")        @Override        public void filterWrite(NextFilter nextFilter, IoSession session, WriteRequest writeRequest) throws Exception {            AbstractIoSession s = (AbstractIoSession) session;            // Maintain counters.            if (writeRequest.getMessage() instanceof IoBuffer) {                IoBuffer buffer = (IoBuffer) writeRequest.getMessage();                // I/O processor implementation will call buffer.reset()                // it after the write operation is finished, because                // the buffer will be specified with messageSent event.                buffer.mark();                int remaining = buffer.remaining();                if (remaining > 0) {                    s.increaseScheduledWriteBytes(remaining);                }            } else {                s.increaseScheduledWriteMessages();            }            WriteRequestQueue writeRequestQueue = s.getWriteRequestQueue();            if (!s.isWriteSuspended()) {                if (writeRequestQueue.isEmpty(session)) {                    // We can write directly the message                    s.getProcessor().write(s, writeRequest);                } else {                    s.getWriteRequestQueue().offer(s, writeRequest);                    s.getProcessor().flush(s);                }            } else {                s.getWriteRequestQueue().offer(s, writeRequest);            }        }        @SuppressWarnings("unchecked")        @Override        public void filterClose(NextFilter nextFilter, IoSession session) throws Exception {            ((AbstractIoSession) session).getProcessor().remove(session);        }    }


然后在Processor线程的run()方法中,来轮询flushIoSession队列。


  // Write the pending requests
                    long currentTime = System.currentTimeMillis();
                    flush(currentTime);

在NioProcessor(AbstractPollingIoProcessor<S>).flushNow(S, long)  方法中,依次把同一个IoSession中的writeRequest 请求写入到系统缓冲区。

    private boolean flushNow(S session, long currentTime) {        if (!session.isConnected()) {            scheduleRemove(session);            return false;        }        final boolean hasFragmentation = session.getTransportMetadata().hasFragmentation();        //获取与IoSession相关联的写队列        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.        final int maxWrittenBytes = session.getConfig().getMaxReadBufferSize()                + (session.getConfig().getMaxReadBufferSize() >>> 1);        int writtenBytes = 0;        WriteRequest req = null;        try {            // Clear 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 = 0;                Object message = req.getMessage();                //在这里是真正写操作                if (message instanceof IoBuffer) {                //返回实际写入数据数量                    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                        writtenBytes += localWrittenBytes;                        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)) {                        writtenBytes += localWrittenBytes;                        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?");                }                //当内核 缓存buff已经满的时候,继续设置写操作                if (localWrittenBytes == 0) {                    // Kernel buffer is full.                    setInterestedInWrite(session, true);                    return false;                }                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;    }


如何在应用层缓冲区的写出数据全部写入到系统缓冲区后才关闭socket???

    /**     * Closes this session immediately or after all queued write requests     * are flushed.  This operation is asynchronous.  Wait for the returned     * {@link CloseFuture} if you want to wait for the session actually closed.     *     * @param immediately {@code true} to close this session immediately     *                    . The pending write requests     *                    will simply be discarded.     *                    {@code false} to close this session after all queued     *                    write requests are flushed (i.e. {@link #close()}).     */    CloseFuture close(boolean immediately);

private final CloseFuture closeOnFlush() {
        getWriteRequestQueue().offer(this, CLOSE_REQUEST);
        getProcessor().flush(this);
        return closeFuture;
    }

 /**
     * An internal write request object that triggers session close.
     *
     * @see #writeRequestQueue
     */
    private static final WriteRequest CLOSE_REQUEST = new DefaultWriteRequest(new Object());


        public synchronized WriteRequest poll(IoSession session) {
            WriteRequest answer = queue.poll(session);


            if (answer == CLOSE_REQUEST) {
                AbstractIoSession.this.close();
                dispose(session);
                answer = null;
            }


            return answer;
        }

当IoSessioni.close(flase) 调用时, 创建了一个CLOSE_Request请求,当轮询flushIosession时,调用了close()方法。因为IoSession.close(flase) 也是一个写请求队列,所以在处理CLOSE_REQUEST请求时,之前的应用层缓冲区数据已经写入到系统缓冲区中。

 private class HeadFilter extends IoFilterAdapter {

       public void filterClose(NextFilter nextFilter, IoSession session) throws Exception {
            ((AbstractIoSession) session).getProcessor().remove(session);
        }

}

    /** A queue used to store the sessions to be removed */
    private final Queue<S> removingSessions = new ConcurrentLinkedQueue<S>();

在HeadFilter处理时,把IoSession加入到removingSessions队列中等待处理。

NioProcessor(AbstractPollingIoProcessor<S>).removeSessions()
AbstractPollingIoProcessor<S>.access$12(AbstractPollingIoProcessor) 
AbstractPollingIoProcessor$Processor.run()

Processor线程在循环run()方法中处理等待被删除的IoSession.

    private int removeSessions() {        int removedSessions = 0;        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                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;    }

如果在IoSession真正关闭时,有数据尚未写入到系统缓冲区,将会有异常抛出。


    private void clearWriteRequestQueue(S session) {        WriteRequestQueue writeRequestQueue = session.getWriteRequestQueue();        WriteRequest req;        List<WriteRequest> failedRequests = new ArrayList<WriteRequest>();        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.                if (buf.hasRemaining()) {                    buf.reset();                    failedRequests.add(req);                } else {                    IoFilterChain filterChain = session.getFilterChain();                    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);        }    }



0 0
原创粉丝点击