4、Tomcat源码分析
4.1源码构建
下载
下载地址 https://tomcat.apache.org/download-80.cgi 下载src源码然后解压
配置文件
将配置文件转移新的文件夹下避免冲突:
在 apache-tomcat-8.5.50-src ⽬录中创建 source ⽂件夹
将 conf、webapps ⽬录移动到刚刚创建的 source ⽂件夹中
配置JSP初始化器
ContextConfig类中的configureStart⽅法中增加⼀⾏代码将 Jsp 引擎初始化。
webConfig();
//初始化jsp引擎
context.addServletContainerInitializer(new JasperInitializer(), null);
启动参数
-Dcatalina.home=D:/Study/tomcat-source/source
-Dcatalina.base=D:/Study/tomcat-source/source
-Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager
-Djava.util.logging.config.file=D:/Study/tomcat-source/source/conf/logging.properties
D:/Study/tomcat-source/source这个就是刚刚咱们创建的文件夹 到时候更换成自己的文件夹
添加PomXml,将tomcat交给maven管理
POM
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
11
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
注意这个Pom需要自己创建。
运行
直接运行Bootstrap中的main函数即可。
4.2Tomcat启动流程源码分析
4.2.1Tomcat启动时序图
通过时序图观察,通过Bootstrap中的main方法启动,先进行初始化,在通过load方法逐级的往下加载,加载完成之后通过start逐级的往下启动。
所有的组件接口都继承了Lifecycle顶级接口,Lifecycle顶级接口指定了启动、销毁、停止等规范,相当于Spring中的BeanFactory;Tomcat通过Lifecycle统一规定了各个组件的生命周期。
4.2.2启动源码追踪
1、加载流程
对应时序图中1~13步
Bootstrap
public static void main(String args[]) {
//添加了一把锁
synchronized (daemonLock) {
if (daemon == null) {
// 创建一个BootStreap实例
Bootstrap bootstrap = new Bootstrap();
try {
//调用bootrap的实例化方法
bootstrap.init();
} catch (Throwable t) {
...
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 (command.equals("start")) {
daemon.setAwait(true);
//加载流程
daemon.load(args);
//启动流程
daemon.start();
if (null == daemon.getServer()) {
System.exit(1);
}
}
...
} catch (Throwable t) {
...
}
}
BootStrap#init方法
public void init() throws Exception {
//初始化classloader 没有使用系统的使用自定义的classLoader后边在讲解
initClassLoaders();
//将自定义的classloader绑定到线程上在使用的时候可以直接拿来使用
Thread.currentThread().setContextClassLoader(catalinaLoader);
//将自定义的classLoader设置到安全的classLoader上
SecurityClassLoad.securityClassLoad(catalinaLoader);
//通过自定义的classLoader加载Catalina Class
Class> startupClass = catalinaLoader.loadClass("org.apache.catalina.startup.Catalina");
//进行实例化
Object startupInstance = startupClass.getConstructor().newInstance();
//获取到父类的类加载器方法-- 将自定义的类加载器设置到父类中
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属性上
catalinaDaemon = startupInstance;
}
BootStrap#load方法
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;
}
//【通过反射的方法调用Catalina的load方法】
Method method =
catalinaDaemon.getClass().getMethod(methodName, paramTypes);
method.invoke(catalinaDaemon, param);
}
Catalina#load方法
public void load() {
//判断是否加载过 默认为false
if (loaded) {
return;
}
loaded = true;
long t1 = System.nanoTime();
//初始化文件夹
initDirs();
// 初始名字Before digester - it may be needed
initNaming();
//保存Tomcat所需要的组件和监听器的ClassName,比如StandardServer、LifecycleListener...
Digester digester = createStartDigester();
InputSource inputSource = null;
InputStream inputStream = null;
File file = null;
try {
try {
//读取conf/server.xml配置文件
file = configFile();
inputStream = new FileInputStream(file);
inputSource = new InputSource(file.toURI().toURL().toString());
} catch (Exception e) {}
//创建输入流
if (inputStream == null) {
try {
inputStream = getClass().getClassLoader()
.getResourceAsStream(getConfigFile());
inputSource = new InputSource
(getClass().getClassLoader()
.getResource(getConfigFile()).toString());
} catch (Exception e) {}
}
// 如果inputstream还不存在继续创建
if (inputStream == null) {
try {
inputStream = getClass().getClassLoader()
.getResourceAsStream("server-embed.xml");
inputSource = new InputSource
(getClass().getClassLoader()
.getResource("server-embed.xml").toString());
} catch (Exception e) {}
}
//如果还存在就返回吧 【省略了一些日志打印】
if (inputStream == null || inputSource == null) { return; }
try {
inputSource.setByteStream(inputStream);
//将当前对象推入到digester
digester.push(this);
//解析server.xml配置文件
digester.parse(inputSource);
} catch (Exception e) {return; }
} finally {
if (inputStream != null) {
try {
inputStream.close();
} catch (IOException e) {}
}
}
//给server设置一些属性
getServer().setCatalina(this);
getServer().setCatalinaHome(Bootstrap.getCatalinaHomeFile());
getServer().setCatalinaBase(Bootstrap.getCatalinaBaseFile());
// Stream redirection 给System设置Out和ErrSystemLogHandler、SystemLogHandler
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("Catalina.start", e);
}
}
}
LifecycleBase的init方法和initInternal方法
public final synchronized void init() throws LifecycleException {
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());
}
}
protected abstract void initInternal() throws LifecycleException;
使用典型的模板设计模式!
调用StandardServer中的initInternal方法
protected void initInternal() throws LifecycleException {
//调用父类LifecycleBase的initInternal初始化属性
super.initInternal();
// Register global String cache
// 初始化缓存组件
onameStringCache = register(new StringCache(), "type=StringCache");
// 注册 MBeanFactory
MBeanFactory factory = new MBeanFactory();
factory.setContainer(this);
onameMBeanFactory = register(factory, "type=MBeanFactory");
//初始化全局naming
globalNamingResources.init();
// Populate the extension validator with JARs from common and shared
//填充一些拓展jar包校验
// 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 e) {
// Ignore
} catch (IOException e) {
// Ignore
}
}
}
}
cl = cl.getParent();
}
}
// 初始化service
for (int i = 0; i < services.length; i++) {
services[i].init();
}
}
services[i].init();实际上还是走的LifecycleBase的init方法通过模板调用StandardService中的initIternal方法。
StandardService#initInternal方法
protected void initInternal() throws LifecycleException {
super.initInternal();
//初始化engine 里边会初始化Engine、Host、Context
if (engine != null) {
engine.init();
}
// Initialize any Executors 线程池
for (Executor executor : findExecutors()) {
if (executor instanceof JmxEnabled) {
((JmxEnabled) executor).setDomain(getDomain());
}
executor.init();
}
// Initialize mapperlistener
mapperListener.init();
// Initialize our defined Connectors
synchronized (connectorsLock) {
for (Connector connector : connectors) {
try {
//初始化connector
connector.init();
} catch (Exception e) {
if (Boolean.getBoolean("org.apache.catalina.startup.EXIT_ON_INIT_FAILURE"))
throw new LifecycleException(message);
}
}
}
}
初始化Engine、Executor就追踪了直接观察初始化Connector方法。
Connector#initInernal方法
protected void initInternal() throws LifecycleException {
super.initInternal();
// 创建一个CoyoteAdapter的适配器
adapter = new CoyoteAdapter(this);
protocolHandler.setAdapter(adapter);
// 确保 parseBodyMethodsSet has a default
if (null == parseBodyMethodsSet) {
setParseBodyMethods(getParseBodyMethods());
}
//判断AprLifecycleListener不可用抛出异常
if (protocolHandler.isAprRequired() && !AprLifecycleListener.isAprAvailable()) {
throw new LifecycleException(sm.getString("coyoteConnector.protocolHandlerNoApr",
getProtocolHandlerClassName()));
}
if (AprLifecycleListener.isAprAvailable() && AprLifecycleListener.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);
}
}
调用AbstracProtocol中的初始化方法
public void init() throws Exception {
if (getLog().isInfoEnabled()) {
getLog().info(sm.getString("abstractProtocolHandler.init", getName()));
}
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) {
rgOname = new ObjectName(domain + ":type=GlobalRequestProcessor,name=" + getName());
Registry.getRegistry(null, null).registerComponent(
getHandler().getGlobal(), rgOname, null);
}
String endpointName = getName();
endpoint.setName(endpointName.substring(1, endpointName.length()-1));
endpoint.setDomain(domain);
//初始化endpoint的init方法
endpoint.init();
}
调用父类的AbstractEndpoing的init方法
public void init() throws Exception {
if (bindOnInit) {
//绑定端口
bind();
bindState = BindState.BOUND_ON_INIT;
}
//注册一些socketProperties
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=ThreadPool,name=\"" + getName() + "\",subType=SocketProperties");
socketProperties.setObjectName(socketPropertiesOname);
Registry.getRegistry(null, null).registerComponent(socketProperties, socketPropertiesOname, null);
for (SSLHostConfig sslHostConfig : findSslHostConfigs()) {
registerJmx(sslHostConfig);
}
}
}
默认调用NIOEndpoint的bind方法
public void bind() throws Exception {
//配置Socket的端口号和一些属性
if (!getUseInheritedChannel()) {
//创建一个serverSocket
serverSock = ServerSocketChannel.open();
socketProperties.setProperties(serverSock.socket());
InetSocketAddress addr = (getAddress()!=null?
new InetSocketAddress(getAddress(),getPort()):new InetSocketAddress(getPort()));
//绑定端口号
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
// Initialize thread count defaults for acceptor, poller
if (acceptorThreadCount == 0) {
// FIXME: Doesn't seem to work that well with multiple accept threads
acceptorThreadCount = 1;
}
if (pollerThreadCount <= 0) {
//minimum one poller thread
pollerThreadCount = 1;
}
setStopLatch(new CountDownLatch(pollerThreadCount));
// Initialize SSL if needed
initialiseSsl();
selectorPool.open();
}
总结 :加载流程将所有的各个组件都进行创建和初始化配置。
2、启动流程
BootStrap.Start方法
public void start() throws Exception {
//如果Catalina实例为null在从新创建
if (catalinaDaemon == null) {
init();
}
//通过反射的方法调用Catalina的start方法
Method method = catalinaDaemon.getClass().getMethod("start", (Class [])null);
method.invoke(catalinaDaemon, (Object [])null);
}
Catalina#Start方法
public void start() {
//校验server为null从新赋值
if (getServer() == null) {
load();
}
if (getServer() == null) {
return;
}
//计时
long t1 = System.nanoTime();
// Start the new server
try {
//调用StandardServer的start方法
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;
}
//打印计时
long t2 = System.nanoTime();
if(log.isInfoEnabled()) {
log.info("Server startup in " + ((t2 - t1) / 1000000) + " ms");
}
// 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);
}
}
//等候 默认为false 当调用关机的时候会执行这些方法进行优雅关机
if (await) {
//调用的是StanardServer#Await 将socket进行监听
await();
//关闭所有进程并且销毁容器
stop();
}
}
StandardServer#startInternal方法
protected void startInternal() throws LifecycleException {
//出发监听的事件
fireLifecycleEvent(CONFIGURE_START_EVENT, null);
setState(LifecycleState.STARTING);
//启动一些namingResource
globalNamingResources.start();
// Start our defined Services
synchronized (servicesLock) {
for (int i = 0; i < services.length; i++) {
//调用StandardService的startInternal方法
services[i].start();
}
}
}
StandardService#startInternal
protected void startInternal() throws LifecycleException {
setState(LifecycleState.STARTING);
// Start our defined Container first
if (engine != null) {
synchronized (engine) {
//engineStart方法 会调用StandardEngine#startInternal方法 在方法内部调用父类的ContainerBase#startInternal
①engine.start();
}
}
synchronized (executors) {
for (Executor executor: executors) {
//启动线程池这一步在初始化的时候已经启动了在这里不会被启动了
executor.start();
}
}
mapperListener.start();
// Start our defined Connectors second
synchronized (connectorsLock) {
for (Connector connector: connectors) {
try {
// If it has already failed, don't try and start it
if (connector.getState() != LifecycleState.FAILED) {
//启动连接器
②connector.start();
}
} catch (Exception e) {}
}
}
}
①engine.start()
会调StandardEngine的startInernal里边会调用父类ContainerBase#startInternal的方法
父类ContainerBase#startInternal的方法
protected synchronized void startInternal() throws LifecycleException {
// Start our subordinate components, if any
logger = null;
getLogger();
//获取集群模式下进行启动
Cluster cluster = getClusterInternal();
if (cluster instanceof Lifecycle) {
((Lifecycle) cluster).start();
}
//启动Realm 认证用户的作用
Realm realm = getRealmInternal();
if (realm instanceof Lifecycle) {
((Lifecycle) realm).start();
}
// 找到所有的子类也就是StanardHost
Container children[] = findChildren();
List> results = new ArrayList<>();
for (int i = 0; i < children.length; i++) {
//通过线程池异步启动子类Host Context Wrapper
/**
* StartChild里边找到所有子类也就是Host去启动,调用StandardHost中的startInternal方法启动之后在调用父类的
* startInternal,进一步找子类进行启动一直找到所有的Servlet被启动
*/
results.add(startStopExecutor.submit(new StartChild(children[i])));
}
MultiThrowable multiThrowable = null;
//对启动的结果进行判断
for (Future 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
if (pipeline instanceof Lifecycle) {
((Lifecycle) pipeline).start();
}
//设置启动状态
setState(LifecycleState.STARTING);
// Start our thread
threadStart();
}
②connector.start
调用的测试Connector#startInternal方法
protected void startInternal() throws LifecycleException {
// Validate settings before starting
if (getPort() < 0) {
throw new LifecycleException(sm.getString(
"coyoteConnector.invalidPort", Integer.valueOf(getPort())));
}
setState(LifecycleState.STARTING);
try {
protocolHandler.start();
} catch (Exception e) {
throw new LifecycleException(
sm.getString("coyoteConnector.protocolHandlerStartFailed"), e);
}
}
调用AbstractProtocol的start方法
public void start() throws Exception {
if (getLog().isInfoEnabled()) {
getLog().info(sm.getString("abstractProtocolHandler.start", getName()));
}
//启动endpoint
endpoint.start();
// Start timeout thread
asyncTimeout = new AsyncTimeout();
Thread timeoutThread = new Thread(asyncTimeout, getNameInternal() + "-AsyncTimeout");
int priority = endpoint.getThreadPriority();
if (priority < Thread.MIN_PRIORITY || priority > Thread.MAX_PRIORITY) {
priority = Thread.NORM_PRIORITY;
}
timeoutThread.setPriority(priority);
timeoutThread.setDaemon(true);
timeoutThread.start();
}
endpoint.start默认调用的是NioEndpoint#startInternal方法
public void startInternal() throws Exception {
if (!running) {
running = true;
paused = false;
processorCache = new SynchronizedStack<>(SynchronizedStack.DEFAULT_SIZE,
socketProperties.getProcessorCache());
eventCache = new SynchronizedStack<>(SynchronizedStack.DEFAULT_SIZE,
socketProperties.getEventCache());
nioChannels = new SynchronizedStack<>(SynchronizedStack.DEFAULT_SIZE,
socketProperties.getBufferPool());
// Create worker collection
if ( getExecutor() == null ) {
createExecutor();
}
initializeConnectionLatch();
// Start poller threads PollerThread是个实现了Runnable的线程类用来处理过来的用户请求
pollers = new Poller[getPollerThreadCount()];
for (int i=0; i
总结 :启动流程和加载流程模式差不多,只不过是设置了必要的启动的一些参数。
4.3Tomcat请求流程分析
Tomcat请求处理流程。 浏览器请求
Tomcat中采用Mapper组件来映射Engine和Host之间的关系。
4.3.1Mapper组件
在Mapper类中有四个内部类,
MapElement作为基类其中只包含name、Object属性供其他三个类使用。整个Mapper组件就是起了一个映射的作用,能够通过Engine层层往下查找。
4.3.2请求处理流程
4.3.3准备
创建一个helloworldWeb项目pom中引入servlet并编写Hello然后将编译好的项目复制到源码中。进行启动并访问。
public class HelloServlet extends HttpServlet {
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
resp.getWriter().write("hello web! ");
}
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
resp.getWriter().write("hello web! ");
}
}
放入到源码工程中。
启动源码的tomcat访问是否能够看到自己的helloWorld程序
4.3.4请求流程源码分析
1、入口
在NioEndpoint的startInternal方法中,调用父类startAcceptorThreads开启了线程监听。
父类AbstractEndpoint#startAcceptorThreads 方法
protected final void startAcceptorThreads() {
int count = getAcceptorThreadCount();
acceptors = new Acceptor[count];
for (int i = 0; i < count; i++) {
//创建一个socket监听器
acceptors[i] = createAcceptor();
String threadName = getName() + "-Acceptor-" + i;
acceptors[i].setThreadName(threadName);
Thread t = new Thread(acceptors[i], threadName);
t.setPriority(getAcceptorThreadPriority());
t.setDaemon(getDaemon());
t.start();
}
}
NioEndpoint的createAcceptor方法
protected AbstractEndpoint.Acceptor createAcceptor() {
return new Acceptor();
}
Acceptor继承图
可以看出来Acceptor属于一个线程类,在父类的startAcceptorThreads方法中最后调用了start直接会执行Acceptor中的run方法。
Acceptor#run方法
public void run() {
int errorDelay = 0;
// 一直循环一直到我们关闭容器会停止。
while (running) {
...
try {
//如果到达了请求连接数就需要等待。
countUpOrAwaitConnection();
SocketChannel socket = null;
try {
// socket 接收Socket请求
socket = serverSock.accept();
} catch (IOException ioe) {
...
}
// Successful accept, reset the error delay
errorDelay = 0;
// Configure the socket
if (running && !paused) {
// setSocketOptions() will hand the socket off to
// 将获取到请求塞入到当前队列中 返回false为失败 会进行重试。
if (!setSocketOptions(socket)) {
//close本次socket
closeSocket(socket);
}
}
...
}
state = AcceptorState.ENDED;
}
setSocketOptions方法
protected boolean setSocketOptions(SocketChannel socket) {
// Process the connection
try {
//设置堵塞为flase 因为是同步非阻塞的NIO
socket.configureBlocking(false);
Socket sock = socket.socket();
socketProperties.setProperties(sock);
//从SynchronizedStack nioChannels获取到一个nioChannel
NioChannel channel = nioChannels.pop();
//channel为null的时候从新创建一个channel
if (channel == null) {
SocketBufferHandler bufhandler = new SocketBufferHandler(
socketProperties.getAppReadBufSize(),
socketProperties.getAppWriteBufSize(),
socketProperties.getDirectBuffer());
if (isSSLEnabled()) {
channel = new SecureNioChannel(socket, bufhandler, selectorPool, this);
} else {
channel = new NioChannel(socket, bufhandler);
}
} else {
//将socket放入到ioChannel
channel.setIOChannel(socket);
channel.reset();//重置顺序
}
//从poller吃中获取到一个Poller 并将channel进行通知
getPoller0().register(channel);
} catch (Throwable t) {
...
return false;
}
return true;
}
public void register(final NioChannel socket) {
socket.setPoller(this);
NioSocketWrapper ka = new NioSocketWrapper(socket, NioEndpoint.this);
socket.setSocketWrapper(ka);
ka.setPoller(this);
ka.setReadTimeout(getSocketProperties().getSoTimeout());
ka.setWriteTimeout(getSocketProperties().getSoTimeout());
ka.setKeepAliveLeft(NioEndpoint.this.getMaxKeepAliveRequests());
ka.setSecure(isSSLEnabled());
ka.setReadTimeout(getConnectionTimeout());
ka.setWriteTimeout(getConnectionTimeout());
PollerEvent r = eventCache.pop();
ka.interestOps(SelectionKey.OP_READ);//this is what OP_REGISTER turns into.
if ( r==null) r = new PollerEvent(socket,ka,OP_REGISTER);
else r.reset(socket,ka,OP_REGISTER);
//上边是包装一些时间,这一步添加到事件中
addEvent(r);
}
private final SynchronizedQueue events =
new SynchronizedQueue<>();
//添加到事件中
private void addEvent(PollerEvent event) {
events.offer(event);
if ( wakeupCounter.incrementAndGet() == 0 ) selector.wakeup();
}
Poller的run方法
一直在循环去检查有没有事件
public void run() {
// Loop until destroy() is called
while (true) {
try {
if (!close) {
hasEvents = events();
...
}
if (close) {
...
}
} catch (Throwable x) {
...
continue;
}
//判断是否有事件 events是查看队列中是否存在事件
if ( keyCount == 0 ) hasEvents = (hasEvents | events());
Iterator iterator =
keyCount > 0 ? selector.selectedKeys().iterator() : null;
// Walk through the collection of ready keys and dispatch
// any active event.
while (iterator != null && iterator.hasNext()) {
if (attachment == null) {
iterator.remove();
} else {
iterator.remove();
//真正开始执行的逻辑
processKey(sk, attachment);
}
}
}
}
2、从Endpoin查找到Servlet
从是processKey开始分析。因为代码比较繁琐直接采用时序图来分析。
总结:Tomcat在执行的过程中运用了比较多的模版设计模式,方便拓展,也一层一层的往下去委派给下一层,其中最为核心的就是在Mapper组件11-12步将请求参数解析成Request对象,并且找好了对应的Engine、Host、Context、Wrapper。有空可以在多多看看源码学习人家的设计思路。
5、Tomcat类加载机制
5.1Jvm类加载机制回顾
什么是类加载机制?通俗的说将编译好的class文件加载到jvm内存中的这个过程称之为类加载过程。这个机制就是类加载机制。执行这个操作的称之为类加载器(Class Loader)。
5.1.1Jvm内置的类加载器
jvm内置的类加载器有:引导类加载器、扩展类加载器、系统类加载器。
类加载器作用引导启动类加载器BootstrapClassLoaderc++编写,加载java核⼼库 java.,⽐如rt.jar中的类,构造ExtClassLoader和AppClassLoader扩展类加载器 ExtClassLoaderjava编写,加载扩展库 JAVA_HOME/lib/ext⽬录下的jar中的类,如classpath中的jre ,javax.或者java.ext.dir指定位置中的类系统类加载器SystemClassLoader/AppClassLoader默认的类加载器,搜索环境变量 classpath 中指明的路径
当然用户也可以自定义类加载器,直接加载指定路径下的class文件。
1) ⽤户⾃⼰的类加载器,把加载请求传给⽗加载器,⽗加载器再传给其⽗加载器,⼀直到加载器树的顶层。
2)最顶层的类加载器⾸先针对其特定的位置加载,如果加载不到就转交给⼦类。
3)如果⼀直到底层的类加载都没有加载到,那么就会抛出异常 ClassNotFoundException。
5.1.2双亲委派机制
什么是双亲委派机制?
说白类就是在加载某个类时,类加载器⾸先把这个任务委托给他的上级类加载器,递归这个操作,一直到最顶层,如果上级的类加载器没有加载,⾃⼰才会去加载这个类。
双亲委派机制的作用?
1)防⽌重复加载同⼀个.class。通过委托去向上⾯问⼀问,加载过了,就不⽤再加载⼀遍。保证数据安全。
2)保证核⼼.class文件【一般指的是jdk自定的】不能被篡改。通过委托⽅式,不会去篡改核⼼.class,即使篡改也不会去加载,即使加载也不会是同⼀个.class对象了。不同的加载器加载同⼀个.class也不是同⼀个.class对象。这样保证了class执⾏安全(如果⼦类加载器先加载,那么我们可以写⼀些与java.lang包中基础类同名
的类, 然后再定义⼀个⼦类加载器,这样整个应⽤使⽤的基础类就都变成我们⾃⼰定义的类了。)
5.2Tomcat类加载机制
5.2.1Tomcat为啥自定义实现类加载
Tomcat自定义实现了类加载器,并未严格按照双亲委派模式的方式进行加载。那么思考下如果Tomcat严格按照双亲委派机制加载类的话会有啥问题?
比如 Tomcat中引入了两个项目只有版本不同比如,hellowolrd-1.0.jar 和 helloworld-2.0.jar那么两个项目中都存在com.demo.ClassA,两个类的因为版本不同所以内容可能不同,如果采取双亲委派机制使用SystemClassLoader的话加载1.0版本之后在去加载2.0版本的时候发现已经加载过了,就不会进行加载了这样子就会有问题。
5.2.2Tomcat类加载器继承体系
说明:
1)其中引导类加载器和扩展类加载器作用不变
2)系统类加载器正常情况下加载的是 CLASSPATH 下的类,但是 Tomcat 的启动脚本并未使⽤该变量,⽽是通过系统类加载器加载tomcat启动的类,⽐如bootstrap.jar,通常在catalina.bat或者catalina.sh中指定。位于bin目录下。
3)Common 通⽤类加载器加载Tomcat依赖的jar包以及应⽤通⽤的⼀些类,位于lib目录下,⽐如servlet-api.jar
4)Catalina ClassLoader ⽤于加载服务器内部可⻅类,这些类应⽤程序不能访问。就是编写Tomcat的类。
5)Shared ClassLoader ⽤于加载应⽤程序共享类,这些类服务器不会依赖。
6)Webapp ClassLoader,给每个应⽤程序都会配置⼀个独⼀⽆⼆的Webapp ClassLoader,他⽤来单独加载本应⽤程序 /WEB-INF/classes 和 /WEB-INF/lib 下的类。
Tomcat加载部署的项目加载顺序
第一步:从 Bootstrap Classloader加载指定的类。
第二步:如果未加载到,则从 /WEB-INF/classes加载
第三步:如果未加载到,则从 /WEB-INF/lib/*.jar 加载
第四步:如果未加载到,则依次从 System、Common、Shared 加载(在这最后⼀步,遵从双亲委派机制)
6、Tomcat对Https的支持
6.1Https简介
百度百科给出的https解释,
简单来说,http属于超文本传输协议,是明文传输的,传输不安全,https在传输数据的时候会对数据进行一层加密。采用加密的协议为SSL(Secure Socket Layer)。现在也衍生出了TLS(Transport Layer Security)协议。
Http和Https主要的区别?
- HTTPS协议使⽤时需要到电⼦商务认证授权机构(CA)申请SSL证书
- HTTP默认使⽤8080端⼝,HTTPS默认使⽤(8)443端⼝
- HTTPS则是具有SSL加密的安全性传输协议,对数据的传输进⾏加密,效果上相当于HTTP的升级版
- HTTP的连接是⽆状态的,不安全的;HTTPS协议是由SSL+HTTP协议构建的可进⾏加密传输、身份认证的⽹络协议,⽐HTTP协议安全
6.2Https工作原理
简单来说,第一次握手会找到适合当前浏览器的加密算法,并返回一个公钥给浏览器;第二次握手浏览器先校验公钥证书,然后生成一串随机数 拿着公钥进行加密,第三握手服务器通过私钥进行解密,并拿着私钥在进行加密返回给客户端,客户端拿着服务器返回的信息进行hash比对,如果统一的话就进行数据传输。
6.3Tomcat配置Https
使用jdk中的keytool生成一个自定义的证书(商用的话找准认证机构)
打开终端或者Command输入下边命令。
keytool -genkey -alias helloworld -keyalg RSA -keystore helloworld.keystore
在conf/server.xml中配置
certificateKeystorePassword="helloworld" type="RSA"/>
启动Tomcat访问https:localhost:8443即可访问。
7、Tomcat性能优化
调优只提供思路,不同配置调优的数值是不固定的。
了解一些基础概念:
1)响应时间:执⾏某个操作的耗时;
2)吞吐量:系统在给定时间内能够⽀持的事务数量,单位为TPS(Transactions PerSecond的缩写, 也就是事务数/秒,⼀个事务是指⼀个客户机向服务器发送请求然后服务器做出反应的过程。
7.1Jvm性能调优
Java 虚拟机的运⾏优化主要是内存分配和垃圾回收策略的优化:
1、内存直接影响服务的运⾏效率和吞吐量
2、垃圾回收机制会不同程度地导致程序运⾏中断(垃圾回收策略不同,垃圾回收次数和回收效率都是不同的)
1、Java 虚拟机内存相关参数
参数参数作⽤优化建议-server启动Server,以服务端模式运⾏服务端模式建议开启-Xms最⼩堆内存建议与-Xmx设置相同-Xmx最⼤堆内存建议设置为可⽤内存的80%-XX:MetaspaceSize元空间初始值一般不会修改-XX:MaxMetaspaceSize元空间最⼤内存默认⽆限-XX:NewRatio年轻代和⽼年代⼤⼩⽐值,取值为整数,默认为2[2:1]不需要修改-XX:SurvivorRatioEden区与Survivor区⼤⼩的⽐值,取值为整数,默认为8[8:1:1]不需要修改
上图为CMS和G1的JVM内存空间分配。
调整Demo:
JAVA_OPTS="-server -Xms2048m -Xmx2048m -XX:MetaspaceSize=256m -XX:MaxMetaspaceSize=512m"
直接配置到bin/catalina.bat或者catalina.sh目录下即可。
查看:使用jdk自带的工具查看
jhsdb jmap -heap -pid xxx【jdk9 之后使用jhsdb】jmap -heap
2、垃圾回收策略
垃圾回收性能指标
1、吞吐量:⼯作时间(排除GC时间)占总时间的百分⽐, ⼯作时间并不仅是程序运⾏的时间,还包含内存分配时间。
2、暂停时间:由垃圾回收导致的应⽤程序停⽌响应次数/时间
垃圾回收器
1、串⾏收集器(Serial Collector):单线程执⾏所有的垃圾回收⼯作, 适⽤于单核CPU服务器
⼯作进程-----|(单线程)垃圾回收线程进⾏垃圾收集|---⼯作进程继续
2、并⾏收集器(Parallel Collector):⼜称为吞吐量收集器(关注吞吐量), 以并⾏的⽅式执⾏年轻代的垃圾回收, 该⽅式可以显著降低垃圾回收的开销(指多条垃圾收集线程并⾏⼯作,但此时⽤户线程仍然处于等待状态)。适⽤于多处理器或多线程硬件上运⾏的数据量较⼤的应⽤。
⼯作进程-----|(多线程)垃圾回收线程进⾏垃圾收集|---⼯作进程继续
3、并发收集器(Concurrent Collector):以并发的⽅式执⾏⼤部分垃圾回收⼯作,以缩短垃圾回收的暂停时间。适⽤于那些响应时间优先于吞吐量的应⽤, 因为该收集器虽然最⼩化了暂停时间(指⽤户线程与垃圾收集线程同时执⾏,但不⼀定是并⾏的,可能会交替进⾏), 但是会降低应⽤程序的性能。
4、CMS收集器(Concurrent Mark Sweep Collector):并发标记清除收集器, 适⽤于那些更愿意缩短垃圾回收暂停时间并且负担的起与垃圾回收共享处理器资源的应⽤。
5、G1收集器(Garbage-First Garbage Collector):适⽤于⼤容量内存的多核服务器, 可以在满⾜垃圾回收暂停时间⽬标的同时, 以最⼤可能性实现高吞吐量(JDK1.7之后)。
配置启动参数
参数描述-XX:+UseSerialGC启⽤串⾏收集器-XX:+UseParallelGC启⽤并⾏垃圾收集器,配置了该选项,那么 -XX:+UseParallelOldGC默认启⽤。-XX:+UseParNewGC年轻代采⽤并⾏收集器,如果设置了 -XX:+UseConcMarkSweepGC选项,⾃动启⽤。-XX:ParallelGCThreads年轻代及⽼年代垃圾回收使⽤的线程数。默认值依赖于JVM使⽤的CPU个数。-XX:+UseConcMarkSweepGC(CMS)对于⽼年代,启⽤CMS垃圾收集器。 当并⾏收集器⽆法满⾜应⽤的延迟需求是,推荐使⽤CMS或G1收集器。启⽤该选项后, -XX:+UseParNewGC⾃动启⽤。-XX:+UseG1GC⽤G1收集器。 G1是服务器类型的收集器, ⽤于多核、⼤内存的机器。它在保持⾼吞吐量的情况下,⾼概率满⾜GC暂停时间的⽬标。
在bin/catalina.sh的脚本中 , 追加如下配置 :
JAVA_OPTS="-XX:+UseConcMarkSweepGC"
可以通过jconsle工具的配置概况查看todo。
7.2Tomcat本身配置调优
1、调整tomcat线程池
2、调整tomcat的连接器
调整tomcat/conf/server.xml 中关于链接器的配置可以提升应⽤服务器的性能。
参数说明maxConnections最⼤连接数,当到达该值后,服务器接收但不会处理更多的请求, 额外的请求将会阻塞直到连接数低于maxConnections 。可通过ulimit -a 查看服务器限制。对于CPU要求更⾼(计算密集型)时,建议不要配置过⼤ ; 对于CPU要求不是特别⾼时,建议配置在2000左右(受服务器性能影响)。 当然这个需要服务器硬件的⽀持maxThreads最⼤线程数,需要根据服务器的硬件情况,进⾏⼀个合理的设置acceptCount最⼤排队等待数,当服务器接收的请求数量到达maxConnections ,此时Tomcat会将后⾯的请求,存放在任务队列中进⾏排序, acceptCount指的就是任务队列中排队等待的请求数 。 ⼀台Tomcat的最⼤的请求处理数量,是maxConnections+acceptCount
3、禁⽤ AJP 连接器
4、调整IO模型
Tomcat8之前的版本默认使⽤BIO(阻塞式IO),对于每⼀个请求都要创建⼀个线程来处理,不适
合⾼并发;Tomcat8以后的版本默认使⽤NIO模式(⾮阻塞式IO)
当Tomcat并发性能有较⾼要求或者出现瓶颈时,我们可以尝试使⽤APR模式,APR(Apache Portable
Runtime)是从操作系统级别解决异步IO问题,使⽤时需要在操作系统上安装APR和Native(因为APR
原理是使⽤使⽤JNI技术调⽤操作系统底层的IO接⼝)。
5、动静分离
可以使⽤Nginx+Tomcat相结合的部署⽅案,Nginx负责静态资源访问,Tomcat负责Jsp等动态资
源访问处理(因为Tomcat不擅⻓处理静态资源)。