本篇文章我详细分析一下Http11NioProtocol
关键的代码部分:
public void bind() throws Exception {
serverSock = ServerSocketChannel.open();
socketProperties.setProperties(serverSock.socket());
InetSocketAddress addr = (getAddress()!=null?new InetSocketAddress(getAddress(),getPort()):new InetSocketAddress(getPort()));
serverSock.socket().bind(addr,getAcceptCount());
serverSock.configureBlocking(true); //mimic APR behavior
// Initialize thread count defaults for acceptor, poller
if (acceptorThreadCount == 0) {
// FIXME: Doesn't seem to work that well with multiple accept threads
acceptorThreadCount = 1;
}
if (pollerThreadCount <= 0) {
//minimum one poller thread
pollerThreadCount = 1;
}
setStopLatch(new CountDownLatch(pollerThreadCount));
// Initialize SSL if needed
initialiseSsl();
selectorPool.open();
}
public void startInternal() throws Exception {
if (!running) {
running = true;
paused = false;
processorCache = new SynchronizedStack<>(SynchronizedStack.DEFAULT_SIZE,
socketProperties.getProcessorCache());
eventCache = new SynchronizedStack<>(SynchronizedStack.DEFAULT_SIZE,
socketProperties.getEventCache());
nioChannels = new SynchronizedStack<>(SynchronizedStack.DEFAULT_SIZE,
socketProperties.getBufferPool());
// Create worker collection
if ( getExecutor() == null ) {
createExecutor();
}
initializeConnectionLatch();
// Start poller threads
pollers = new Poller[getPollerThreadCount()];
for (int i=0; i
Acceptor: 作用接受客户端的请求进行封装,并将类型为OP_REGISTER的PollerEvent添加到Poller(轮训器)的SynchronizedQueue实现Runnable接口,开起线程的数量由acceptorThreadCount控制
1.接受客户端的连接 SocketChannel socket = serverSock.accept();
2.对于连接数进行限流 LimitLatch.countUpOrAwait() and LimitLatch.countDown() 参数设置:setMaxConnections
3.对SocketChannel进行socket设置并包装为NioChannel
protected boolean setSocketOptions(SocketChannel socket) {
// Process the connection
try {
//disable blocking, APR style, we are gonna be polling it
socket.configureBlocking(false);
Socket sock = socket.socket();
socketProperties.setProperties(sock);
//nioChannels 为NioChannel对象池
NioChannel channel = nioChannels.pop();
if (channel == null) {
SocketBufferHandler bufhandler = new SocketBufferHandler(
socketProperties.getAppReadBufSize(),
socketProperties.getAppWriteBufSize(),
socketProperties.getDirectBuffer());
if (isSSLEnabled()) {
channel = new SecureNioChannel(socket, bufhandler, selectorPool, this);
} else {
channel = new NioChannel(socket, bufhandler);
}
} else {
channel.setIOChannel(socket);
channel.reset();
}
getPoller0().register(channel);
} catch (Throwable t) {
ExceptionUtils.handleThrowable(t);
// Tell to close the socket
return false;
}
return true;
}
4.按顺序获取轮询器Poller并注册NioChannel
getPoller0().register(channel);
public void register(final NioChannel socket) {
socket.setPoller(this);
NioSocketWrapper ka = new NioSocketWrapper(socket, NioEndpoint.this);
socket.setSocketWrapper(ka);
ka.setPoller(this);
ka.setReadTimeout(getSocketProperties().getSoTimeout());
ka.setWriteTimeout(getSocketProperties().getSoTimeout());
ka.setKeepAliveLeft(NioEndpoint.this.getMaxKeepAliveRequests());
ka.setSecure(isSSLEnabled());
ka.setReadTimeout(getConnectionTimeout());
ka.setWriteTimeout(getConnectionTimeout());
PollerEvent r = eventCache.pop();
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);
}
PollerEvent:作为Poller中SynchronizedQueue的单元,实现Runnable接口,作用是将SocketChannel注册到Poller的Selector上监听socket是否可读
socket.getIOChannel().register(socket.getPoller().getSelector(), SelectionKey.OP_READ, socketWrapper);
Poller(轮训器):实现Runnable接口,开起线程的数量由pollerThreadCount控制,构造函数为this.selector = Selector.open();由一个SynchronizedQueue构成,流程如下:
1.从SynchronizedQueue获取PollerEvent,执行PollerEvent.run方法,监听读事件
2.selector.select()得到可以进行操作的SelectionKeys,遍历进行处理processKey(selectionKey, attachment);
3.判断selectionKey的读写就绪,并封装为SocketProcessorBase,丢进线程池进行处理
processSocket(attachment, SocketEvent.OPEN_READ, true)
public boolean processSocket(SocketWrapperBase socketWrapper,
SocketEvent event, boolean dispatch) {//dispatch是否需要线程池中执行
try {
if (socketWrapper == null) {
return false;
}
SocketProcessorBase sc = processorCache.pop();
if (sc == null) {
sc = createSocketProcessor(socketWrapper, event);
} else {
sc.reset(socketWrapper, event);
}
Executor executor = getExecutor();
if (dispatch && executor != null) {
executor.execute(sc);
} else {
sc.run();
}
} catch (RejectedExecutionException ree) {
getLog().warn(sm.getString("endpoint.executor.fail", socketWrapper) , ree);
return false;
} catch (Throwable t) {
ExceptionUtils.handleThrowable(t);
// This means we got an OOM or similar creating a thread, or that
// the pool and its queue are full
getLog().error(sm.getString("endpoint.process.fail"), t);
return false;
}
return true;
}
SocketProcessor extends SocketProcessorBase
implements Runnable
1.判断是否完成TLS握手,我们使用是非https协议,默认返回true
2.state = getHandler().process(socketWrapper, event); 这个getHandler()返回的是ConnectionHandler,ConnectionHandler创建代码:
public abstract class AbstractHttp11Protocol extends AbstractProtocol {
...
public AbstractHttp11Protocol(AbstractEndpoint endpoint) {
super(endpoint);
setConnectionTimeout(Constants.DEFAULT_CONNECTION_TIMEOUT);
ConnectionHandler cHandler = new ConnectionHandler<>(this);
setHandler(cHandler);
getEndpoint().setHandler(cHandler);
}
...
3.缓存中获取Processor,如果不存在则创建
protected Processor createProcessor() {
Http11Processor processor = new Http11Processor(getMaxHttpHeaderSize(),
getAllowHostHeaderMismatch(), getRejectIllegalHeaderName(), getEndpoint(),
getMaxTrailerSize(), allowedTrailerHeaders, getMaxExtensionSize(),
getMaxSwallowSize(), httpUpgradeProtocols, getSendReasonPhrase(),
relaxedPathChars, relaxedQueryChars);
processor.setAdapter(getAdapter());
processor.setMaxKeepAliveRequests(getMaxKeepAliveRequests());
processor.setConnectionUploadTimeout(getConnectionUploadTimeout());
processor.setDisableUploadTimeout(getDisableUploadTimeout());
processor.setCompressionMinSize(getCompressionMinSize());
processor.setCompression(getCompression());
processor.setNoCompressionUserAgents(getNoCompressionUserAgents());
processor.setCompressibleMimeTypes(getCompressibleMimeTypes());
processor.setRestrictedUserAgents(getRestrictedUserAgents());
processor.setMaxSavePostSize(getMaxSavePostSize());
processor.setServer(getServer());
processor.setServerRemoveAppProvidedValues(getServerRemoveAppProvidedValues());
return processor;
}
4.执行processor.process(wrapper, status);Http11Processor从读取的流拿到数据封装为request,调用Adapter.service就业务数据写入response,调用endRequest就response写入数据流
request:
GET / HTTP/1.1
Host: 127.0.0.1:8080
Connection: keep-alive
Cache-Control: no-cache
User-Agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36
Postman-Token: 7e9c95a5-9333-c2c5-6306-96f4b90cab00
Accept: */*
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9
response:
HTTP/1.1 200
Transfer-Encoding: chunked
Date: Fri, 31 Aug 2018 06:19:15 GMT
Connection: close
f
hello world !!!
0
.
5.执行完成之后close连接,这里为什么关闭:浏览器默认使用http1.1是keep-alive,通过服务器设置配置中maxKeepAliveRequests=1,设置服务器为不支持keep-alive,生产对于keep-alive还是要根据实际需求使用,默认tomcat有对于keep-alive有策略,当持有的数量大于75%时,会主要关闭连接