- 浏览器访问服务器使用的是Http协议,用于定义数据通信的格式
- 具体的数据传输使用的是TCP/IP协议
- Http协议对应于应用层协议,Tcp/IP协议对应于传输层/网络层
三次握手
四次挥手
流程图
说明
- Tomcat的两个重要身份:HTTP服务器()和Servlet容器
- HTTP服务器接收到请求之后把请求交给Servlet容器来处理
- Servlet容器通过Servlet接口调用业务类
流程图
说明
- HTTP服务器会把请求信息使用ServletRequest对象封装起来
- 进一步去调用Servlet容器中某个具体的Servlet(根据URL和Servlet的映射关系,找到相应的Servlet)
- 如果Servlet还没有被加载,就用反射机制创建这个Servlet,并调用Servlet的init方法来完成初始化
- 接着调用这个具体Servlet的service方法来处理请求,请求处理结果使用ServletResponse对象封装
- 把ServletResponse对象返回给HTTP服务器,HTTP服务器会把响应发送给客户端
架构图
说明
- Tomcat设计了两个核心组件:连接器(Connector)和容器(Container)来完成Tomcat的两个核心功能:HTTP处理器和Servlet容器
- 连接器(Connector)负责对外交流:处理Socket连接,负责网络字节流与Request和Response对象的转化;对应Tomcat连接器组件Coyote
- 容器(Container)负责内部处理:加载和管理Servlet,以及具体处理Request请求;对应Tomcat Servlet容器Catalina
组件图
说明
- Coyote是Tomcat中连接器的组件名称, 是对外的接口。客户端通过Coyote与服务器建立连接、发送请求并接受响应
- Coyote封装了底层的网络通信(Socket请求及响应处理),Coyote负责的是具体协议(应用层)和IO(传输层)相关内容
- Coyote使Catalina容器(容器组件)与具体的请求协议及IO操作方式完全解耦
- Coyote将Socket输入转换封装为Request对象,进一步封装成ServletRequest交由Catalina容器进行处理,处理请求完成后,Catalina返回的ServletResponse封装成Coyote提供的Response对象将结果写入输出流
Tomcat Coyote ⽀持的 IO模型与协议
说明:默认协议是HTTP/1.1,默认IO模型是NIO
流程图
Coyote 组件及作⽤
| 组件 | 作用描述 |
|---|---|
| EndPoint | EndPoint是Coyote通信端点,即通信监听的接口,是具体Socket接收和发送处理器,是对传输层的抽象,因此EndPoint是用来实现TCP/IP协议的 |
| Processor | Processor是Coyote协议处理接口,如果说EndPoint是用来实现TCP/IP协议的,那么Processor用来实现HTTP协议,Processor接收来自EndPoint的Socket,读取字节流解析成Tomcat的Request和Response对象,并通过Adapter将其提交到容器处理,Processor是对应用层协议的抽象 |
| ProtocolHandler | Coyote协议接口,通过Endpoint和Processor,实现针对具体协议的处理能力。Tomcat按照协议和I/O提供了6个实现类:AjpNioProtocol、AjpAprProtocol、AjpNio2Protocol、Http11NioProtocol、Http11Nio2Protocol、Http11AprProtocol |
| Adapter | 由于协议不同,客户端发过来的请求信息也不尽相同,Tomcat定义了自己的Request类来封装这些请求信息。ProtocolHandler接口负责解析请求并生成Tomcat Request类。但是这个Request对象不是标准的ServletRequest,不能用Tomcat Request作为参数来调用容器。Tomcat设计者的解决方案是引入CoyoteAdapter,连接器调用CoyoteAdapter的Sevice方法,传入的是Tomcat Request对象,CoyoteAdapter负责将Tomcat Request转成ServletRequest,再调用容器 |
模块分层结构图
说明
- Tomcat是一个由一系列可配置(conf/server.xml)的组件构成的Web容器,Catalina是Tomcat的servlet容器
- Tomcat本质上就是一款Servlet容器,因为Catalina才是Tomcat的核心,其他模块都是为Catalina提供支撑的
- 其他支撑模块 :
Coyote模块提供链接通信
Jasper模块提供JSP引擎
Naming提供JNDI服务
Juli提供日志服务
结构图
说明
- Catalina负责解析Tomcat的配置⽂件(server.xml) , 以此来创建服务器Server组件并进⾏管理
- Server服务器表示整个Catalina Servlet容器以及其它组件,负责组装并启动Servlet引擎,Tomcat连接器。Server通过实现Lifecycle接⼝,提供了⼀种优雅的启动和关闭整个系统的⽅式
- Service服务是Server内部的组件,⼀个Server包含多个Service。它将若⼲个Connector组件绑定到⼀个Container
- Container容器,负责处理⽤户的servlet请求,并返回对象给web⽤户的模块
结构图
说明
- Container的子容器Engine、Host、Context、Wrapper是逐层包含的关系,其中Engine是最顶层,每个service 最多只能有一个Engine, Engine里面可以有多个Host,每个Host下可以有多个Context,每个Context下可以有多个Wrapper
- Engine :引擎,用来管理多个站点,一个Service 最多只能有一个Engine
- Host :代表一个站点,也可以叫虚拟主机,通过配置Host 就可以添加站点
- Context :代表一个应用程序,对应着平时开发的一套程序,或者一个WEB-INF目录以及下面的web.xml 文件
- Wrapper :每个Wrapper 封装着一个servlet
核⼼配置在tomcat⽬录下conf/server.xml⽂件
2.1 主要标签结构2.2 Server标签
2.3 Service标签...
2.4 Executor标签...
2.5 Connector标签
2.6 Engine标签
2.7 Host标签...
2.8 Context标签...
3、手写实现迷你版Tomcat 3.1 思路
作为一个服务器软件提供服务,可以通过浏览器发送http请求,服务接收到请求进行处理,处理之后的结果可以返回浏览器客户端
- 提供服务,接受请求(Socket通信)
- 请求信息封装Request对象
- 客户端请求资源,资源分为静态资源(html)和动态资源(Servlet)
- 封装Response对象,资源返回给客户端浏览器
- 启动类Bootstrap.java
package server;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Executors;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class Bootstrap {
// 默认端口号
private int port = 8080;
private Map servletMap = new HashMap();
public int getPort() {
return port;
}
public void setPort(int port) {
this.port = port;
}
public void start() throws Exception {
ServerSocket serverSocket = new ServerSocket(port);
System.out.println("==========> MiniCat start,port:" + port);
// 加载web.xml配置文件
loadServlet();
// 自定义线程池
int corePoolSize = 10;
int maximumPoolSize = 50;
long keepAliveTime = 100L;
TimeUnit unit = TimeUnit.SECONDS;
BlockingQueue workQueue = new ArrayBlockingQueue<>(50);
ThreadFactory threadFactory = Executors.defaultThreadFactory();
RejectedExecutionHandler handler = new ThreadPoolExecutor.AbortPolicy();
ThreadPoolExecutor threadPoolExecutor =
new ThreadPoolExecutor(
corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, handler);
// 监听socket请求
while (true) {
Socket socket = serverSocket.accept();
// 创建线程
RequestProcessor requestProcessor = new RequestProcessor(socket, servletMap);
threadPoolExecutor.execute(requestProcessor);
}
}
public static void main(String[] args) {
Bootstrap bootstrap = new Bootstrap();
try {
bootstrap.start();
} catch (Exception e) {
e.printStackTrace();
}
}
private void loadServlet() {
String absolutePath = StaticResoutceUtil.getAbsolutePath("web.xml");
SAXReader reader = new SAXReader();
try {
Document document = reader.read(absolutePath);
Element element = document.getRootElement(); // 获取顶级节点
List servletElements = element.selectNodes("//servlet");
for (Element servletElement : servletElements) {
Element servletName = (Element) servletElement.selectSingleNode("//servlet-name");
Element servletClass = (Element) servletElement.selectSingleNode("//servlet-class");
// 获取servlet实例
Servlet servelt = (Servlet) Class.forName(servletClass.getStringValue()).newInstance();
// 根据servlet-name获取对应的servlet-mapping中的url-pattern>
Element servletMapping =
(Element)
element.selectSingleNode(
"/web-app/servlet-mapping[servlet-name='"
+ servletName.getStringValue()
+ "']");
String stringValue = servletMapping.selectSingleNode("url-pattern").getStringValue();
servletMap.put(stringValue, servelt);
}
} catch (DocumentException | ClassNotFoundException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
}
}
}
- 请求对象Request.java和相应对象Response.java
package server;
import java.io.IOException;
import java.io.InputStream;
public class Request {
private String method; // 请求方式(GET/POST)
private String url; // 请求资源
private InputStream inputStream; // 输入流
public String getMethod() {
return method;
}
public void setMethod(String method) {
this.method = method;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public InputStream getInputStream() {
return inputStream;
}
public void setInputStream(InputStream inputStream) {
this.inputStream = inputStream;
}
public Request() {}
public Request(InputStream inputStream) throws IOException {
this.inputStream = inputStream;
int count = 0;
while (count == 0) {
count = inputStream.available();
}
byte[] bytes = new byte[count];
inputStream.read(bytes);
String inputStr = new String(bytes);
String[] split = inputStr.split("\n"); // 根据换行符分隔
String firstLine = split[0]; // 获取第一行
String[] strs = firstLine.split(" ");
method = strs[0]; // 方法
url = strs[1]; // 请求资源
System.out.println(method);
System.out.println(url);
}
}
package server;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStream;
public class Response {
private OutputStream outputStream;
public OutputStream getOutputStream() {
return outputStream;
}
public void setOutputStream(OutputStream outputStream) {
this.outputStream = outputStream;
}
public Response(OutputStream outputStream) {
this.outputStream = outputStream;
}
public void outPut(String content) throws IOException {
outputStream.write(content.getBytes());
}
public void outPutHtml(String path) throws IOException {
String absoluteResourcePah = StaticResoutceUtil.getAbsolutePath(path);
File file = new File(absoluteResourcePah);
if (file.exists() && file.isFile()) {
// 资源存在,输出静态资源
StaticResoutceUtil.outPutStaticResource(new FileInputStream(file), outputStream);
} else {
// 资源不存在,输出404
outPut(HttpProtocolUtil.getHttpHeader404());
}
}
}
- 多线程处理类RequestProcessor.java
package server;
import java.io.InputStream;
import java.net.Socket;
import java.util.Map;
public class RequestProcessor extends Thread {
private Socket socket;
private Map servletMap;
public Socket getSocket() {
return socket;
}
public void setSocket(Socket socket) {
this.socket = socket;
}
public Map getServletMap() {
return servletMap;
}
public void setServletMap(Map servletMap) {
this.servletMap = servletMap;
}
public RequestProcessor(Socket socket, Map servletMap) {
this.socket = socket;
this.servletMap = servletMap;
}
@Override
public void run() {
try {
InputStream inputStream = socket.getInputStream();
int count = 0;
while (count == 0) {
count = inputStream.available();
}
// 封装请求信息
Request request = new Request(inputStream);
// 封装响应信息
Response response = new Response(socket.getOutputStream());
// 判断是否为静态资源
if (servletMap.get(request.getUrl()) == null) {
response.outPutHtml(request.getUrl());
} else {
// 动态资源servlet请求
Servlet httpServlet = servletMap.get(request.getUrl());
httpServlet.service(request, response);
}
socket.close();
} catch (Exception e) {
e.getStackTrace();
}
}
}
- Servlet接口定义Servlet.java
package server;
public interface Servlet {
public void init();
public void service(Request req, Response response);
public void destory();
}
- HttpServlet抽象类定义HttpServlet.java
package server;
public abstract class HttpServlet implements Servlet {
@Override
public void init() {}
@Override
public void service(Request req, Response response) {
if ("GET".equals(req.getMethod())) {
doGet(req, response);
} else {
doPost(req, response);
}
}
@Override
public void destory() {}
public abstract void doGet(Request req, Response response);
public abstract void doPost(Request req, Response response);
}
- 业务类Servlet定义MyServlet.java
package server;
import java.io.IOException;
public class MyServlet extends HttpServlet {
@Override
public void doGet(Request req, Response response) {
// 执行doGet方法
System.out.println("doGet");
String context = "do Get
";
try {
response.outPut(HttpProtocolUtil.getHttpHeader200(context.length()) + context);
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void doPost(Request req, Response response) {
// 执行doPost方法
System.out.println("doPost");
String context = "do Post
";
try {
response.outPut(HttpProtocolUtil.getHttpHeader200(context.length()) + context);
} catch (IOException e) {
e.printStackTrace();
}
}
}
- 工具类之Http协议工具类HttpProtocolUtil.java
package server;
public class HttpProtocolUtil {
public static String getHttpHeader200(int contentLength) {
return "HTTP/1.1 200 ok n"
+ "Content-Type: text/html n"
+ "Content-Length:"
+ contentLength
+ "n"
+ "rn";
}
public static String getHttpHeader404() {
String str404 = "404 not found";
return "HTTP/1.1 404 NOT Found n"
+ "Content-Type: text/html n"
+ "Content-Length:"
+ str404.length()
+ "n"
+ "rn"
+ str404;
}
}
- 工具类之Http静态资源请求处理工具类StaticResoutceUtil.java
package server;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
public class StaticResoutceUtil {
public static String getAbsolutePath(String path) {
String absolutePath = StaticResoutceUtil.class.getResource("/").getPath();
return absolutePath.replaceAll("\\", "/") + path;
}
public static void outPutStaticResource(InputStream inputStream, OutputStream outputStream)
throws IOException {
int count = 0;
while (count == 0) {
count = inputStream.available();
}
int resourceSize = count;
// 输出http相应头
outputStream.write(HttpProtocolUtil.getHttpHeader200(resourceSize).getBytes());
// 读取内容输出
long written = 0; // 已经读取的内容长度
int byteSize = 1024; // 计划每次缓冲的长度
byte[] bytes = new byte[byteSize];
// 判断已经读取的内容长度如果小于总长度,需要继续读取
while (written < resourceSize) {
// 判断本次要读取的长度是否小于1024
if (resourceSize - written < byteSize) {
byteSize = resourceSize - (int) written;
bytes = new byte[byteSize];
}
inputStream.read(bytes);
outputStream.write(bytes);
written += byteSize;
outputStream.flush();
}
}
}
- web.xml配置文件
4、源码构建及核心流程源码剖析 4.1 源码构建 4.1.1 下载源码 4.1.2 源码导入IDE之前准备工作myServlet server.MyServlet myServlet /myServlet
- 解压tar.gz压缩包,得到目录apache-tomcat-8.5.50-src
- 进入apache-tomcat-8.5.50-src目录,创建一个pom.xml文件,文件内容如下
4.0.0 org.apache.tomcat apache-tomcat-8.5.50-src Tomcat8.5 8.5 Tomcat8.5 java java org.apache.maven.plugins maven-compiler-plugin 3.1 UTF-8 1.8 1.8 org.easymock easymock 3.4 ant ant 1.7.0 wsdl4j wsdl4j 1.6.2 javax.xml jaxrpc 1.1 org.eclipse.jdt.core.compiler ecj 4.5.1 javax.xml.soap javax.xml.soap-api 1.4.0
- 在apache-tomcat-8.5.50-src目录中创建source文件夹
- 将conf、webapps目录移动到刚刚创建的source文件夹中
- 将源码工程导入到IDEA中
- 给tomcat的源码程序启动类Bootstrap配置VM参数,因为tomcat源码运行也需要加载配置文件
-Dcatalina.home=/tomcat源文件目录/apache-tomcat-8.5.50-src/source -Dcatalina.base=/tomcat源文件目录/apache-tomcat-8.5.50-src/source -Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager -Djava.util.logging.config.file=/tomcat源文件目录//apache-tomcat-8.5.50-src/source/conf/logging.properties
- 运行Bootstrap类的main函数,此时就启动了tomcat,启动时候会去加载所配置的conf目录下的server.xml等配置文件,访问8080会出现如下错误==(错误原因:Jsp引擎Jasper没有被初始化)==
解决办法
在tomcat的源码ContextConfig类中的configureStart方法中增加一行代码将Jsp引擎初始化
// 初始化jsp搜索引擎 context.addServletContainerInitializer(new JasperInitializer(), null);4.2 核心源码剖析 4.2.1 生命周期接口Lifecycle
什么是Lifecycle
- Tomcat里面有各种各样的组件,每个组件各司其职,组件之间又相互协作共同完成web服务器这样的工程。在这些组件之上,Lifecycle(生命周期机制)至关重要
- Lifecycle,其实就是一个状态机,对组件的由生到死状态的管理
Lifecycle的方法
public interface Lifecycle {
// 添加监听器
public void addLifecycleListener(LifecycleListener listener);
// 获取所以监听器
public LifecycleListener[] findLifecycleListeners();
// 移除某个监听器
public void removeLifecycleListener(LifecycleListener listener);
// 初始化方法
public void init() throws LifecycleException;
// 启动方法
public void start() throws LifecycleException;
// 停止方法,和start对应
public void stop() throws LifecycleException;
// 销毁方法,和init对应
public void destroy() throws LifecycleException;
// 获取生命周期状态
public LifecycleState getState();
// 获取字符串类型的生命周期状态
public String getStateName();
}
Lifecycle生命周期接口继承体系
Lifecycle的基本实现LifecycleBase的核心方法
- 增加、删除和获取监听器(操作监听器集合lifecycleListeners)
private final ListlifecycleListeners = new CopyOnWriteArrayList<>(); @Override public void addLifecycleListener(LifecycleListener listener) { lifecycleListeners.add(listener); } @Override public LifecycleListener[] findLifecycleListeners() { return lifecycleListeners.toArray(new LifecycleListener[0]); } @Override public void removeLifecycleListener(LifecycleListener listener) { lifecycleListeners.remove(listener); }
- 初始化方法init(),抽象方法initInternal()需要组件自行实现
@Override
public final synchronized void init() throws LifecycleException {
// 非NEW状态,不允许调用init()方法
if (!state.equals(LifecycleState.NEW)) {
invalidTransition(Lifecycle.BEFORE_INIT_EVENT);
}
try {
// 初始化逻辑之前,先将状态变更为`INITIALIZING`
setStateInternal(LifecycleState.INITIALIZING, null, false);
// 初始化,该方法为一个abstract方法,需要组件自行实现
initInternal();
// 初始化完成之后,状态变更为`INITIALIZED`
setStateInternal(LifecycleState.INITIALIZED, null, false);
} catch (Throwable t) {
// 初始化的过程中,可能会有异常抛出,这时需要捕获异常,并将状态变更为`FAILED`
ExceptionUtils.handleThrowable(t);
setStateInternal(LifecycleState.FAILED, null, false);
throw new LifecycleException(
sm.getString("lifecycleBase.initFail",toString()), t);
}
}
- 启动方法start(),抽象方法startInternal()需要组件自行实现
@Override
public final synchronized void start() throws LifecycleException {
// `STARTING_PREP`、`STARTING`和`STARTED时,将忽略start()逻辑
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;
}
// `NEW`状态时,执行init()方法
if (state.equals(LifecycleState.NEW)) {
init();
}
// `FAILED`状态时,执行stop()方法
else if (state.equals(LifecycleState.FAILED)) {
stop();
}
// 不是`INITIALIZED`和`STOPPED`时,则说明是非法的操作
else if (!state.equals(LifecycleState.INITIALIZED) &&
!state.equals(LifecycleState.STOPPED)) {
invalidTransition(Lifecycle.BEFORE_START_EVENT);
}
try {
// start前的状态设置
setStateInternal(LifecycleState.STARTING_PREP, null, false);
// start逻辑,抽象方法,由组件自行实现
startInternal();
// start过程中,可能因为某些原因失败,这时需要stop操作
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 {
// 设置状态为STARTED
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.
ExceptionUtils.handleThrowable(t);
setStateInternal(LifecycleState.FAILED, null, false);
throw new LifecycleException(sm.getString("lifecycleBase.startFail", toString()), t);
}
}
- 停止方法stop(),抽象方法stopInternal()需要组件自行实现
@Override
public final synchronized void stop() throws LifecycleException {
// `STOPPING_PREP`、`STOPPING`和STOPPED时,将忽略stop()的执行
if (LifecycleState.STOPPING_PREP.equals(state) || LifecycleState.STOPPING.equals(state) ||
LifecycleState.STOPPED.equals(state)) {
if (log.isDebugEnabled()) {
Exception e = new LifecycleException();
log.debug(sm.getString("lifecycleBase.alreadyStopped", toString()), e);
} else if (log.isInfoEnabled()) {
log.info(sm.getString("lifecycleBase.alreadyStopped", toString()));
}
return;
}
// `NEW`状态时,直接将状态变更为`STOPPED`
if (state.equals(LifecycleState.NEW)) {
state = LifecycleState.STOPPED;
return;
}
// stop()的执行,必须要是`STARTED`和`FAILED`
if (!state.equals(LifecycleState.STARTED) && !state.equals(LifecycleState.FAILED)) {
invalidTransition(Lifecycle.BEFORE_STOP_EVENT);
}
try {
// `FAILED`时,直接触发BEFORE_STOP_EVENT事件
if (state.equals(LifecycleState.FAILED)) {
// Don't transition to STOPPING_PREP as that would briefly mark the
// component as available but do ensure the BEFORE_STOP_EVENT is
// fired
fireLifecycleEvent(BEFORE_STOP_EVENT, null);
} else {
// 设置状态为STOPPING_PREP
setStateInternal(LifecycleState.STOPPING_PREP, null, false);
}
// stop逻辑,抽象方法,组件自行实现
stopInternal();
// Shouldn't be necessary but acts as a check that sub-classes are
// doing what they are supposed to.
if (!state.equals(LifecycleState.STOPPING) && !state.equals(LifecycleState.FAILED)) {
invalidTransition(Lifecycle.AFTER_STOP_EVENT);
}
// 设置状态为STOPPED
setStateInternal(LifecycleState.STOPPED, null, false);
} catch (Throwable t) {
ExceptionUtils.handleThrowable(t);
setStateInternal(LifecycleState.FAILED, null, false);
throw new LifecycleException(sm.getString("lifecycleBase.stopFail",toString()), t);
} finally {
if (this instanceof Lifecycle.SingleUse) {
// Complete stop process first
setStateInternal(LifecycleState.STOPPED, null, false);
destroy();
}
}
}
- 销毁方法destroy(),抽象方法destroyInternal()需要组件自行实现
@Override
public final synchronized void destroy() throws LifecycleException {
// `FAILED`状态时,直接触发stop()逻辑
if (LifecycleState.FAILED.equals(state)) {
try {
// Triggers clean-up
stop();
} catch (LifecycleException e) {
// Just log. Still want to destroy.
log.warn(sm.getString(
"lifecycleBase.destroyStopFail", toString()), e);
}
}
// `DESTROYING`和`DESTROYED`时,忽略destroy的执行
if (LifecycleState.DESTROYING.equals(state) ||
LifecycleState.DESTROYED.equals(state)) {
if (log.isDebugEnabled()) {
Exception e = new LifecycleException();
log.debug(sm.getString("lifecycleBase.alreadyDestroyed", toString()), e);
} else if (log.isInfoEnabled() && !(this instanceof Lifecycle.SingleUse)) {
// Rather than have every component that might need to call
// destroy() check for SingleUse, don't log an info message if
// multiple calls are made to destroy()
log.info(sm.getString("lifecycleBase.alreadyDestroyed", toString()));
}
return;
}
// 非法状态判断
if (!state.equals(LifecycleState.STOPPED) &&
!state.equals(LifecycleState.FAILED) &&
!state.equals(LifecycleState.NEW) &&
!state.equals(LifecycleState.INITIALIZED)) {
invalidTransition(Lifecycle.BEFORE_DESTROY_EVENT);
}
try {
// destroy前状态设置
setStateInternal(LifecycleState.DESTROYING, null, false);
// 抽象方法,组件自行实现
destroyInternal();
// destroy后状态设置
setStateInternal(LifecycleState.DESTROYED, null, false);
} catch (Throwable t) {
ExceptionUtils.handleThrowable(t);
setStateInternal(LifecycleState.FAILED, null, false);
throw new LifecycleException(
sm.getString("lifecycleBase.destroyFail",toString()), t);
}
}
总结:
- 启动方法start()和停止方法stop()相对应
- 初始化方法init()和销毁方法destory()相对应
- LifecycleBase是使用了状态机+模板模式来实现的,模板方法如下
// 初始化方法 protected abstract void initInternal() throws LifecycleException; // 启动方法 protected abstract void startInternal() throws LifecycleException; // 停止方法 protected abstract void stopInternal() throws LifecycleException; // 销毁方法 protected abstract void destroyInternal() throws LifecycleException;4.2.2 核心流程源码剖析之Tomcat启动流程
- Tomcat启动命令startup.bat(这里分析windows系统的命令),里面执行了catalina.bat
- Tomcat命令catalina.bat(这里分析windows系统的命令),里面配置了Bootstrap
- Tomcat启动流程图
- 请求流程分析
- 请求处理流程示意图
- Mapper组件体系结构
- 类的加载:JVM将指定的class文件读取到内存里,并运行该class文件里的Java程序的过程
- 类加载器ClassLoader:是java的运行环境JRE(包括java虚拟机和基础类库)的一部分,负责将java类加载到虚拟机中
分类
| 类加载器 | 作用 |
|---|---|
| 引导类加载器(Bootstrap) | c++编写,加载java核心库 java.*,比如rt.jar中的类,构造ExtClassLoader和AppClassLoader |
| 扩展类加载器(ExtClassLoader) | java编写,加载扩展库 JAVA_HOME/lib/ext?录下的jar中的类,如classpath中的jre,javax.*或者java.ext.dir指定位置中的类 |
| 应用类加载器(AppClassLoader) | 默认的类加载器,搜索环境变量classpath中指明的路径 |
关系图(父子关系)
- 用户自己的类加载器,把加载请求传给父加载器,父加载器再传给其父加载器,一直到加载器树的顶层
- 最顶层的类加载器首先针对其特定的位置加载,如果加载不到就转交给子类
- 如果一直到底层的类加载都没有加载到,那么就会抛出异常ClassNotFoundException
- 防止重复加载同一个.class。父类加载器加载过了,子类加载就不会再加载一遍。保证数据安全
- 保证核心.class不能被篡改。通过委托方式,不会去篡改核心.class,即使篡改也不会去加载,即使加载也不会是同一个.class对象了。不同的加载器加载同一个.class也不是同一个.class对象。这样
保证了class执行安全
关系图
tomcat类加载器说明
| 类加载器 | 说明 |
|---|---|
| Common类加载器 | 负责加载Tomcat和Web应用都复用的类 |
| Catalina类加载器 | 负责加载Tomcat专用的类,而这些被加载的类在Web应用中将不可见 |
| Shared类加载器 | 负责加载Tomcat下所有的Web应用程序都复用的类,而这些被加载的类在Tomcat中将不可见 |
| WebApp类加载器 | 负责加载具体的某个Web应用程序所使用到的类,而这些被加载的类在Tomcat和其他的Web应用程序都将不可见 |
| Jsp类加载器 | 每个jsp页面一个类加载器,不同的jsp页面有不同的类加载器,方便实现jsp页面的热插拔 |
tomcat类加载器为什么打破双亲委派机制
- tomcat中可以有多个webapp,每个webapp有自己的目录和类库
- 比如一个webapp使用类库A1.0版本,一个webapp使用类库A2.0版本
- 父类加载器加载类库A1.0版本,如果使用双亲委派,会由commonClassLoader去加载类库A1.0版本,就不能实现不同webapp使用不同版本的类库
tomcat8.5默认改变了严格的双亲委派机制
- 首先从Bootstrap Classloader加载指定的类
- 如果未加载到,则从/WEB-INF/classes加载
- 如果未加载到,则从/WEB-INF/lib/*.jar 加载
- 如果未加载到,则依次从 System、Common、Shared 加载(在这最后一步,遵从双亲委派机制)
HTTPS (全称:Hyper Text Transfer Protocol over SecureSocket Layer),是以安全为目标的 HTTP 通道,在HTTP的基础上通过传输加密和身份认证保证了传输过程的安全性 [1] 。HTTPS 在HTTP 的基础下加入SSL 层,HTTPS 的安全基础是 SSL,因此加密的详细内容就需要 SSL
6.1.2 HTTPS和HTTP的主要区别- HTTPS协议使用时需要到电子商务认证授权机构(CA)申请SSL证书
- HTTP默认使用8080端口,HTTPS默认使用8443端口
- HTTPS则是具有SSL加密的安全性传输协议,对数据的传输进行加密,效果上相当于HTTP的升级版
- HTTP的连接是无状态的,不安全的;HTTPS协议是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议,比HTTP协议安全
http协议存在的问题:
- 数据隐私性问题:通信使用明文(不加密),所以可能遭遇窃听
- 数据完整性问题:无法证明报文的完整性,所以可能遭遇篡改
- 身份认证问题:不验证通信方的身份,所以可能遭遇伪装
htts协议解决这些问题
- 数据隐私性问题:内容经过对称加密,每个连接生成一个唯一的加密密钥
- 数据完整性问题:内容传输经过完整性校验
- 身份认证问题:第三方无法伪造服务端(客户端)身份
-
解决内容可能被窃听的问题—对称加密+非对称加密
具体做法是:发送密文的一方使用对方的公钥进行加密处理“对称的密钥”,然后对方用自己的私钥解密拿到“对称的密钥”,这样可以确保交换的密钥是安全的前提下,使用对称加密方式进行通信。所以,HTTPS采用对称加密和非对称加密两者并用的混合加密机制。 -
解决报文可能遭篡改问题——数字签名
-
解决通信方身份可能被伪装的问题——数字证书
- Client发起一个HTTPS(比如https://juejin.im/user)的请求,根据RFC2818的规定,Client知道需要连接Server的8443(默认)端口
- Server把事先配置好的公钥证书(public key certificate)返回给客户端
- Client验证公钥证书:比如是否在有效期内,证书的用途是不是匹配Client请求的站点,是不是在CRL吊销列表里面,它的上一级证书是否有效,这是一个递归的过程,直到验证到根证书(操作系统内置的Root证书或者Client内置的Root证书)。如果验证通过则继续,不通过则显示警告信息
- Client使用伪随机数生成器生成加密所使用的对称密钥,然后用证书的公钥加密这个对称密钥,发给Server
- Server使用自己的私钥(private key)解密这个消息,得到对称密钥。至此,Client和Server双方都持有了相同的对称密钥
- Server使用对称密钥加密“明文内容A”,发送给Client
- Client使用对称密钥解密响应的密文,得到“明文内容A”
- Client再次发起HTTPS的请求,使用对称密钥加密请求的“明文内容B”,然后Server使用对称密钥解密密文,得到“明文内容B”
到JAVA_HOMEbin目录下执行命令
keytool -genkey -alias test -keyalg RSA -keystore test.keystore
操作指令
6.2.2 配置conf/server.xml6.2.3 使用https协议访问8443端⼝(https://localhost:8443) 7、Tomcat性能优化策略 7.1 系统性能衡量指标
- 响应时间:执行某个操作的耗时
- 吞吐量:系统在给定时间内能够支持的事务数量,单位为TPS(Transactions PerSecond的缩写,也就是事务数/秒,一个事务是指一个客户机向服务器发送请求然后服务器做出反应的过程)
Java虚拟机的运行优化点
- 内存分配优化:内存直接影响服务的运行效率和吞吐量
- 垃圾回收策略优化:垃圾回收机制会不同程度地导致程序运行中断(垃圾回收策略不同,垃圾回收次数和回收效率都是不同的)
内存分配优化
- JVM内存模型回顾
- Java 虚拟机内存相关参数
| 参数 | 参数作用 | 优化建议 |
|---|---|---|
| -server | 启动Server,以服务端模式运行 | 服务端模式建议开启 |
| -Xms | 最小堆内存 | 建议与-Xmx设置相同 |
| -Xmx | 最大堆内存 | 建议设置为可用内存的80% |
| -XX:MetaspaceSize | 元空间初始值 | |
| -XX:MaxMetaspaceSize | 元空间最大内存 | 默认无限 |
| -XX:NewRatio | 年轻代和老年大小比值,取值为整数,默认为2 | 不需要修改 |
| -XX:SurvivorRatio | Eden区与Survivor区大小的比值,取值为整数,默认为8 | 不需要修改 |
- 参数调整示例
tomcat的home文件夹下bin/catalina.bat
JAVA_OPTS="-server -Xms2048m -Xmx2048m -XX:MetaspaceSize=256m -XX:MaxMetaspaceSize=512m"
- 调整后查看可使⽤JDK提供的内存映射⼯具
jhsdb --heap --pid
// TODO:
垃圾回收策略优化
- 垃圾回收性能指标
吞吐量:工作时间(排除GC时间)占总时间的百分比,工作时间并不仅是程序运行的时间,还包含内存分配时间。
暂停时间:由垃圾回收导致的应用程序停止响应次数/时间
- 垃圾回收器参数
在bin/catalina.sh的脚本中, 追加如下配置:
JAVA_OPTS="-XX:+UseConcMarkSweepGC"7.2.2 Tomcat本身配置的优化
- 调整tomcat线程池
- 调整tomcat的连接器
调整tomcat/conf/server.xml 中关于链接器的配置可以提升应⽤服务器的性能
- 禁⽤ AJP 连接器
- 调整 IO 模式
Tomcat8之前的版本默认使用BIO(阻塞式IO),对于每个个请求都要创建一个线程来处理,不适合?并发;Tomcat8以后的版本默认使?NIO模式(非阻塞式IO)
- 动静分离
可以使用Nginx+Tomcat相结合的部署方案,Nginx负责静态资源访问,Tomcat负责Jsp等动态资源访问处理(因为Tomcat不擅长处理静态资源)



