NIO framework-mina source code analysis (1): Background
The underlying layer of Mina still utilizes the NIO function provided by JDK. Mina only encapsulates NiO, including the thread pool used by Mina, which is directly provided by JDK.
The Mina server mainly includes
Accept, processor, sessionIt consists of three parts. Where
Accept is mainly responsible for listening on the specified port. If there is a new connection, a new session is established. processor is responsible for processing the sent data and received data of the session and calling the upper layer for processing; session caches the current connection data.
Mina uses the thread-initiated lazy technology, that is, the minimum number of threads to start. When Mina server is started, there is only one thread-accept, and there is only one accept thread, listen on the specified port (multiple ports can be monitored at the same time, and Mina can be bound to multiple ports ).
1. Acceptor
Let's take a look at the main class diagrams of the acceptor.
The Mina server startup entry is in niosocketacceptor. BIND (inetsocketaddress) orNiosocketacceptor. BIND (socketaddress...) Method, Acceptor. BIND (New inetsocketaddress (1234 ));
Then it will call abstractpollingioacceptor. bindinternal (list <? Extends socketaddress>) method. In the bindinternal method, the startupacceptor () method is called to submit an accept thread to the thread pool (Submit only once), And initialize the selector of the acceptor, so that an acceptor thread starts.
The acceptor is relatively simple. It is equivalent to listening to the acceptor event in the current selector, processing new connections, and creating a new session and placing it in the corresponding processor.
The acceptor code is very simple.
Private class acceptor implements runnable {public void run () {assert (acceptorref. get () = This); int nhandles = 0; // release the lock. release (); While (selectable) {try {// detect if we have some keys ready to be processed // The select () will be woke up if some new connection // have occurred, or if the selector has been explicitly // Woke Up int selected = select (); // This actually s ETS the selector to op_accept, // and binds to the port on which this class will // listen on register the connection event nhandles + = registerhandles () in the channel; // now, if the number of registred handles is 0, we can // quit the loop: We don't have any socket listening // for incoming connection. if (nhandles = 0) {acceptorref. set (null); If (registerqueue. isempty () & cancelqueue. isempty () {assert (acceptorref. get ()! = This); break;} If (! Acceptorref. compareandset (null, this) {assert (acceptorref. Get ()! = This); break;} assert (acceptorref. get () = This);} If (selected> 0) {// we have some connection request, Let's process // them here. process the connection processhandles (selectedhandles ();} // check to see if any cancellation request has been made. nhandles-= unregisterhandles ();} catch (closedselectorexception CSE) {// If the selector has been closed, we can exit the loop break;} catch (throwable e) {exceptionmonitor. getinstance (). exceptioncaught (E); try {thread. sleep (1000);} catch (interruptedexception E1) {predictionmonitor. getinstance (). exceptioncaught (E1) ;}}// cleanup all the processors, and shutdown the acceptor. if (selectable & isdisposing () {selectable = false; try {If (createdprocessor) {processor. dispose () ;}} finally {try {synchronized (disposallock) {If (isdisposing () {destroy () ;}} catch (exception e) {exceptionmonitor. getinstance (). predictioncaught (E);} finally {disposalfuture. setdone ();}}}}
2. Processor
Processor, as its name implies, implements Io processing, reads and writes data in the current session, and performs business processing.
During Mina server initialization, a processor pool will be initialized and the pool size will be imported through the niosocketacceptor constructor. The default value is the number of current processors plus 1.
The processor pool contains the thread pool-executors. newcachedthreadpool () provided by JDK (). Each processor thread references this thread pool, that is, each processor thread runs in this thread pool.
In the actual processing of the Mina server, each processor is equivalent to a thread that processes data in the current session queue in turn (Sessions in each processor are equivalent to sequential processing and share a thread.).
Each processor has a selector object..
Processor class diagram
The processing logic on the processor side is relatively complicated. Let's look at the following flowchart.
1. register the newly added session to the read event in the selector of the current processor and initialize the session;
2. Determine whether the current selector has a read/write event;
3. If there is a read event in Step 4, go to Step 4. If there is no read event in Step 4, go directly to step 2;
4. process the current read event and put the processed data into the flush queue;
5. Flush the result of step 1 to the client;
6. process the session, such as the session idle time.
7. Execute Step 1 again and execute it cyclically.
Processor code.
Private class processor implements runnable {public void run () {assert (processorref. get () = This); int nsessions = 0; lastidlechecktime = system. currenttimemillis (); 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 (); Int selected = select (select_timeout); long T1 = system. currenttimemillis (); long Delta = (T1-T0); If (selected = 0 )&&! Wakeupcalled. get () & (delta <100) {// last chance: The select () may have been // interrupted because we have had an closed channel. if (isbrokenconnection () {log. warn ("Broken connection"); // We Can reselect immediately // set back the flag to false wakeupcalled. getandset (false); continue;} else {log. warn ("Create a New selector. selected is 0, Delta = "+ (T1-T0); // OK, we are hit by T He 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. registernewselector ();} // set back the flag to false wakeupcalled. getandset (false); // and continue the loop continue;} // manage newly created session first processes newly added sessions, register the selector read event 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... process read events and put the results in the flush queue process ();} // write the pending requests to send the processed data of the session in the flush queue to the client long currenttime = system. currenttimemillis (); flush (currenttime); // and manage removed sessions nsessions-= removesessions (); // last, not least, send idle events to the idle sessions policyidlesessions (currenttime); // get a chance to exit the infinite Lo OP if there are no // more sessions on this processor 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. if (isdisposing () {for (iterator <S> I = allsessions (); I. hasnext ();) {scheduleremove (I. next () ;}wakeup () ;}} catch (closedselectorexception CSE) {// If the selector has been closed, we can exit the loop break;} catch (throwable T) {exceptionmonitor. getinstance (). exceptioncaught (t); try {thread. sleep (1000);} catch (interruptedexception E1) {predictionmonitor. getinstance (). exceptioncaught (E1) ;}}try {synchronized (disposallock) {If (disposing) {dodispose () ;}} catch (throwable t) {exceptionmonitor. getinstance (). predictioncaught (t);} finally {disposalfuture. setvalue (true );}}}
3. Session
Session is used as a connection object to cache information of the current connected user.
Session Diagram
The Session object is an attach bound to the selectablechannel.
Class nioprocessor @ override protected void Init (niosession) throws exception {selectablechannel CH = (selectablechannel) session. getchannel (); Ch. configureblocking (false); Session. setselectionkey (ch. register (selector, selectionkey. op_read, session); // Chanel registers the read event and treats the session as an attach tutoring selectablechannel. }
NIO framework-mina source code analysis (2): Mina Core Engine