Tomcat源码分析使用NIO接收HTTP请求(一)----简单实现Acceptor、Poller、PollerEvent
注:本文基于Tomcat8.5撰写
下面的代码主要功能是使用NIO来接收一次http请求,主要包括三个步骤:
1.打开服务器通道接收请求
2.接收请求后注册通道
3.输出请求的内容。
这个示例是极其简单的,在下一小节笔者会在此示例基础上来探讨Tomcat是如何接收http请求的。
public static void main(String[] args) { try { // 打开服务器套接字通道 ServerSocketChannel serverSock = ServerSocketChannel.open(); // 绑定端口 InetSocketAddress address = new InetSocketAddress(8001); serverSock.socket().bind(address); // 设置阻塞模式 serverSock.configureBlocking(true); SocketChannel socket; Selector selector = Selector.open(); ByteBuffer byteBuffer = ByteBuffer.allocate(1000); while(true) { // 接收请求 socket = serverSock.accept(); socket.configureBlocking(false); // 注册通道 socket.register(selector, SelectionKey.OP_READ); int keyCount = selector.selectNow(); // 处理请求 Iterator<SelectionKey> iterator = null; if ( keyCount > 0) { iterator = selector.selectedKeys().iterator(); } while (iterator != null && iterator.hasNext()) { SelectionKey key = iterator.next(); iterator.remove(); if (key.isReadable()) { key.interestOps(key.interestOps() & (~key.readyOps())); SocketChannel channel = (SocketChannel) key.channel(); boolean flag = true; while (flag) { int count = channel.read(byteBuffer); if (count > 0) { flag = false; // 解析请求 System.out.println(new String(byteBuffer.array())); } Thread.sleep(2000); } } } } } catch (Exception e) { e.printStackTrace(); } }
Tomcat接收Http请求
在上一小节中介绍了使用NIO接收Http请求的三个主要步骤,第一步是很简单的,除了使用的bind方法不同以外,其余并没有太大差别。这里着重探讨一下第二步,即Tomcat如何接收一个Http请求。Tomcat使用NIO接收请求的类叫NioEndpoint,在这个类中定义了五个内部类用来处理请求。第一个类叫Acceptor,它的主要作用是等待连接(SocketChannel),当有连接到来时Tomcat会使用PollerEvent对连接进行注册,注册后Tomcat会使用Poller处理请求事件,当Poller获取到请求事件后会将请求交给SocketProcessor类进行解析。以上对Tomcat处理请求的文字描述所对应的代码思路就是上一小节while循环中的注释。到目前为止我们已经知道了四个类,第五个类是NioSocketWrapper,这个主要用来包装SocketChannel。Tomcat会将SocketChannel包装成NioChannel进而在包装成NioSocketWrapper,至此Tomcat处理http请求所需要的五个类就介绍完了,接下来让我们来写几行代码,来实现上述逻辑。
实现Acceptor、Poller、PollerEvent
首先让我们先将目光聚焦在Acceptor、Poller、PollerEvent这三个内部组件。接下来要写的代码主要 目的是理解这三个组件,下面这张图简单描述了我们即将要开始的示例程序。
public class NioEndpoint { protected volatile ServerSocketChannel serverSock = null; protected int pollerThreadCount = Math.min(2,Runtime.getRuntime().availableProcessors()); protected Poller[] pollers = null; private AtomicInteger pollerRotater = new AtomicInteger(0); public static final int OP_REGISTER = 0x100; ...... public Poller getPoller0() { int idx = Math.abs(pollerRotater.incrementAndGet()) % pollers.length; return pollers[idx]; } ...... }
第二步:新建一个bind方法
protected void bind() { try { // 打开服务器套接字通道 serverSock = ServerSocketChannel.open(); // 绑定端口 InetSocketAddress address = new InetSocketAddress(8000); int acceptCount = 10; serverSock.socket().bind(address, acceptCount); // 设置阻塞模式 serverSock.configureBlocking(true); } catch (Exception e) { e.printStackTrace(); } }
第三步:新建Acceptor内部类
protected class Acceptor implements Runnable{ @Override public void run() { SocketChannel socket = null; while (true) { try { // 接收请求 socket = serverSock.accept(); setSocketOptions(socket); Thread.sleep(2000); } catch (Exception e) { e.printStackTrace(); } } } }
第四步:新建setSocketOptions()方法
protected void setSocketOptions(SocketChannel socket) { try { socket.configureBlocking(false); getPoller0().register(socket); } catch (Exception e) { } }
第五步:新建SynchronizedQueue类(这个不是内部类)
public class SynchronizedQueue<T> { public static final int DEFAULT_SIZE = 128; private Object[] queue; private int size; private int insert = 0; private int remove = 0; public SynchronizedQueue() { this(DEFAULT_SIZE); } public SynchronizedQueue(int initialSize) { queue = new Object[initialSize]; size = initialSize; } public synchronized boolean offer(T t) { queue[insert++] = t; // Wrap if (insert == size) { insert = 0; } if (insert == remove) { expand(); } return true; } public synchronized T poll() { if (insert == remove) { // empty return null; } @SuppressWarnings("unchecked") T result = (T) queue[remove]; queue[remove] = null; remove++; // Wrap if (remove == size) { remove = 0; } return result; } private void expand() { int newSize = size * 2; Object[] newQueue = new Object[newSize]; System.arraycopy(queue, insert, newQueue, 0, size - insert); System.arraycopy(queue, 0, newQueue, size - insert, insert); insert = size; remove = 0; queue = newQueue; size = newSize; } public synchronized int size() { int result = insert - remove; if (result < 0) { result += size; } return result; } public synchronized void clear() { queue = new Object[size]; insert = 0; remove = 0; } }
第六步:新建Poller内部类
public class Poller implements Runnable { private Selector selector; private volatile int keyCount = 0; private final SynchronizedQueue<PollerEvent> events = new SynchronizedQueue<>(); public Poller() throws IOException { this.selector = Selector.open(); } @Override public void run() { try { while (true) { events(); keyCount = selector.selectNow(); Iterator<SelectionKey> iterator = null; if ( keyCount > 0) { iterator = selector.selectedKeys().iterator(); } while (iterator != null && iterator.hasNext()) { SelectionKey sk = iterator.next(); iterator.remove(); SocketChannel socket = (SocketChannel) sk.attachment(); // 得到socket用于进行后续处理...... System.out.println(socket.socket()); } } } catch (Exception e) { e.printStackTrace(); } } public void register(final SocketChannel socket) { PollerEvent r = new PollerEvent(socket, this,OP_REGISTER); addEvent(r); } private void addEvent(PollerEvent event) { events.offer(event); } public boolean events() { boolean result = false; PollerEvent pe = null; for (int i = 0, size = events.size(); i < size && (pe = events.poll()) != null; i++ ) { result = true; try { pe.run(); pe.reset(); } catch ( Throwable x ) { } } return result; } public Selector getSelector() { return selector; } }
第七步:新建PollerEvent内部类
public static class PollerEvent implements Runnable { private SocketChannel socket; private int interestOps; private Poller poller; public PollerEvent(SocketChannel socket,Poller poller, int intOps) { reset(socket, poller, intOps); } @Override public void run() { if (interestOps == OP_REGISTER) { try { socket.register( poller.getSelector(), SelectionKey.OP_READ, socket); } catch (Exception e) { } } } public void reset(SocketChannel socket, Poller poller, int intOps) { this.socket = socket; interestOps = intOps; this.poller = poller; } public void reset() { reset(null, null, 0); } }
第八步:实现main方法
public static void main(String[] args) { try { NioEndpoint server = new NioEndpoint(); server.bind(); server.pollers = new Poller[server.pollerThreadCount]; for (int i=0; i<server.pollers.length; i++) { server.pollers[i] = server.new Poller(); Thread pollerThread = new Thread(server.pollers[i]); // 设置线程优先级 pollerThread.setPriority(5); // 设置守护线程 pollerThread.setDaemon(true); pollerThread.start(); } Runnable acceptor = server.new Acceptor(); new Thread(acceptor).start(); } catch (Exception e) { e.printStackTrace(); } }
运行main方法后发送http://localhost:8000/请求结果如下图,在这里仅仅是打印了socket对象。

上面的示例程序是根据Tomcat源码抽简而来,其主要目的是想让读者理解Acceptor、Poller、PollerEvent这三个组件是如何相互影响的。下面的时序图展示一次请求被程序处理的过程。

在示例程序中,我们在main方法中创建了Acceptor等组件,而在Tomcat源码中是在startInternal()方法中创建组件的。读者可以先从该方法开始阅读Tomcat源码,并且把当下的阅读重点放在Acceptor、Poller、PollerEvent这三个组件是如何接收一次请求的,先从整体上有一个大致了解为之后打下基础。在接下来我们需要继续来完善上述代码。
结束!!!