通过Tomcat的Http11NioProtocol源码学习Java NIO设计

Tomcat的Http11NioProtocol协议使用Java NIO技术实现高性能Web服务器。本文通过分析Http11NioProtocol源码来学习Java NIO的使用。从中可以了解到阻塞IO和非阻塞IO的配合,NIO的读写操作以及Selector.wakeup的使用。

1. 初始化阶段

Java NIO服务器端实现的第一步是开启一个新的ServerSocketChannel对象。Http11NioProtocol的实现也不例外, 在NioEndPoint类的init方法可以看到这段代码。

代码1:NioEndPoint.init()方法

publicvoid init() throws Exception { if (initialized ) return; //开启一个新的ServerSocketChannel serverSock = ServerSocketChannel.open(); //设置socket的性能偏好 serverSock.socket().setPerformancePreferences(socketProperties .getPerformanceConnectionTime(), socketProperties.getPerformanceLatency(), socketProperties.getPerformanceBandwidth()); InetSocketAddress addr = ( address!=null ?new InetSocketAddress(address ,port ):new InetSocketAddress(port)); //绑定端口号,并设置backlog serverSock.socket().bind(addr,backlog ); //将serverSock设置成阻塞IO serverSock.configureBlocking(true); //mimic APR behavior //初始化acceptor线程数 if (acceptorThreadCount == 0) { // FIXME: Doesn’t seem to work that well with multiple accept threads acceptorThreadCount = 1; } //初始化poller线程数 if (pollerThreadCount <= 0) { //minimum one poller thread pollerThreadCount = 1; } // 根据需要,初始化SSL // 因为主要关注Java NIO, 所以这一块代码就省略掉了 if (isSSLEnabled()) { …… } //OutOfMemoryError策略 if (oomParachute >0) reclaimParachute(true); //开启NioSelectorPool selectorPool.open(); initialized = true ;}

在NioEndPoint.init方法中,可以看到ServerSocketChannel被设置成阻塞IO,并且没有注册任何就绪事件。这样可以和阻塞ServerSocket一样方便地使用阻塞accept方法来接收客户端新来的连接。但不同的是当NioEndPoint.Accept线程通过accept方法获得一个新的SocketChannel后会构建一个OP_REGISTER类型的PollerEvent事件并放到Poller.events队列中。而我们使用ServerSocket实现服务器的时候,在接收到新连接后,一般是从线程池中取出一个线程来处理这个连接。

在NioEndPoint.Accept的setSocketOptions方法中可以看到获得SocketChannel后的处理过程。步骤如下:

1)将SocketChannel设置成非阻塞;

2)构建OP_REGISTER类型的PollerEvent对象,并放入到Poller.events队列中。

代码2:NioEndPoint.Accept类的setSocketOptions方法

protectedboolean setSocketOptions(SocketChannel socket) { try { //将客户端Socket设置为非阻塞, APR风格 socket.configureBlocking( false); Socket sock = socket.socket(); socketProperties.setProperties(sock); //从缓存中取NioChannel对象,如果取不到直接构建一个 NioChannel channel = nioChannels.poll(); if ( channel == null ) { // 如果sslContext不等于null, 需要启动ssl if (sslContext != null) { …. } //正常tcp启动 else { //构建NioBufferHandler对象 NioBufferHandler bufhandler = new NioBufferHandler(socketProperties .getAppReadBufSize(), socketProperties.getAppWriteBufSize(), socketProperties.getDirectBuffer()); //构建NioChannel对象 channel = new NioChannel(socket, bufhandler); } } else { //从缓存中取的NioChannel对象,将客户端socket设置进去 channel.setIOChannel(socket); if ( channel instanceof SecureNioChannel ) { SSLEngine engine = createSSLEngine(); ((SecureNioChannel)channel).reset(engine); } else { channel.reset(); } } //注册NioChannel对象 getPoller0().register(channel); } catch (Throwable t) { try { log.error("" ,t); } catch ( Throwable tt){} // Tell to close the socket return false ; } return true ;}

Poller线程会从Poller.events队列中取出PollerEvent对象,并运行PollerEvent.run()方法。在PollerEvent.run()方法中发现是OP_REGISTER事件,则会在Poller.selector上注册SocketChannel对象的OP_READ就绪事件。

代码3:PollerEvent.run()方法代码片段

人的一生是奋斗的一生,人们为了取得成功都在不断地努力着,

通过Tomcat的Http11NioProtocol源码学习Java NIO设计

相关文章:

你感兴趣的文章:

标签云: