EndPoint提供基礎的網路IO服務,用來實現網路連線和控制,它是伺服器對外I/O操作的接入點。主要任務是管理對外的socket連線,同時將建立好的socket連線交到合適的工作執行緒中去。
裡面兩個主要的屬性類是Acceptor和Poller、SocketProcessor
Acceptor
Acceptor類實現了Runnable介面,主要用於接收網路請求,建立連線,連線建立之後,將一個SocketChannel物件包裝成一個NioChannel,並註冊到Poller中。由Poller來負責執行資料的讀取和業務執行。
我們看一下Acceptor的run方法:
public void run() { SocketChannel socket = serverSock.accept();//從監聽的serversocket中獲取新的連線 setSocketOptions(socket);//設定通道的屬性 …… } protected boolean setSocketOptions(SocketChannel socket) { NioChannel = channel = new NioChannel(socket, bufhandler);//將通道包裝成NioChannel getPoller0().register(channel);//從poller陣列中選擇一個poller,將channel註冊到poller中 …… }
Poller
Poller實現了Runnable介面,在NioEndpoint的時候,會初始化pollers陣列,同時啟動pollers陣列中的執行緒,讓pollers開始工作。
封裝後socketchannel的放入Poller執行緒內部維護的一個PollerEvent佇列中,然後在Poller執行緒執行時處理佇列,將socketchannel註冊到這個Poller的Selector上。
當事件到來的時候,Selector發現要處理的事件,通過selector.select系列方法來獲取資料,然後經由processKey到processSocket方法,封裝成一個SocketProcessor物件後,放在EndPoint的執行緒池中執行。
SocketChannel是如何註冊到Poller中的?
protected ConcurrentLinkedQueue<Runnable> events = new ConcurrentLinkedQueue<Runnable>();//內部維護的事件佇列 public void register(final NioChannel socket) { socket.setPoller(this); KeyAttachment key = keyCache.poll(); final KeyAttachment ka = key!=null?key:new KeyAttachment(); ka.reset(this,socket,getSocketProperties().getSoTimeout()); PollerEvent r = eventCache.poll(); ka.interestOps(SelectionKey.OP_READ);//this is what OP_REGISTER turns into. if ( r==null) r = new PollerEvent(socket,ka,OP_REGISTER); else r.reset(socket,ka,OP_REGISTER); addEvent(r);//socketchannel、key一同加入到了events佇列 }
SocketChannel是如何註冊到每個Poller的selector中的?答案在event()方法中,在該方法中遍歷events佇列,依次執行run方法
public boolean events() { while ( (r = (Runnable)events.poll()) != null ) { result = true; r.run(); } …… } public void run() { if ( interestOps == OP_REGISTER ) { socket.getIOChannel().register(socket.getPoller().getSelector(), SelectionKey.OP_READ, key);//註冊到selector中 } else { final SelectionKey key = socket.getIOChannel().keyFor(socket.getPoller().getSelector()); int ops = key.interestOps() | interestOps; att.interestOps(ops); key.interestOps(ops); att.setCometOps(ops);//另外一種註冊方法? }}
……
Poller的執行在其run方法中,主要是將請求封裝成SocketProcessor物件,交給執行緒池處理。
public void run() { if ( keyCount == 0 ) hasEvents = (hasEvents | events());//通過事件機制監控感興趣的網路事件,見上面的events()分析 //遍歷渠道上到來的key,交給processor去處理 Iterator iterator = keyCount > 0 ? selector.selectedKeys().iterator() : null; for() { SelectionKey sk = (SelectionKey) iterator.next(); KeyAttachment attachment = (KeyAttachment)sk.attachment(); processKey(sk, attachment); } }
//processKey(sk, attachment)呼叫processSocket(channel, SocketStatus.OPEN),最終使用Endpoint的執行緒池執行請求
protected boolean processSocket(NioChannel socket, SocketStatus status, boolean dispatch) { KeyAttachment attachment = (KeyAttachment)socket.getAttachment(false); attachment.setCometNotify(false); //will get reset upon next reg sc = new SocketProcessor(socket,status); if ( dispatch ) executor.execute(sc);=====>任務被封裝成SocketProcessor物件,在成功獲取執行緒池後,則通過執行緒池來進行socket資料資料的讀寫操作。 else sc.run(); …… }
SocketProcessor
請求到達socketProcess之後,首先執行其run方法,請求被轉移到handler.process,根據上下文,我們知道這了的hander指的是Http11ConnectionHandler
public void run() { (status==null)?(handler.process(socket)==Handler.SocketState.CLOSED) : (handler.event(socket,status)==Handler.SocketState.CLOSED); }
參考文獻:
http://blog.csdn.net/yanlinwang/