tomcat
源码–1
–启动流程
文章目录
1 启动入口
tomcat
和普通的java
程序一样,都是通过main()
方法启动。tomcat
的main()
方法定义在其源码的org.apache.catalina.startup.Bootstrap
类中。我们先来看这个类中静态代码块。
1.1 Bootstrap
类的静态代码块
private static final File catalinaHomeFile;
static {
// Will always be non-null
//首先获取到系统属性user.dir,实际上就是项目路径
String userDir = System.getProperty("user.dir");
// Home first
//Constants.CATALINA_HOME_PROP = "catalina.home"
//就是catalina文件夹的位置,默认是null,我们可以在启动参数中指定
String home = System.getProperty(Constants.CATALINA_HOME_PROP);
File homeFile = null;
//用户在启动参数中指定了catalina.home参数的值,就会进入此分支
if (home != null) {
File f = new File(home);
try {
//尝试以相对路径的方式获取File对象
homeFile = f.getCanonicalFile();
} catch (IOException ioe) {
//相对路径方式产生异常,则使用绝对路径的方式获取File对象
homeFile = f.getAbsoluteFile();
}
}
//用户未指定启动参数catalina.home的值,就会进行两次回退操作
//第一次回退
if (homeFile == null) {
// First fall-back. See if current directory is a bin directory
// in a normal Tomcat install
//加载项目根路径下的bootstrap.jar包,实际上该jar包在bin目录下
File bootstrapJar = new File(userDir, "bootstrap.jar");
//如果tomcat是以bin目录下的startup.bat文件启动的,则会进入下面的if分支
if (bootstrapJar.exists()) {
File f = new File(userDir, "..");
try {
homeFile = f.getCanonicalFile();
} catch (IOException ioe) {
homeFile = f.getAbsoluteFile();
}
}
}
//第二次回退,如果是源码方式启动,则进入下面分支
if (homeFile == null) {
// Second fall-back. Use current directory
//直接使用当前项目根路径
File f = new File(userDir);
try {
homeFile = f.getCanonicalFile();
} catch (IOException ioe) {
homeFile = f.getAbsoluteFile();
}
}
//经过上面3次判定,最终得到catalina的路径
catalinaHomeFile = homeFile;
//CATALINA_HOME_PROP = "catalina.home"
//将catalina的资源路径保存到系统属性中
System.setProperty(
Constants.CATALINA_HOME_PROP, catalinaHomeFile.getPath());
// Then base
//下面是获取并校验catalina的基础路径,确保路径正确
//CATALINA_BASE_PROP = "catalina.base"
String base = System.getProperty(Constants.CATALINA_BASE_PROP);
//指定了catalina.base启动参数,就会使用启动参数的
if (base == null) {
catalinaBaseFile = catalinaHomeFile;
// 否则使用catalina.home的路径
} else {
File baseFile = new File(base);
try {
baseFile = baseFile.getCanonicalFile();
} catch (IOException ioe) {
baseFile = baseFile.getAbsoluteFile();
}
catalinaBaseFile = baseFile;
}
//将catalina.base属性保存到系统属性中
System.setProperty(
Constants.CATALINA_BASE_PROP, catalinaBaseFile.getPath());
}
可以发现,这个静态代码块的作用主要就是获取并校验catalina
的路径。
1.2 main()
方法
静态代码块执行完成了之后,main()
方法就开始执行了,我们接下来看看这个方法做了什么事情。
/**
* 用来加锁的对象
*/
private static final Object daemonLock = new Object();
//用来保存当前主类对象的引用
private static volatile Bootstrap daemon = null;
/**
* 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) {
//首次daemon字段肯定是null,必然会进入此分支
if (daemon == null) {
// Don't set daemon until init() has completed
//创建启动类的对象,后面会调用这个对象中的三个生命周期方法,来实现tomcat的生命周期过程
Bootstrap bootstrap = new Bootstrap();
try {
//初始化启动器,见2
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 {
//初始命令start,表示启动tomcat
String command = "start";
//如果注册参数不为null,则将主方法最后一个参数值作为本次操作的命令
if (args.length > 0) {
command = args[args.length - 1];
}
//startd
if (command.equals("startd")) {
args[args.length - 1] = "start";
daemon.load(args);
daemon.start();
}
//stopd
else if (command.equals("stopd")) {
args[args.length - 1] = "stop";
daemon.stop();
}
//start,tomcat启动
else if (command.equals("start")) {
daemon.setAwait(true);
daemon.load(args);
daemon.start();
if (null == daemon.getServer()) {
System.exit(1);
}
}
//stop
else if (command.equals("stop")) {
daemon.stopServer(args);
}
//configtest
else if (command.equals("configtest")) {
daemon.load(args);
if (null == daemon.getServer()) {
System.exit(1);
}
System.exit(0);
}
//非上述5种命令,则输出警告日志
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);
}
}
很容易发现,启动
tomcat
,无非就是下面三步
- 初始化
bootstrap.init();
- 解析
server.xml
文件daemon.load(args);
- 正式启动
daemon.start();
而
daemon
其实就是bootstrap
接下来,我们就按照这个顺序来看看
tomcat
具体的启动过程
2 init()
方法,初始化类加载器和实例化Catalina
/**
* catalina对象,方便以后使用
*/
private Object catalinaDaemon = null;
/**
* Initialize daemon.
* @throws Exception Fatal initialization error
*/
public void init() throws Exception {
//初始化类加载器,见2.1
initClassLoaders();
Thread.currentThread().setContextClassLoader(catalinaLoader);
SecurityClassLoad.securityClassLoad(catalinaLoader);
// Load our startup class and call its process() method
if (log.isDebugEnabled())
log.debug("Loading startup class");
//使用Catalina类加载器加载Catalina服务类
Class<?> startupClass = catalinaLoader.loadClass("org.apache.catalina.startup.Catalina");
//反射实例化Catalina
Object startupInstance = startupClass.getConstructor().newInstance();
// Set the shared extensions class loader
if (log.isDebugEnabled())
log.debug("Setting startup class properties");
//下面这一部分代码实际上就是反射调用Catalina对象的setParentClassLoader()方法,将
//sharedLoader共享扩展类加载器作为Catalina对象的父类加载器
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);
//将刚刚反射创建的Catalina对象赋值给Catalina字段
catalinaDaemon = startupInstance;
}
总结这个方法做的事情:
- 创建
3
个类加载器- 实例化
org.apache.catalina.startup.Catalina
类- 反射调用上面实例化对象的
setParentClassLoader()
方法,将sharedLoader
共享扩展类加载器作为Catalina对
象的父类加载器
2.1 初始化类加载器
ClassLoader commonLoader = null;
ClassLoader catalinaLoader = null;
ClassLoader sharedLoader = null;
private void initClassLoaders() {
try {
//公用的类加载器
commonLoader = createClassLoader("common", null);
if (commonLoader == null) {
// no config file, default to this loader - we might be in a 'single' env.
//将加载当前Bootstrap类的加载器作为公共类加载器
commonLoader = this.getClass().getClassLoader();
}
//创建一个加载catalina服务的类加载器
catalinaLoader = createClassLoader("server", commonLoader);
//创建一个共享类加载器
sharedLoader = createClassLoader("shared", commonLoader);
} catch (Throwable t) {
handleThrowable(t);
log.error("Class loader creation threw exception", t);
System.exit(1);
}
}
该方法初始化
3
个类加载器
commonLoader
catalinaLoader
:用来加载catalina
服务的类加载器sharedLoader
3 load()
方法,解析server.xml
文件,初始化tomcat
/**
* Load daemon.
*/
private void load(String[] arguments) throws Exception {
// Call the load() method
String methodName = "load";
Object param[];
Class<?> paramTypes[];
if (arguments==null || arguments.length==0) {
paramTypes = null;
param = null;
} else {
paramTypes = new Class[1];
paramTypes[0] = arguments.getClass();
param = new Object[1];
param[0] = arguments;
}
Method method =
catalinaDaemon.getClass().getMethod(methodName, paramTypes);
if (log.isDebugEnabled()) {
log.debug("Calling startup class " + method);
}
method.invoke(catalinaDaemon, param);
}
这个方法流程没什么看的, 主要是通过反射调用
Catalina
类的load()
方法,所以下面的篇幅我们就来看看Catalina
类的load()
方法做了什么?
/**
* 防止load()方法重复调用
*/
protected boolean loaded = false;
/**
* Start a new server instance.
*/
public void load() {
//load()方法,只允许被调用一次
if (loaded) {
return;
}
loaded = true;
long t1 = System.nanoTime();
//过时方法,从tomcat10开始被删除了
initDirs();
// Before digester - it may be needed
//初始化命名策略
initNaming();
// Parse main server.xml
//解析conf目录下的server.xml文件,并根据配置创建tomcat各种组件对象,具体过程忽略
parseServerXml(true);
//获取创建的Server组件对象,默认为StandardServer
Server s = getServer();
if (s == null) {
return;
}
//保存当前Catalina的配置到Server组件对象中
getServer().setCatalina(this);
getServer().setCatalinaHome(Bootstrap.getCatalinaHomeFile());
getServer().setCatalinaBase(Bootstrap.getCatalinaBaseFile());
// Stream redirection
//用自定义的打印流替换System.out和System.err。日志相关配置,不用理会
initStreams();
// Start the new server
try {
//初始化server组件
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);
}
}
if(log.isInfoEnabled()) {
log.info(sm.getString("catalina.init", Long.toString(TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - t1))));
}
}
可以发现,该方法会先解析
server.xml
文件,根据文件配置生成对应的组件对象,并形成层级关系
Server
Service
Engine
Connector
Host
Context
此处会调用生成的
Server
组件init()
方法,开始tomcat
的初始化过程
我们接下来看一下这几个组件的类图
很容易发现,这些组件都继承自LifecycleMBeanBase
类,那么它们为什么不直接实现Lifecycle
接口呢?其实啊,这里用到可一种设计模式–模板方法,它将每一个生命周期方法的公有逻辑提取到LifecycleBase
类中,而各自特有的逻辑则由自己本类实现,减少了代码冗余。
下面就是父类LifecycleBase
的init()
方法
@Override
public final synchronized void init() throws LifecycleException {
//第一次调用组件的init()方法,state的状态是NEW,不会进入该分支
if (!state.equals(LifecycleState.NEW)) {
invalidTransition(Lifecycle.BEFORE_INIT_EVENT);
}
try {
//设置状态为初始前,会打印一堆日志
setStateInternal(LifecycleState.INITIALIZING, null, false);
//初始化的具体流程,由具体的子类实现
initInternal();
//设置状态为初始完成
setStateInternal(LifecycleState.INITIALIZED, null, false);
} catch (Throwable t) {
handleSubClassException(t, "lifecycleBase.initFail", toString());
}
}
后面所有的组件的初始化过程均不贴上面方法代码。
废话不多说,下面开始正文
3.1 Server
初始化
/**
* Invoke a pre-startup initialization. This is used to allow connectors
* to bind to restricted ports under Unix operating environments.
*/
@Override
protected void initInternal() throws LifecycleException {
super.initInternal();
// Initialize utility executor
//创建ScheduledThreadPoolExecutor线程池
reconfigureUtilityExecutor(getUtilityThreadsInternal(utilityThreads));
//将这个定时任务线程池注册到JMX中
register(utilityExecutor, "type=UtilityExecutor");
// Register global String cache
// Note although the cache is global, if there are multiple Servers
// present in the JVM (may happen when embedding) then the same cache
// will be registered under multiple names
onameStringCache = register(new StringCache(), "type=StringCache");
// Register the MBeanFactory
MBeanFactory factory = new MBeanFactory();
factory.setContainer(this);
onameMBeanFactory = register(factory, "type=MBeanFactory");
// Register the naming resources
globalNamingResources.init();
// Populate the extension validator with JARs from common and shared
// class loaders
if (getCatalina() != null) {
ClassLoader cl = getCatalina().getParentClassLoader();
// Walk the class loader hierarchy. Stop at the system class loader.
// This will add the shared (if present) and common class loaders
while (cl != null && cl != ClassLoader.getSystemClassLoader()) {
if (cl instanceof URLClassLoader) {
URL[] urls = ((URLClassLoader) cl).getURLs();
for (URL url : urls) {
if (url.getProtocol().equals("file")) {
try {
File f = new File (url.toURI());
if (f.isFile() &&
f.getName().endsWith(".jar")) {
ExtensionValidator.addSystemResource(f);
}
} catch (URISyntaxException | IOException e) {
// Ignore
}
}
}
}
cl = cl.getParent();
}
}
// Initialize our defined Services
//上面的代码完全可以忽略掉,等我们弄懂了tomcat之后,再回头来看这些细节。
//重点在这里,这里会遍历解析server.xml文件service节点创建的对象,调用它的初始化方法
for (Service service : services) {
service.init();
}
}
Service
的类型为StandardService
,它是tomcat
定义的唯一实现。Catalina
的load()
方法会调用它的init()
方法, 然后init()
方法又会调用每一个Service
对象的init()
方法。
3.2 初始化Service
那么接下来,我们就看看
StandardService
的init()
方法做了什么?首先,你需要知道的是
tomcat
中每一个组件都实现了LifecycleBase
类,都定义了生命周期方法,StandardService
也不例外,所以它的init()
方法和Server
组件是一样的,我们只需要看initInternal()
方法。
protected final MapperListener mapperListener = new MapperListener(this);
//下面这3个字段会根据server.xml文件的解析结果给定初始值
private Engine engine = null;
protected final ArrayList<Executor> executors = new ArrayList<>();
protected Connector connectors[] = new Connector[0];
/**
* Invoke a pre-startup initialization. This is used to allow connectors
* to bind to restricted ports under Unix operating environments.
*/
@Override
protected void initInternal() throws LifecycleException {
super.initInternal();
// 初始化Engine,见3.2
if (engine != null) {
engine.init();
}
// Initialize any Executors
//初始化线程池
for (Executor executor : findExecutors()) {
if (executor instanceof JmxEnabled) {
((JmxEnabled) executor).setDomain(getDomain());
}
executor.init();
}
// Initialize mapper listener
//初始化监听器
mapperListener.init();
// Initialize our defined Connectors
// 初始化Connector,见3.3
synchronized (connectorsLock) {
for (Connector connector : connectors) {
connector.init();
}
}
}
Service
组件又初始化了4
个组件
Engine
:唯一实现StandardEngine
Executors
,默认未在server.xml
中配置<Executor name="tomcatThreadPool" namePrefix="catalina-exec-" maxThreads="150" minSpareThreads="4"/>
listener
Connector
3.3 初始化Engine
@Override
protected void initInternal() throws LifecycleException {
// Ensure that a Realm is present before any attempt is made to start
// one. This will create the default NullRealm if necessary.
// 身份认证相关,见3.31
getRealm();
// 调用父类的方法初始化了一个线程池,这个线程池非常重要,
// 需要重点关注,后面很多地方都会用到,见3.32
super.initInternal();
}
初始化
Engine
的过程中会做两件事情
- 初始化一个
Realm
,用来做身份认证- 创建一个线程池,供后续使用
3.3.1 身份认证相关
/**
* Obtain the configured Realm and provide a default Realm implementation
* when no explicit configuration is set.
*
* @return configured realm, or a {@link NullRealm} by default
*/
@Override
public Realm getRealm() {
//获取当前引擎的Realm,默认为LockOutRealm
Realm configured = super.getRealm();
// If no set realm has been called - default to NullRealm
// This can be overridden at engine, context and host level
//未指定,则使用NullRealm
if (configured == null) {
configured = new NullRealm();
this.setRealm(configured);
}
return configured;
}
Realm
其实是用来进行身份认证的,了解过shiro
的应该知道,shiro
中就是使用Realm
来进行认证授权的
3.3.2 创建一个线程池startStopExecutor
@Override
protected void initInternal() throws LifecycleException {
// 初始化线程池
reconfigureStartStopExecutor(getStartStopThreads());
super.initInternal();
}
private void reconfigureStartStopExecutor(int threads) {
// 使用新的线程池
if (threads == 1) {
// Use a fake executor
if (!(startStopExecutor instanceof InlineExecutorService)) {
startStopExecutor = new InlineExecutorService();
}
}
// 使用全局的线程池
else {
// Delegate utility execution to the Service
Server server = Container.getService(this).getServer();
server.setUtilityThreads(threads);
startStopExecutor = server.getUtilityExecutor();
}
}
我们可以发现,
Engine
初始化最终就是初始化一个线程池startStopExecutor
,供后续使用
3.4 初始化Connector
// 解析server.xml的connertor节点时,根据配置生成一个Connector和ProtocolHandler
// 默认情况下为Http11NioProtocol,即http1.1协议
protected final ProtocolHandler protocolHandler;
@Override
protected void initInternal() throws LifecycleException {
super.initInternal();
//如果配置的协议不正确,就不能自动生成一个ProtocolHandler对象,tomcat就会报错
if (protocolHandler == null) {
throw new LifecycleException(
sm.getString("coyoteConnector.protocolHandlerInstantiationFailed"));
}
// Initialize adapter
// Coyote用来处理请求,负责将Request适配为HttpServletRequest对象
adapter = new CoyoteAdapter(this);
protocolHandler.setAdapter(adapter);
// 将在server中配置的公用线程池应用到协议上
if (service != null) {
protocolHandler.setUtilityExecutor(service.getServer().getUtilityExecutor());
}
// Make sure parseBodyMethodsSet has a default
if (null == parseBodyMethodsSet) {
setParseBodyMethods(getParseBodyMethods());
}
//下面三个都是校验配置
if (protocolHandler.isAprRequired() && !AprStatus.isInstanceCreated()) {
throw new LifecycleException(sm.getString("coyoteConnector.protocolHandlerNoAprListener",
getProtocolHandlerClassName()));
}
if (protocolHandler.isAprRequired() && !AprStatus.isAprAvailable()) {
throw new LifecycleException(sm.getString("coyoteConnector.protocolHandlerNoAprLibrary",
getProtocolHandlerClassName()));
}
if (AprStatus.isAprAvailable() && AprStatus.getUseOpenSSL() &&
protocolHandler instanceof AbstractHttp11JsseProtocol) {
AbstractHttp11JsseProtocol<?> jsseProtocolHandler =
(AbstractHttp11JsseProtocol<?>) protocolHandler;
if (jsseProtocolHandler.isSSLEnabled() &&
jsseProtocolHandler.getSslImplementationName() == null) {
// OpenSSL is compatible with the JSSE configuration, so use it if APR is available
jsseProtocolHandler.setSslImplementationName(OpenSSLImplementation.class.getName());
}
}
try {
// ProtocolHandler初始化
protocolHandler.init();
} catch (Exception e) {
throw new LifecycleException(
sm.getString("coyoteConnector.protocolHandlerInitializationFailed"), e);
}
}
Connector
初始化做的事情
- 校验用户在
server.xml
中connector
节点的配置是否正确- 带动
ProtocolHandler
进行初始化,见3.5
3.5 初始化ProtocolHandler
@Override
public void init() throws Exception {
// Upgrade protocols have to be configured first since the endpoint
// init (triggered via super.init() below) uses this list to configure
// the list of ALPN protocols to advertise
for (UpgradeProtocol upgradeProtocol : upgradeProtocols) {
configureUpgradeProtocol(upgradeProtocol);
}
//
super.init();
// Set the Http11Protocol (i.e. this) for any upgrade protocols once
// this has completed initialisation as the upgrade protocols may expect this
// to be initialised when the call is made
for (UpgradeProtocol upgradeProtocol : upgradeProtocols) {
upgradeProtocol.setHttp11Protocol(this);
}
}
核心逻辑在父类
init()
方法中
/**
* Endpoint that provides low-level network I/O - must be matched to the
* ProtocolHandler implementation (ProtocolHandler using NIO, requires NIO
* Endpoint etc.).
* tomcat 9 默认为NioEndpoint
*/
private final AbstractEndpoint<S,?> endpoint;
/*
* NOTE: There is no maintenance of state or checking for valid transitions
* within this class. It is expected that the connector will maintain state
* and prevent invalid state transitions.
*/
@Override
public void init() throws Exception {
if (getLog().isInfoEnabled()) {
getLog().info(sm.getString("abstractProtocolHandler.init", getName()));
logPortOffset();
}
if (oname == null) {
// Component not pre-registered so register it
oname = createObjectName();
if (oname != null) {
Registry.getRegistry(null, null).registerComponent(this, oname, null);
}
}
if (this.domain != null) {
ObjectName rgOname = new ObjectName(domain + ":type=GlobalRequestProcessor,name=" + getName());
this.rgOname = rgOname;
Registry.getRegistry(null, null).registerComponent(
getHandler().getGlobal(), rgOname, null);
}
String endpointName = getName();
endpoint.setName(endpointName.substring(1, endpointName.length()-1));
endpoint.setDomain(domain);
// 初始化Endpoint,绑定端口和主机
endpoint.init();
}
带动
Endpoint
初始化,绑定端口和主机
private boolean bindOnInit = true;
private volatile BindState bindState = BindState.UNBOUND;
public final void init() throws Exception {
if (bindOnInit) {
// 绑定端口和主机
bindWithCleanup();
// 将端点状态置为BindState.BOUND_ON_INIT,表明已经初始化
bindState = BindState.BOUND_ON_INIT;
}
if (this.domain != null) {
// Register endpoint (as ThreadPool - historical name)
oname = new ObjectName(domain + ":type=ThreadPool,name=\"" + getName() + "\"");
Registry.getRegistry(null, null).registerComponent(this, oname, null);
ObjectName socketPropertiesOname = new ObjectName(domain +
":type=SocketProperties,name=\"" + getName() + "\"");
socketProperties.setObjectName(socketPropertiesOname);
Registry.getRegistry(null, null).registerComponent(socketProperties, socketPropertiesOname, null);
for (SSLHostConfig sslHostConfig : findSslHostConfigs()) {
registerJmx(sslHostConfig);
}
}
}
private void bindWithCleanup() throws Exception {
try {
// 子类NioEndpoint重写该方法,完成绑定
bind();
} catch (Throwable t) {
// Ensure open sockets etc. are cleaned up if something goes
// wrong during bind
ExceptionUtils.handleThrowable(t);
unbind();
throw t;
}
}
子类
NioEndpoint
重写bind()
方法, 完成绑定
/**
* Initialize the endpoint.
*/
@Override
public void bind() throws Exception {
// 绑定端口和主机
initServerSocket();
setStopLatch(new CountDownLatch(1));
// Initialize SSL if needed
initialiseSsl();
selectorPool.open(getName());
}
// Separated out to make it easier for folks that extend NioEndpoint to
// implement custom [server]sockets
protected void initServerSocket() throws Exception {
if (getUseInheritedChannel()) {
// 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"));
}
} else if (getUnixDomainSocketPath() != null) {
SocketAddress sa = JreCompat.getInstance().getUnixDomainSocketAddress(getUnixDomainSocketPath());
serverSock = JreCompat.getInstance().openUnixDomainServerSocketChannel();
serverSock.bind(sa, getAcceptCount());
if (getUnixDomainSocketPathPermissions() != null) {
FileAttribute<Set<PosixFilePermission>> attrs =
PosixFilePermissions.asFileAttribute(PosixFilePermissions.fromString(getUnixDomainSocketPathPermissions()));
Path path = Paths.get(getUnixDomainSocketPath());
Files.setAttribute(path, attrs.name(), attrs.value());
}
}
// 绑定过程走这个分支
else {
serverSock = ServerSocketChannel.open();
socketProperties.setProperties(serverSock.socket());
InetSocketAddress addr = new InetSocketAddress(getAddress(), getPortWithOffset());
serverSock.bind(addr, getAcceptCount());
}
// 设置阻塞
serverSock.configureBlocking(true); //mimic APR behavior
}
了解
NIO
的应该知道,这上面就服务器端的程序的创建的过程
4 start()
方法,启动tomcat
服务
/**
* Start the Catalina daemon.
* @throws Exception Fatal start error
*/
public void start() throws Exception {
if (catalinaDaemon == null) {
init();
}
// 反射调用Catalina的start()方法
Method method = catalinaDaemon.getClass().getMethod("start", (Class [])null);
method.invoke(catalinaDaemon, (Object [])null);
}
该方法反射调用
Catalina
的start()
方法,我们重点看Catalina
的start()
方法
/**
* Start a new server instance.
*/
public void start() {
// 如果没有Server,就重写调用load()方法解析server.xml
if (getServer() == null) {
load();
}
// 还找不到,直接返回,tomcat不能正常启动
if (getServer() == null) {
log.fatal(sm.getString("catalina.noServer"));
return;
}
long t1 = System.nanoTime();
// Start the new server
try {
// 启动server,见4.1
getServer().start();
} catch (LifecycleException e) {
log.fatal(sm.getString("catalina.serverStartFail"), e);
try {
getServer().destroy();
} catch (LifecycleException e1) {
log.debug("destroy() failed for failed Server ", e1);
}
return;
}
if (log.isInfoEnabled()) {
log.info(sm.getString("catalina.startup", Long.toString(TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - t1))));
}
if (generateCode) {
// Generate loader which will load all generated classes
generateLoader();
}
// Register shutdown hook
if (useShutdownHook) {
if (shutdownHook == null) {
shutdownHook = new CatalinaShutdownHook();
}
Runtime.getRuntime().addShutdownHook(shutdownHook);
// If JULI is being used, disable JULI's shutdown hook since
// shutdown hooks run in parallel and log messages may be lost
// if JULI's hook completes before the CatalinaShutdownHook()
LogManager logManager = LogManager.getLogManager();
if (logManager instanceof ClassLoaderLogManager) {
((ClassLoaderLogManager) logManager).setUseShutdownHook(
false);
}
}
if (await) {
await();
stop();
}
}
start()
方法是生命周期方法,所以这里我将它们父类LifecycleBase
的start()
方法先展示出来,后面不在贴这个方法的源码了
下面就是父类LifecycleBase
的init()
方法
/**
* {@inheritDoc}
*/
@Override
public final synchronized void start() throws LifecycleException {
// 三种状态之一就表明tomcat已经启动或正在启动,那么就跳过后面的启动流程
if (LifecycleState.STARTING_PREP.equals(state) || LifecycleState.STARTING.equals(state) ||
LifecycleState.STARTED.equals(state)) {
if (log.isDebugEnabled()) {
Exception e = new LifecycleException();
log.debug(sm.getString("lifecycleBase.alreadyStarted", toString()), e);
} else if (log.isInfoEnabled()) {
log.info(sm.getString("lifecycleBase.alreadyStarted", toString()));
}
return;
}
// 根据状态执行对应的操作
if (state.equals(LifecycleState.NEW)) {
init();
} else if (state.equals(LifecycleState.FAILED)) {
stop();
} else if (!state.equals(LifecycleState.INITIALIZED) &&
!state.equals(LifecycleState.STOPPED)) {
invalidTransition(Lifecycle.BEFORE_START_EVENT);
}
try {
// 设置状态为STARTING_PREP预启动
setStateInternal(LifecycleState.STARTING_PREP, null, false);
// 启动组件
startInternal();
// 根据状态执行对应的操作
if (state.equals(LifecycleState.FAILED)) {
// This is a 'controlled' failure. The component put itself into the
// FAILED state so call stop() to complete the clean-up.
stop();
} else if (!state.equals(LifecycleState.STARTING)) {
// Shouldn't be necessary but acts as a check that sub-classes are
// doing what they are supposed to.
invalidTransition(Lifecycle.AFTER_START_EVENT);
} else {
setStateInternal(LifecycleState.STARTED, null, false);
}
} catch (Throwable t) {
// This is an 'uncontrolled' failure so put the component into the
// FAILED state and throw an exception.
handleSubClassException(t, "lifecycleBase.startFail", toString());
}
}
该方法是
LifecycleBase
的一个方法, 用来包装所有组件的启动过程,真正的业务逻辑都在startInternal()
方法中
4.1 启动Server
/**
* Start nested components ({@link Service}s) and implement the requirements
* of {@link org.apache.catalina.util.LifecycleBase#startInternal()}.
*
* @exception LifecycleException if this component detects a fatal error
* that prevents this component from being used
*/
@Override
protected void startInternal() throws LifecycleException {
fireLifecycleEvent(CONFIGURE_START_EVENT, null);
// 设置状态为启动中
setState(LifecycleState.STARTING);
// 全局命名服务启动
globalNamingResources.start();
// Start our defined Services
// 启动Service组件
synchronized (servicesLock) {
for (Service service : services) {
service.start();
}
}
if (periodicEventDelay > 0) {
monitorFuture = getUtilityExecutor().scheduleWithFixedDelay(
() -> startPeriodicLifecycleEvent(), 0, 60, TimeUnit.SECONDS);
}
}
带动
Service
组件启动
4.2 启动Service
/**
* Start nested components ({@link Executor}s, {@link Connector}s and
* {@link Container}s) and implement the requirements of
* {@link org.apache.catalina.util.LifecycleBase#startInternal()}.
*
* @exception LifecycleException if this component detects a fatal error
* that prevents this component from being used
*/
@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,见4.3
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,见4.4
connector.start();
}
}
}
}
在这个方法中,我们重点关注
Engine
和Connector
的start()
方法,看看它是如何启动这两个组件的?
4.3 启动Engine
/**
* Start this component and implement the requirements
* of {@link org.apache.catalina.util.LifecycleBase#startInternal()}.
*
* @exception LifecycleException if this component detects a fatal error
* that prevents this component from being used
*/
@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();
}
下面是它父类的
startInternal()
方法
/**
* Start this component and implement the requirements
* of {@link org.apache.catalina.util.LifecycleBase#startInternal()}.
*
* @exception LifecycleException if this component detects a fatal error
* that prevents this component from being used
*/
@Override
protected synchronized void startInternal() throws LifecycleException {
// Start our subordinate components, if any
logger = null;
getLogger();
// Cluster以后再说,现在还不知道有什么作用
Cluster cluster = getClusterInternal();
if (cluster instanceof Lifecycle) {
((Lifecycle) cluster).start();
}
Realm realm = getRealmInternal();
if (realm instanceof Lifecycle) {
((Lifecycle) realm).start();
}
// Start our child containers, if any
// 查找拥有的子容器
Container children[] = findChildren();
List<Future<Void>> results = new ArrayList<>();
for (Container child : children) {
// 包装为StartChild,交给线程池处理
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
// 启动流水线Pipeline,你可以把它理解为过滤器链,Valve理解为过滤器
if (pipeline instanceof Lifecycle) {
((Lifecycle) pipeline).start();
}
setState(LifecycleState.STARTING);
// Start our thread
if (backgroundProcessorDelay > 0) {
monitorFuture = Container.getService(ContainerBase.this).getServer()
.getUtilityExecutor().scheduleWithFixedDelay(
new ContainerBackgroundProcessorMonitor(), 0, 60, TimeUnit.SECONDS);
}
}
总结一下
Engine
启动做的事情
- 将
Engine
的子容器Host
的处理过程交给线程池处理。注意,这个Host
是用户在server.xml
文件中配置Host
节点的那个,如果用户配置了多个Host
节点,那么就会使用多个线程处理,加快启动速度。- 启动流水线
Pipeline
,你可以把它理解为过滤器链,Valve
理解为过滤器
4.3.1 启动Host
(由另外的线程启动)
我们先来看一下这个StartChild
,这个类是ContainerBase
的一个嵌套类,没什么逻辑,主要实现了Callable
接口,用来启动Host
private static class StartChild implements Callable<Void> {
private Container child;
public StartChild(Container child) {
this.child = child;
}
@Override
public Void call() throws LifecycleException {
// 子容器的Start()方法启动子容器
child.start();
return null;
}
}
下面是这4
个容器的关系图
Engine
和Context
、Host
、Wrapper
都是一个容器,实现Container
接口,都继承了ContainerBase
类。并且它们之间的层级关系是Engine
->Host
->Context
->Wrapper
接下来我们就看看启动过程中做了什么?
所有子容器组件都实现了生命周期接口
Lifecycle
,所以我们直接看它启动的核心方法startInternal()
/**
* The Java class name of the default error reporter implementation class
* for deployed web applications.
* 主机使用的错误报告Valve类的完全限定名,已经写死在StandardHost类中
*/
private String errorReportValveClass =
"org.apache.catalina.valves.ErrorReportValve";
/**
* Start this component and implement the requirements
* of {@link org.apache.catalina.util.LifecycleBase#startInternal()}.
*
* @exception LifecycleException if this component detects a fatal error
* that prevents this component from being used
*/
@Override
protected synchronized void startInternal() throws LifecycleException {
// Set error report valve
// 获取当前主机使用的错误报告Valve类的完全限定名
String errorValve = getErrorReportValveClass();
if ((errorValve != null) && (!errorValve.equals(""))) {
try {
boolean found = false;
// 得到当前主机下配置的所有Valve类
// 默认情况下有两个,分别为AccessLogValve,StandardHostValve
// AccessLogValve是在server.xml文件中配置的
// StandardHostValue则是Host自带的
Valve[] valves = getPipeline().getValves();
// 遍历,如果用户在server.xml中配置了,那就使用用户配置的
for (Valve valve : valves) {
// 比较两个类的完全限定名
if (errorValve.equals(valve.getClass().getName())) {
found = true;
break;
}
}
// 否则使用系统默认的错误报告Valve
if(!found) {
// 反射实例化系统默认的错误报告Valve类
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);
}
}
// 这个方法流程和StandardEngine是完全一模一样的,都是父类ContainerBase的方法,主要做三个事情
// 1.它也会去找它的子容器Context,并把它的处理过程交给线程池完成
// 2.还会启动Host中的Pepeline,三个,
// 分别为ErrorReportValve,AccessLogValve,StandardHostValue
// 3.发布事件LifecycleState.STARTING,HostConfig监听到该事件后,
// 开始扫描解析webapps目录下的war包和Context,见(1-3)
super.startInternal();
}
下面是父类ContainerBase
的startInternal()
方法
/**
* Start this component and implement the requirements
* of {@link org.apache.catalina.util.LifecycleBase#startInternal()}.
*
* @exception LifecycleException if this component detects a fatal error
* that prevents this component from being used
*/
@Override
protected synchronized void startInternal() throws LifecycleException {
// Start our subordinate components, if any
logger = null;
getLogger();
// Cluster以后再说,现在还不知道有什么作用
Cluster cluster = getClusterInternal();
if (cluster instanceof Lifecycle) {
((Lifecycle) cluster).start();
}
Realm realm = getRealmInternal();
if (realm instanceof Lifecycle) {
((Lifecycle) realm).start();
}
// Start our child containers, if any
// 查找拥有的子容器Context
Container children[] = findChildren();
List<Future<Void>> results = new ArrayList<>();
for (Container child : children) {
// 包装为StartChild,交给线程池处理
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
// 启动流水线Pipeline,你可以把它理解为过滤器链,Valve理解为过滤器
// 启动三个ErrorReportValve,AccessLogValve,StandardHostValue,见(1-2)
if (pipeline instanceof Lifecycle) {
((Lifecycle) pipeline).start();
}
// 发布事件,扫描解析webapps目录,见(1-3)
setState(LifecycleState.STARTING);
// Start our thread
if (backgroundProcessorDelay > 0) {
monitorFuture = Container.getService(ContainerBase.this).getServer()
.getUtilityExecutor().scheduleWithFixedDelay(
new ContainerBackgroundProcessorMonitor(), 0, 60, TimeUnit.SECONDS);
}
}
4.3.2 启动Pipeline
Pipeline
也实现了生命周期接口Lifecycle
,所以我们直接看它启动的核心方法startInternal()
/**
* The first valve associated with this Pipeline.
*/
protected Valve first = null;
/**
* Start {@link Valve}s) in this pipeline and implement the requirements
* of {@link LifecycleBase#startInternal()}.
*
* @exception LifecycleException if this component detects a fatal error
* that prevents this component from being used
*/
@Override
protected synchronized void startInternal() throws LifecycleException {
// Start the Valves in our pipeline (including the basic), if any
Valve current = first;
if (current == null) {
current = basic;
}
// 遍历,启动容器拥有的每一个valve
while (current != null) {
if (current instanceof Lifecycle)
((Lifecycle) current).start();
current = current.getNext();
}
// 发布事件,被观察者监听处理,这里的观察者数量为0
setState(LifecycleState.STARTING);
}
StandardHost
会启动三个Valve
,分别为ErrorReportValve
,AccessLogValve
,StandardHostValue
4.3.3 发布事件,扫描解析webapps
目录
/**
* Provides a mechanism for sub-classes to update the component state.
* Calling this method will automatically fire any associated
* {@link Lifecycle} event. It will also check that any attempted state
* transition is valid for a sub-class.
*
* @param state The new state for this component
* @throws LifecycleException when attempting to set an invalid state
*/
protected synchronized void setState(LifecycleState state) throws LifecycleException {
setStateInternal(state, null, true);
}
private synchronized void setStateInternal(LifecycleState state, Object data, boolean check)
throws LifecycleException {
if (log.isDebugEnabled()) {
log.debug(sm.getString("lifecycleBase.setState", this, state));
}
if (check) {
// Must have been triggered by one of the abstract methods (assume
// code in this class is correct)
// null is never a valid state
if (state == null) {
invalidTransition("null");
// Unreachable code - here to stop eclipse complaining about
// a possible NPE further down the method
return;
}
// Any method can transition to failed
// startInternal() permits STARTING_PREP to STARTING
// stopInternal() permits STOPPING_PREP to STOPPING and FAILED to
// STOPPING
// 检查组件状态
if (!(state == LifecycleState.FAILED ||
(this.state == LifecycleState.STARTING_PREP &&
state == LifecycleState.STARTING) ||
(this.state == LifecycleState.STOPPING_PREP &&
state == LifecycleState.STOPPING) ||
(this.state == LifecycleState.FAILED &&
state == LifecycleState.STOPPING))) {
// No other transition permitted
invalidTransition(state.name());
}
}
this.state = state;
// lifecycleEvent="start"
String lifecycleEvent = state.getLifecycleEvent();
if (lifecycleEvent != null) {
// 发布事件
fireLifecycleEvent(lifecycleEvent, data);
}
}
/**
* The list of registered LifecycleListeners for event notifications.
* 默认情况下,里面只有一个监听器,那就是HostConfig
*/
private final List<LifecycleListener> lifecycleListeners = new CopyOnWriteArrayList<>();
/**
* Allow sub classes to fire {@link Lifecycle} events.
*
* @param type Event type
* @param data Data associated with event.
*/
protected void fireLifecycleEvent(String type, Object data) {
// this就是当前StandardHost对象,type为"start"
LifecycleEvent event = new LifecycleEvent(this, type, data);
for (LifecycleListener listener : lifecycleListeners) {
// HostConfig完成对webapps目录的扫描解析
listener.lifecycleEvent(event);
}
}
其实这个
setState()
方法就是用来发布事件的,典型的观察者模式注意:
HostConfig
扫描到Context
后,会自动的将其添加到Host
中,作为它的子容器,并同时调用这些子容器的start()
方法, 启动子容器Context
4.4 启动Connector
/**
* Begin processing requests via this Connector.
*
* @exception LifecycleException if a fatal startup error occurs
*/
@Override
protected void startInternal() throws LifecycleException {
// Validate settings before starting
if (getProperty("unixDomainSocketPath") == null && getPortWithOffset() < 0) {
throw new LifecycleException(sm.getString(
"coyoteConnector.invalidPort", Integer.valueOf(getPortWithOffset())));
}
// 虽然发布了事件,但是并没有监听器监听
setState(LifecycleState.STARTING);
try {
// 启动ProtocolHandler
protocolHandler.start();
} catch (Exception e) {
throw new LifecycleException(
sm.getString("coyoteConnector.protocolHandlerStartFailed"), e);
}
}
Connector
只是简单的带动ProtocolHandler
启动
4.5 启动ProtocolHandler
tomcat 9
使用的是Http11NioProtocol
/**
* 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;
@Override
public void start() throws Exception {
if (getLog().isInfoEnabled()) {
getLog().info(sm.getString("abstractProtocolHandler.start", getName()));
logPortOffset();
}
// 启动Endipoint
endpoint.start();
monitorFuture = getUtilityExecutor().scheduleWithFixedDelay(
() -> {
if (!isPaused()) {
startAsyncTimeout();
}
}, 0, 60, TimeUnit.SECONDS);
}
带动
NioEndpoint
启动
private volatile BindState bindState = BindState.UNBOUND;
public final void start() throws Exception {
// 此时不为UNBOUND,Endpoint初始化的时候已经绑定端口和主机了
if (bindState == BindState.UNBOUND) {
bindWithCleanup();
bindState = BindState.BOUND_ON_START;
}
// 启动NioEndpoint
startInternal();
}
启动NioEndpoint
/**
* 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();
}
initializeConnectionLatch();
// Start poller thread
// 这个Poller很重要,它实现了Runable接口,Acceptor接收到Socket连接后
// 就将Socket全权交给这个Poller异步处理
poller = new Poller();
Thread pollerThread = new Thread(poller, getName() + "-ClientPoller");
pollerThread.setPriority(threadPriority);
pollerThread.setDaemon(true);
pollerThread.start();
// 启动Acceptor,开始接收Socket连接
startAcceptorThread();
}
}
启动
Acceptor
,开始接收Socket
连接
Acceptor
由一个单独的线程处理,也就是说有一个线程专门接收Socket
连接,但不处理,处理过程交给Poller
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();
}
接下来看看
Acceptor
源码,它是如何接收一个Socket
连接的?
4.6 Acceptor
接收Socket
连接
@Override
public void run() {
int errorDelay = 0;
try {
// Loop until we receive a shutdown command
while (!stopCalled) {
// Loop if endpoint is paused
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
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
// 线程阻塞,接收连接,见4.6.1
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
// 把连接交给Poller处理,见4.6.2
if (!endpoint.setSocketOptions(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;
}
4.6.1 接收Socket
连接
/**
* Server socket "pointer".
*/
private volatile ServerSocketChannel serverSock = null;
@Override
protected SocketChannel serverSocketAccept() throws Exception {
return serverSock.accept();
}
终于看到原生的
nio
接收连接的代码了,不容易啊
4.6.2 把连接交给Poller
处理
/**
* Process the specified connection.
* @param socket The socket channel
* @return <code>true</code> if the socket was correctly configured
* and processing may continue, <code>false</code> if the socket needs to be
* close immediately
*/
@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
NioSocketWrapper newWrapper = new NioSocketWrapper(channel, this);
channel.reset(socket, newWrapper);
connections.put(socket, newWrapper);
socketWrapper = newWrapper;
// Set socket properties
// Disable blocking, polling will be used
socket.configureBlocking(false);
if (getUnixDomainSocketPath() == null) {
socketProperties.setProperties(socket.socket());
}
socketWrapper.setReadTimeout(getConnectionTimeout());
socketWrapper.setWriteTimeout(getConnectionTimeout());
socketWrapper.setKeepAliveLeft(NioEndpoint.this.getMaxKeepAliveRequests());
// 然后注册到Poller对象中,由Poller处理
poller.register(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;
}
Acceptor
不负责处理接收到的socket
连接,而是把它交给了Poller
处理
到此为止,tomcat
的启动流程就说完了,但是还有一些细节地方没有探究,后面会补上