tomcat总体结构
Server:接受请求并解析,完成相关任务,返回处理结果。通常情况下使用Socket监听服务器指定端口来实现该功能,一个最简单的服务设计如下:
Start():启动服务器,打开socket连接,监听服务端口,接受客户端请求、处理、返回响应
Stop():关闭服务器,释放资源
缺点:请求监听和请求处理放一起扩展性很差(协议的切换 tomcat独立部署使用HTTP协议,与Apache集成时使用AJP协议
改进一:网络协议与请求处理分离:一个Server包含多个Connector(链接器)和Container(容器)
Connector:开启Socket并监听客户端请求,返回响应数据;
Container:负责具体的请求处理
缺点:Connector接受的请求由那个Container处理,需要建立映射规则
改进二:一个Server可以包含多个Service,每一个Service都是独立的,他们共享一个JVM以及系统类库。一个Service负责维护多个Connector和一个Container,这样来自Connector的请求只能有它所属的Service维护的Container处理。
在这里Container是一个通用的概念,为了明确功能,并与Tomcat中的组件名称相同,可以将Container命名为Engineer
在Engine容器中需要支持管理WEB应用,当接收到Connector的处理请求时,Engine容器能够找到一个合适的Web应用来处理,因此在上面设计的基础上增加Context来表示一个WEB应用,并且一个Engine可以包含多个Context。
缺点:应用服务器需要将每个域名抽象为一个虚拟主机
在一个web应用中,可以包含多个Servlet实例来处理来自不同的链接请求,因此我们还需要一个组件概念来表示Servlet定义,即Wrapper。在前面的多次Container容器中,有Engine、Host、Context、Wrapper等,可以理解为Container的子类.
tomcat源码生命周期分析:从官网上下载tomcat源码包,此次分析使用的版本tomcat-8.5.54
一般分为两类:链接器的实现(LifecycleBase),容器的实现(container)。及相关抽象类的实现。
BootStrap(引导程序)是Tomcat的入口。找到java目录下org.apache.catalina.startup包下的Bootstrap类。
Main方法和static语句块:
Static语句块的作用(看代码):在静态代码块中设置catalinaHome和catalinaBase两个路径
static {...
System.setProperty(Globals.CATALINA_HOME_PROP, catalinaHomeFile.getPath());//Globals.CATALINA_HOME_PROP="catalina.home",我们可以通过设置vm参数的形式传入:editConfig:-Dcatalina.home=catalina-home -Dcatalina.base=catalina-home
// Then base
String base = System.getProperty(Globals.CATALINA_BASE_PROP);...
}
mian方法的作用:实例化BootStrap 初始化BootStrap daemon.load(args) daemon.start();
main{...
try {
bootstrap.init();
}...
try {
String command = "start";
if (command.equals("startd")) {...
} 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")) {...
}...
}
我们先分析init方法: 初始化commonLoader、catalinaLoader、sharedLoader,设置catalinaDaemon为Catalina实例
init(){...
initClassLoaders(){
try {
commonLoader = createClassLoader("common", null);//摆脱了类加载的双亲委派机制
if (commonLoader == null) {
// no config file, default to this loader - we might be in a 'single' env.
commonLoader = this.getClass().getClassLoader();
}
catalinaLoader = createClassLoader("server", commonLoader);
sharedLoader = createClassLoader("shared", commonLoader);
...
};// 初始化commonLoader、catalinaLoader、sharedLoader
Class<?> startupClass = catalinaLoader.loadClass("org.apache.catalina.startup.Catalina");
Object startupInstance = startupClass.getConstructor().newInstance();
// 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);//此处实际是调用Catalina相关方法
catalinaDaemon = startupInstance;
}
load、start实际上是由Bootstrap反射调用Catalina的load、start,这一部分代码将在下面的Catalina部分进行分析。load阶段主要是通过读取conf/server.xml或者server-embed.xml,实例化Server、Service、Connector、Engine、Host等组件,并调用Lifecycle#init()完成初始化动作,以及发出INITIALIZING、INITIALIZED事件
load() {...
initDirs();//初始化jmx的环境变量
initNaming();// Before digester - it may be needed 初始化命名空间
Digester digester = createStartDigester();//Create and execute our Digester 定义解析server.xml的配置,告诉Digester哪个xml标签应该解析成什么类
...
// 如果还是加载不到xml,则直接return,省略部分代码......
try {
inputSource.setByteStream(inputStream);
// 把Catalina作为一个顶级实例
digester.push(this);
// 解析过程会实例化各个组件,比如Server、Container、Connector等
digester.parse(inputSource);
} catch (SAXParseException spe) {
// 处理异常......
}
} finally {
// 关闭IO流......
}
// 给Server设置catalina信息
getServer().setCatalina(this);
getServer().setCatalinaHome(Bootstrap.getCatalinaHomeFile());
getServer().setCatalinaBase(Bootstrap.getCatalinaBaseFile());
// Stream redirection
initStreams();
// 调用Lifecycle的init阶段
try {
getServer().init();//初始化Server,找到具体实现类方法
} catch (LifecycleException e) {
// ......
}
// ......
}
StandardServer.initInternal(){...//组件都继承了LifeCycleBase,该类中的init会调用initInternal()由子类具体去实现
// Initialize our defined Services
for (int i = 0; i < services.length; i++) {
services[i].init();//初始化service
}...
}
StandardService.initInternal(){...
engine.init();//需要注意的是此处engine初始化后并没有接着初始化子类host、context、wapper这几个是在star生命周期初始启动的
executor.init();
mapperListener.init();
connector.init();...//依次类推查看其他实现类的初始化
}
在connector->ProtocolHandler->AbstractProtocol调用init初始化的时候,还会去执行AbstractEndpoint的init方法,完成请求端口绑定、初始化NIO等操作
AbstractProtocol.init(){...
String endpointName = getName();
endpoint.setName(endpointName.substring(1, endpointName.length()-1));
endpoint.setDomain(domain);
endpoint.init();...
}
AbstractEndpoint.init() throws Exception {
if (bindOnInit) {
bind(){
}
NioEndpoint.bind(){
if (!getUseInheritedChannel()) {
serverSock = ServerSocketChannel.open();
socketProperties.setProperties(serverSock.socket());
InetSocketAddress addr = (getAddress()!=null?new InetSocketAddress(getAddress(),getPort()):new InetSocketAddress(getPort()));
serverSock.socket().bind(addr,getAcceptCount());
Engine初始化StandardEngine在init阶段,需要获取Realm(域)是用于对单个用户进行身份验证的底层安全领域的只读外观,并标识与这些用户相关联的安全角色。域可以在任何容器级别上附加,但是通常只附加到Context,或者更高级别的容器。