# tomcat概述
# 什么是tomcat(百度百科)
- 一个免费,开放源代码的web应用服务器,属于轻量级应用服务器
- tomcat实际上是Apache服务器的扩展,但它是独立运行的
- 一个servlet和jsp容器
- 是 apache软件基金会Jakarta项目中的一个核心项目,由Apache、Sun和其它一些公司及个人共同开发而成
# 发展历程
- Sun创建的第一个Servlet容器是JavaWebServer;Apache软件基金会同事创建了JServ,它是一个servlet引擎;
- 1999年,Sun将JavaWebServer容器的源码贡献给Apache软件基金会,使得 JavaWebServer和 JServ合并为了Tomcat;
# 主要版本
- Tomcat3.X,1999年对外发布;
- Tomcat4.1,2002年9月;
- tomcat5.0,2003年12月;
- tomcat6.0,2007年2月;
- tomcat7.0,2011年1月;
- tomcat8.0,2014年6月;
- tomcat9.0,2016年;
# tomcat设计--架构设计

# connector(连接器,coyoto模块)
- 连接器、协议、端点、io的关系
//连接器
public class Connector extends LifecycleMBeanBase {
/*xxx: 核心属性:协议处理器
* tomcat有一个默认的实现: coyoteProtocolHandler*/
/*xxx: 它的作用在于: 对 协议 以及 I/O 进行抽象设计*/
protected final ProtocolHandler protocolHandler;
}
/*xxx: 对不同的 协议 以及 I/O 方式 进行适配*/
/*xxx: 提供了对协议的生命周期的模板方法: init,start,pause,resume,stop,destroy等*/
public interface ProtocolHandler {
}
public abstract class AbstractProtocol<S> implements ProtocolHandler,
MBeanRegistration {
/*xxx: 抽象端点*/
/*xxx: 与之处于统一抽象层次的 处理器,由子类进行托管*/
private final AbstractEndpoint<S,?> endpoint;
}
//协议本身,主要处理应用层面的一些参数
public abstract class AbstractHttp11Protocol<S> extends AbstractProtocol<S> {
protected String getProtocolName() {
return "Http";
}
private int maxHttpHeaderSize = 8 * 1024;
//省略其他抽象...
}
//端点,io的载体
public abstract class AbstractEndpoint<S,U> {
}
public class NioEndpoint extends AbstractJsseEndpoint<NioChannel,SocketChannel> {
}
- NIO如何工作
/*xxx: NIO测试用例*/
public class NioStartTest {
/*xxx: tomcat7及以下版本默认使用 bio, tomcat8及以上版本使用 nio*/
/*xxx: bio,nio,aio的简单区别,来自网络: bio是一个连接一个线程(可能只是连接,不管有没有io操作),
nio是一个请求一个线程(具有io请求的连接),
aio是一个有效请求,一个线程(操作系统先把io请求操作完,再通知线程)*/
public static void main(String[] args) throws IOException, InterruptedException {
new Thread(new NioServer()).start();
Thread.sleep(5000);
System.out.println("服务端启动成功,启动客户端");
new Thread(new NioClient()).start();
}
public static class NioServer implements Runnable{
private Selector selector;
private ServerSocketChannel acceptorChannel;
private volatile boolean stop;
public NioServer() throws IOException {
//监听客户端连接
acceptorChannel = ServerSocketChannel.open();
//绑定监听端口
acceptorChannel.bind(new InetSocketAddress(InetAddress.getByName("127.0.0.1"),8055));
//并设置为非阻塞模式
acceptorChannel.configureBlocking(false);
//创建多路复用器,并启动线程
selector= Selector.open();
acceptorChannel.register(selector, SelectionKey.OP_ACCEPT);
}
public void stop(){
this.stop = true;
}
@Override
public void run() {
while (!stop){
try {
selector.select(1000);
Set selectedKeys = selector.selectedKeys();
Iterator iterator = selectedKeys.iterator();
SelectionKey key = null;
while (iterator.hasNext()){
key = (SelectionKey) iterator.next();
iterator.remove();
try {
//这里可以用线程池启线程去单独处理客户端的请求业务,核心在于此处
handleInput(key);
} catch (Exception e) {
if (key != null) {
key.cancel();
if (key.channel() != null)
key.channel().close();
}
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
private void handleInput(SelectionKey key) throws IOException {
if (key.isValid()) {
//根据SelectionKey的操作位进行判断即可获知网络事件的类型,
if (key.isAcceptable()) {
//通过ServerSocketChannel的accept接收客户端的连接请求并创建SocketChannel实例,
//完成上述操作后,相当于完成了TCP的三次握手,TCP物理链路正式建立。
//注意,我们需要将新创建的SocketChannel设置为异步非阻塞,同时也可以对其TCP参数进行设置,
//例如TCP接收和发送缓冲区的大小等,作为入门的例子,没有进行额外的参数设置。
ServerSocketChannel ssc = (ServerSocketChannel) key.channel();
/*xxx: 该方法是阻塞的*/
SocketChannel sc = ssc.accept();
sc.configureBlocking(false);
// Add the new connection to the selector
sc.register(selector, SelectionKey.OP_READ);
}
if (key.isReadable()) {
//首先创建一个ByteBuffer,由于我们事先无法得知客户端发送的码流大小,
//作为例程,我们开辟一个1M的缓冲区。然后调用SocketChannel的read方法读取请求码流。
//注意,由于我们已经将SocketChannel设置为异步非阻塞模式,因此它的read是非阻塞的。
//使用返回值进行判断,看读取到的字节数
SocketChannel sc = (SocketChannel) key.channel();
ByteBuffer readBuffer = ByteBuffer.allocate(1024);
int readBytes = sc.read(readBuffer);
//返回值有以下三种可能的结果
//返回值大于0:读到了字节,对字节进行编解码;
//返回值等于0:没有读取到字节,属于正常场景,忽略;
//返回值为-1:链路已经关闭,需要关闭SocketChannel,释放资源。
if (readBytes > 0) {
//当读取到码流以后,我们进行解码,首先对readBuffer进行flip操作,
//它的作用是将缓冲区当前的limit设置为position,position设置为0,用于后续对缓冲区的读取操作。
//然后根据缓冲区可读的字节个数创建字节数组,
//调用ByteBuffer的get操作将缓冲区可读的字节数组复制到新创建的字节数组中,
//最后调用字符串的构造函数创建请求消息体并打印。
//如果请求指令是"QUERY TIME ORDER"则把服务器的当前时间编码后返回给客户端
readBuffer.flip();
byte[] bytes = new byte[readBuffer.remaining()];
readBuffer.get(bytes);
String body = new String(bytes, "UTF-8");
System.out.println("The server receive order : "
+ body);
String currentTime = "QUERY TIME ORDER"
.equalsIgnoreCase(body) ? new java.util.Date(
System.currentTimeMillis()).toString()
: "BAD ORDER";
//异步发送应答消息给客户端
doWrite(sc, currentTime);
} else if (readBytes < 0) {
// 对端链路关闭
key.cancel();
sc.close();
} else
; // 读到0字节,忽略
}
}
}
private void doWrite(SocketChannel channel, String response)
throws IOException {
//首先将字符串编码成字节数组,根据字节数组的容量创建ByteBuffer,
//调用ByteBuffer的put操作将字节数组复制到缓冲区中,然后对缓冲区进行flip操作,
//最后调用SocketChannel的write方法将缓冲区中的字节数组发送出去。
//需要指出的是,由于SocketChannel是异步非阻塞的,它并不保证一次能够把需要发送的字节数组发送完,
//此时会出现“写半包”问题,我们需要注册写操作,不断轮询Selector将没有发送完的ByteBuffer发送完毕,
//可以通过ByteBuffer的hasRemain()方法判断消息是否发送完成。
//此处仅仅是个简单的入门级例程,没有演示如何处理“写半包”场景。
if (response != null && response.trim().length() > 0) {
byte[] bytes = response.getBytes();
ByteBuffer writeBuffer = ByteBuffer.allocate(bytes.length);
writeBuffer.put(bytes);
writeBuffer.flip();
channel.write(writeBuffer);
}
}
}
public static class NioClient implements Runnable{
private Selector selector;
private SocketChannel socketChannel;
private volatile boolean stop;
public NioClient(){
try {
selector = Selector.open();
socketChannel = SocketChannel.open();
socketChannel.configureBlocking(false);
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void run() {
try {
//连接不成功,可以进行重连操作
doConnect();
} catch (IOException e) {
e.printStackTrace();
}
while (!stop) {
try {
//在循环体中轮询多路复用器Selector,当有就绪的Channel时,执行handleInput(key)方法
selector.select(1000);
Set selectedKeys = selector.selectedKeys();
Iterator it = selectedKeys.iterator();
SelectionKey key = null;
while (it.hasNext()) {
key = (SelectionKey) it.next();
it.remove();
try {
handleInput(key);
} catch (Exception e) {
if (key != null) {
key.cancel();
if (key.channel() != null)
key.channel().close();
}
}
}
} catch (Exception e) {
e.printStackTrace();
System.exit(1);
}
}
//线程退出循环后,我们需要对连接资源进行释放,以实现“优雅退出”.
//由于多路复用器上可能注册成千上万的Channel或者pipe,如果一一对这些资源进行释放显然不合适。
//因此,JDK底层会自动释放所有跟此多路复用器关联的资源。
//多路复用器关闭后,所有注册在上面的Channel和Pipe等资源都会被自动去注册并关闭,所以不需要重复释放资源
if (selector != null)
try {
selector.close();
} catch (IOException e) {
e.printStackTrace();
}
System.out.println("客户端退出.");
}
private void handleInput(SelectionKey key) throws IOException {
//我们首先对SelectionKey进行判断,看它处于什么状态。
if (key.isValid()) {
// 判断是否连接成功
SocketChannel sc = (SocketChannel) key.channel();
//如果是处于连接状态,说明服务端已经返回ACK应答消息。
//这时我们需要对连接结果进行判断,调用SocketChannel的finishConnect()方法,
//如果返回值为true,说明客户端连接成功;如果返回值为false或者直接抛出IOException,说明连接失败。
//在本例程中,返回值为true,说明连接成功。
if (key.isConnectable()) {
if (sc.finishConnect()) {
//将SocketChannel注册到多路复用器上,注册SelectionKey.OP_READ操作位,
//监听网络读操作,然后发送请求消息给服务端。
sc.register(selector, SelectionKey.OP_READ);
doWrite(sc);
} else
System.exit(1);// 连接失败,进程退出
}
//客户端是如何读取时间服务器应答消息的。
if (key.isReadable()) {
//如果客户端接收到了服务端的应答消息,则SocketChannel是可读的,
//由于无法事先判断应答码流的大小,我们就预分配1M的接收缓冲区用于读取应答消息,
//调用SocketChannel的read()方法进行异步读取操作。由于是异步操作,所以必须对读取的结果进行判断。
ByteBuffer readBuffer = ByteBuffer.allocate(1024);
int readBytes = sc.read(readBuffer);
if (readBytes > 0) {
//如果读取到了消息,则对消息进行解码,最后打印结果。执行完成后将stop置为true,线程退出循环。
readBuffer.flip();
byte[] bytes = new byte[readBuffer.remaining()];
readBuffer.get(bytes);
String body = new String(bytes, "UTF-8");
System.out.println("Now is : " + body);
this.stop = true;
} else if (readBytes < 0) {
// 对端链路关闭
key.cancel();
sc.close();
} else
; // 读到0字节,忽略
}
}
}
//首先对SocketChannel的connect()操作进行判断,如果连接成功,
//则将SocketChannel注册到多路复用器Selector上,注册SelectionKey.OP_READ,
//如果没有直接连接成功,则说明服务端没有返回TCP握手应答消息,
//但这并不代表连接失败,我们需要将SocketChannel注册到多路复用器Selector上,
//注册SelectionKey.OP_CONNECT,当服务端返回TCP syn-ack消息后,
//Selector就能够轮询到这个SocketChannel处于连接就绪状态。
private void doConnect() throws IOException {
// 如果直接连接成功,则注册到多路复用器上,发送请求消息,读应答
if (socketChannel.connect(new InetSocketAddress("127.0.0.1", 8055))) {
socketChannel.register(selector, SelectionKey.OP_READ);
doWrite(socketChannel);
} else {
socketChannel.register(selector, SelectionKey.OP_CONNECT);
}
}
//构造请求消息体,然后对其编码,写入到发送缓冲区中,最后调用SocketChannel的write方法进行发送。
//由于发送是异步的,所以会存在“半包写”问题。最后通过hasRemaining()方法对发送结果进行判断,
//如果缓冲区中的消息全部发送完成,打印"Send order 2 server succeed."
private void doWrite(SocketChannel sc) throws IOException {
byte[] req = "QUERY TIME ORDER".getBytes();
ByteBuffer writeBuffer = ByteBuffer.allocate(req.length);
writeBuffer.put(req);
writeBuffer.flip();
sc.write(writeBuffer);
if (!writeBuffer.hasRemaining())
System.out.println("Send order 2 server succeed.");
}
}
}
- NIO工作机制如何被启动
//package org.apache.catalina.core;
public class StandardService extends LifecycleMBeanBase implements Service {
public void addConnector(Connector connector) {
//省略其它抽象...
try {
/*xxx: 在springBoot定制的start方法,会触发该 start条件*/
if (getState().isAvailable()) {
//该方法会开启相应的协议特性
connector.start();
}
} catch (LifecycleException e) {
throw new IllegalArgumentException(
sm.getString("standardService.connector.startFailed", connector), e);
}
// Report this property change to interested listeners
support.firePropertyChange("connector", null, connector);
}
//省略其它抽象...
}
/*xxx: 连接器,对应于协议,用于 处理协议*/
public class Connector extends LifecycleMBeanBase {
@Override
protected void startInternal() throws LifecycleException {
//省略其它抽象...
setState(LifecycleState.STARTING);
try {
protocolHandler.start();
} catch (Exception e) {
throw new LifecycleException(
sm.getString("coyoteConnector.protocolHandlerStartFailed"), e);
}
}
//省略其它抽象...
}
public abstract class AbstractProtocol<S> implements ProtocolHandler,
MBeanRegistration {
@Override
public void start() throws Exception {
//省略其它抽象...
endpoint.start();
//省略其它抽象...
}
//省略其它抽象...
}
public abstract class AbstractEndpoint<S,U> {
//省略其它抽象...
public final void start() throws Exception {
if (bindState == BindState.UNBOUND) {
//开启服务端监听
bindWithCleanup();
bindState = BindState.BOUND_ON_START;
}
startInternal();
}
private void bindWithCleanup() throws Exception {
try {
bind();
} catch (Throwable t) {
// Ensure open sockets etc. are cleaned up if something goes
// wrong during bind
ExceptionUtils.handleThrowable(t);
unbind();
throw t;
}
}
//省略其它抽象...
}
public class NioEndpoint extends AbstractJsseEndpoint<NioChannel,SocketChannel> {
/*xxx: nio的服务端 channel(本类) */
private volatile ServerSocketChannel serverSock = null;
/*xxx: 接收新的连接,并且将它们转为 工作线程 (父类)*/
protected Acceptor<U> acceptor;
/*xxx: 工作线程池(父类)*/
private Executor executor = null;
@Override
public void bind() throws Exception {
initServerSocket();
setStopLatch(new CountDownLatch(1));
// Initialize SSL if needed
initialiseSsl();
selectorPool.open(getName());
}
/*xxx:初始化时,会进行 创建服务端的 nio socketChannel*/
protected void initServerSocket() throws Exception {
if (!getUseInheritedChannel()) {
serverSock = ServerSocketChannel.open();
socketProperties.setProperties(serverSock.socket());
InetSocketAddress addr = new InetSocketAddress(getAddress(), getPortWithOffset());
/*xxx: accept队列的长度,该值作用于操作系统
当accept队列中连接的个数达到acceptCount时,队列满,进来的请求一律被拒绝。默认值是100*/
serverSock.socket().bind(addr,getAcceptCount());
} else {
// Retrieve the channel provided by the OS
Channel ic = System.inheritedChannel();
if (ic instanceof ServerSocketChannel) {
serverSock = (ServerSocketChannel) ic;
}
if (serverSock == null) {
throw new IllegalArgumentException(sm.getString("endpoint.init.bind.inherited"));
}
}
serverSock.configureBlocking(true); //mimic APR behavior
}
//省略其它抽象...
}
//开启监听的流程
public class NioEndpoint extends AbstractJsseEndpoint<NioChannel,SocketChannel> {
private Poller poller = null;
/*xxx: nio的服务端 channel(本类) */
private volatile ServerSocketChannel serverSock = null;
/*xxx: 接收新的连接,并且将它们转为 工作线程 (父类)*/
protected Acceptor<U> acceptor;
/*xxx: 工作线程池(父类)*/
private Executor executor = null;
@Override
public void startInternal() throws Exception {
if (!running) {
running = true;
paused = false;
if (socketProperties.getProcessorCache() != 0) {
processorCache = new SynchronizedStack<>(SynchronizedStack.DEFAULT_SIZE,
socketProperties.getProcessorCache());
}
if (socketProperties.getEventCache() != 0) {
eventCache = new SynchronizedStack<>(SynchronizedStack.DEFAULT_SIZE,
socketProperties.getEventCache());
}
if (socketProperties.getBufferPool() != 0) {
nioChannels = new SynchronizedStack<>(SynchronizedStack.DEFAULT_SIZE,
socketProperties.getBufferPool());
}
// Create worker collection
//创建线程池
if (getExecutor() == null) {
createExecutor();
}
initializeConnectionLatch();
// Start poller thread
poller = new Poller();
Thread pollerThread = new Thread(poller, getName() + "-ClientPoller");
pollerThread.setPriority(threadPriority);
pollerThread.setDaemon(true);
pollerThread.start();
startAcceptorThread();
}
}
protected void startAcceptorThread() {
acceptor = new Acceptor<>(this);
String threadName = getName() + "-Acceptor";
acceptor.setThreadName(threadName);
Thread t = new Thread(acceptor, threadName);
t.setPriority(getAcceptorThreadPriority());
t.setDaemon(getDaemon());
t.start();
}
/*xxx: 创建线程池,*/
public void createExecutor() {
internalExecutor = true;
TaskQueue taskqueue = new TaskQueue();
/*xxx: 工作线程池创建的线程名称,遵循: 协议名-exec-端口-线程号*/
TaskThreadFactory tf = new TaskThreadFactory(getName() + "-exec-", daemon, getThreadPriority());
/*xxx: 创建工作线程池时,线程池最低保持了10个线程,最大可以达到参数设置的线程数,默认情况下是200 */
executor = new ThreadPoolExecutor(getMinSpareThreads(), getMaxThreads(), 60, TimeUnit.SECONDS,taskqueue, tf);
taskqueue.setParent( (ThreadPoolExecutor) executor);
}
/*xxx: tomcat8处理 selector的核心封装方法 */
public class Poller implements Runnable {
private Selector selector;
public Poller() throws IOException {
this.selector = Selector.open();
}
@Override
/*xxx: 多路复用器,进行 客户端活跃线程的筛选 */
public void run() {
// Loop until destroy() is called
while (true) {
boolean hasEvents = false;
try {
if (!close) {
hasEvents = events();
if (wakeupCounter.getAndSet(-1) > 0) {
// If we are here, means we have other stuff to do
// Do a non blocking select
keyCount = selector.selectNow();
} else {
keyCount = selector.select(selectorTimeout);
}
wakeupCounter.set(0);
}
if (close) {
events();
timeout(0, false);
try {
selector.close();
} catch (IOException ioe) {
log.error(sm.getString("endpoint.nio.selectorCloseFail"), ioe);
}
break;
}
} catch (Throwable x) {
ExceptionUtils.handleThrowable(x);
log.error(sm.getString("endpoint.nio.selectorLoopError"), x);
continue;
}
// Either we timed out or we woke up, process events first
if (keyCount == 0) {
hasEvents = (hasEvents | events());
}
Iterator<SelectionKey> iterator =
keyCount > 0 ? selector.selectedKeys().iterator() : null;
// Walk through the collection of ready keys and dispatch
// any active event.
/*xxx: 对客户端的事件进行处理 */
while (iterator != null && iterator.hasNext()) {
SelectionKey sk = iterator.next();
NioSocketWrapper socketWrapper = (NioSocketWrapper) sk.attachment();
// Attachment may be null if another thread has called
// cancelledKey()
if (socketWrapper == null) {
iterator.remove();
} else {
iterator.remove();
processKey(sk, socketWrapper);
}
}
// Process timeouts
timeout(keyCount,hasEvents);
}
getStopLatch().countDown();
}
/*xxx: 处理客户端事件 */
/*xxx: 在进行客户端处理时,多种情况下,都会触发 cancelKey方法,进而导致关闭有效的socket连接 */
protected void processKey(SelectionKey sk, NioSocketWrapper socketWrapper) {
try {
/*xxx: 如果当前的poller已经关闭,则关闭当前的 socket连接 */
if (close) {
cancelledKey(sk, socketWrapper);
} else if (sk.isValid() && socketWrapper != null) {
if (sk.isReadable() || sk.isWritable()) {
if (socketWrapper.getSendfileData() != null) {
processSendfile(sk, socketWrapper, false);
} else {
unreg(sk, socketWrapper, sk.readyOps());
boolean closeSocket = false;
// Read goes before write
if (sk.isReadable()) {
if (socketWrapper.readOperation != null) {
if (!socketWrapper.readOperation.process()) {
closeSocket = true;
}
/*xxx: 处理客户端的 读取事件*/
} else if (!processSocket(socketWrapper, SocketEvent.OPEN_READ, true)) {
closeSocket = true;
}
}
if (!closeSocket && sk.isWritable()) {
if (socketWrapper.writeOperation != null) {
if (!socketWrapper.writeOperation.process()) {
closeSocket = true;
}
/*xxx: 处理客户端的写入事件 */
} else if (!processSocket(socketWrapper, SocketEvent.OPEN_WRITE, true)) {
closeSocket = true;
}
}
/*xxx: 如果读写失败,也需要关闭当前正在读写的 socket*/
if (closeSocket) {
cancelledKey(sk, socketWrapper);
}
/*xxx: 否则,socket保持为当前的长连接,继续参与下一次处理*/
}
}
} else {
// Invalid key
/*xxx: 如果当前的 selectKey非法,也需要关闭 socket*/
cancelledKey(sk, socketWrapper);
}
} catch (CancelledKeyException ckx) {
cancelledKey(sk, socketWrapper);
} catch (Throwable t) {
ExceptionUtils.handleThrowable(t);
log.error(sm.getString("endpoint.nio.keyProcessingError"), t);
}
}
//省略其它抽象...
}
}
//package org.apache.tomcat.util.net;
public class Acceptor<U> implements Runnable {
private final AbstractEndpoint<?,U> endpoint;
public Acceptor(AbstractEndpoint<?,U> endpoint) {
this.endpoint = endpoint;
}
@Override
public void run() {
int errorDelay = 0;
try {
// Loop until we receive a shutdown command
while (!stopCalled) {
// Loop if endpoint is paused
/*xxx: 当endpoint暂停时,就不从 os中获取 客户端socket连接,此时 客户端的 socket是阻塞的*/
while (endpoint.isPaused() && !stopCalled) {
state = AcceptorState.PAUSED;
try {
Thread.sleep(50);
} catch (InterruptedException e) {
// Ignore
}
}
if (stopCalled) {
break;
}
state = AcceptorState.RUNNING;
try {
//if we have reached max connections, wait
/*xxx: 增加endPoint的连接数,如果达到了最大连接数,则阻塞等待*/
/*xxx: 当 endpoint的连接数,小于 最大连接数时,才从队列中,获取socket连接*/
endpoint.countUpOrAwaitConnection();
// Endpoint might have been paused while waiting for latch
// If that is the case, don't accept new connections
if (endpoint.isPaused()) {
continue;
}
U socket = null;
try {
// Accept the next incoming connection from the server
// socket
socket = endpoint.serverSocketAccept();
} catch (Exception ioe) {
// We didn't get a socket
endpoint.countDownConnection();
if (endpoint.isRunning()) {
// Introduce delay if necessary
errorDelay = handleExceptionWithDelay(errorDelay);
// re-throw
throw ioe;
} else {
break;
}
}
// Successful accept, reset the error delay
errorDelay = 0;
// Configure the socket
if (!stopCalled && !endpoint.isPaused()) {
// setSocketOptions() will hand the socket off to
// an appropriate processor if successful
/*xxx: 该方法会将 新的连接 与 selector进行关联*/
if (!endpoint.setSocketOptions(socket)) {
/*xxx: 如果于selector关联失败,则会释放当前有效的 socket连接 */
endpoint.closeSocket(socket);
}
} else {
endpoint.destroySocket(socket);
}
} catch (Throwable t) {
ExceptionUtils.handleThrowable(t);
String msg = sm.getString("endpoint.accept.fail");
// APR specific.
// Could push this down but not sure it is worth the trouble.
if (t instanceof Error) {
Error e = (Error) t;
if (e.getError() == 233) {
// Not an error on HP-UX so log as a warning
// so it can be filtered out on that platform
// See bug 50273
log.warn(msg, t);
} else {
log.error(msg, t);
}
} else {
log.error(msg, t);
}
}
}
} finally {
stopLatch.countDown();
}
state = AcceptorState.ENDED;
}
}
//连接的保持
public class NioEndpoint extends AbstractJsseEndpoint<NioChannel,SocketChannel> {
/*xxx: 暂存所有与当前的socket相连的所有连接,通过 maxConnections 可以进行控制 */
protected Map<U, SocketWrapperBase<S>> connections = new ConcurrentHashMap<>();
@Override
protected boolean setSocketOptions(SocketChannel socket) {
NioSocketWrapper socketWrapper = null;
try {
// Allocate channel and wrapper
NioChannel channel = null;
if (nioChannels != null) {
channel = nioChannels.pop();
}
if (channel == null) {
SocketBufferHandler bufhandler = new SocketBufferHandler(
socketProperties.getAppReadBufSize(),
socketProperties.getAppWriteBufSize(),
socketProperties.getDirectBuffer());
if (isSSLEnabled()) {
channel = new SecureNioChannel(bufhandler, selectorPool, this);
} else {
channel = new NioChannel(bufhandler);
}
}
NioSocketWrapper newWrapper = new NioSocketWrapper(channel, this);
channel.reset(socket, newWrapper);
/*xxx: 将新建的连接进行保存,该变量保存了 tomcat任意时刻的活跃连接数 */
connections.put(socket, newWrapper);
socketWrapper = newWrapper;
// Set socket properties
// Disable blocking, polling will be used
socket.configureBlocking(false);
socketProperties.setProperties(socket.socket());
socketWrapper.setReadTimeout(getConnectionTimeout());
socketWrapper.setWriteTimeout(getConnectionTimeout());
socketWrapper.setKeepAliveLeft(NioEndpoint.this.getMaxKeepAliveRequests());
/*xxx: 将channel 与 selector 进行注册关联 */
poller.register(channel, socketWrapper);
return true;
} catch (Throwable t) {
ExceptionUtils.handleThrowable(t);
try {
log.error(sm.getString("endpoint.socketOptionsError"), t);
} catch (Throwable tt) {
ExceptionUtils.handleThrowable(tt);
}
if (socketWrapper == null) {
destroySocket(socket);
}
}
// Tell to close the socket if needed
return false;
}
}
NIO在tomcat中如何工作
- Poller线程充当Selector工作,完成客户端读写事件
- Acceptor线程负责接收连接请求,连接请求完成,将客户连接缓存并与selector相关联
- NioEndpoint充当ServerSocketChannel角色
某次请求如何进入到容器中
public abstract class AbstractEndpoint<S,U> {
/*xxx: 处理socket,实际上是处理 tcp协议与应用层协议的转换*/
/*xxx: tcp 转换为 http */
public boolean processSocket(SocketWrapperBase<S> socketWrapper,
SocketEvent event, boolean dispatch) {
try {
if (socketWrapper == null) {
return false;
}
/*xxx: 并发请求时,可能有多个线程通过线程池同时运行并调度*/
/*xxx: 这些线程共享,各自通过 SocketProcessor实例进行处理 */
/*xxx: 这些不同的 SocketProcessor实例,共享同一个 handler*/
/*xxx: 这些handler会被多个工作线程共享, 工作线程最少有10个,最大默认是200 */
SocketProcessorBase<S> sc = null;
if (processorCache != null) {
sc = processorCache.pop();
}
if (sc == null) {
sc = createSocketProcessor(socketWrapper, event);
} else {
sc.reset(socketWrapper, event);
}
Executor executor = getExecutor();
if (dispatch && executor != null) {
/*xxx: 通过线程池,响应客户端请求*/
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;
}
}
public class NioEndpoint extends AbstractJsseEndpoint<NioChannel,SocketChannel> {
@Override
protected SocketProcessorBase<NioChannel> createSocketProcessor(
SocketWrapperBase<NioChannel> socketWrapper, SocketEvent event) {
return new SocketProcessor(socketWrapper, event);
}
}
public abstract class SocketProcessorBase<S> implements Runnable {
@Override
/*xxx: 工作线程处理的起点*/
public final void run() {
synchronized (socketWrapper) {
// It is possible that processing may be triggered for read and
// write at the same time. The sync above makes sure that processing
// does not occur in parallel. The test below ensures that if the
// first event to be processed results in the socket being closed,
// the subsequent events are not processed.
if (socketWrapper.isClosed()) {
return;
}
doRun();
}
}
/*xxx: 模板方法,交由子类进行实现 */
protected abstract void doRun();
}
//package org.apache.tomcat.util.net.NioEndpoint;
protected class SocketProcessor extends SocketProcessorBase<NioChannel> {
@Override
protected void doRun() {
NioChannel socket = socketWrapper.getSocket();
//省略其它抽象...
try {
int handshake = -1;
try {
if (handshake == 0) {
SocketState state = SocketState.OPEN;
// Process the request from this socket
System.out.println("handler实例为:"+getHandler()+" 线程为:"+Thread.currentThread().getName());
/*xxx: 握手成功,则处理请求 */
if (event == null) {
state = getHandler().process(socketWrapper, SocketEvent.OPEN_READ);
} else {
state = getHandler().process(socketWrapper, event);
}
if (state == SocketState.CLOSED) {
poller.cancelledKey(socket.getIOChannel().keyFor(poller.getSelector()), socketWrapper);
}
}else{
//省略其他抽象...
}
}catch (Exception e1){
//省略其他抽象...
}
}catch (Exception e2){
//省略其他抽象...
}
}
}
//package org.apache.tomcat.util.net.AbstractEndpoint<S,U>
/*xxx: 请求处理器 */
public static interface Handler<S> {
public SocketState process(SocketWrapperBase<S> socket,
SocketEvent status);
}
//package org.apache.coyote.AbstractProtocol
protected static class ConnectionHandler<S> implements Handler<S> {
@Override
/*xxx: 协议处理器*/
/*xxx: 当前方法是在多线程环境下执行的 */
public SocketState process(SocketWrapperBase<S> wrapper, SocketEvent status) {
S socket = wrapper.getSocket();
/*xxx: 获取当前的协议处理器 */
Processor processor = (Processor) wrapper.getCurrentProcessor();
if (SocketEvent.TIMEOUT == status &&
(processor == null ||
!processor.isAsync() && !processor.isUpgrade() ||
processor.isAsync() && !processor.checkAsyncTimeoutGeneration())) {
// This is effectively a NO-OP
return SocketState.OPEN;
}
SocketState state = SocketState.CLOSED;
/*xxx: 协议处理器处理http请求(多线程环境下) */
state = processor.process(wrapper, status);
return state;
//省略其他抽象,及简化主要逻辑...
}
}
//package org.apache.coyote;
/*xxx: 处理器 */
public interface Processor {
SocketState process(SocketWrapperBase<?> socketWrapper, SocketEvent status) throws IOException;
}
public abstract class AbstractProcessorLight implements Processor {
@Override
/*xxx: 处理请求,多线程环境下*/
public SocketState process(SocketWrapperBase<?> socketWrapper, SocketEvent status)
throws IOException {
SocketState state = SocketState.CLOSED;
Iterator<DispatchType> dispatches = null;
do {
if (dispatches != null) {
DispatchType nextDispatch = dispatches.next();
if (getLog().isDebugEnabled()) {
getLog().debug("Processing dispatch type: [" + nextDispatch + "]");
}
state = dispatch(nextDispatch.getSocketStatus());
if (!dispatches.hasNext()) {
state = checkForPipelinedData(state, socketWrapper);
}
} else if (status == SocketEvent.DISCONNECT) {
// Do nothing here, just wait for it to get recycled
} else if (isAsync() || isUpgrade() || state == SocketState.ASYNC_END) {
state = dispatch(status);
state = checkForPipelinedData(state, socketWrapper);
} else if (status == SocketEvent.OPEN_WRITE) {
// Extra write event likely after async, ignore
state = SocketState.LONG;
} else if (status == SocketEvent.OPEN_READ) {
/*xxx: 处理请求,多线程环境下 */
state = service(socketWrapper);
} else if (status == SocketEvent.CONNECT_FAIL) {
logAccess(socketWrapper);
} else {
// Default to closing the socket if the SocketEvent passed in
// is not consistent with the current state of the Processor
state = SocketState.CLOSED;
}
if (getLog().isDebugEnabled()) {
getLog().debug("Socket: [" + socketWrapper +
"], Status in: [" + status +
"], State out: [" + state + "]");
}
if (isAsync()) {
state = asyncPostProcess();
if (getLog().isDebugEnabled()) {
getLog().debug("Socket: [" + socketWrapper +
"], State after async post processing: [" + state + "]");
}
}
if (dispatches == null || !dispatches.hasNext()) {
// Only returns non-null iterator if there are
// dispatches to process.
dispatches = getIteratorAndClearDispatches();
}
} while (state == SocketState.ASYNC_END ||
dispatches != null && state != SocketState.CLOSED);
return state;
}
//省略其它抽象...
/*xxx: 模板方法,处理请求, 多线程环境*/
protected abstract SocketState service(SocketWrapperBase<?> socketWrapper) throws IOException;
}
public class Http11Processor extends AbstractProcessor {
//(父类)
protected final Request request=new org.apache.coyote.Request();
//(父类)
protected final Response response=new org.apache.coyote.Response();
/*xxx: 通过该属性,与Mapper,MapperListener发生关联,用于按照 servlet规范查找对应容器 (service)*/
//父类
protected final Adapter adapter;
@Override
/*xxx: http协议处理器, tomcat的处理核心部分 (多线程环境)*/
public SocketState service(SocketWrapperBase<?> socketWrapper)
throws IOException {
RequestInfo rp = request.getRequestProcessor();
rp.setStage(org.apache.coyote.Constants.STAGE_PARSE);
// Setting up the I/O
setSocketWrapper(socketWrapper);
// Flags
keepAlive = true;
openSocket = false;
readComplete = true;
boolean keptAlive = false;
SendfileState sendfileState = SendfileState.DONE;
while (!getErrorState().isError() && keepAlive && !isAsync() && upgradeToken == null &&
sendfileState == SendfileState.DONE && !protocol.isPaused()) {
//省略其他抽象...
// Has an upgrade been requested?
if (isConnectionToken(request.getMimeHeaders(), "upgrade")) {
// Check the protocol
String requestedProtocol = request.getHeader("Upgrade");
UpgradeProtocol upgradeProtocol = protocol.getUpgradeProtocol(requestedProtocol);
if (upgradeProtocol != null) {
if (upgradeProtocol.accept(request)) {
response.setStatus(HttpServletResponse.SC_SWITCHING_PROTOCOLS);
response.setHeader("Connection", "Upgrade");
response.setHeader("Upgrade", requestedProtocol);
action(ActionCode.CLOSE, null);
getAdapter().log(request, response, 0);
InternalHttpUpgradeHandler upgradeHandler =
upgradeProtocol.getInternalUpgradeHandler(
socketWrapper, getAdapter(), cloneRequest(request));
UpgradeToken upgradeToken = new UpgradeToken(upgradeHandler, null, null);
action(ActionCode.UPGRADE, upgradeToken);
return SocketState.UPGRADING;
}
}
}
//省略其他抽象...
getAdapter().service(request, response);
//省略其他抽象...
}
rp.setStage(org.apache.coyote.Constants.STAGE_ENDED);
if (getErrorState().isError() || (protocol.isPaused() && !isAsync())) {
return SocketState.CLOSED;
} else if (isAsync()) {
return SocketState.LONG;
} else if (isUpgrade()) {
return SocketState.UPGRADING;
} else {
if (sendfileState == SendfileState.PENDING) {
return SocketState.SENDFILE;
} else {
if (openSocket) {
if (readComplete) {
return SocketState.OPEN;
} else {
return SocketState.LONG;
}
} else {
return SocketState.CLOSED;
}
}
}
}
public Adapter getAdapter() {
return adapter;
}
}
//package org.apache.coyote;
public interface Adapter {
/*xxx: 调用适配器进行http服务的处理*/
public void service(Request req, Response res) throws Exception;
}
/*xxx: 适配器,用于找到请求对应的 组件*/
public class CoyoteAdapter implements Adapter {
private final Connector connector;
@Override
/*xxx: 可以看成tomcat的核心流程的处理起点 多线程环境*/
public void service(org.apache.coyote.Request req, org.apache.coyote.Response res)
throws Exception {
//省略其它抽象...
//进入到容器部分逻辑
connector.getService().getContainer().getPipeline().getFirst().invoke(
request, response);
//省略其它抽象...
}
}
# Engine(容器,catalina模块)
- 容器的构造逻辑
/*xxx: 连接器,对应于协议,用于 处理协议, 一个具有生命周期的组件*/
public class Connector extends LifecycleMBeanBase {
/*xxx: 如果service进行维护外,本身也持有对应的组件信息*/
protected Service service = null;
public Service getService() {
return this.service;
}
}
public interface Service extends Lifecycle {
public Engine getContainer();
}
/*xxx: 声明周期,super抽象,用于将整个应用流程进行统一管理*/
/*xxx: 主要由一些特定的事件,以及相应的 生命周期函数组成*/
/*xxx: 直接子类有: Server,service,container,executor
* WebResource,WebappClassLoader*/
public interface Lifecycle {
/*xxx: 能够添加生命周期监听器*/
public void addLifecycleListener(LifecycleListener listener);
public void init() throws LifecycleException;
public void start() throws LifecycleException;
public void stop() throws LifecycleException;
public void destroy() throws LifecycleException;
}
/*xxx: 抽象组件,所有组件都具有的公共属性及模板方法 */
/*xxx: 抽象组件对内置的组件具有一定的耦合性*/
public interface Container extends Lifecycle {
/*xxx:获取管道*/
public Pipeline getPipeline();
/*xxx: 获取父组件*/
public Container getParent();
public void addChild(Container child);
}
/*xxx: pipeline 与 valve 不是 javax.servlet标准的接口,而是tomcat独有的*/
/*xxx: Tomcat定义了 Pipeline 和 Valve两个接口,前者用于构造职责链,后者代表职责链的每个处理器*/
public interface Pipeline extends Contained {
public Valve[] getValves();
}
public interface Engine extends Container {
/*xxx: 持有Service,双向绑定*/
public Service getService();
}
public class StandardEngine extends ContainerBase implements Engine {
@Override
public void addChild(Container child) {
if (!(child instanceof Host))
throw new IllegalArgumentException
(sm.getString("standardEngine.notHost"));
super.addChild(child);
}
}
/*xxx: 就host组件而言,它的核心功能在于自身,而非抽象组件*/
public interface Host extends Container {
/*xxx: host组件,具有一个基本的 应用路径*/
public String getAppBase();
/*xxx: host组件,具有自动部署的能力 */
public boolean getAutoDeploy();
/*xxx: 启动时自动部署的的能力*/
public boolean getDeployOnStartup();
}
public class StandardHost extends ContainerBase implements Host {
@Override
public void addChild(Container child) {
if (!(child instanceof Context))
throw new IllegalArgumentException
(sm.getString("standardHost.notContext"));
child.addLifecycleListener(new MemoryLeakTrackingListener());
// Avoid NPE for case where Context is defined in server.xml with only a
// docBase
Context context = (Context) child;
if (context.getPath() == null) {
ContextName cn = new ContextName(context.getDocBase(), true);
context.setPath(cn.getPath());
}
super.addChild(child);
}
}
public interface Context extends Container, ContextBind {
/*xxx: 获取编码*/
public String getCharset(Locale locale);
/*xxx: 是否允许 cookie作为session track模式*/
public boolean getCookies();
/*xxx: 获取当前web应用的 上下文路径*/
public String getPath();
/*xxx: 获取servlet上下文*/
public ServletContext getServletContext();
/*xxx: servlet包装器*/
public Wrapper createWrapper();
/*xxx: 管理器*/
public Manager getManager();
/*xxx: cookie处理器*/
public CookieProcessor getCookieProcessor();
}
public class StandardContext extends ContainerBase
implements Context, NotificationEmitter {
@Override
public void addChild(Container child) {
// Global JspServlet
Wrapper oldJspServlet = null;
if (!(child instanceof Wrapper)) {
throw new IllegalArgumentException
(sm.getString("standardContext.notWrapper"));
}
boolean isJspServlet = "jsp".equals(child.getName());
// Allow webapp to override JspServlet inherited from global web.xml.
if (isJspServlet) {
oldJspServlet = (Wrapper) findChild("jsp");
if (oldJspServlet != null) {
removeChild(oldJspServlet);
}
}
super.addChild(child);
if (isJspServlet && oldJspServlet != null) {
/*
* The webapp-specific JspServlet inherits all the mappings
* specified in the global web.xml, and may add additional ones.
*/
String[] jspMappings = oldJspServlet.findMappings();
for (int i=0; jspMappings!=null && i<jspMappings.length; i++) {
addServletMappingDecoded(jspMappings[i], child.getName());
}
}
}
}
/*xxx: Wrapper组件,逻辑上为Context的直接子类,其是用于处理 Servlet规范 的直观实现
* 从代码中可以分析出: 一个Wrapper对应一个Servlet,Wrapper可以由多个映射进行对应
* 一个servlet可以有多个实例*/
public interface Wrapper extends Container {
/*xxx: 获取启动的加载顺序*/
public int getLoadOnStartup();
/*xxx: 获取当前Wrapper对应的ServletClass的完整类名,
* 换言之,一个Wrappper对应一个Servlet*/
public String getServletClass();
/*xxx: 获取当前关联的servlet实例*/
public Servlet getServlet();
/*xxx: 为当前的Wrapper添加映射*/
public void addMapping(String mapping);
/*xxx: 释放 当前servlet的所有实例*/
public void unload() throws ServletException;
}
public class StandardWrapper extends ContainerBase
implements ServletConfig, Wrapper, NotificationEmitter {
@Override
public void addChild(Container child) {
throw new IllegalStateException
(sm.getString("standardWrapper.notChild"));
}
}
- 容器的启动逻辑
public class TomcatServletWebServerFactory extends AbstractServletWebServerFactory implements ConfigurableTomcatWebServerFactory {
@Override
public WebServer getWebServer(ServletContextInitializer... initializers) {
Tomcat tomcat = new Tomcat();
File baseDir = (this.baseDirectory != null) ? this.baseDirectory : createTempDir("tomcat");
tomcat.setBaseDir(baseDir.getAbsolutePath());
Connector connector = new Connector(this.protocol);
tomcat.getService().addConnector(connector);
customizeConnector(connector);
tomcat.setConnector(connector);
/*xxx: host组件,用的默认的 standardHost组件*/
tomcat.getHost().setAutoDeploy(false);
configureEngine(tomcat.getEngine());
for (Connector additionalConnector : this.additionalTomcatConnectors) {
tomcat.getService().addConnector(additionalConnector);
}
prepareContext(tomcat.getHost(), initializers);
return getTomcatWebServer(tomcat);
}
}
/*xxx: 提供一种轻量级的扩展方式启动 tomcat,使得应用可以内嵌运行*/
public class Tomcat {
public Host getHost() {
Engine engine = getEngine();
if (engine.findChildren().length > 0) {
return (Host) engine.findChildren()[0];
}
Host host = new StandardHost();
host.setName(hostname);
getEngine().addChild(host);
return host;
}
public Engine getEngine() {
Service service = getServer().findServices()[0];
if (service.getContainer() != null) {
return service.getContainer();
}
Engine engine = new StandardEngine();
engine.setName( "Tomcat" );
engine.setDefaultHost(hostname);
engine.setRealm(createDefaultRealm());
service.setContainer(engine);
return engine;
}
public Server getServer() {
if (server != null) {
return server;
}
System.setProperty("catalina.useNaming", "false");
server = new StandardServer();
initBaseDir();
// Set configuration source
ConfigFileLoader.setSource(new CatalinaBaseConfigurationSource(new File(basedir), null));
server.setPort( -1 );
Service service = new StandardService();
service.setName("Tomcat");
server.addService(service);
return server;
}
}
public class TomcatServletWebServerFactory extends AbstractServletWebServerFactory implements ConfigurableTomcatWebServerFactory {
protected void prepareContext(Host host, ServletContextInitializer[] initializers) {
File documentRoot = getValidDocumentRoot();
/*xxx: 是 StandardContext的变体*/
TomcatEmbeddedContext context = new TomcatEmbeddedContext();
if (documentRoot != null) {
context.setResources(new LoaderHidingResourceRoot(context));
}
context.setName(getContextPath());
context.setDisplayName(getDisplayName());
context.setPath(getContextPath());
File docBase = (documentRoot != null) ? documentRoot : createTempDir("tomcat-docbase");
context.setDocBase(docBase.getAbsolutePath());
context.addLifecycleListener(new Tomcat.FixContextListener());
context.setParentClassLoader((this.resourceLoader != null) ? this.resourceLoader.getClassLoader()
: ClassUtils.getDefaultClassLoader());
resetDefaultLocaleMapping(context);
addLocaleMappings(context);
context.setUseRelativeRedirects(false);
try {
context.setCreateUploadTargets(true);
}
catch (NoSuchMethodError ex) {
// Tomcat is < 8.5.39. Continue.
}
configureTldSkipPatterns(context);
WebappLoader loader = new WebappLoader(context.getParentClassLoader());
loader.setLoaderClass(TomcatEmbeddedWebappClassLoader.class.getName());
loader.setDelegate(true);
context.setLoader(loader);
if (isRegisterDefaultServlet()) {
addDefaultServlet(context);
//如果不注册默认的servlet,则需要自定义,20211013扩展, 相当于在外置tomcat进行与springWebMVC的集成
}else {
addCustomServlet(context);
}
/*xxx: springSession的过滤器,需要在 spirngSecurity之前*/
if (isSpringSessionEnvironment){
addSpringSessionFilter(context);
}
if (isSpringSecurityEnvironment){
addSpringSecurityFilter(context);
}
if (shouldRegisterJspServlet()) {
addJspServlet(context);
addJasperInitializer(context);
}
context.addLifecycleListener(new StaticResourceConfigurer(context));
ServletContextInitializer[] initializersToUse = mergeInitializers(initializers);
/*xxx: 将 context添加为 host的子组件*/
host.addChild(context);
/*xxx: 为 context 添加 Initializers*/
configureContext(context, initializersToUse);
postProcessContext(context);
}
}
public class StandardEngine extends ContainerBase implements Engine {
public StandardEngine() {
super();
pipeline.setBasic(new StandardEngineValve());
/* Set the jmvRoute using the system property jvmRoute */
try {
setJvmRoute(System.getProperty("jvmRoute"));
} catch(Exception ex) {
log.warn(sm.getString("standardEngine.jvmRouteFail"));
}
// By default, the engine will hold the reloading thread
backgroundProcessorDelay = 10;
}
}
final class StandardEngineValve extends ValveBase {'
@Override
public final void invoke(Request request, Response response)
throws IOException, ServletException {
// Select the Host to be used for this Request
/*xxx: 在连接器阶段,已经将请求所需要对应的host进行了映射*/
/*xxx: 如果当前的容器没有设置 host,则也会报404错误, 是tomcat容器层面报的*/
Host host = request.getHost();
if (host == null) {
// HTTP 0.9 or HTTP 1.0 request without a host when no default host
// is defined.
// Don't overwrite an existing error
if (!response.isError()) {
response.sendError(404);
}
return;
}
if (request.isAsyncSupported()) {
request.setAsyncSupported(host.getPipeline().isAsyncSupported());
}
// Ask this Host to process this request
host.getPipeline().getFirst().invoke(request, response);
}
}
public class StandardHost extends ContainerBase implements Host {
public StandardHost() {
super();
pipeline.setBasic(new StandardHostValve());
}
}
final class StandardHostValve extends ValveBase {
@Override
public final void invoke(Request request, Response response)
throws IOException, ServletException {
// Select the Context to be used for this Request
/*xxx: 从请求中获取 context容器 , 在连接器阶段,已经将该请求,所需要使用到的容器进行了映射*/
Context context = request.getContext();
if (context == null) {
// Don't overwrite an existing error
if (!response.isError()) {
response.sendError(404);
}
return;
}
if (request.isAsyncSupported()) {
request.setAsyncSupported(context.getPipeline().isAsyncSupported());
}
boolean asyncAtStart = request.isAsync();
try {
context.bind(Globals.IS_SECURITY_ENABLED, MY_CLASSLOADER);
if (!asyncAtStart && !context.fireRequestInitEvent(request.getRequest())) {
// Don't fire listeners during async processing (the listener
// fired for the request that called startAsync()).
// If a request init listener throws an exception, the request
// is aborted.
return;
}
// Ask this Context to process this request. Requests that are
// already in error must have been routed here to check for
// application defined error pages so DO NOT forward them to the the
// application for processing.
try {
if (!response.isErrorReportRequired()) {
context.getPipeline().getFirst().invoke(request, response);
}
} catch (Throwable t) {
ExceptionUtils.handleThrowable(t);
container.getLogger().error("Exception Processing " + request.getRequestURI(), t);
// If a new error occurred while trying to report a previous
// error allow the original error to be reported.
if (!response.isErrorReportRequired()) {
request.setAttribute(RequestDispatcher.ERROR_EXCEPTION, t);
throwable(request, response, t);
}
}
// Now that the request/response pair is back under container
// control lift the suspension so that the error handling can
// complete and/or the container can flush any remaining data
response.setSuspended(false);
Throwable t = (Throwable) request.getAttribute(RequestDispatcher.ERROR_EXCEPTION);
// Protect against NPEs if the context was destroyed during a
// long running request.
if (!context.getState().isAvailable()) {
return;
}
// Look for (and render if found) an application level error page
if (response.isErrorReportRequired()) {
// If an error has occurred that prevents further I/O, don't waste time
// producing an error report that will never be read
AtomicBoolean result = new AtomicBoolean(false);
response.getCoyoteResponse().action(ActionCode.IS_IO_ALLOWED, result);
if (result.get()) {
if (t != null) {
throwable(request, response, t);
} else {
status(request, response);
}
}
}
if (!request.isAsync() && !asyncAtStart) {
context.fireRequestDestroyEvent(request.getRequest());
}
} finally {
// Access a session (if present) to update last accessed time, based
// on a strict interpretation of the specification
if (ACCESS_SESSION) {
request.getSession(false);
}
context.unbind(Globals.IS_SECURITY_ENABLED, MY_CLASSLOADER);
}
}
}
public class StandardContext extends ContainerBase
implements Context, NotificationEmitter {
public StandardContext() {
super();
pipeline.setBasic(new StandardContextValve());
broadcaster = new NotificationBroadcasterSupport();
// Set defaults
if (!Globals.STRICT_SERVLET_COMPLIANCE) {
// Strict servlet compliance requires all extension mapped servlets
// to be checked against welcome files
resourceOnlyServlets.add("jsp");
}
}
}
final class StandardContextValve extends ValveBase {
@Override
public final void invoke(Request request, Response response)
throws IOException, ServletException {
// Disallow any direct access to resources under WEB-INF or META-INF
/*xxx: 上下文路径,不能使用 META-INF 或者 WEB-INF,否则报错*/
MessageBytes requestPathMB = request.getRequestPathMB();
if ((requestPathMB.startsWithIgnoreCase("/META-INF/", 0))
|| (requestPathMB.equalsIgnoreCase("/META-INF"))
|| (requestPathMB.startsWithIgnoreCase("/WEB-INF/", 0))
|| (requestPathMB.equalsIgnoreCase("/WEB-INF"))) {
response.sendError(HttpServletResponse.SC_NOT_FOUND);
return;
}
// Select the Wrapper to be used for this Request
/*xxx: 如果没有与当前请求相匹配的Wrapper,也会报错*/
Wrapper wrapper = request.getWrapper();
if (wrapper == null || wrapper.isUnavailable()) {
response.sendError(HttpServletResponse.SC_NOT_FOUND);
return;
}
// Acknowledge the request
try {
response.sendAcknowledgement(ContinueResponseTiming.IMMEDIATELY);
} catch (IOException ioe) {
container.getLogger().error(sm.getString(
"standardContextValve.acknowledgeException"), ioe);
request.setAttribute(RequestDispatcher.ERROR_EXCEPTION, ioe);
response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
return;
}
if (request.isAsyncSupported()) {
request.setAsyncSupported(wrapper.getPipeline().isAsyncSupported());
}
wrapper.getPipeline().getFirst().invoke(request, response);
}
}
public class StandardWrapper extends ContainerBase
implements ServletConfig, Wrapper, NotificationEmitter {
public StandardWrapper() {
super();
swValve=new StandardWrapperValve();
pipeline.setBasic(swValve);
broadcaster = new NotificationBroadcasterSupport();
}
}
final class StandardWrapperValve
extends ValveBase {
@Override
public final void invoke(Request request, Response response)
throws IOException, ServletException {
// Initialize local variables we may need
boolean unavailable = false;
Throwable throwable = null;
// This should be a Request attribute...
long t1=System.currentTimeMillis();
requestCount.incrementAndGet();
StandardWrapper wrapper = (StandardWrapper) getContainer();
Servlet servlet = null;
Context context = (Context) wrapper.getParent();
// Check for the application being marked unavailable
if (!context.getState().isAvailable()) {
response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE,
sm.getString("standardContext.isUnavailable"));
unavailable = true;
}
// Check for the servlet being marked unavailable
if (!unavailable && wrapper.isUnavailable()) {
container.getLogger().info(sm.getString("standardWrapper.isUnavailable",
wrapper.getName()));
long available = wrapper.getAvailable();
if ((available > 0L) && (available < Long.MAX_VALUE)) {
response.setDateHeader("Retry-After", available);
response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE,
sm.getString("standardWrapper.isUnavailable",
wrapper.getName()));
} else if (available == Long.MAX_VALUE) {
response.sendError(HttpServletResponse.SC_NOT_FOUND,
sm.getString("standardWrapper.notFound",
wrapper.getName()));
}
unavailable = true;
}
// Allocate a servlet instance to process this request
try {
if (!unavailable) {
/*xxx: 默认的 servlet从这个方法而来*/
servlet = wrapper.allocate();
}
} catch (UnavailableException e) {
container.getLogger().error(
sm.getString("standardWrapper.allocateException",
wrapper.getName()), e);
long available = wrapper.getAvailable();
if ((available > 0L) && (available < Long.MAX_VALUE)) {
response.setDateHeader("Retry-After", available);
response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE,
sm.getString("standardWrapper.isUnavailable",
wrapper.getName()));
} else if (available == Long.MAX_VALUE) {
response.sendError(HttpServletResponse.SC_NOT_FOUND,
sm.getString("standardWrapper.notFound",
wrapper.getName()));
}
} catch (ServletException e) {
container.getLogger().error(sm.getString("standardWrapper.allocateException",
wrapper.getName()), StandardWrapper.getRootCause(e));
throwable = e;
exception(request, response, e);
} catch (Throwable e) {
ExceptionUtils.handleThrowable(e);
container.getLogger().error(sm.getString("standardWrapper.allocateException",
wrapper.getName()), e);
throwable = e;
exception(request, response, e);
servlet = null;
}
MessageBytes requestPathMB = request.getRequestPathMB();
DispatcherType dispatcherType = DispatcherType.REQUEST;
if (request.getDispatcherType()==DispatcherType.ASYNC) dispatcherType = DispatcherType.ASYNC;
request.setAttribute(Globals.DISPATCHER_TYPE_ATTR,dispatcherType);
request.setAttribute(Globals.DISPATCHER_REQUEST_PATH_ATTR,
requestPathMB);
// Create the filter chain for this request
/*xxx: 从这里,开始进入 Servlet规范,进行处理*/
ApplicationFilterChain filterChain =
ApplicationFilterFactory.createFilterChain(request, wrapper, servlet);
// Call the filter chain for this request
// NOTE: This also calls the servlet's service() method
Container container = this.container;
try {
if ((servlet != null) && (filterChain != null)) {
// Swallow output if needed
if (context.getSwallowOutput()) {
try {
SystemLogHandler.startCapture();
if (request.isAsyncDispatching()) {
request.getAsyncContextInternal().doInternalDispatch();
} else {
filterChain.doFilter(request.getRequest(),
response.getResponse());
}
} finally {
String log = SystemLogHandler.stopCapture();
if (log != null && log.length() > 0) {
context.getLogger().info(log);
}
}
} else {
if (request.isAsyncDispatching()) {
request.getAsyncContextInternal().doInternalDispatch();
} else {
filterChain.doFilter
(request.getRequest(), response.getResponse());
}
}
}
} catch (ClientAbortException | CloseNowException e) {
if (container.getLogger().isDebugEnabled()) {
container.getLogger().debug(sm.getString(
"standardWrapper.serviceException", wrapper.getName(),
context.getName()), e);
}
throwable = e;
exception(request, response, e);
} catch (IOException e) {
container.getLogger().error(sm.getString(
"standardWrapper.serviceException", wrapper.getName(),
context.getName()), e);
throwable = e;
exception(request, response, e);
} catch (UnavailableException e) {
container.getLogger().error(sm.getString(
"standardWrapper.serviceException", wrapper.getName(),
context.getName()), e);
// throwable = e;
// exception(request, response, e);
wrapper.unavailable(e);
long available = wrapper.getAvailable();
if ((available > 0L) && (available < Long.MAX_VALUE)) {
response.setDateHeader("Retry-After", available);
response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE,
sm.getString("standardWrapper.isUnavailable",
wrapper.getName()));
} else if (available == Long.MAX_VALUE) {
response.sendError(HttpServletResponse.SC_NOT_FOUND,
sm.getString("standardWrapper.notFound",
wrapper.getName()));
}
// Do not save exception in 'throwable', because we
// do not want to do exception(request, response, e) processing
} catch (ServletException e) {
Throwable rootCause = StandardWrapper.getRootCause(e);
if (!(rootCause instanceof ClientAbortException)) {
container.getLogger().error(sm.getString(
"standardWrapper.serviceExceptionRoot",
wrapper.getName(), context.getName(), e.getMessage()),
rootCause);
}
throwable = e;
exception(request, response, e);
} catch (Throwable e) {
ExceptionUtils.handleThrowable(e);
container.getLogger().error(sm.getString(
"standardWrapper.serviceException", wrapper.getName(),
context.getName()), e);
throwable = e;
exception(request, response, e);
} finally {
// Release the filter chain (if any) for this request
if (filterChain != null) {
filterChain.release();
}
// Deallocate the allocated servlet instance
try {
if (servlet != null) {
wrapper.deallocate(servlet);
}
} catch (Throwable e) {
ExceptionUtils.handleThrowable(e);
container.getLogger().error(sm.getString("standardWrapper.deallocateException",
wrapper.getName()), e);
if (throwable == null) {
throwable = e;
exception(request, response, e);
}
}
// If this servlet has been marked permanently unavailable,
// unload it and release this instance
try {
if ((servlet != null) &&
(wrapper.getAvailable() == Long.MAX_VALUE)) {
wrapper.unload();
}
} catch (Throwable e) {
ExceptionUtils.handleThrowable(e);
container.getLogger().error(sm.getString("standardWrapper.unloadException",
wrapper.getName()), e);
if (throwable == null) {
exception(request, response, e);
}
}
long t2=System.currentTimeMillis();
long time=t2-t1;
processingTime += time;
if( time > maxTime) maxTime=time;
if( time < minTime) minTime=time;
}
}
}
- 容器如何运行及初始化
public class Tomcat {
public void start() throws LifecycleException {
getServer();
server.start();
}
}
public final class StandardServer extends LifecycleMBeanBase implements Server {
@Override
protected void startInternal() throws LifecycleException {
fireLifecycleEvent(CONFIGURE_START_EVENT, null);
setState(LifecycleState.STARTING);
globalNamingResources.start();
// Start our defined Services
synchronized (servicesLock) {
for (Service service : services) {
service.start();
}
}
if (periodicEventDelay > 0) {
monitorFuture = getUtilityExecutor().scheduleWithFixedDelay(
new Runnable() {
@Override
public void run() {
startPeriodicLifecycleEvent();
}
}, 0, 60, TimeUnit.SECONDS);
}
}
}
public class StandardService extends LifecycleMBeanBase implements Service {
@Override
protected void startInternal() throws LifecycleException {
if(log.isInfoEnabled())
log.info(sm.getString("standardService.start.name", this.name));
setState(LifecycleState.STARTING);
// Start our defined Container first
if (engine != null) {
synchronized (engine) {
engine.start();
}
}
synchronized (executors) {
for (Executor executor: executors) {
executor.start();
}
}
mapperListener.start();
// Start our defined Connectors second
synchronized (connectorsLock) {
for (Connector connector: connectors) {
// If it has already failed, don't try and start it
if (connector.getState() != LifecycleState.FAILED) {
connector.start();
}
}
}
}
}
public abstract class ContainerBase extends LifecycleMBeanBase
implements Container {
@Override
protected synchronized void startInternal() throws LifecycleException {
// Start our subordinate components, if any
logger = null;
getLogger();
/*xxx: 如果配置了集群组件,则启动*/
Cluster cluster = getClusterInternal();
if (cluster instanceof Lifecycle) {
((Lifecycle) cluster).start();
}
/*xxx: 如果配置了安全组件,则启动*/
Realm realm = getRealmInternal();
if (realm instanceof Lifecycle) {
((Lifecycle) realm).start();
}
// Start our child containers, if any
/*xxx: 启动子节点,如果有的话*/
Container children[] = findChildren();
List<Future<Void>> results = new ArrayList<>();
for (Container child : children) {
results.add(startStopExecutor.submit(new StartChild(child)));
}
MultiThrowable multiThrowable = null;
for (Future<Void> result : results) {
try {
result.get();
} catch (Throwable e) {
log.error(sm.getString("containerBase.threadedStartFailed"), e);
if (multiThrowable == null) {
multiThrowable = new MultiThrowable();
}
multiThrowable.add(e);
}
}
if (multiThrowable != null) {
throw new LifecycleException(sm.getString("containerBase.threadedStartFailed"),
multiThrowable.getThrowable());
}
// Start the Valves in our pipeline (including the basic), if any
/*xxx: 如果有pipeline组件,则启动*/
if (pipeline instanceof Lifecycle) {
((Lifecycle) pipeline).start();
}
/*xxx: 设置 主机状态为 starting */
/*xxx: 此时会触发 start_event生命周期事件*/
/*xxx: hostConfig监听了该事件,会进行自动部署*/
setState(LifecycleState.STARTING);
// Start our thread
/*xxx: 启动 主机层级的后台任务处理 包括: 集群的心跳检测,安全组件后台任务处理,pipeline的后台任务处理*/
if (backgroundProcessorDelay > 0) {
monitorFuture = Container.getService(ContainerBase.this).getServer()
.getUtilityExecutor().scheduleWithFixedDelay(
new ContainerBackgroundProcessorMonitor(), 0, 60, TimeUnit.SECONDS);
}
}
}
public class StandardEngine extends ContainerBase implements Engine {
@Override
protected synchronized void startInternal() throws LifecycleException {
// Log our server identification information
if (log.isInfoEnabled()) {
log.info(sm.getString("standardEngine.start", ServerInfo.getServerInfo()));
}
// Standard container startup
super.startInternal();
}
}
public class StandardHost extends ContainerBase implements Host {
@Override
protected synchronized void startInternal() throws LifecycleException {
// Set error report valve
/*xxx: 首先,添加一个 全局的错误处理Valve,在 web.xml没有添加错误页面时,tomcat返回的异常栈就是由这个valve生成的*/
String errorValve = getErrorReportValveClass();
if ((errorValve != null) && (!errorValve.equals(""))) {
try {
boolean found = false;
Valve[] valves = getPipeline().getValves();
for (Valve valve : valves) {
if (errorValve.equals(valve.getClass().getName())) {
found = true;
break;
}
}
if(!found) {
Valve valve = ErrorReportValve.class.getName().equals(errorValve) ?
new ErrorReportValve() :
(Valve) Class.forName(errorValve).getConstructor().newInstance();
getPipeline().addValve(valve);
}
} catch (Throwable t) {
ExceptionUtils.handleThrowable(t);
log.error(sm.getString(
"standardHost.invalidErrorReportValveClass",
errorValve), t);
}
}
/*xxx: 然后调用父类的方法启动虚拟主机*/
super.startInternal();
}
}
public class StandardContext extends ContainerBase
implements Context, NotificationEmitter {
@Override
/*xxx: context的启动逻辑,比较复杂*/
protected synchronized void startInternal() throws LifecycleException {
// Send j2ee.state.starting notification
if (this.getObjectName() != null) {
Notification notification = new Notification("j2ee.state.starting",
this.getObjectName(), sequenceNumber.getAndIncrement());
/*xxx:1. 发布正在启动的jmx通知*/
broadcaster.sendNotification(notification);
}
setConfigured(false);
boolean ok = true;
// Currently this is effectively a NO-OP but needs to be called to
// ensure the NamingResources follows the correct lifecycle
if (namingResources != null) {
/*xxx: 2.启动当前上下文维护的jndi资源*/
namingResources.start();
}
/*xxx: 7.初始化临时目录,默认为 work/engine名称/主机名称/上下文名称*/
// Post work directory
postWorkDirectory();
// Add missing components as necessary
if (getResources() == null) { // (1) Required by Loader
try {
/*xxx: 3.初始化当前上下文使用的 webResourceRoot并启动,用于管理目录资源*/
/*xxx: 查找资源时,按照指定顺序处理: pre资源 -> main资源(web-inf/classess,web-inf/lib)->jar资源->post资源*/
setResources(new StandardRoot(this));
} catch (IllegalArgumentException e) {
log.error(sm.getString("standardContext.resourcesInit"), e);
ok = false;
}
}
if (ok) {
resourcesStart();
}
if (getLoader() == null) {
/*xxx: 4.创建web应用类加载器*/
WebappLoader webappLoader = new WebappLoader();
webappLoader.setDelegate(getDelegate());
setLoader(webappLoader);
}
/*xxx: 5.设置cookie处理器*/
if (cookieProcessor == null) {
cookieProcessor = new Rfc6265CookieProcessor();
}
/*xxx: 6.获取字符集映射*/
getCharsetMapper();
/*xxx: 8.web应用的依赖检测,主要检测依赖扩展点完整性*/
boolean dependencyCheck = true;
try {
dependencyCheck = ExtensionValidator.validateApplication
(getResources(), this);
} catch (IOException ioe) {
log.error(sm.getString("standardContext.extensionValidationError"), ioe);
dependencyCheck = false;
}
if (!dependencyCheck) {
// do not make application available if dependency check fails
ok = false;
}
// Reading the "catalina.useNaming" environment variable
String useNamingProperty = System.getProperty("catalina.useNaming");
if ((useNamingProperty != null)
&& (useNamingProperty.equals("false"))) {
useNaming = false;
}
/*xxx: 9.如果当前上下文使用JNDI,则为其添加 NamingContextListener*/
if (ok && isUseNaming()) {
if (getNamingContextListener() == null) {
NamingContextListener ncl = new NamingContextListener();
ncl.setName(getNamingContextName());
ncl.setExceptionOnFailedWrite(getJndiExceptionOnFailedWrite());
addLifecycleListener(ncl);
setNamingContextListener(ncl);
}
}
// Binding thread
ClassLoader oldCCL = bindThread();
if (ok) {
// Start our subordinate components, if any
Loader loader = getLoader();
/*xxx: 10启动web应用类加载器*/
if (loader instanceof Lifecycle) {
//省略部分抽象...
Realm realm = getRealmInternal();
if(null != realm) {
if (realm instanceof Lifecycle) {
/*xxx:11.启动安全组件,realm*/
((Lifecycle) realm).start();
}
//省略部分抽象...
}
/*xxx:12,发布 configure_start_event事件, ContextConfig监听该事件,以完成 Servlet的创建*/
fireLifecycleEvent(Lifecycle.CONFIGURE_START_EVENT, null);
for (Container child : findChildren()) {
if (!child.getState().isAvailable()) {
/*xxx: 13.启动Context的子节点,即 Wrapper*/
child.start();
}
}
if (pipeline instanceof Lifecycle) {
/*xxx: 14.启动Context维护的pipeline*/
((Lifecycle) pipeline).start();
}
// Acquire clustered manager
Manager contextManager = null;
Manager manager = getManager();
if (manager == null) {
if ((getCluster() != null) && distributable) {
/*xxx:15 创建会话管理器,如果配置了集群组件,则由集群组件创建,否则使用标准的会话管理器*/
try {
contextManager = getCluster().createManager(getName());
} catch (Exception ex) {
log.error(sm.getString("standardContext.cluster.managerError"), ex);
ok = false;
}
} else {
contextManager = new StandardManager();
}
}
// Configure default manager if none was specified
if (contextManager != null) {
setManager(contextManager);
}
if (manager!=null && (getCluster() != null) && distributable) {
//let the cluster know that there is a context that is distributable
//and that it has its own manager
getCluster().registerManager(manager);
}
}
if (!getConfigured()) {
log.error(sm.getString("standardContext.configurationFail"));
ok = false;
}
// We put the resources into the servlet context
if (ok) {
/*xxx: 16.将 Context的web资源集合,添加到 ServletContext的属性,名为 org.apache.catalina.resources*/
getServletContext().setAttribute
(Globals.RESOURCES_ATTR, getResources());
if (getInstanceManager() == null) {
/*xxx: 17.创建实例管理器,用于创建对象实例,如Servlet,Filter等*/
setInstanceManager(createInstanceManager());
}
getServletContext().setAttribute(
InstanceManager.class.getName(), getInstanceManager());
InstanceManagerBindings.bind(getLoader().getClassLoader(), getInstanceManager());
/*xxx: 18.将 jar包扫描器 添加到 ServletContext属性,属性名为 org.apache.tomcat.JarScanner属性*/
getServletContext().setAttribute(
JarScanner.class.getName(), getJarScanner());
// Make the version info available
getServletContext().setAttribute(Globals.WEBAPP_VERSION, getWebappVersion());
}
/*xxx: 19.合并 servletContext初始化参数 和 Context组件中的ApplicationParameter,根据applicationParameter的配置决定是否可以覆盖同名参数*/
mergeParameters();
// Call ServletContainerInitializers
for (Entry<ServletContainerInitializer, Set<Class<?>>> entry :
initializers.entrySet()) {
try {
/*xxx: 20.启动添加到当前上下文的 ServletContainerInitializer,该类的实例,由 ContextConfig查找并添加*/
/*xxx: 该类主要用于 以可编程的方式添加Web应用的配置,如Servlet,Filter等 */
/*xxx: 注意,这里就似乎 SPI机制的体现了*/
entry.getKey().onStartup(entry.getValue(),
getServletContext());
} catch (ServletException e) {
log.error(sm.getString("standardContext.sciFail"), e);
ok = false;
break;
}
}
if (ok) {
/*xxx: 21.实例化应用监听器 ,分为事件监听器,一级生命周期监听器, 这些监听器 可以通过 Context部署描述文件,可编程方式,或者web.xml添加*/
if (!listenerStart()) {
log.error(sm.getString("standardContext.listenerFail"));
ok = false;
}
}
if (ok) {
/*xxx: 22.检测未覆盖的 HTTP方法的安全约束*/
checkConstraintsForUncoveredMethods(findConstraints());
}
try {
// Start manager
/*xxx: 23.启动会话管理器*/
Manager manager = getManager();
if (manager instanceof Lifecycle) {
((Lifecycle) manager).start();
}
} catch(Exception e) {
log.error(sm.getString("standardContext.managerFail"), e);
ok = false;
}
// Configure and call application filters
if (ok) {
/*xxx: 24.实例化 FilterConfig,Filter,并调用 Filter.init初始化*/
if (!filterStart()) {
log.error(sm.getString("standardContext.filterFail"));
ok = false;
}
}
// Load and initialize all "load on startup" servlets
if (ok) {
/*xxx: 25.对于 loadOnStartup>=0的包装器,调用 wrapper.load,该方法负责实例化Servlet,并调用 Servlet.init进行初始化*/
if (!loadOnStartup(findChildren())){
log.error(sm.getString("standardContext.servletFail"));
ok = false;
}
}
/*xxx: 26.启动后台定时处理线程,只有当 backgroundProcessorDelay>0时才启动*/
super.threadStart();
}
startTime=System.currentTimeMillis();
// Send j2ee.state.running notification
if (ok && (this.getObjectName() != null)) {
/*xxx: 27.发布正在运行的jmx通知*/
Notification notification =
new Notification("j2ee.state.running", this.getObjectName(),
sequenceNumber.getAndIncrement());
broadcaster.sendNotification(notification);
}
/*xxx: 28.调用 webResourceRoot.gc释放资源(加载资源时,为了提高性能会缓存某些信息,该方法用于清理这些资源,如关闭jar文件)*/
getResources().gc();
// Reinitializing if something went wrong
if (!ok) {
/*xxx:29.设置上下文的状态*/
setState(LifecycleState.FAILED);
// Send j2ee.object.failed notification
if (this.getObjectName() != null) {
Notification notification = new Notification("j2ee.object.failed",
this.getObjectName(), sequenceNumber.getAndIncrement());
broadcaster.sendNotification(notification);
}
} else {
setState(LifecycleState.STARTING);
}
}
}
public class StandardWrapper extends ContainerBase
implements ServletConfig, Wrapper, NotificationEmitter {
@Override
protected synchronized void startInternal() throws LifecycleException {
// Send j2ee.state.starting notification
if (this.getObjectName() != null) {
Notification notification = new Notification("j2ee.state.starting",
this.getObjectName(),
sequenceNumber++);
broadcaster.sendNotification(notification);
}
// Start up this component
super.startInternal();
setAvailable(0L);
// Send j2ee.state.running notification
if (this.getObjectName() != null) {
Notification notification =
new Notification("j2ee.state.running", this.getObjectName(),
sequenceNumber++);
broadcaster.sendNotification(notification);
}
}
}
- 常用容器
- StandardServer
- 与tomcat容器等价,一个tomcat包括单个server
- StandardService
- 一个server包含单个service,service持有一个Connector和一个Engine
- StandardEngine
- 标准引擎,是host的父类
- StandardEngineValve
- 标准引擎阀门,运行逻辑向子容器流转
- StandardHost
- 标准主机,是engine的子容器,context的父容器
- 实例化主机时,会反向实例化engine(如果还未实例化)
- StandardHostValve
- 标准主机阀门,运行逻辑向子容器流转
- StandardContext
- 标准上下文,是标准主机的子容器,Wrapper的父容器
- StandardContextValve
- 标准上下文阀门,运行逻辑向子容器流转
- StandardWrapper
- 标准包装器,是context的子容器
- 其不再被允许添加子容器
- StandardWrapperValve
- 标准包装器阀门,运行逻辑向servlet规范流转
- StandardServer
# tomcat设计--运行逻辑
# 请求的转换
/*xxx: 适配器,用于找到请求对应的 组件*/
public class CoyoteAdapter implements Adapter {
@Override
/*xxx: 可以看成tomcat的核心流程的处理起点 多线程环境*/
public void service(org.apache.coyote.Request req, org.apache.coyote.Response res)
throws Exception {
/*xxx:1.根据Connector的请求和响应对象,创建Servlet请求和响应对象*/
Request request = (Request) req.getNote(ADAPTER_NOTES);
Response response = (Response) res.getNote(ADAPTER_NOTES);
/*xxx: 传入的参数允许为空,为空时,则通过 连接器创建相应的request,response*/
if (request == null) {
// Create objects
request = connector.createRequest();
request.setCoyoteRequest(req);
response = connector.createResponse();
response.setCoyoteResponse(res);
// Link objects
request.setResponse(response);
response.setRequest(request);
// Set as notes
req.setNote(ADAPTER_NOTES, request);
res.setNote(ADAPTER_NOTES, response);
// Set query string encoding
req.getParameters().setQueryStringCharset(connector.getURICharset());
}
boolean postParseSuccess = false;
/*xxx: 2.进行请求后置解析,转换请求参数,并完成请求映射, 该映射,是 tomcat层面的映射,比如 根据url映射到 host,context,wrapper等*/
postParseSuccess = postParseRequest(req, request, res, response);
//省略其它抽象...
}
}
/*xxx: 连接器,对应于协议,用于 处理协议, 一个具有生命周期的组件*/
public class Connector extends LifecycleMBeanBase {
public Request createRequest() {
return new Request(this);
}
}
public class Request implements HttpServletRequest {
public Request(Connector connector) {
this.connector = connector;
formats = new SimpleDateFormat[formatsTemplate.length];
for(int i = 0; i < formats.length; i++) {
formats[i] = (SimpleDateFormat) formatsTemplate[i].clone();
}
}
}
# 容器的映射
/*xxx: 适配器,用于找到请求对应的 组件*/
public class CoyoteAdapter implements Adapter {
@Override
/*xxx: 可以看成tomcat的核心流程的处理起点 多线程环境*/
public void service(org.apache.coyote.Request req, org.apache.coyote.Response res)
throws Exception {
//省略其它抽象...
boolean postParseSuccess = false;
/*xxx: 2.进行请求后置解析,转换请求参数,并完成请求映射, 该映射,是 tomcat层面的映射,比如 根据url映射到 host,context,wrapper等*/
postParseSuccess = postParseRequest(req, request, res, response);
//省略其它抽象...
}
/*xxx: 后置解析请求方法*/
/*xxx: 通过整个方法,tomcat确保得到的Context符合如下要求:
* 匹配请求路径
* 如果为有效会话,则包含会话的最新版本
* 如没有有效会话,则为所有匹配请求的最新版本
* context必须是有效的(非暂停状态)*/
protected boolean postParseRequest(org.apache.coyote.Request req, Request request,
org.apache.coyote.Response res, Response response) throws IOException, ServletException {
/*xxx: 请求RUI节码,初始化请求的路径参数*/
MessageBytes undecodedURI = req.requestURI();
// Check for ping OPTIONS * request
if (undecodedURI.equals("*")) {
/*xxx: 检测URI是否合法,如果非法,则返回响应码400*/
if (req.method().equalsIgnoreCase("OPTIONS")) {
StringBuilder allow = new StringBuilder();
allow.append("GET, HEAD, POST, PUT, DELETE, OPTIONS");
// Trace if allowed
if (connector.getAllowTrace()) {
allow.append(", TRACE");
}
res.setHeader("Allow", allow.toString());
// Access log entry as processing won't reach AccessLogValve
connector.getService().getContainer().logAccess(request, response, 0, true);
return false;
} else {
response.sendError(400, "Invalid URI");
}
}
/*xxx:进行请求映射 */
MessageBytes serverName;
if (connector.getUseIPVHosts()) {
serverName = req.localName();
if (serverName.isNull()) {
// well, they did ask for it
res.action(ActionCode.REQ_LOCAL_NAME_ATTRIBUTE, null);
}
} else {
serverName = req.serverName();
}
boolean mapRequired = true;
while (mapRequired) {
/*xxx: 通过绑定的service,进行映射, 映射规则为: 主机名,uri,servlet版本号,映射数据*/
/*xxx: 请求映射处理最终 会根据 URI定位到 一个有效的 Wrapper, 该wrapper会处理好 request的映射信息*/
connector.getService().getMapper().map(serverName, decodedURI,
version, request.getMappingData());
if (request.getContext() == null) {
return true;
}
String sessionID;
/*xxx: 这一步为 恢复SessionId */
/*xxx: 无论是url中的session,还是 cookie中的session,最终都会 设置到当前 request中去 */
/*xxx: 默认情况下,tracking-mode包括了两种形式,即 cookie 以及 url, 可以通过配置,动态配置相应特性*/
if (request.getServletContext().getEffectiveSessionTrackingModes()
.contains(SessionTrackingMode.URL)) {
// Get the session ID if there was one
/*xxx: 获取servlet请求的session信息,从路径中获取,这是tomcat本身就支持的方式*/
sessionID = request.getPathParameter(
SessionConfig.getSessionUriParamName(
request.getContext()));
if (sessionID != null) {
request.setRequestedSessionId(sessionID);
request.setRequestedSessionURL(true);
}
}
// Look for session ID in cookies and SSL session
try {
/*xxx: 从cookie中以及 ssl上下文中,获取sessionId*/
parseSessionCookiesId(request);
} catch (IllegalArgumentException e) {
// Too many cookies
if (!response.isError()) {
response.setError();
response.sendError(400);
}
return true;
}
/*xxx: 最后,也可从 ssl中,恢复sessionId*/
parseSessionSslId(request);
/*xxx: 获取sessionId信息*/
sessionID = request.getRequestedSessionId();
mapRequired = false;
if (version != null && request.getContext() == versionContext) {
// We got the version that we asked for. That is it.
} else {
version = null;
versionContext = null;
Context[] contexts = request.getMappingData().contexts;
/*xxx: 因为同一个web应用,可能版本号不一致,因此要根据 sessionId首先去查找*/
if (contexts != null && sessionID != null) {
// Find the context associated with the session
for (int i = contexts.length; i > 0; i--) {
Context ctxt = contexts[i - 1];
/*xxx: 原生tomcat中,每个context都有一个 manager用于管理session*/
if (ctxt.getManager().findSession(sessionID) != null) {
if (!ctxt.equals(request.getMappingData().context)) {
// Set version so second time through mapping
// the correct context is found
version = ctxt.getWebappVersion();
versionContext = ctxt;
// Reset mapping
request.getMappingData().recycle();
mapRequired = true;
request.recycleSessionInfo();
request.recycleCookieInfo(true);
}
break;
}
}
}
}
if (!mapRequired && request.getContext().getPaused()) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// Should never happen
}
// Reset mapping
request.getMappingData().recycle();
mapRequired = true;
}
}
}
}
/*xxx: 二级抽象: 用于维护 连接器 与 组件 的关系 ,多个 连接器(协议) 对应于一个 组件
* xxx: 在这里,它的名称叫组件,实际上指的是 Engine,用于 处理请求
* xxx: 在jvm中,对程序的执行,实际上 也是通过一个 Engine完成,它本质上是一个有限自动机*/
public interface Service extends Lifecycle {
/*xxx:这个也是一个关键属性,映射器,用于请求查找*/
Mapper getMapper();
}
/*xxx: 映射器,用于将请求与容器(host,context,Wrapper)进行映射*/
public final class Mapper {
public void map(MessageBytes host, MessageBytes uri, String version,
MappingData mappingData) throws IOException {
if (host.isNull()) {
String defaultHostName = this.defaultHostName;
if (defaultHostName == null) {
return;
}
host.getCharChunk().append(defaultHostName);
}
host.toChars();
uri.toChars();
/*xxx: 内部映射*/
internalMap(host.getCharChunk(), uri.getCharChunk(), version, mappingData);
}
private final void internalMap(CharChunk host, CharChunk uri,
String version, MappingData mappingData) throws IOException {
if (mappingData.host != null) {
throw new AssertionError();
}
// Virtual host mapping
MappedHost[] hosts = this.hosts;
/*xxx: 找到虚拟主机*/
MappedHost mappedHost = exactFindIgnoreCase(hosts, host);
if (mappedHost == null) {
int firstDot = host.indexOf('.');
if (firstDot > -1) {
int offset = host.getOffset();
try {
host.setOffset(firstDot + offset);
mappedHost = exactFindIgnoreCase(hosts, host);
} finally {
// Make absolutely sure this gets reset
host.setOffset(offset);
}
}
if (mappedHost == null) {
mappedHost = defaultHost;
if (mappedHost == null) {
return;
}
}
}
mappingData.host = mappedHost.object;
if (uri.isNull()) {
// Can't map context or wrapper without a uri
return;
}
uri.setLimit(-1);
// Context mapping
/*xxx: 根据uri进行 应用映射*/
ContextList contextList = mappedHost.contextList;
MappedContext[] contexts = contextList.contexts;
int pos = find(contexts, uri);
if (pos == -1) {
return;
}
int lastSlash = -1;
int uriEnd = uri.getEnd();
int length = -1;
boolean found = false;
MappedContext context = null;
while (pos >= 0) {
context = contexts[pos];
if (uri.startsWith(context.name)) {
length = context.name.length();
if (uri.getLength() == length) {
found = true;
break;
} else if (uri.startsWithIgnoreCase("/", length)) {
found = true;
break;
}
}
if (lastSlash == -1) {
lastSlash = nthSlash(uri, contextList.nesting + 1);
} else {
lastSlash = lastSlash(uri);
}
uri.setEnd(lastSlash);
pos = find(contexts, uri);
}
uri.setEnd(uriEnd);
if (!found) {
if (contexts[0].name.equals("")) {
context = contexts[0];
} else {
context = null;
}
}
if (context == null) {
return;
}
mappingData.contextPath.setString(context.name);
ContextVersion contextVersion = null;
ContextVersion[] contextVersions = context.versions;
final int versionCount = contextVersions.length;
if (versionCount > 1) {
Context[] contextObjects = new Context[contextVersions.length];
for (int i = 0; i < contextObjects.length; i++) {
contextObjects[i] = contextVersions[i].object;
}
mappingData.contexts = contextObjects;
if (version != null) {
contextVersion = exactFind(contextVersions, version);
}
}
if (contextVersion == null) {
contextVersion = contextVersions[versionCount - 1];
}
mappingData.context = contextVersion.object;
mappingData.contextSlashCount = contextVersion.slashCount;
// Wrapper mapping
/*xxx: 最后进行 servlet的映射查找*/
if (!contextVersion.isPaused()) {
internalMapWrapper(contextVersion, uri, mappingData);
}
}
private final void internalMapWrapper(ContextVersion contextVersion,
CharChunk path,
MappingData mappingData) throws IOException {
int pathOffset = path.getOffset();
int pathEnd = path.getEnd();
boolean noServletPath = false;
int length = contextVersion.path.length();
if (length == (pathEnd - pathOffset)) {
noServletPath = true;
}
int servletPath = pathOffset + length;
path.setOffset(servletPath);
/*xxx: 首先,进行精确匹配*/
MappedWrapper[] exactWrappers = contextVersion.exactWrappers;
internalMapExactWrapper(exactWrappers, path, mappingData);
/*xxx: 其次,进行前缀匹配*/
boolean checkJspWelcomeFiles = false;
MappedWrapper[] wildcardWrappers = contextVersion.wildcardWrappers;
if (mappingData.wrapper == null) {
internalMapWildcardWrapper(wildcardWrappers, contextVersion.nesting,
path, mappingData);
if (mappingData.wrapper != null && mappingData.jspWildCard) {
char[] buf = path.getBuffer();
if (buf[pathEnd - 1] == '/') {
mappingData.wrapper = null;
checkJspWelcomeFiles = true;
} else {
// See Bugzilla 27704
mappingData.wrapperPath.setChars(buf, path.getStart(),
path.getLength());
mappingData.pathInfo.recycle();
}
}
}
if(mappingData.wrapper == null && noServletPath &&
contextVersion.object.getMapperContextRootRedirectEnabled()) {
// The path is empty, redirect to "/"
path.append('/');
pathEnd = path.getEnd();
mappingData.redirectPath.setChars
(path.getBuffer(), pathOffset, pathEnd - pathOffset);
path.setEnd(pathEnd - 1);
return;
}
/*xxx: 然后,进行 扩展名 匹配*/
MappedWrapper[] extensionWrappers = contextVersion.extensionWrappers;
if (mappingData.wrapper == null && !checkJspWelcomeFiles) {
internalMapExtensionWrapper(extensionWrappers, path, mappingData,
true);
}
// Rule 4 -- Welcome resources processing for servlets
/*xxx: 然后,进行欢迎资源处理的servlet映射*/
if (mappingData.wrapper == null) {
boolean checkWelcomeFiles = checkJspWelcomeFiles;
if (!checkWelcomeFiles) {
char[] buf = path.getBuffer();
checkWelcomeFiles = (buf[pathEnd - 1] == '/');
}
if (checkWelcomeFiles) {
for (int i = 0; (i < contextVersion.welcomeResources.length)
&& (mappingData.wrapper == null); i++) {
path.setOffset(pathOffset);
path.setEnd(pathEnd);
path.append(contextVersion.welcomeResources[i], 0,
contextVersion.welcomeResources[i].length());
path.setOffset(servletPath);
// Rule 4a -- Welcome resources processing for exact macth
internalMapExactWrapper(exactWrappers, path, mappingData);
// Rule 4b -- Welcome resources processing for prefix match
if (mappingData.wrapper == null) {
internalMapWildcardWrapper
(wildcardWrappers, contextVersion.nesting,
path, mappingData);
}
// Rule 4c -- Welcome resources processing
// for physical folder
if (mappingData.wrapper == null
&& contextVersion.resources != null) {
String pathStr = path.toString();
WebResource file =
contextVersion.resources.getResource(pathStr);
if (file != null && file.isFile()) {
internalMapExtensionWrapper(extensionWrappers, path,
mappingData, true);
if (mappingData.wrapper == null
&& contextVersion.defaultWrapper != null) {
mappingData.wrapper =
contextVersion.defaultWrapper.object;
mappingData.requestPath.setChars
(path.getBuffer(), path.getStart(),
path.getLength());
mappingData.wrapperPath.setChars
(path.getBuffer(), path.getStart(),
path.getLength());
mappingData.requestPath.setString(pathStr);
mappingData.wrapperPath.setString(pathStr);
}
}
}
}
path.setOffset(servletPath);
path.setEnd(pathEnd);
}
}
if (mappingData.wrapper == null) {
boolean checkWelcomeFiles = checkJspWelcomeFiles;
if (!checkWelcomeFiles) {
char[] buf = path.getBuffer();
checkWelcomeFiles = (buf[pathEnd - 1] == '/');
}
if (checkWelcomeFiles) {
for (int i = 0; (i < contextVersion.welcomeResources.length)
&& (mappingData.wrapper == null); i++) {
path.setOffset(pathOffset);
path.setEnd(pathEnd);
path.append(contextVersion.welcomeResources[i], 0,
contextVersion.welcomeResources[i].length());
path.setOffset(servletPath);
internalMapExtensionWrapper(extensionWrappers, path,
mappingData, false);
}
path.setOffset(servletPath);
path.setEnd(pathEnd);
}
}
// Rule 7 -- Default servlet
/*xxx: 最后,采用 默认的servlet*/
if (mappingData.wrapper == null && !checkJspWelcomeFiles) {
if (contextVersion.defaultWrapper != null) {
mappingData.wrapper = contextVersion.defaultWrapper.object;
mappingData.requestPath.setChars
(path.getBuffer(), path.getStart(), path.getLength());
mappingData.wrapperPath.setChars
(path.getBuffer(), path.getStart(), path.getLength());
mappingData.matchType = MappingMatch.DEFAULT;
}
// Redirection to a folder
char[] buf = path.getBuffer();
if (contextVersion.resources != null && buf[pathEnd -1 ] != '/') {
String pathStr = path.toString();
if (contextVersion.object.getMapperDirectoryRedirectEnabled()) {
WebResource file;
// Handle context root
if (pathStr.length() == 0) {
file = contextVersion.resources.getResource("/");
} else {
file = contextVersion.resources.getResource(pathStr);
}
if (file != null && file.isDirectory()) {
path.setOffset(pathOffset);
path.append('/');
mappingData.redirectPath.setChars
(path.getBuffer(), path.getStart(), path.getLength());
} else {
mappingData.requestPath.setString(pathStr);
mappingData.wrapperPath.setString(pathStr);
}
} else {
mappingData.requestPath.setString(pathStr);
mappingData.wrapperPath.setString(pathStr);
}
}
}
path.setOffset(pathOffset);
path.setEnd(pathEnd);
}
}
# Valve如何流转
- 标准Valve流转 ---流转至子容器的Pipeline的第一个valve
final class StandardEngineValve extends ValveBase {
@Override
public final void invoke(Request request, Response response)
throws IOException, ServletException {
/*xxx: 在连接器阶段,已经将请求所需要对应的host进行了映射*/
/*xxx: 如果当前的容器没有设置 host,则也会报404错误, 是tomcat容器层面报的*/
Host host = request.getHost();
host.getPipeline().getFirst().invoke(request, response);
}
}
final class StandardHostValve extends ValveBase {
@Override
public final void invoke(Request request, Response response)
throws IOException, ServletException {
/*xxx: 从请求中获取 context容器 , 在连接器阶段,已经将该请求,所需要使用到的容器进行了映射*/
Context context = request.getContext();
context.getPipeline().getFirst().invoke(request, response);
}
}
final class StandardContextValve extends ValveBase {
@Override
public final void invoke(Request request, Response response)
throws IOException, ServletException {
/*xxx: 如果没有与当前请求相匹配的Wrapper,也会报错*/
Wrapper wrapper = request.getWrapper();
if (wrapper == null || wrapper.isUnavailable()) {
response.sendError(HttpServletResponse.SC_NOT_FOUND);
return;
}
wrapper.getPipeline().getFirst().invoke(request, response);
}
}
final class StandardWrapperValve
extends ValveBase {
@Override
public final void invoke(Request request, Response response)
throws IOException, ServletException {
Servlet servlet = null;
long available = wrapper.getAvailable();
if (!unavailable) {
/*xxx: 默认的 servlet从这个方法而来*/
servlet = wrapper.allocate();
}
/*xxx: 从这里,开始进入 Servlet规范,进行处理*/
ApplicationFilterChain filterChain =
ApplicationFilterFactory.createFilterChain(request, wrapper, servlet);
if ((servlet != null) && (filterChain != null)) {
filterChain.doFilter(request.getRequest(), response.getResponse());
}
}
}
- 普通Valve流转--流转至本容器的下一个valve
public class ErrorReportValve extends ValveBase {
@Override
public void invoke(Request request, Response response) throws IOException, ServletException {
getNext().invoke(request, response);
//省略其他抽象...
}
}
public final class JDBCAccessLogValve extends ValveBase implements AccessLog {
@Override
public void invoke(Request request, Response response) throws IOException,
ServletException {
getNext().invoke(request, response);
}
}
- 可以得出结论,标准valve总是位于pipeline的最后一个valve
# 最佳实践--常见模型
# service与映射器
- service层面的映射器,将当前请求对应的host,context,wrapper进行映射;
- 具体的映射过程,见上文运行逻辑-容器映射部分源码;
# container与pipeline
- 每个容器组件,都具有一个pipeline
- pipeline用于存放valve
- valve具有两类,标准valve,以及普通valve,具体的区别见上文运行逻辑-valve如何流转部分源码;
# context与管理器
- 原生tomcat环境下,管理器Manager,用于管理session,包括生成session,管理session,移除session等;
- 常用实现为: StandardManager
- 抽象模型
public interface Manager {
/*xxx: id生成器 */
public SessionIdGenerator getSessionIdGenerator();
/*xxx: 创建session */
public Session createSession(String sessionId);
/*xxx: 添加session */
public void add(Session session);
}
public abstract class ManagerBase extends LifecycleMBeanBase implements Manager {
/*xxx: 在该 StandardManager中,对sessionId 与 Session进行了缓存, 结果缓存在内存中的*/
protected Map<String, Session> sessions = new ConcurrentHashMap<>();
protected SessionIdGenerator sessionIdGenerator = null;
protected Class<? extends SessionIdGenerator> sessionIdGeneratorClass = null;
@Override
public Session createSession(String sessionId) {
if ((maxActiveSessions >= 0) &&
(getActiveSessions() >= maxActiveSessions)) {
rejectedSessions++;
throw new TooManyActiveSessionsException(
sm.getString("managerBase.createSession.ise"),
maxActiveSessions);
}
// Recycle or create a Session instance
Session session = createEmptySession();
// Initialize the properties of the new session and return it
session.setNew(true);
session.setValid(true);
session.setCreationTime(System.currentTimeMillis());
/*xxx: 设置新创建的session的超时时间 */
session.setMaxInactiveInterval(getContext().getSessionTimeout() * 60);
String id = sessionId;
if (id == null) {
id = generateSessionId();
}
session.setId(id);
sessionCounter++;
SessionTiming timing = new SessionTiming(session.getCreationTime(), 0);
synchronized (sessionCreationTiming) {
sessionCreationTiming.add(timing);
sessionCreationTiming.poll();
}
return session;
}
@Override
public SessionIdGenerator getSessionIdGenerator() {
if (sessionIdGenerator != null) {
return sessionIdGenerator;
} else if (sessionIdGeneratorClass != null) {
try {
sessionIdGenerator = sessionIdGeneratorClass.getConstructor().newInstance();
return sessionIdGenerator;
} catch(ReflectiveOperationException ex) {
// Ignore
}
}
return null;
}
@Override
/*xxx: 每次添加的时候,会进行最大session量的控制,超过后,则阻塞*/
public void add(Session session) {
sessions.put(session.getIdInternal(), session);
int size = getActiveSessions();
if( size > maxActive ) {
synchronized(maxActiveUpdateLock) {
if( size > maxActive ) {
maxActive = size;
}
}
}
}
}
# 原生tomcat的session的生成逻辑
- 从cookie或者url中恢复sessionId(再根据sessionId恢复)
- 或者从request创建,并写入到response的cookie中(允许使用cookie的话)
//package org.apache.catalina.connector;
public class Request implements HttpServletRequest {
protected Session session = null;
protected org.apache.catalina.connector.Response response = null;
@Override
public HttpSession getSession(boolean create) {
Session session = doGetSession(create);
if (session == null) {
return null;
}
return session.getSession();
}
protected Session doGetSession(boolean create) {
Context context = getContext();
if (context == null) {
return null;
}
// Return the current session if it exists and is valid
if ((session != null) && !session.isValid()) {
session = null;
}
/*xxx: 如果之前已经获取过session,并且session是合法的,则直接返回*/
if (session != null) {
return session;
}
/*xxx: StandardManager实例是从 context中获取的,换言之,session是存在内存中的,并且以Context为单位进行隔离*/
Manager manager = context.getManager();
if (manager == null) {
return null; // Sessions are not supported
}
if (requestedSessionId != null) {
try {
/*xxx: 获取session,实际上是根据 coyoteAdaptor阶段,恢复的sessionId,通过该Session去 Manager中,寻找与之映射的 session实例 */
session = manager.findSession(requestedSessionId);
} catch (IOException e) {
session = null;
}
if ((session != null) && !session.isValid()) {
session = null;
}
if (session != null) {
/*xxx: 刷新session的最近访问时间*/
session.access();
return session;
}
}
// Create a new session if requested and the response is not committed
if (!create) {
return null;
}
/*xxx: session的创建逻辑 */
boolean trackModesIncludesCookie =
context.getServletContext().getEffectiveSessionTrackingModes().contains(SessionTrackingMode.COOKIE);
if (trackModesIncludesCookie && response.getResponse().isCommitted()) {
throw new IllegalStateException(sm.getString("coyoteRequest.sessionCreateCommitted"));
}
String sessionId = getRequestedSessionId();
if (requestedSessionSSL) {
// If the session ID has been obtained from the SSL handshake then
// use it.
} else if (("/".equals(context.getSessionCookiePath())
&& isRequestedSessionIdFromCookie())) {
if (context.getValidateClientProvidedNewSessionId()) {
boolean found = false;
for (Container container : getHost().findChildren()) {
Manager m = ((Context) container).getManager();
if (m != null) {
try {
if (m.findSession(sessionId) != null) {
found = true;
break;
}
} catch (IOException e) {
}
}
}
if (!found) {
sessionId = null;
}
}
} else {
sessionId = null;
}
/*xxx: session的创建,最终是通过 session管理器完成的。 */
session = manager.createSession(sessionId);
/*xxx: 当创建session成功后,同时会将当前的session,写入到 response头部中 */
if (session != null && trackModesIncludesCookie) {
Cookie cookie = ApplicationSessionCookieConfig.createSessionCookie(
context, session.getIdInternal(), isSecure());
response.addSessionCookieInternal(cookie);
}
if (session == null) {
return null;
}
session.access();
return session;
}
}
# ApplicationContext上下文(cataina)
- 抽象结构
//package javax.servlet;
public interface ServletContext {
/*xxx: 获取资源*/
public URL getResource(String path) throws MalformedURLException;
/*xxx: 获取请求分发器*/
public RequestDispatcher getRequestDispatcher(String path);
/*xxx: 获取初始化参数名称枚举对象*/
public Enumeration<String> getInitParameterNames();
/*xxx: 添加servlet*/
public ServletRegistration.Dynamic addServlet(String servletName, Servlet servlet);
/*xxx: 获取servlet注册表*/
public ServletRegistration getServletRegistration(String servletName);
/*xxx: 添加过滤器*/
public FilterRegistration.Dynamic addFilter(String filterName, String className);
/*xxx: 获取过滤器注册表*/
public FilterRegistration getFilterRegistration(String filterName);
/*xxx: 获取session配置信息*/
public SessionCookieConfig getSessionCookieConfig();
/*xxx: 添加监听器 */
public void addListener(Class<? extends EventListener> listenerClass);
/*xxx: 根据uri,获取servletContext上下文对象 */
public ServletContext getContext(String uripath);
}
/*xxx:应用上下文,实现了servlet的上下文规范*/
public class ApplicationContext implements ServletContext {
private final StandardContext context;
private final Service service;
private SessionCookieConfig sessionCookieConfig;
public ApplicationContext(StandardContext context) {
super();
this.context = context;
this.service = ((Engine) context.getParent().getParent()).getService();
this.sessionCookieConfig = new ApplicationSessionCookieConfig(context);
// Populate session tracking modes
/*xxx: 填充session的 track-mode ,如果没有,则用默认的。 默认包含了两种 即 url 和 cookie */
populateSessionTrackingModes();
}
}
- 应用实例
public class StandardContext extends ContainerBase
implements Context, NotificationEmitter {
/*xxx: 应用上下文,是servletContext的直接子类,其为 protected属性*/
protected ApplicationContext context = null;
@Override
public ServletContext getServletContext() {
if (context == null) {
context = new ApplicationContext(this);
if (altDDName != null)
context.setAttribute(Globals.ALT_DD_ATTR,altDDName);
}
return context.getFacade();
}
}
# ServletContext
- 抽象结构
- 见上文,ApplicationContext上下文(cataina)
- 如何获取资源;
public interface ServletContext {
/*xxx: 获取资源*/
public URL getResource(String path) throws MalformedURLException;
}
/*xxx:应用上下文,实现了servlet的上下文规范*/
public class ApplicationContext implements ServletContext {
private final StandardContext context;
@Override
public URL getResource(String path) throws MalformedURLException {
String validatedPath = validateResourcePath(path, false);
if (validatedPath == null) {
throw new MalformedURLException(
sm.getString("applicationContext.requestDispatcher.iae", path));
}
WebResourceRoot resources = context.getResources();
if (resources != null) {
return resources.getResource(validatedPath).getURL();
}
return null;
}
}
public class StandardContext extends ContainerBase
implements Context, NotificationEmitter {
private WebResourceRoot resources;
@Override
public WebResourceRoot getResources() {
Lock readLock = resourcesLock.readLock();
readLock.lock();
try {
return resources;
} finally {
readLock.unlock();
}
}
}
public interface WebResourceRoot extends Lifecycle {
/*xxx: 根据路径获取web资源 */
WebResource getResource(String path);
/*xxx: 添加pre类型的资源*/
void addPreResources(WebResourceSet webResourceSet);
/*xxx: 添加jar类型资源*/
void addJarResources(WebResourceSet webResourceSet);
/*xxx: 添加post类型资源*/
void addPostResources(WebResourceSet webResourceSet);
/*xxx: 资源类型*/
enum ResourceSetType {
PRE,
RESOURCE_JAR,
POST,
CLASSES_JAR
}
}
public class StandardRoot extends LifecycleMBeanBase implements WebResourceRoot {
private final Cache cache = new Cache(this);
private WebResourceSet main;
@Override
protected void startInternal() throws LifecycleException {
main = createMainResourceSet();
}
protected WebResourceSet createMainResourceSet() {
String docBase = context.getDocBase();
WebResourceSet mainResourceSet;
if (docBase == null) {
mainResourceSet = new EmptyResourceSet(this);
} else {
File f = new File(docBase);
if (!f.isAbsolute()) {
f = new File(((Host)context.getParent()).getAppBaseFile(), f.getPath());
}
if (f.isDirectory()) {
mainResourceSet = new DirResourceSet(this, "/", f.getAbsolutePath(), "/");
} else if(f.isFile() && docBase.endsWith(".war")) {
mainResourceSet = new WarResourceSet(this, "/", f.getAbsolutePath());
} else {
throw new IllegalArgumentException(
sm.getString("standardRoot.startInvalidMain",
f.getAbsolutePath()));
}
}
return mainResourceSet;
}
@Override
public WebResource getResource(String path) {
return getResource(path, true, false);
}
protected WebResource getResource(String path, boolean validate,
boolean useClassLoaderResources) {
if (validate) {
path = validate(path);
}
if (isCachingAllowed()) {
return cache.getResource(path, useClassLoaderResources);
} else {
return getResourceInternal(path, useClassLoaderResources);
}
}
/*xxx: 实际获取资源的方法 */
protected final WebResource getResourceInternal(String path,
boolean useClassLoaderResources) {
WebResource result = null;
WebResource virtual = null;
WebResource mainEmpty = null;
/*xxx: 从 preResource,jarReosurce,postResource,mainResource,classResource中获取资源 */
for (List<WebResourceSet> list : allResources) {
for (WebResourceSet webResourceSet : list) {
if (!useClassLoaderResources && !webResourceSet.getClassLoaderOnly() ||
useClassLoaderResources && !webResourceSet.getStaticOnly()) {
result = webResourceSet.getResource(path);
/*xxx: 获取到资源,则直接返回*/
if (result.exists()) {
return result;
}
/*xxx: 没获取到资源*/
if (virtual == null) {
/*xxx: 看是否是虚拟资源*/
if (result.isVirtual()) {
virtual = result;
} else if (main.equals(webResourceSet)) {
mainEmpty = result;
}
}
}
}
}
// Use the first virtual result if no real result was found
if (virtual != null) {
return virtual;
}
// Default is empty resource in main resources
return mainEmpty;
}
}
public interface WebResourceSet extends Lifecycle {
/*xxx: 根据路径获取资源 */
WebResource getResource(String path);
}
public class FileResourceSet extends AbstractFileResourceSet {
}
public class DirResourceSet extends AbstractFileResourceSet {
}
public class WarResourceSet extends AbstractSingleArchiveResourceSet {
}
- 与WEB-INF的关系
- 由源码中得知,WEB-INF 与资源并无直接关系,而是通过 context的docBase决定的资源位置;
- 但是WEB-INF确实有其特殊性,它是war包打包规范中的一个默认路径;
# ApplicationFilterChain原生过滤链
- 原生过滤链的构建
final class StandardWrapperValve
extends ValveBase {
/*xxx: 一个合法的 wrapper,包含一个 servlet*/
/*xxx: 服务器轻量级程序,在service的映射器时,已经将本次请求的wrapper与相应的servlet进行了映射 */
Servlet servlet = null;
@Override
public final void invoke(Request request, Response response)
throws IOException, ServletException {
/*xxx: 默认的 servlet从这个方法而来*/
servlet = wrapper.allocate();
/*xxx: 从这里,开始进入 Servlet规范,进行处理*/
/*xxx: 过滤链的构建,由过滤链构造器完成 */
ApplicationFilterChain filterChain =
ApplicationFilterFactory.createFilterChain(request, wrapper, servlet);
/*xxx: 通过原生过滤链的过滤操作,完成 servlet的功能*/
filterChain.doFilter(request.getRequest(),
response.getResponse());
//xxx: 最后,会进行 filterChain的释放,servlet的卸载等等操作
}
}
/*xxx: 过滤链构造器,由tomcat提供 */
public final class ApplicationFilterFactory {
/*xxx: 创建过滤链 */
public static ApplicationFilterChain createFilterChain(ServletRequest request,
Wrapper wrapper, Servlet servlet) {
/*xxx: 创建过滤链必须有指定的servlet,否则返空 */
if (servlet == null)
return null;
/*xxx: 原生过滤链 */
ApplicationFilterChain filterChain = null;
if (request instanceof Request) {
Request req = (Request) request;
/*xxx: 从请求中,获取过滤链, 没有则 进行设置 */
filterChain = (ApplicationFilterChain) req.getFilterChain();
if (filterChain == null) {
filterChain = new ApplicationFilterChain();
req.setFilterChain(filterChain);
}
}else{
/*xxx: 分发器使用时,则直接实例化 */
filterChain = new ApplicationFilterChain();
}
filterChain.setServlet(servlet);
/*xxx: 从上下文中,获取 过滤器注册表 */
FilterMap filterMaps[] = context.findFilterMaps();
if ((filterMaps == null) || (filterMaps.length == 0))
return filterChain;
/*xxx: 根据请求的路径,决定需要经过过滤器过滤组件 */
for (FilterMap filterMap : filterMaps) {
/*xxx: 首先进行分发模式匹配,包括 forward,include,request,error,async */
if (!matchDispatcher(filterMap, dispatcher)) {
continue;
}
/*xxx: 其次进行url匹配,包括全匹配,模式匹配,扩展名匹配*/
if (!matchFiltersURL(filterMap, requestPath))
continue;
ApplicationFilterConfig filterConfig = (ApplicationFilterConfig)
context.findFilterConfig(filterMap.getFilterName());
if (filterConfig == null) {
// FIXME - log configuration problem
continue;
}
/*xxx: 加入过滤器 */
filterChain.addFilter(filterConfig);
}
// Add filters that match on servlet name second
/*xxx: 根据servlet的名称,筛选需要过滤的过滤器*/
for (FilterMap filterMap : filterMaps) {
if (!matchDispatcher(filterMap, dispatcher)) {
continue;
}
/*xxx: 特定servlet 名称的过滤器 */
if (!matchFiltersServlet(filterMap, servletName))
continue;
ApplicationFilterConfig filterConfig = (ApplicationFilterConfig)
context.findFilterConfig(filterMap.getFilterName());
if (filterConfig == null) {
// FIXME - log configuration problem
continue;
}
filterChain.addFilter(filterConfig);
}
// Return the completed filter chain
return filterChain;
}
}
- 原生过滤链过滤器的流转 以及 与servlet的流转
/*xxx: servlet规范 中 原生filterChain接口 */
/*xxx: springSecurity主要就是基于该标准的扩展,在原生的应用级过滤链上面,新增虚拟过滤链*/
public interface FilterChain {
/*xxx: 进行过滤动作 */
public void doFilter(ServletRequest request, ServletResponse response)
throws IOException, ServletException;
}
/*xxx: 应用过滤器,由tomcat 实现的servlet 过滤器规范 */
public final class ApplicationFilterChain implements FilterChain {
/*xxx: 当前过滤链过滤器的个数*/
private int n = 0;
/*xxx: 当前过滤链过滤的位置,过滤链与上过滤器的推进,全靠这个参数 */
/*xxx: 通过这个参数,各过滤器的执行可以完成类似出入栈的递归结构*/
/*xxx: 在某个过滤器没有调用过滤链的迭代方法时,又可完成水平迭代的功能*/
private int pos = 0;
/*xxx: 应用过滤器配置信息数组,存储了所有的原生过滤器 */
private ApplicationFilterConfig[] filters = new ApplicationFilterConfig[0];
@Override
/*xxx: 进行过滤动作 */
public void doFilter(ServletRequest request, ServletResponse response)
throws IOException, ServletException {
//xxx: 省略其他抽象...
internalDoFilter(req,res);
}
/*xxx: 实际过滤动作 */
private void internalDoFilter(ServletRequest request,
ServletResponse response)
throws IOException, ServletException {
if (pos < n) {
/*xxx: 从原生过滤器配置信息 数组中进行迭代 */
ApplicationFilterConfig filterConfig = filters[pos++];
/*xxx: 根据过滤器配置信息,获取实际的过滤器实例 */
Filter filter = filterConfig.getFilter();
/*xxx: 根据获取到的过滤器 执行 过滤,并将当前的 过滤器实例作为参数传入 */
filter.doFilter(request, response, this);
}
/*xxx: 当原生过滤链执行完毕后,就会执行servlet的service方法*/
/*xxx: filter 到 servlet 的流转,是通过 过滤链完成的*/
servlet.service(request, response);
}
//xxx: 省略其他抽象...
}
# ApplicationDispatcher应用分发器
- 应用分发器的构造逻辑
public interface ServletContext {
/*xxx: 获取请求分发器*/
public RequestDispatcher getRequestDispatcher(String path);
}
/*xxx:应用上下文,实现了servlet的上下文规范*/
public class ApplicationContext implements ServletContext {
@Override
public RequestDispatcher getRequestDispatcher(final String path) {
/*xxx: 通过映射器去查找 wrapper */
service.getMapper().map(context, uriMB, mappingData);
/*xxx: 通过映射器的映射结果,来实例化新的 应用分发器 */
return new ApplicationDispatcher(wrapper, uri, wrapperPath, pathInfo,
queryString, mapping, null);
}
}
- 应用分发器的流转逻辑
public interface RequestDispatcher {
/*xxx: 重定向 */
public void forward(ServletRequest request, ServletResponse response)
throws ServletException, IOException;
}
public interface AsyncDispatcher {
/*xxx: 异步分发*/
public void dispatch(ServletRequest request, ServletResponse response)
throws ServletException, IOException;
}
final class ApplicationDispatcher implements AsyncDispatcher, RequestDispatcher {
/*xxx: 异步分发*/
private void doDispatch(ServletRequest request, ServletResponse response)
throws ServletException, IOException {
State state = new State(request, response, false);
invoke(state.outerRequest, state.outerResponse, state);
}
private void invoke(ServletRequest request, ServletResponse response,
State state) throws IOException, ServletException {
ApplicationFilterChain filterChain =
ApplicationFilterFactory.createFilterChain(request, wrapper, servlet);
filterChain.doFilter(request, response);
}
private void doForward(ServletRequest request, ServletResponse response)
throws ServletException, IOException
{
State state = new State(request, response, false);
processRequest(request,response,state);
}
private void processRequest(ServletRequest request,
ServletResponse response,
State state)
throws IOException, ServletException {
invoke(state.outerRequest, response, state);
}
}
# 最佳实践--解析JSP视图
# 添加jspServlet,并指定名称
- springBoot项目需要依赖项目:
embbed-tomcat-jasper
,该项目中包含了 JspServlet,包含了该类,embbed-tomcat启动时,才会自动装载; - spring项目,默认tomcat容器已经包含了JspServlet所在的模块,并自动将之装载了,以 jsp为名;
# 调用jsp的service方法,获取jsp方法执行渲染
- jsp的service方法,会使用访问者模式,进行元素节点的渲染;具体过程略
# 最佳实践--springMvc的结合
# xml方式如何对DispatcherServlet进行装载
/*xxx: 在标准的catalina容器启动过程中,会为上下文默认添加一个生命周期监听器 --- contextConfig*/
/*xxx: 它将会处理六个事件,核心事件由三个: AFTER_INIT_EVENT,
BEFORE_START_EVENT,
CONFIGURE_START_EVENT*/
public class ContextConfig implements LifecycleListener {
@Override
public void lifecycleEvent(LifecycleEvent event) {
if (event.getType().equals(Lifecycle.CONFIGURE_START_EVENT)) {
/*xxx: 通过该事件,解析web.xml,创建 Wrapper包装器,过滤器,上下文监听器 的鞥一系列容器相关的对象,完成容器的初始化*/
configureStart();
} else if (event.getType().equals(Lifecycle.BEFORE_START_EVENT)) {
/*xxx: 该事件在 Context启动之前触发, 用于更新 context的docBase属性和解决 web目录锁的问题*/
beforeStart();
} else if (event.getType().equals(Lifecycle.AFTER_START_EVENT)) {
// Restore docBase for management tools
if (originalDocBase != null) {
context.setDocBase(originalDocBase);
}
}else{
//xxx: 省略其他抽象...
}
}
protected synchronized void configureStart() {
/*xxx: 根据配置创建包装器,过滤器,监听器, 除了解析web.xml,还包括 tomcat默认配置,web-fragment.xml,ServletContainerInitializer,以及相关xml文件的排序和合并*/
webConfig();
if (!context.getIgnoreAnnotations()) {
/*xxx: 若 标准上下文的ignoreAnnotations为false,则解析应用程序注解配置,添加相关的JNDI资源引用*/
applicationAnnotationsConfig();
}
}
protected void webConfig() {
/*xxx: 1.解析默认配置,生成web.xml对象
* 从上述代码看出,先解析容器级配置(config/web.xml),
* 再解析 host级配置,对于同名配置,host级将覆盖容器级,为了提高性能,会对容器级配置进行缓存,避免重复解析*/
WebXml webXml = createWebXml();
/*xxx: 2.解析web应用的wb.xml文件配置, 注意 StandardContext的 'org.apache.catalina.deploy.alt_dd'属性,用于指定web.xml路径,如未指定,则使用默认的 WEB-INF/web.xml*/
InputSource contextWebXml = getContextWebXmlSource();
/*xxx: 3.扫描web应用所有的jar包,如果含有 META-INF/web-fragment.xml,则解析,并创建webXml对象*/
Map<String,WebXml> fragments = processJarsForWebFragments(webXml, webXmlParser);
/*xxx: 4.将 web-fragment.xml创建的WebXml对象按照Servlet规范进行排序,同时将对相应的结果设置到 ServletContext属性中*/
orderedFragments =
WebXml.orderWebFragments(webXml, fragments, sContext);
/*xxx: 5.查找ServletContainerInitializer实现,并创建实例,查找范围分为两部分:
* web应用下的包: 如果上下文中属性:javax.servlet.conetxt.orderedLibs不为空,则仅搜索包含的包,否则搜索WEB-INF/lib下的所有包
* 容器包: 搜索所有包
* 容器包中的实现,将优先加载*/
/*xxx: 6.根据 servletContainerInitializer的结果,以及相应的 javax.servlet.annotaion.HandlesTypes注解配置,维护Initializer映射*/
processServletContainerInitializers();
/*xxx: 7.处理WEB-INF/classess下的注解,以及含有 web-fragment.xml的jar中的注解,然后进行合并*/
processClasses(webXml, orderedFragments);
/*xxx:10.对于web应用中 JspFile属性不为空的Servlet,将其 servletClass设为 org.apache.jasper.servelt.JspServlet*/
convertJsps(webXml);
/*xxx: 查找 含有 web-fragment.xml的jar包 “META-INF/resources/”下的静态资源,并添加到StandardContext*/
Set<WebXml> resourceJars = new LinkedHashSet<>(orderedFragments);
for (WebXml fragment : fragments.values()) {
if (!resourceJars.contains(fragment)) {
resourceJars.add(fragment);
}
}
/*xxx: 将xml配置信息,应用于上下文中,也是最重要的一步*/
configureContext(webXml);
}
private void configureContext(WebXml webxml) {
/*xxx: 遍历 配置过滤器 */
for (FilterDef filter : webxml.getFilters().values()) {
if (filter.getAsyncSupported() == null) {
filter.setAsyncSupported("false");
}
context.addFilterDef(filter);
}
/*xxx: 遍历配置过滤器映射信息,包括servlet名称映射(*代表所有servlet),路径映射(路径用的更多) */
for (FilterMap filterMap : webxml.getFilterMappings()) {
context.addFilterMap(filterMap);
}
/*xxx: 遍历添加监听器信息 */
for (String listener : webxml.getListeners()) {
context.addApplicationListener(listener);
}
/*xxx: 遍历取出 servlet,并将之包装成 wrapper */
for (ServletDef servlet : webxml.getServlets().values()) {
Wrapper wrapper = context.createWrapper();
/*xxx: wrapper的名字,就是servlet 的名字 */
wrapper.setName(servlet.getServletName());
Map<String,String> params = servlet.getParameterMap();
for (Entry<String, String> entry : params.entrySet()) {
/*xxx: wrapper的初始化参数,就是 servlet的初始化参数 */
wrapper.addInitParameter(entry.getKey(), entry.getValue());
}
/*xxx: wrapper指代的类,就是servlet指代的类*/
wrapper.setServletClass(servlet.getServletClass());
/*xxx: 并将wrapper 作为上下文容器的子容器,进行保存 */
context.addChild(wrapper);
}
}
}
# spring上下文的初始化方式
- spring上下文的初始化,本质上就是
dispatcherServlet
的初始化; dispatcherServlet
初始化本身有自己的初始化时机,tomcat与spring上下文的结合也就体现在这里
public abstract class GenericServlet implements Servlet, ServletConfig,
java.io.Serializable {
}
public abstract class HttpServlet extends javax.servlet.GenericServlet {
}
//package org.springframework.web.servlet;
public abstract class HttpServletBean extends HttpServlet implements EnvironmentCapable, EnvironmentAware {
@Override
/*xxx: 在服务器容器启动后,将会自动初始化 (init-param 设置为非负数,或者没设 在context完成后,会自动执行初始化)*/
public final void init() throws ServletException {
/*xxx: 将 servlet 中配置的参数 封装到 pvs 变量中, requiredProperties 为 必需参数,如果没配置,将报异常*/
PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
/*xxx: 模板方法,可以在子类调用,做一些初始化工作, bw代表 DispatcherServlet*/
initBeanWrapper(bw);
/*xxx: 将配置的 初始值,(如 contextConfigLocation) 设置到 DispatcherServlet*/
bw.setPropertyValues(pvs, true);
/*xxx: 模板方法,子类初始化的入口方法*/
initServletBean();
}
protected void initServletBean() throws ServletException {
}
}
/*xxx: 该方法重写了 除了 doHead外的所有方法*/
public abstract class FrameworkServlet extends HttpServletBean implements ApplicationContextAware {
/*xxx: springmvc 相关的bean 是否已经初始化完成 */
private volatile boolean refreshEventReceived;
private boolean publishContext = true;
@Nullable
private WebApplicationContext webApplicationContext;
@Override
protected final void initServletBean() throws ServletException {
/*xxx: 初始化 WebApplicationContext*/
this.webApplicationContext = initWebApplicationContext();
/*xxx: 初始化 FrameworkServlet*/
initFrameworkServlet();
}
protected WebApplicationContext initWebApplicationContext() {
/*xxx: 从servletContext中 获取spring的 WebApplicationContext,名称为 WebApplicationContext的类全名 + .root */
WebApplicationContext rootContext =
WebApplicationContextUtils.getWebApplicationContext(getServletContext());
WebApplicationContext wac = null;
/*xxx: 如果 webApplicationContext还没有创建,则创建一个*/
/*xxx: 正常情况下,就是走这个分支*/
wac = createWebApplicationContext(rootContext);
if (wac == null) {
/*xxx: 当 webAppicationContext 已经存在 ServletContext中时,通过配置在 Servlet中的 contextAttribute参数获取*/
/*xxx: 这种情况下,属于 不同的 context,有些没有初始化 spring的上下文,会使用其他context已经初始化好的 spring应用上下文 */
wac = findWebApplicationContext();
}
/*xxx: 应用上下文初始化完成后,如果仍然没有更新 mvc的配置,则进行更新 */
if (!this.refreshEventReceived) {
synchronized (this.onRefreshMonitor) {
/*xxx: 当ContextRefreshedEvent事件没有触发时,调用此方法,为模板方法*/
/*xxx: 初始化springMvc的流程 */
onRefresh(wac);
}
}
/*xxx: 如果允许为公共的spring上下文 */
/*xxx: 该值默认为 true */
if (this.publishContext) {
/*xxx: 将 ApplicationContext 保存到 ServletContext中*/
String attrName = getServletContextAttributeName();
getServletContext().setAttribute(attrName, wac);
}
}
protected WebApplicationContext createWebApplicationContext(@Nullable WebApplicationContext parent) {
return createWebApplicationContext((ApplicationContext) parent);
}
protected WebApplicationContext createWebApplicationContext(@Nullable ApplicationContext parent) {
/*xxx: 获取创建类型, 默认为 org.springframework.web.context.support.XmlWebApplicationContext*/
Class<?> contextClass = getContextClass();
/*xxx: 检查创建类型*/
if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {
//阻断,并 报错
}
/*xxx: 具体创建相应的 上下文类 */
ConfigurableWebApplicationContext wac =
(ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
/*xxx: 将设置的 contextConfigLocation参数传给 wac 配置了才传递*/
String configLocation = getContextConfigLocation();
if (configLocation != null) {
wac.setConfigLocation(configLocation);
}
/*xxx: 创建了上下文后,需要进行配置,以及 调用refresh方法*/
configureAndRefreshWebApplicationContext(wac);
return wac;
}
protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac) {
/*xxx: 添加监听 ContextRefreshedEvent 的监听器*/
wac.addApplicationListener(new SourceFilteringListener(wac, new ContextRefreshListener()));
/*xxx: 这里实际上,就进入了 spring上下文的逻辑 */
/*xxx: 该方法会调用 applicationInitializer, 并进行应用*/
applyInitializers(wac);
wac.refresh();
}
/*xxx: spring层面的上下文初始化好后,将会通过事件机制,触发该方法的执行 */
public void onApplicationEvent(ContextRefreshedEvent event) {
this.refreshEventReceived = true;
synchronized (this.onRefreshMonitor) {
onRefresh(event.getApplicationContext());
}
}
}
public class DispatcherServlet extends FrameworkServlet {
/*xxx: org.springframework.web.servlet.DispatcherServlet.properties里面定义的键值对*/
/*xxx: 默认情况下,处理上传组件 multipartResolver是没有默认配置的*/
private static final String DEFAULT_STRATEGIES_PATH = "DispatcherServlet.properties";
/*xxx: 检测所有的 handlerMappings 还是 仅仅从上下文中获取 bean*/
private boolean detectAllHandlerMappings = true;
@Nullable
private List<HandlerMapping> handlerMappings;
@Override
/*xxx: 该方法 在 spring层面的上下文执行完成后,进行调用 */
protected void onRefresh(ApplicationContext context) {
/*xxx: 初始化9个组件*/
initStrategies(context);
}
protected void initStrategies(ApplicationContext context) {
/*xxx: 1.MultipartResolver组件*/
initMultipartResolver(context);
/*xxx: 2.localeResolver组件*/
initLocaleResolver(context);
/*xxx: 3.themeResolver组件*/
initThemeResolver(context);
/*xxx: 4.handlerMapping组件*/
initHandlerMappings(context);
/*xxx: 5.handlerAdapter组件*/
initHandlerAdapters(context);
/*xxx: 6.handlerExceptionResolver组件*/
initHandlerExceptionResolvers(context);
/*xxx: 7.requestToViewNameTranslator组件*/
initRequestToViewNameTranslator(context);
/*xxx: 8.viewResolver组件*/
initViewResolvers(context);
/*xxx: 9.flashMapManager组件*/
initFlashMapManager(context);
}
private void initHandlerMappings(ApplicationContext context) {
/*xxx: 获取所有的 handlerMapping */
if (this.detectAllHandlerMappings) {
/*xxx: 从上下文中,以类型的方式,获取 bean, 父级上下文中的同类型bean,也会获取到 */
Map<String, HandlerMapping> matchingBeans =
BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
if (!matchingBeans.isEmpty()) {
this.handlerMappings = new ArrayList<>(matchingBeans.values());
/*xxx: handlerMapping 会根据 order 进行排序 */
AnnotationAwareOrderComparator.sort(this.handlerMappings);
}
}else{
HandlerMapping hm = context.getBean("handlerMapping", HandlerMapping.class);
this.handlerMappings = Collections.singletonList(hm);
}
/*xxx: 如果没有配置 handlerMapping,则会使用默认的 handlerMapping */
/*xxx: 默认情况下,包括: BeanNameUrlHandlerMapping, RequestMappingHandlerMapping, RouterFunctionMapping */
if (this.handlerMappings == null) {
/*xxx: 默认配置的 handlerMapping ,不会进行排序,按照先后顺序 依次排开 */
this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
}
}
private void initHandlerAdapters(ApplicationContext context) {
/*xxx: 同 handlerMapping,
默认的 handlerMapping 包括:HttpRequestHandlerAdapter,SimpleControllerHandlerAdapter,RequestMappingHandlerAdapter,HandlerFunctionAdapter* */
}
}
# 最佳实践--springBoot的结合
# 内置tomcat如何装载Servlet,Filter和Listener
- 动态注册体系设计
//package org.springframework.boot.web.servlet;
public interface ServletContextInitializer {
/*xxx: 启动钩子 */
void onStartup(ServletContext servletContext) throws ServletException;
}
public abstract class RegistrationBean implements ServletContextInitializer, Ordered {
public final void onStartup(ServletContext servletContext) throws ServletException {
String description = this.getDescription();
this.register(description, servletContext);
}
protected abstract void register(String description, ServletContext servletContext);
}
public abstract class DynamicRegistrationBean<D extends Dynamic> extends RegistrationBean {
protected final void register(String description, ServletContext servletContext) {
D registration = this.addRegistration(description, servletContext);
}
protected abstract D addRegistration(String description, ServletContext servletContext);
}
public class ServletRegistrationBean<T extends Servlet> extends DynamicRegistrationBean<Dynamic> {
protected Dynamic addRegistration(String description, ServletContext servletContext) {
String name = this.getServletName();
return servletContext.addServlet(name, this.servlet);
}
}
- 动态注册流程设计
/*xxx: servlet规范的 容器初始化器 */
public interface ServletContainerInitializer {
void onStartup(Set<Class<?>> c, ServletContext ctx) throws ServletException;
}
final class TomcatStarter implements ServletContainerInitializer {
private final ServletContextInitializer[] initializers;
TomcatStarter(ServletContextInitializer[] initializers) {
this.initializers = initializers;
}
@Override
public void onStartup(Set<Class<?>> classes, ServletContext servletContext) throws ServletException {
for (ServletContextInitializer initializer : this.initializers) {
initializer.onStartup(servletContext);
}
}
}
public interface ServletWebServerFactory {
WebServer getWebServer(ServletContextInitializer... initializers);
}
public class TomcatServletWebServerFactory extends AbstractServletWebServerFactory implements ConfigurableTomcatWebServerFactory {
@Override
public WebServer getWebServer(ServletContextInitializer... initializers) {
Tomcat tomcat = new Tomcat();
prepareContext(tomcat.getHost(), initializers);
}
protected void prepareContext(Host host, ServletContextInitializer[] initializers) {
/*xxx: 是 StandardContext的变体*/
TomcatEmbeddedContext context = new TomcatEmbeddedContext();
/*xxx: 为 context 添加 Initializers*/
configureContext(context, initializersToUse);
}
protected void configureContext(Context context, ServletContextInitializer[] initializers) {
TomcatStarter starter = new TomcatStarter(initializers);
/*xxx:为context 添加servlet容器初始化器*/
context.addServletContainerInitializer(starter, NO_CLASSES);
}
}
public class StandardContext extends ContainerBase
implements Context, NotificationEmitter {
@Override
/*xxx: context的启动逻辑,比较复杂*/
protected synchronized void startInternal() throws LifecycleException {
for (Entry<ServletContainerInitializer, Set<Class<?>>> entry :
initializers.entrySet()) {
/*xxx: 20.启动添加到当前上下文的 ServletContainerInitializer,该类的实例,由 ContextConfig查找并添加*/
/*xxx: 该类主要用于 以可编程的方式添加Web应用的配置,如Servlet,Filter等 */
entry.getKey().onStartup(entry.getValue(),
getServletContext());
}
}
}
public class ServletWebServerApplicationContext extends GenericWebApplicationContext
implements ConfigurableWebServerApplicationContext {
/*xxx: 获取contextInitializer集合*/
private org.springframework.boot.web.servlet.ServletContextInitializer getSelfInitializer() {
return servletContext -> selfInitialize(servletContext);
}
private void selfInitialize(ServletContext servletContext) throws ServletException {
prepareWebApplicationContext(servletContext);
registerApplicationScope(servletContext);
WebApplicationContextUtils.registerEnvironmentBeans(getBeanFactory(), servletContext);
for (ServletContextInitializer beans : getServletContextInitializerBeans()) {
beans.onStartup(servletContext);
}
}
protected Collection<ServletContextInitializer> getServletContextInitializerBeans() {
return new ServletContextInitializerBeans(getBeanFactory());
}
}
public class ServletContextInitializerBeans extends AbstractCollection<ServletContextInitializer> {
/*xxx: 该bean 在初始化的时候,还会调用适配方法,添加 动态注册的filter以及 servlet,listener等*/
public ServletContextInitializerBeans(ListableBeanFactory beanFactory,
Class<? extends ServletContextInitializer>... initializerTypes) {
addServletContextInitializerBeans(beanFactory);
addAdaptableBeans(beanFactory);
}
private void addServletContextInitializerBeans(ListableBeanFactory beanFactory) {
for (Class<? extends ServletContextInitializer> initializerType : this.initializerTypes) {
for (Entry<String, ? extends ServletContextInitializer> initializerBean : getOrderedBeansOfType(beanFactory,
initializerType)) {
addServletContextInitializerBean(initializerBean.getKey(), initializerBean.getValue(), beanFactory);
}
}
}
/*xxx: 通过bean注册相应的组件 */
private void addServletContextInitializerBean(String beanName, ServletContextInitializer initializer,
ListableBeanFactory beanFactory) {
if (initializer instanceof ServletRegistrationBean) {
Servlet source = ((ServletRegistrationBean<?>) initializer).getServlet();
addServletContextInitializerBean(Servlet.class, beanName, initializer, beanFactory, source);
}
else if (initializer instanceof FilterRegistrationBean) {
Filter source = ((FilterRegistrationBean<?>) initializer).getFilter();
addServletContextInitializerBean(Filter.class, beanName, initializer, beanFactory, source);
}
/*xxx: 代理filter,单独作为了一个 Filter类型进行处理 */
else if (initializer instanceof DelegatingFilterProxyRegistrationBean) {
String source = ((DelegatingFilterProxyRegistrationBean) initializer).getTargetBeanName();
addServletContextInitializerBean(Filter.class, beanName, initializer, beanFactory, source);
}
else if (initializer instanceof ServletListenerRegistrationBean) {
EventListener source = ((ServletListenerRegistrationBean<?>) initializer).getListener();
addServletContextInitializerBean(EventListener.class, beanName, initializer, beanFactory, source);
}
else {
addServletContextInitializerBean(ServletContextInitializer.class, beanName, initializer, beanFactory,
initializer);
}
}
private void addServletContextInitializerBean(Class<?> type, String beanName, ServletContextInitializer initializer,
ListableBeanFactory beanFactory, Object source) {
this.initializers.add(type, initializer);
}
protected void addAdaptableBeans(ListableBeanFactory beanFactory) {
addAsRegistrationBean(beanFactory, Servlet.class, new ServletRegistrationBeanAdapter(multipartConfig));
addAsRegistrationBean(beanFactory, Filter.class, new FilterRegistrationBeanAdapter());
}
protected <T> void addAsRegistrationBean(ListableBeanFactory beanFactory, Class<T> type,
RegistrationBeanAdapter<T> adapter) {
addAsRegistrationBean(beanFactory, type, type, adapter);
}
private <T, B extends T> void addAsRegistrationBean(ListableBeanFactory beanFactory, Class<T> type,
Class<B> beanType, RegistrationBeanAdapter<T> adapter) {
List<Entry<String, B>> entries = getOrderedBeansOfType(beanFactory, beanType, this.seen);
/*xxx: 遍历相应的组件集合 */
for (Entry<String, B> entry : entries) {
String beanName = entry.getKey();
B bean = entry.getValue();
/*xxx: 如果该组件 尚未被注册,则将之包装成 registration,并进行注册*/
if (this.seen.add(bean)) {
RegistrationBean registration = adapter.createRegistrationBean(beanName, bean, entries.size());
int order = getOrder(bean);
registration.setOrder(order);
this.initializers.add(type, registration);
}
}
}
}
# spring上下文的初始化方式及启动方式
public class ServletWebServerApplicationContext extends GenericWebApplicationContext
implements ConfigurableWebServerApplicationContext {
@Override
/*xxx: springBoot的web应用,就是在该方法中,启动了内置的tomcat容器 */
protected void onRefresh() {
super.onRefresh();
try {
createWebServer();
}
catch (Throwable ex) {
throw new ApplicationContextException("Unable to start web server", ex);
}
}
private void createWebServer() {
WebServer webServer = this.webServer;
ServletContext servletContext = getServletContext();
/*xxx: webServer与 servletContext都不为空, fatJar环境走的就是该分支 */
if (webServer == null && servletContext == null) {
StartupStep createWebServer = this.getApplicationStartup().start("spring.boot.webserver.create");
ServletWebServerFactory factory = getWebServerFactory();
createWebServer.tag("factory", factory.getClass().toString());
/*xxx: 第一步比较关键的是,通过工厂获取 webServer时,进行初始化工作*/
/*xxx: 在初始化时,同时指定了 ServletContextInitializer集合*/
this.webServer = factory.getWebServer(getSelfInitializer());
createWebServer.end();
getBeanFactory().registerSingleton("webServerGracefulShutdown",
new WebServerGracefulShutdownLifecycle(this.webServer));
/*xxx: 第二部关键点是,为上下文容器新增一个 webServer的起停器,它是一个 smartLifecycle,会自动启动*/
getBeanFactory().registerSingleton("webServerStartStop",
new WebServerStartStopLifecycle(this, this.webServer));
}
/*xxx: 如果webServer为空,但是 servlet不为空,war部署的时候,走的该分支*/
else if (servletContext != null) {
try {
/*xxx: war时的机制,与fatjar保持一致*/
getSelfInitializer().onStartup(servletContext);
}
catch (ServletException ex) {
throw new ApplicationContextException("Cannot initialize servlet context", ex);
}
}
}
/*xxx: 获取contextInitializer集合*/
private org.springframework.boot.web.servlet.ServletContextInitializer getSelfInitializer() {
return servletContext -> selfInitialize(servletContext);
}
private void selfInitialize(ServletContext servletContext) throws ServletException {
prepareWebApplicationContext(servletContext);
registerApplicationScope(servletContext);
WebApplicationContextUtils.registerEnvironmentBeans(getBeanFactory(), servletContext);
for (ServletContextInitializer beans : getServletContextInitializerBeans()) {
beans.onStartup(servletContext);
}
}
protected Collection<ServletContextInitializer> getServletContextInitializerBeans() {
return new ServletContextInitializerBeans(getBeanFactory());
}
}
public class ServletContextInitializerBeans extends AbstractCollection<ServletContextInitializer> {
/*xxx: 该bean 在初始化的时候,还会调用适配方法,添加 动态注册的filter以及 servlet,listener等*/
public ServletContextInitializerBeans(ListableBeanFactory beanFactory,
Class<? extends ServletContextInitializer>... initializerTypes) {
addServletContextInitializerBeans(beanFactory);
addAdaptableBeans(beanFactory);
}
private void addServletContextInitializerBeans(ListableBeanFactory beanFactory) {
for (Class<? extends ServletContextInitializer> initializerType : this.initializerTypes) {
for (Entry<String, ? extends ServletContextInitializer> initializerBean : getOrderedBeansOfType(beanFactory,
initializerType)) {
addServletContextInitializerBean(initializerBean.getKey(), initializerBean.getValue(), beanFactory);
}
}
}
/*xxx: 通过bean注册相应的组件 */
private void addServletContextInitializerBean(String beanName, ServletContextInitializer initializer,
ListableBeanFactory beanFactory) {
if (initializer instanceof ServletRegistrationBean) {
Servlet source = ((ServletRegistrationBean<?>) initializer).getServlet();
addServletContextInitializerBean(Servlet.class, beanName, initializer, beanFactory, source);
}
else if (initializer instanceof FilterRegistrationBean) {
Filter source = ((FilterRegistrationBean<?>) initializer).getFilter();
addServletContextInitializerBean(Filter.class, beanName, initializer, beanFactory, source);
}
/*xxx: 代理filter,单独作为了一个 Filter类型进行处理 */
else if (initializer instanceof DelegatingFilterProxyRegistrationBean) {
String source = ((DelegatingFilterProxyRegistrationBean) initializer).getTargetBeanName();
addServletContextInitializerBean(Filter.class, beanName, initializer, beanFactory, source);
}
else if (initializer instanceof ServletListenerRegistrationBean) {
EventListener source = ((ServletListenerRegistrationBean<?>) initializer).getListener();
addServletContextInitializerBean(EventListener.class, beanName, initializer, beanFactory, source);
}
else {
addServletContextInitializerBean(ServletContextInitializer.class, beanName, initializer, beanFactory,
initializer);
}
}
private void addServletContextInitializerBean(Class<?> type, String beanName, ServletContextInitializer initializer,
ListableBeanFactory beanFactory, Object source) {
this.initializers.add(type, initializer);
if (source != null) {
this.seen.add(source);
}
}
protected void addAdaptableBeans(ListableBeanFactory beanFactory) {
MultipartConfigElement multipartConfig = getMultipartConfig(beanFactory);
addAsRegistrationBean(beanFactory, Servlet.class, new ServletRegistrationBeanAdapter(multipartConfig));
addAsRegistrationBean(beanFactory, Filter.class, new FilterRegistrationBeanAdapter());
for (Class<?> listenerType : ServletListenerRegistrationBean.getSupportedTypes()) {
addAsRegistrationBean(beanFactory, EventListener.class, (Class<EventListener>) listenerType,
new ServletListenerRegistrationBeanAdapter());
}
}
private <T, B extends T> void addAsRegistrationBean(ListableBeanFactory beanFactory, Class<T> type,
Class<B> beanType, RegistrationBeanAdapter<T> adapter) {
List<Entry<String, B>> entries = getOrderedBeansOfType(beanFactory, beanType, this.seen);
/*xxx: 遍历相应的组件集合 */
for (Entry<String, B> entry : entries) {
String beanName = entry.getKey();
B bean = entry.getValue();
/*xxx: 如果该组件 尚未被注册,则将之包装成 registration,并进行注册*/
if (this.seen.add(bean)) {
RegistrationBean registration = adapter.createRegistrationBean(beanName, bean, entries.size());
int order = getOrder(bean);
registration.setOrder(order);
this.initializers.add(type, registration);
}
}
}
}
- springBoot上下文如何与embbed-tomcat联动
public interface ApplicationContextAware extends Aware {
void setApplicationContext(ApplicationContext var1) throws BeansException;
}
public abstract class FrameworkServlet extends HttpServletBean implements ApplicationContextAware {
private WebApplicationContext webApplicationContext;
private boolean webApplicationContextInjected;
public void setApplicationContext(ApplicationContext applicationContext) {
if (this.webApplicationContext == null && applicationContext instanceof WebApplicationContext) {
this.webApplicationContext = (WebApplicationContext)applicationContext;
this.webApplicationContextInjected = true;
}
}
}
- dispatcher 在 tomcat环境中,充当的是普通的java类实例,spring的上下文由servlet流程初始化;
- dispatcher 在 tomcat-embbed环境中,充当的是spring的bean,spring的上下文,由spring流程自动注入;
# 最佳实践--性能探究
# 工作线程数
- 代码体现
public abstract class AbstractEndpoint<S,U> {
/*xxx: 工作线程池*/
private Executor executor = null;
/*xxx: 最大工作线程数,默认200*/
private int maxThreads = 200;
/*xxx: 是否使用内部线程池*/
protected volatile boolean internalExecutor = true;
/*xxx: 创建线程池,*/
public void createExecutor() {
internalExecutor = true;
TaskQueue taskqueue = new TaskQueue();
TaskThreadFactory tf = new TaskThreadFactory(getName() + "-exec-", daemon, getThreadPriority());
/*xxx: 创建工作线程池时,线程池最低保持了10个线程,最大可以达到参数设置的线程数,默认情况下是200 */
executor = new ThreadPoolExecutor(getMinSpareThreads(), getMaxThreads(), 60, TimeUnit.SECONDS,taskqueue, tf);
taskqueue.setParent( (ThreadPoolExecutor) executor);
}
public int getMaxThreads() {
if (internalExecutor) {
return maxThreads;
} else {
return -1;
}
}
}
- 配置项
配置 tomcat/conf/server.xml
<Connector port="8080" protocol="org.apache.coyote.http11.Http11NioProtocol" maxThreads="500" />
- 物理意义 每一次HTTP请求到达Web服务,tomcat都会创建一个线程来处理该请求,那么最大线程数决定了Web服务容器可以同时处理多少个请求。maxThreads默认200,肯定建议增加。但是,增加线程是有成本的,更多的线程,不仅仅会带来更多的线程上下文切换成本,而且意味着带来更多的内存消耗。JVM中默认情况下在创建新线程时会分配大小为1M的线程栈,所以,更多的线程异味着需要更多的内存。线程数的经验值为:1核2g内存,线程数经验值200;4核8g内存,线程数经验值800。
# 连接队列数
- 代码体现
public abstract class AbstractEndpoint<S,U> {
/*xxx: 允许服务端接收的线程,默认 100*/
private int acceptCount = 100;
public int getAcceptCount() { return acceptCount; }
/*xxx:初始化时,会进行 创建服务端的 nio socketChannel*/
protected void initServerSocket() throws Exception {
InetSocketAddress addr = new InetSocketAddress(getAddress(), getPortWithOffset());
/*xxx: accept队列的长度,该值作用于操作系统
当accept队列中连接的个数达到acceptCount时,队列满,进来的请求一律被拒绝。默认值是100*/
serverSock.socket().bind(addr,getAcceptCount());
}
}
- 配置项
配置 tomcat/conf/server.xml
<Connector port="8080" protocol="org.apache.coyote.http11.Http11NioProtocol" acceptCount="500" />
- 物理意义
官方文档的说明为:当所有的请求处理线程都在使用时,所能接收的连接请求的队列的最大长度。当队列已满时,任何的连接请求都将被拒绝。accept-count的默认值为100。 详细的来说:当调用HTTP请求数达到tomcat的最大线程数时,还有新的HTTP请求到来,这时tomcat会将该请求放在等待队列中,这个acceptCount就是指能够接受的最大等待数,默认100。如果等待队列也被放满了,这个时候再来新的请求就会被tomcat拒绝(connection refused)
# 最大连接数
- 代码体现
public abstract class AbstractEndpoint<S,U> {
/*xxx: Tomcat在任意时刻接收和处理的最大连接数, 默认为 8192*/
private int maxConnections = 8*1024;
/*xxx: 暂存所有与当前的socket相连的所有连接,通过 maxConnections 可以进行控制 */
protected Map<U, SocketWrapperBase<S>> connections = new ConcurrentHashMap<>();
public int getMaxConnections() { return this.maxConnections; }
}
public class NioEndpoint extends AbstractJsseEndpoint<NioChannel,SocketChannel> {
@Override
protected boolean setSocketOptions(SocketChannel socket) {
NioSocketWrapper newWrapper = new NioSocketWrapper(channel, this);
channel.reset(socket, newWrapper);
/*xxx: 将新建的连接进行保存,该变量保存了 tomcat任意时刻的活跃连接数 */
connections.put(socket, newWrapper);
return true;
}
}
public class Acceptor<U> implements Runnable {
@Override
public void run() {
while (!stopCalled) {
/*xxx: 增加endPoint的连接数,如果达到了最大连接数,则阻塞等待*/
/*xxx: 当 endpoint的连接数,小于 最大连接数时,才从队列中,获取socket连接*/
endpoint.countUpOrAwaitConnection();
}
}
}
- 配置项
配置 tomcat/conf/server.xml
<Connector port="8080" protocol="org.apache.coyote.http11.Http11NioProtocol" maxConnections="1000" />
- 物理意义
这个参数是指在同一时间,tomcat能够接受的最大连接数。对于Java的阻塞式BIO,默认值是maxthreads的值;如果在BIO模式使用定制的Executor执行器,默认值将是执行器中maxthreads的值。对于Java 新的NIO模式,maxConnections 默认值是10000。 对于windows上APR/native IO模式,maxConnections默认值为8192,这是出于性能原因,如果配置的值不是1024的倍数,maxConnections 的实际值将减少到1024的最大倍数。 如果设置为-1,则禁用maxconnections功能,表示不限制tomcat容器的连接数。 maxConnections和accept-count的关系为:当连接数达到最大值maxConnections后,系统会继续接收连接,但不会超过acceptCount的值。
# 外置tomcat的主要配置
# server.xml配置文件
<?xml version="1.0" encoding="UTF-8"?>
<Server port="8005" shutdown="SHUTDOWN">
<Listener className="org.apache.catalina.startup.VersionLoggerListener" />
<Listener className="org.apache.catalina.core.AprLifecycleListener" SSLEngine="on" />
<Listener className="org.apache.catalina.core.JreMemoryLeakPreventionListener" />
<Listener className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener" />
<Listener className="org.apache.catalina.core.ThreadLocalLeakPreventionListener" />
<GlobalNamingResources>
<Resource name="UserDatabase" auth="Container"
type="org.apache.catalina.UserDatabase"
description="User database that can be updated and saved"
factory="org.apache.catalina.users.MemoryUserDatabaseFactory"
pathname="conf/tomcat-users.xml" />
</GlobalNamingResources>
<Service name="Catalina">
<Connector port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443" />
<Engine name="Catalina" defaultHost="localhost">
<Realm className="org.apache.catalina.realm.LockOutRealm">
<Realm className="org.apache.catalina.realm.UserDatabaseRealm"
resourceName="UserDatabase"/>
</Realm>
<Host name="localhost" appBase="webapps"
unpackWARs="true" autoDeploy="true">
<Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
prefix="localhost_access_log" suffix=".txt"
pattern="%h %l %u %t "%r" %s %b" />
</Host>
</Engine>
</Service>
</Server>
# web.xml的配置结构及优先级
- 配置结构
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
version="3.1">
<servlet>
<servlet-name>default</servlet-name>
<servlet-class>org.apache.catalina.servlets.DefaultServlet</servlet-class>
<init-param>
<param-name>debug</param-name>
<param-value>0</param-value>
</init-param>
<init-param>
<param-name>listings</param-name>
<param-value>false</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet>
<servlet-name>jsp</servlet-name>
<servlet-class>org.apache.jasper.servlet.JspServlet</servlet-class>
<init-param>
<param-name>fork</param-name>
<param-value>false</param-value>
</init-param>
<init-param>
<param-name>xpoweredBy</param-name>
<param-value>false</param-value>
</init-param>
<load-on-startup>3</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<!-- The mappings for the JSP servlet -->
<servlet-mapping>
<servlet-name>jsp</servlet-name>
<url-pattern>*.jsp</url-pattern>
<url-pattern>*.jspx</url-pattern>
</servlet-mapping>
<session-config>
<session-timeout>30</session-timeout>
</session-config>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
<welcome-file>index.htm</welcome-file>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
</web-app>
- 优先级 context的web.xml优先级> 全局的web.xml
# 外置tomcat与内置tomcat的本质区别
# 外置tomcat创建容器的方式
public class MBeanFactory {
public String createStandardContext(String parent, String path, String docBase, boolean xmlValidation, boolean xmlNamespaceAware) throws Exception {
StandardContext context = new StandardContext();
ContextConfig contextConfig = new ContextConfig();
context.addLifecycleListener(contextConfig);
Service service = this.getService(pname);
Engine engine = service.getContainer();
Host host = (Host)engine.findChild(pname.getKeyProperty("host"));
host.addChild(context);
}
public String createStandardHost(String parent, String name, String appBase, boolean autoDeploy, boolean deployOnStartup, boolean deployXML, boolean unpackWARs) throws Exception {
StandardHost host = new StandardHost();
host.setName(name);
host.setAppBase(appBase);
host.setAutoDeploy(autoDeploy);
host.setDeployOnStartup(deployOnStartup);
host.setDeployXML(deployXML);
host.setUnpackWARs(unpackWARs);
HostConfig hostConfig = new HostConfig();
host.addLifecycleListener(hostConfig);
ObjectName pname = new ObjectName(parent);
Service service = this.getService(pname);
Engine engine = service.getContainer();
engine.addChild(host);
return host.getObjectName().toString();
}
public String createStandardServiceEngine(String domain, String defaultHost, String baseDir) throws Exception {
if (!(this.container instanceof Server)) {
throw new Exception(sm.getString("mBeanFactory.notServer"));
} else {
StandardEngine engine = new StandardEngine();
engine.setDomain(domain);
engine.setName(domain);
engine.setDefaultHost(defaultHost);
Service service = new StandardService();
service.setContainer(engine);
service.setName(domain);
((Server)this.container).addService(service);
return engine.getObjectName().toString();
}
}
}
# 内置tomcat创建容器的流程
public class TomcatServletWebServerFactory extends AbstractServletWebServerFactory implements ConfigurableTomcatWebServerFactory {
protected void prepareContext(Host host, ServletContextInitializer[] initializers) {
/*xxx: 是 StandardContext的变体*/
TomcatEmbeddedContext context = new TomcatEmbeddedContext();
/*xxx: 将 context添加为 host的子组件*/
host.addChild(context);
}
@Override
public WebServer getWebServer(ServletContextInitializer... initializers) {
Tomcat tomcat = new Tomcat();
/*xxx: host组件,用的默认的 standardHost组件*/
tomcat.getHost().setAutoDeploy(false);
prepareContext(tomcat.getHost(), initializers);
}
}
```java
/*xxx: 提供一种轻量级的扩展方式启动 tomcat,使得应用可以内嵌运行*/
public class Tomcat {
public Server getServer() {
server = new StandardServer();
Service service = new StandardService();
service.setName("Tomcat");
server.addService(service);
}
public Engine getEngine() {
Service service = getServer().findServices()[0];
Engine engine = new StandardEngine();
service.setContainer(engine);
}
public Host getHost() {
Engine engine = getEngine();
Host host = new StandardHost();
getEngine().addChild(host);
}
}
# 过滤器改造
# 过滤器改造优先级
- 对于springBoot环境,
@Order
越小,优先级越高 - 对于非springBoot环境,采用web.xml方式配置过滤器时,按照先后顺序构建过滤链
# 响应头设置的要点
- 设置响应头,对于先后时序要求的,尽量在 当前链过滤前加上,避免因调用了response.commit导致添加失败
- 对于有先后时序要求的(比如改写cookie),需要确保response的未commit. 常见的
flushBuffer,缓冲区满
均会导致response切换为commit状态; - 对于某些自定义的请求头,要被浏览器保留并识别,需要设置请求头:
response.setHeader("Access-Control-Expose-Headers", "the header name")
# servlet外观模式专题
# 外观模式的架构
- coyoter Request 和 Response
public class Http11Processor extends AbstractProcessor {
public Http11Processor(AbstractHttp11Protocol<?> protocol, Adapter adapter) {
super(adapter);
//省略其他抽象,协议将原生的socket流,转为 coyote 的Request 和Response
}
}
public abstract class AbstractProcessor extends AbstractProcessorLight implements ActionHook {
protected final Request request;
protected final Response response;
public AbstractProcessor(Adapter adapter) {
this(adapter, new Request(), new Response());
}
}
/*xxx: 适配器,用于找到请求对应的 组件*/
public class CoyoteAdapter implements Adapter {
/*xxx: 可以看成tomcat的核心流程的处理起点 多线程环境*/
public void service(org.apache.coyote.Request req, org.apache.coyote.Response res)
throws Exception {
//... 省略其他抽象
request = connector.createRequest();
request.setCoyoteRequest(req);
response = connector.createResponse();
response.setCoyoteResponse(res);
//... 省略其他抽象
}
}
- connector Request 和 Response (已经是属于servlet规范的Request和Response)
/*xxx: 连接器,对应于协议,用于 处理协议, 一个具有生命周期的组件*/
public class Connector extends LifecycleMBeanBase {
public Request createRequest() {
return new Request(this);
}
public Response createResponse() {
int size = protocolHandler.getDesiredBufferSize();
if (size > 0) {
return new Response(size);
} else {
return new Response();
}
}
}
public class Request implements HttpServletRequest {
//xxx: 原生连接器, 本质上是一个连接处理器 (单线程,将进来的请求分发给 其他线程处理,之后的处理过程将与该线程无关)
protected final Connector connector;
//xxx: coyote Request,servlet规范的很多方法的最终实现,均由其完成
//xxx: 不为空
/*xxx: 个别规范,也会在 当前类 进行处理*/
protected org.apache.coyote.Request coyoteRequest;
//xxx: 外观模式的代理对象 的 出口
protected RequestFacade facade = null;
/*xxx: 外观模式 代理*/
private HttpServletRequest applicationRequest = null;
/*xxx: 设置 外观包装器 的代理对象*/
public void setRequest(HttpServletRequest applicationRequest) {
// Check the wrapper wraps this request
ServletRequest r = applicationRequest;
while (r instanceof HttpServletRequestWrapper) {
r = ((HttpServletRequestWrapper) r).getRequest();
}
/*xxx: 包装器的尽头,必需是 RequestFacade*/
if (r != facade) {
throw new IllegalArgumentException(sm.getString("request.illegalWrap"));
}
this.applicationRequest = applicationRequest;
}
/*xxx: 获取外观模式代理对象*/
public HttpServletRequest getRequest() {
if (facade == null) {
facade = new RequestFacade(this);
}
if (applicationRequest == null) {
applicationRequest = facade;
}
return applicationRequest;
}
}
public class Response implements HttpServletResponse {
/*xxx: 缓存流处理器*/
protected final OutputBuffer outputBuffer;
/*xxx: 外观模式代理的最终对象*/
protected ResponseFacade facade = null;
/*xxx: 实际响应的处理*/
/*xxx: 个别规范,也会在 当前类 进行处理*/
protected org.apache.coyote.Response coyoteResponse;
/*xxx: 外观模式的代理对象*/
private HttpServletResponse applicationResponse = null;
public Response() {
//xxx: 默认流缓存处理8M数据
this(OutputBuffer.DEFAULT_BUFFER_SIZE);
}
/*xxx: 获取外观代理对象 */
public HttpServletResponse getResponse() {
if (facade == null) {
facade = new ResponseFacade(this);
}
if (applicationResponse == null) {
applicationResponse = facade;
}
return applicationResponse;
}
/*xxx: 设置外观代理对象*/
public void setResponse(HttpServletResponse applicationResponse) {
ServletResponse r = applicationResponse;
while (r instanceof HttpServletResponseWrapper) {
r = ((HttpServletResponseWrapper) r).getResponse();
}
/*xxx: 外观代理模式的尽头,必需是 facade */
if (r != facade) {
throw new IllegalArgumentException(sm.getString("response.illegalWrap"));
}
this.applicationResponse = applicationResponse;
}
}
- 外观对象,是在filter,servelt流通的基本对象
public class RequestFacade implements HttpServletRequest {
public RequestFacade(Request request) {
this.request = request;
}
}
public class ResponseFacade implements HttpServletResponse {
public ResponseFacade(Response response) {
this.response = response;
}
}
# 外观模式的流程
final class StandardWrapperValve
extends ValveBase {
public final void invoke(Request request, Response response)
throws IOException, ServletException {
//..... 省略其他抽象
/*xxx: 从这里,开始进入 Servlet规范,进行处理*/
/*xxx: 过滤链的构建,由过滤链构造器完成 */
ApplicationFilterChain filterChain =
ApplicationFilterFactory.createFilterChain(request, wrapper, servlet);
/*xxx: 通过原生过滤链的过滤操作,完成 servlet的功能*/
/*xxx: request.getRequest(),respon.getResponse(),获取的是外观对象。 本质上来说,获取的是一个代理对象*/
filterChain.doFilter(request.getRequest(),
response.getResponse());
//xxx: 省略其他抽象...
}
}
在过滤链的传递过程中,可以通过ServletRequestWrapper 为 外观对象进行 穿衣服 的过程,当过滤器出栈的时候,又会进入 脱衣服 的过程.
换言之,在不进行过滤器包装的情况下,流通的实际servlet对象为 Facade.
如果要进行保证,包装器最里面的核心对象,必需为 Facade.
# 外观模式的包装架构
public class ServletRequestWrapper implements ServletRequest {
private ServletRequest request;
public ServletRequestWrapper(ServletRequest request) {
if (request == null) {
throw new IllegalArgumentException(lStrings.getString("wrapper.nullRequest"));
}
this.request = request;
}
}
public class HttpServletRequestWrapper extends ServletRequestWrapper implements
HttpServletRequest {
public HttpServletRequestWrapper(HttpServletRequest request) {
super(request);
}
/*xxx: 获取包装对象*/
private HttpServletRequest _getHttpServletRequest() {
return (HttpServletRequest) super.getRequest();
}
}
# 外观模式的关键点
- 外观模式的代理操作,操作一定是单向的,有明确的代理出口,不能存在环
- 外观模式,有点类似于穿衣服,于脱衣服,有两个关键因素都会影响最终的效果
- 在何时包装
- 在何时调用