源码导读
Connector
// connector 通过protocolHandler来实现具体的请求处理
// 每种ProtocolHandler包含3个重要组件来处理请求:
// 1. Adapter:用于将封装好的Request交给Container,将请求适配到servlet容器。请求响应对象从连接器传送到容器需要一个桥梁CoyoteAdapter 。
// 2. Endpoint:用于处理底层Socket连接(nio和nio2实现的是TCP/IP协议,Apr实现的是SSL/TLS协议)
// 3. Processer:用于将Endpoint接收到的Socket封装成Request(实现HTTP协议或websocket协议或AJP协议)
protected final ProtocolHandler protocolHandler;
protected void startInternal() throws LifecycleException {
// Validate settings before starting
if (getPortWithOffset() < 0) {
throw new LifecycleException(sm.getString(
"coyoteConnector.invalidPort", Integer.valueOf(getPortWithOffset())));
}
setState(LifecycleState.STARTING);
try {
protocolHandler.start();
} catch (Exception e) {
throw new LifecycleException(
sm.getString("coyoteConnector.protocolHandlerStartFailed"), e);
}
}
// AbstractProtocol impls ProtocolHandler
// 其有两个实现类AbstractHttp11Protocol与AbstractAjpProtocol分别对应了具体协议的实现,这两个类其下的子类分别实现了NIO,NIO2,与Apr(Apache portable Run-time libraries) - 从操作系统级别解决异步IO问题,apache tomcat自己的native技术。
// 每一个endpoint都代表了不同协议的不同IO数据处理方式,比如使用http11的nio2
// 一个endpoint实例对应着一个ServerSocketChannel通道
// 其一共三个实现类NIO,NIO2,与Ajp自己的Apr
/**
* Endpoint that provides low-level network I/O - must be matched to the
* ProtocolHandler implementation (ProtocolHandler using NIO, requires NIO
* Endpoint etc.).
*/
private final AbstractEndpoint<S,?> endpoint;
public void start() throws Exception {
if (getLog().isInfoEnabled()) {
getLog().info(sm.getString("abstractProtocolHandler.start", getName()));
logPortOffset();
}
endpoint.start();
monitorFuture = getUtilityExecutor().scheduleWithFixedDelay(
new Runnable() {
@Override
public void run() {
if (!isPaused()) {
startAsyncTimeout();
}
}
}, 0, 60, TimeUnit.SECONDS);
}
public final void start() throws Exception {
if (bindState == BindState.UNBOUND) {
bindWithCleanup();
bindState = BindState.BOUND_ON_START;
}
startInternal();
}
// 以NIO为例
/**
* Start the NIO endpoint, creating acceptor, poller threads.
*/
@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();
}
// 初始化最大连接数 默认10000
initializeConnectionLatch();
// Start poller thread
// 线程内部类,启动线程监听是否有请求,有则处理,没有则循环等待
poller = new Poller();
Thread pollerThread = new Thread(poller, getName() + "-ClientPoller");
pollerThread.setPriority(threadPriority);
pollerThread.setDaemon(true);
pollerThread.start();
// 启动acceptor线程接收 socket
// 默认只有一个acceptor线程,用于接收请求,并将请求信息封装为PollEvent对象放入PollEvent待处理队列中。如果端点处于暂停状态,50s探测一次,否则连接数+1,并判断是否超最大连接数(LimitLantch实现),如果超了,阻塞等待。
// ServerSocketChannel.accept()等待新的请求将SocketChannel请求信息封装为NioChannel(NioChannel从缓存中读取,没有就新生成),将NioChannel封装为PollerEvent(PollerEvent从缓存中读取,没有就新生成)
startAcceptorThread();
}
}
Container
- StandardEngine、StandardContext、StandardHost、StandardWrapper都继承 了ContainerBase类,ContainerBase内部类ContainerBackgroundProcessor会周期的执行 run(), 它是运行在一个后台线程中, run()会周期调用所有容器的 backgroundProcess 方法
- backgroundProcess方法主要通过四个组件完成工作:
- cluster:Catalina的集群组件,实现集群应用部署,多个Tomcat实例,不需要每个都分别部署应用,只需要在某个实例上部署,整个集群中的各个实例都会自动同步应用进行部署。 它监听指定文件夹下有没有新增的war包或者文件是新增的还是修改的已决定来重新部署和通知其他tomcat集群成员。
- loader Catalina在启动过程中创建的classloader的实例,查看Context容器是否需要重新加载,热部署就是利用这个机制来完成的
- manager Catalina中的Session管理器,主要的功能是将过期会话(session)置为无效
- realm Catalina中的安全组件,可以用来管理资源的访问权限 pipeline遍历并调用容器pipeline的valve链表的backgroundProcess
// stanard engine, 添加的子容器只能是Host类型的
public void addChild(Container child) {
if (!(child instanceof Host))
throw new IllegalArgumentException
(sm.getString("standardEngine.notHost"));
super.addChild(child);
}
// standard context, start context的时候会调用LifecycleBase的start方法,从而调用context的实现方法startInternal
// 主要作用是设置资源与各种管理组件,启动子容器与pipleline
// 代码太长,截出下面的关键代码
protected synchronized void startInternal() throws LifecycleException {
...
//
if (ok) {
resourcesStart();
}
if (getLoader() == null) {
WebappLoader webappLoader = new WebappLoader(getParentClassLoader());
webappLoader.setDelegate(getDelegate());
setLoader(webappLoader);
}
// An explicit cookie processor hasn't been specified; use the default
if (cookieProcessor == null) {
cookieProcessor = new Rfc6265CookieProcessor();
}
...
try {
if (ok) {
// Start our subordinate components, if any
Loader loader = getLoader();
if (loader instanceof Lifecycle) {
((Lifecycle) loader).start();
}
...
Realm realm = getRealmInternal();
if(null != realm) {
if (realm instanceof Lifecycle) {
((Lifecycle) realm).start();
}
...
}
// Notify our interested LifecycleListeners
fireLifecycleEvent(Lifecycle.CONFIGURE_START_EVENT, null);
// Start our child containers, if not already started
for (Container child : findChildren()) {
if (!child.getState().isAvailable()) {
child.start();
}
}
// Start the Valves in our pipeline (including the basic),
// if any
if (pipeline instanceof Lifecycle) {
((Lifecycle) pipeline).start();
}
// Acquire clustered manager
Manager contextManager = null;
Manager manager = getManager();
...
// Configure default manager if none was specified
if (contextManager != null) {
if (log.isDebugEnabled()) {
log.debug(sm.getString("standardContext.manager",
contextManager.getClass().getName()));
}
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);
}
}
...
// We put the resources into the servlet context
if (ok)
getServletContext().setAttribute
(Globals.RESOURCES_ATTR, getResources());
if (ok ) {
if (getInstanceManager() == null) {
setInstanceManager(createInstanceManager());
}
getServletContext().setAttribute(
InstanceManager.class.getName(), getInstanceManager());
InstanceManagerBindings.bind(getLoader().getClassLoader(), getInstanceManager());
}
// Create context attributes that will be required
if (ok) {
getServletContext().setAttribute(
JarScanner.class.getName(), getJarScanner());
}
...
try {
// Start manager
Manager manager = getManager();
if (manager instanceof Lifecycle) {
((Lifecycle) manager).start();
}
} catch(Exception e) {
log.error(sm.getString("standardContext.managerFail"), e);
ok = false;
}
...
// Start ContainerBackgroundProcessor thread
super.threadStart();
} finally {
// Unbinding thread
unbindThread(oldCCL);
}
...
// Reinitializing if something went wrong
if (!ok) {
setState(LifecycleState.FAILED);
} else {
setState(LifecycleState.STARTING);
}
}
// stanard Wrapper
启动类
Tomcat启动类图.jpg
/**
* Main method and entry point when starting Tomcat via the provided
* scripts.
*
* @param args Command line arguments to be processed
*/
public static void main(String args[]) {
synchronized (daemonLock) {
if (daemon == null) {
// Don't set daemon until init() has completed
Bootstrap bootstrap = new Bootstrap();
try {
// 初始化加载
bootstrap.init();
} catch (Throwable t) {
handleThrowable(t);
t.printStackTrace();
return;
}
daemon = bootstrap;
} else {
// When running as a service the call to stop will be on a new
// thread so make sure the correct class loader is used to
// prevent a range of class not found exceptions.
Thread.currentThread().setContextClassLoader(daemon.catalinaLoader);
}
}
try {
String command = "start";
if (args.length > 0) {
command = args[args.length - 1];
}
// 监听各种命令,然后通过daemon.load(args)反射的方式调用
if (command.equals("startd")) {
args[args.length - 1] = "start";
daemon.load(args);
daemon.start();
} else if (command.equals("stopd")) {
args[args.length - 1] = "stop";
daemon.stop();
} else if (command.equals("start")) {
daemon.setAwait(true);
daemon.load(args);
daemon.start();
if (null == daemon.getServer()) {
System.exit(1);
}
} else if (command.equals("stop")) {
daemon.stopServer(args);
} else if (command.equals("configtest")) {
daemon.load(args);
if (null == daemon.getServer()) {
System.exit(1);
}
System.exit(0);
} else {
log.warn("Bootstrap: command \"" + command + "\" does not exist.");
}
} catch (Throwable t) {
// Unwrap the Exception for clearer error reporting
if (t instanceof InvocationTargetException &&
t.getCause() != null) {
t = t.getCause();
}
handleThrowable(t);
t.printStackTrace();
System.exit(1);
}
}
// 初始化方法
public void init() throws Exception {
//初始化三个类加载器 commonLoader、catalinaLoader、sharedLoader
initClassLoaders();
// 给当前线程设置类加载器
Thread.currentThread().setContextClassLoader(catalinaLoader);
// 通过catalinaLoader去加载tomcat各个Package中的类
SecurityClassLoad.securityClassLoad(catalinaLoader);
// Load our startup class and call its process() method
if (log.isDebugEnabled())
log.debug("Loading startup class");
Class<?> startupClass = catalinaLoader.loadClass("org.apache.catalina.startup.Catalina");
Object startupInstance = startupClass.getConstructor().newInstance();
// 通过反射给启动类实例 设置父加载器 sharedLoader
// Set the shared extensions class loader
if (log.isDebugEnabled())
log.debug("Setting startup class properties");
String methodName = "setParentClassLoader";
Class<?> paramTypes[] = new Class[1];
paramTypes[0] = Class.forName("java.lang.ClassLoader");
Object paramValues[] = new Object[1];
paramValues[0] = sharedLoader;
Method method =
startupInstance.getClass().getMethod(methodName, paramTypes);
method.invoke(startupInstance, paramValues);
catalinaDaemon = startupInstance;
}
// 容器 监听到start时,调用了Catatina的load
public void load() {
if (loaded) {
return;
}
loaded = true;
long t1 = System.nanoTime();
// 创建初始化目录
initDirs();
// Before digester - it may be needed
// 初始化命名系统(initNaming)向System.setProperty注入java.naming.factory.initial类,
// 用于上下文查询的工厂类 org.apache.naming.java.javaURLContextFactory
initNaming();
// Set configuration source
ConfigFileLoader.setSource(new CatalinaBaseConfigurationSource(Bootstrap.getCatalinaBaseFile(), getConfigFile()));
File file = configFile();
// Create and execute our Digester
// 创建一个digester对象。Digester主要是将XML映射成Java类,简化XML的处理。
Digester digester = createStartDigester();
// digester通过parse方法解析server.xml的输入流(inputSource)
// 从而生成Server对象,并将如Engine、Host、Context、Wrapper包含的子容器等相关组件加到对象中。
try (ConfigurationSource.Resource resource = ConfigFileLoader.getSource().getServerXml()) {
InputStream inputStream = resource.getInputStream();
InputSource inputSource = new InputSource(resource.getURI().toURL().toString());
inputSource.setByteStream(inputStream);
digester.push(this);
digester.parse(inputSource);
} catch (Exception e) {
log.warn(sm.getString("catalina.configFail", file.getAbsolutePath()), e);
if (file.exists() && !file.canRead()) {
log.warn(sm.getString("catalina.incorrectPermissions"));
}
return;
}
getServer().setCatalina(this);
getServer().setCatalinaHome(Bootstrap.getCatalinaHomeFile());
getServer().setCatalinaBase(Bootstrap.getCatalinaBaseFile());
// Stream redirection
// System.out and System.err 的输出流重定向处理
initStreams();
// Start the new server
try {
getServer().init();
} catch (LifecycleException e) {
if (Boolean.getBoolean("org.apache.catalina.startup.EXIT_ON_INIT_FAILURE")) {
throw new java.lang.Error(e);
} else {
log.error(sm.getString("catalina.initError"), e);
}
}
long t2 = System.nanoTime();
if (log.isInfoEnabled()) {
log.info(sm.getString("catalina.init", Long.valueOf((t2 - t1) / 1000000)));
}
}
参考:
深入分析Java Web技术内幕 - 许令波
网友评论