Tomcat8.0.11优化相关
Tomcat 8.0.11:
要了解tomcat的优化,我们先看看Tomcat的官方定义:The Apache Tomcat® software is an open source implementation of the Java Servlet, JavaServer Pages, Java Expression Language and Java WebSocket technologies. The Java Servlet, JavaServer Pages, Java Expression Language and Java WebSocket specifications are developed under the Java Community Process.
Apache Tomcat软件是一个开源的Java Servlet实现,JavaServer Pages,Java表达式语言和Java WebSocket技术。Java Servlet、JavaServer页面、Java表达式语言和Java WebSocket规范都是在Java Community Process下开发的。
Tomcat 系统架构:
Tomcat 的缺省配置是不能稳定长期运行的,也就是不适合生产环境,它会死机,让你不断重新启动,甚至在午夜时分唤醒你。对于操作系统优化来说,是尽可能的增大可使用的内存容量、提高CPU 的频率,保证文件系统的读写速率等。经过压力测试验证,在并发连接很多的情况下,CPU 的处理能力越强,系统运行速度越快。
从系统架构图再结合 conf/server.xml 中的标签配置来说,再结合tomcat的源码来看,每个组件都是对应Java中的一个类或者接口,先加载 server.xml 文件,解析文件中的标签组装成一个个的类,最后相互之间协同工作从而支撑起整个服务的运行,如果要对Tomcat本身进行优化的话,可以通过server.xml来改变相应组件的参数属性及行为方式来达到优化性能的目的,比如从架构图结合 server.xml我们可以得知其中比较重要的标签:Server,Services,Connector,Excutor,Engine,Host,Context等等。在官网中由如下介绍:
- Server:在Tomcat世界中,服务器代表整个容器。Tomcat提供了一个服务器接口的默认实现,很少由用户自定义。
- Service:Service是一个中间组件,它位于服务器内部并绑定一个或多个更多的连接器到一个引擎。服务元素很少由用户,作为默认的实现简单而充分:Service interface.Engine
- Connector:Connector处理与客户机的通信。有多个连接器使用Tomcat。其中包括用于大多数HTTP的HTTP连接器流量,特别是在将Tomcat作为独立服务器和AJP连接器运行时哪个实现了将Tomcat连接到诸如此类的web服务器时使用的AJP协议Apache HTTPD服务器。创建一个定制的连接器是一项重要的工作。
- Engine:Engine表示特定服务的请求处理管道。作为服务可能有多个连接器,引擎接收和处理所有请求将响应返回给相应的连接器传输到客户端。可以实现引擎接口来提供定制引擎,虽然这是不常见的。注意,该引擎可以通过jvmRoute用于Tomcat服务器集群参数。
- Host:Host是一个网络名称(如www.yourcompany.com)与Tomcat的关联服务器。一个引擎可以包含多个Host,并且主机元素也支持网络别名,如yourcompany.com和abc.yourcompany.com。用户很少创建自定义主机,因为标准主机实现提供了重要的附加功能功能。
- Context:Context表示web应用程序。一个主机可以包含多个上下文有唯一的路径。可以实现上下文接口来创建自定义但是这种情况很少发生,因为StandardContext提供了重要的信息额外的功能。Context中的Manager对象,查看Manager中的方法,可以发现有跟Session相关的。Session 的核心原理是通过 Filter 拦截 Servlet 请求,将标准的 ServletRequest 包装一下,换成 Spring 的Request 对象,这样当我们调用 Request 对象的 getSession 方法时,Spring 在背后为我们创建和管理Session。
两个核心组件:
Connector:主要负责处理Socket连接,以及Request与Response的转化。设计思想 :高内聚、低耦合,其中涉及到的三个对象如下:
- EndPoint:提供字节流给Processor,监听通信端口,是对传输层的抽象,用来实现 TCP/IP 协议的。对应的抽象类为AbstractEndPoint,有很多实现类,比如NioEndPoint,JIoEndPoint等。在其中有两个组件,一个是Acceptor,另外一个是SocketProcessor。Acceptor用于监听Socket连接请求,SocketProcessor用于处理接收到的Socket请求。
- Processor:提供Tomcat Request对象给Adapter。Processor是用于实现HTTP协议的,也就是说Processor是针对应用层协议的抽象。Processor接受来自EndPoint的Socket,然后解析成Tomcat Request和Tomcat Response对象,最后通过Adapter提交给容器。对应的抽象类为AbstractProcessor,有很多实现类,比如AjpProcessor、Http11Processor等。
- Adapter:提供ServletRequest给容器。ProtocolHandler接口负责解析请求并生成 Tomcat Request 类。需要把这个 Request 对象转换成 ServletRequest。Tomcat 引入CoyoteAdapter,这是适配器模式的经典运用,连接器调用 CoyoteAdapter 的 sevice 方法,传入的是Tomcat Request 对象,CoyoteAdapter 负责将 Tomcat Request 转成 ServletRequest,再调用容器的 service 方法。
Endpoint接收Socket连接,生成一个SocketProcessor任务提交到线程池去处理。SocketProcessor的run方法会调用Processor组件去解析应用层协议,Processor通过解析生成Request对象后,会调用Adapter的service方法。
Container:包括Engine、Host、Context和Wrapper,主要负责内部的处理以及Servlet的管理。
初始化及启动:
接下去可以通过源码的方式来看看 tomcat 的初始化及启动的主流程。官网下载地址:https://archive.apache.org/dist/tomcat
Tomcat 的入口类是 BootStrap 类的 main 方法:
public static void main(String args[]) { if (daemon == null) {
// Don't set daemon until init() has completed
Bootstrap bootstrap = new Bootstrap();
try {
bootstrap.init();
} catch (Throwable t) {
handleThrowable(t);
t.printStackTrace();
return;
}
daemon = bootstrap;
} else {
// When running as a service the call to stop will be on a new
// thread so make sure the correct class loader is used to prevent
// a range of class not found exceptions.
Thread.currentThread().setContextClassLoader(daemon.catalinaLoader);
} try {
String command = "start";
if (args.length > ) {
command = args[args.length - ];
} if (command.equals("startd")) {
args[args.length - ] = "start";
daemon.load(args);
daemon.start();
} else if (command.equals("stopd")) {
args[args.length - ] = "stop";
daemon.stop();
} else if (command.equals("start")) {
daemon.setAwait(true);
daemon.load(args);
daemon.start();
} else if (command.equals("stop")) {
daemon.stopServer(args);
} else if (command.equals("configtest")) {
daemon.load(args);
if (null==daemon.getServer()) {
System.exit();
}
System.exit();
} 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();
} }
启动的时候会走 bootstrap.init() :
public void init() throws Exception { 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");
Class<?> startupClass =
catalinaLoader.loadClass
("org.apache.catalina.startup.Catalina");
Object startupInstance = startupClass.newInstance(); // Set the shared extensions class loader
if (log.isDebugEnabled())
log.debug("Setting startup class properties");
String methodName = "setParentClassLoader";
Class<?> paramTypes[] = new Class[];
paramTypes[] = Class.forName("java.lang.ClassLoader");
Object paramValues[] = new Object[];
paramValues[] = sharedLoader;
Method method =
startupInstance.getClass().getMethod(methodName, paramTypes);
method.invoke(startupInstance, paramValues); catalinaDaemon = startupInstance; }
这里的主要操作是初始化自定义的ClassLoader ,然后反射调用 Catalina 的 setParentClassLoader 方法设置父加载类。值得注意的是,tomcat 定义的类加载器是WebAppClassLoader,打破了双亲委派模型:先自己尝试去加载这个类,找不到再委托给父类加载器。通过复写findClass和loadClass实现。初始化完成后会进入daemon.load(args),daemon.start(); 两个方法,也就是加载及启动,加载还是很好理解的,就是读取 Tomcat 的 server.xml 进行解析,加载成对应的类对象.然后启动。这里的 load 方法又通过反射调用 Catalina 的 load 方法,其中的一段代码如下:
InputSource inputSource = null;
InputStream inputStream = null;
File file = null;
try {
file = configFile();
inputStream = new FileInputStream(file);
inputSource = new InputSource(file.toURI().toURL().toString());
} catch (Exception e) {
if (log.isDebugEnabled()) {
log.debug(sm.getString("catalina.configFail", file), e);
}
}
可以发现这里有个文件,我们进入 configFile() 发现这个 File 就是server.xml
/**
* Pathname to the server configuration file.
*/
protected String configFile = "conf/server.xml"; protected File configFile() { File file = new File(configFile);
if (!file.isAbsolute()) {
file = new File(Bootstrap.getCatalinaBase(), configFile);
}
return (file); }
然后进行解析初始化,然后调用 getServer().init() ,从Tomcat 系统架构图来看,各个组件的初始化理应从外圈开始,合理的流程应该是 server -> service ->Connector ->container .继续跟进到 LifecycleBase 的 init() 方法,然后调用本类的抽象方法 initInternal()。在这里所有的组件的生命周期是超级接口类 Lifecycle 来管理的。我们这里很显然会先走 Server。
在 StandardServer 的 initInternal() 方法的最后会循环调用 Service 的初始化:
// Initialize our defined Services
for (int i = ; i < services.length; i++) {
services[i].init();
}
在StandardService 的 initInternal() 方法中我们可以看到会初始化 container ,executor ,connector 三个组件,这里的 container 就是 Engine、Host、Context和 servlet。
@Override
protected void initInternal() throws LifecycleException { super.initInternal(); if (container != null) {
container.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
synchronized (connectorsLock) {
for (Connector connector : connectors) {
try {
connector.init();
} catch (Exception e) {
String message = sm.getString(
"standardService.connector.initFailed", connector);
log.error(message, e); if (Boolean.getBoolean("org.apache.catalina.startup.EXIT_ON_INIT_FAILURE"))
throw new LifecycleException(message);
}
}
}
}
也就是这样,一步步的由外到内的一个初始化,daemon.start() 也是这么个流程,其中有一条线路是 Catalina#start() --> LifecycleBase#start() ---> Connector#startInternal() :
这里涉及到的采用什么协议取决去 server.xml 的配置。本版本8.0.11 默认采用org.apache.coyote.http11.Http11NioProtocol,可以在 Connector 的构造函数中找到:Connector(String protocol) ---> setProtocol(protocol):
public void setProtocol(String protocol) { if (AprLifecycleListener.isAprAvailable()) {
if ("HTTP/1.1".equals(protocol)) {
setProtocolHandlerClassName
("org.apache.coyote.http11.Http11AprProtocol");
} else if ("AJP/1.3".equals(protocol)) {
setProtocolHandlerClassName
("org.apache.coyote.ajp.AjpAprProtocol");
} else if (protocol != null) {
setProtocolHandlerClassName(protocol);
} else {
setProtocolHandlerClassName
("org.apache.coyote.http11.Http11AprProtocol");
}
} else {
if ("HTTP/1.1".equals(protocol)) {
setProtocolHandlerClassName
("org.apache.coyote.http11.Http11NioProtocol");
} else if ("AJP/1.3".equals(protocol)) {
setProtocolHandlerClassName
("org.apache.coyote.ajp.AjpNioProtocol");
} else if (protocol != null) {
setProtocolHandlerClassName(protocol);
}
} }
关注到上述图解中的ContainerBase.startInternal()方法
// Start our child containers, if any
Container children[] = findChildren();
List<Future<Void>> results = new ArrayList<>();
for (int i = ; i < children.length; i++) {
// 这句代码就是会调用ContainerBase下的一个个子容器的call方法
results.add(startStopExecutor.submit(new StartChild(children[i])));
}
查看new StartChild要执行的call方法
@Override
public Void call() throws LifecycleException {
child.start();
return null;
}
StandardHost 将一个个web项目部署起来, StandardHost#startInternal() ---> ContainerBase#startInternal() --> ContainerBase#threadStart() ---> ContainerBackgroundProcessor#run() ---> ContainerBase#backgroundProcess() --->HostConfig#lifecycleEvent(event)
public void lifecycleEvent(LifecycleEvent event) { // Identify the host we are associated with
try {
host = (Host) event.getLifecycle();
if (host instanceof StandardHost) {
setCopyXML(((StandardHost) host).isCopyXML());
setDeployXML(((StandardHost) host).isDeployXML());
setUnpackWARs(((StandardHost) host).isUnpackWARs());
setContextClass(((StandardHost) host).getContextClass());
}
} catch (ClassCastException e) {
log.error(sm.getString("hostConfig.cce", event.getLifecycle()), e);
return;
} // Process the event that has occurred
if (event.getType().equals(Lifecycle.PERIODIC_EVENT)) {
check();
} else if (event.getType().equals(Lifecycle.START_EVENT)) {
start();
} else if (event.getType().equals(Lifecycle.STOP_EVENT)) {
stop();
}
}
根据事件类型去处理,这里是 check() :
protected void check() {
if (host.getAutoDeploy()) {
// Check for resources modification to trigger redeployment
// 检查资源修改以触发重新部署
DeployedApplication[] apps =
deployed.values().toArray(new DeployedApplication[]);
for (int i = ; i < apps.length; i++) {
if (!isServiced(apps[i].name))
checkResources(apps[i]);
}
// Check for old versions of applications that can now be undeployed
// 检查现在可以取消部署的应用程序的旧版本 if (host.getUndeployOldVersions()) {
checkUndeploy();
}
// Hotdeploy applications 部署应用
deployApps();
}
}
deployApps() :
protected void deployApps() { File appBase = host.getAppBaseFile();
File configBase = host.getConfigBaseFile();
String[] filteredAppPaths = filterAppPaths(appBase.list());
// Deploy XML descriptors from configBase
deployDescriptors(configBase, configBase.list());
// Deploy WARs
deployWARs(appBase, filteredAppPaths);
// Deploy expanded folders
deployDirectories(appBase, filteredAppPaths); }
StandardContext.startInternal()解析web.xml和添加wrapper 。StandardContext#startInternal() ---> ContainerBase#startInternal() --> ContainerBase#threadStart() ---> ContainerBackgroundProcessor#run() ---> ContainerBase#backgroundProcess() --->ContextConfig#lifecycleEvent(event)
public void lifecycleEvent(LifecycleEvent event) { // Identify the context we are associated with
try {
context = (Context) event.getLifecycle();
} catch (ClassCastException e) {
log.error(sm.getString("contextConfig.cce", event.getLifecycle()), e);
return;
} // Process the event that has occurred
if (event.getType().equals(Lifecycle.CONFIGURE_START_EVENT)) {
configureStart();
} else if (event.getType().equals(Lifecycle.BEFORE_START_EVENT)) {
beforeStart();
} else if (event.getType().equals(Lifecycle.AFTER_START_EVENT)) {
// Restore docBase for management tools
if (originalDocBase != null) {
context.setDocBase(originalDocBase);
}
} else if (event.getType().equals(Lifecycle.CONFIGURE_STOP_EVENT)) {
configureStop();
} else if (event.getType().equals(Lifecycle.AFTER_INIT_EVENT)) {
init();
} else if (event.getType().equals(Lifecycle.AFTER_DESTROY_EVENT)) {
destroy();
} }
configureStart() ---> ContextConfig#webConfig() ---> ContextConfig#getContextWebXmlSource() 进行解析对应应用的 web.xml:
protected InputSource getContextWebXmlSource() {
InputStream stream = null;
InputSource source = null;
URL url = null; String altDDName = null; // Open the application web.xml file, if it exists
ServletContext servletContext = context.getServletContext();
if (servletContext != null) {
altDDName = (String)servletContext.getAttribute(Globals.ALT_DD_ATTR);
if (altDDName != null) {
try {
stream = new FileInputStream(altDDName);
url = new File(altDDName).toURI().toURL();
} catch (FileNotFoundException e) {
log.error(sm.getString("contextConfig.altDDNotFound",
altDDName));
} catch (MalformedURLException e) {
log.error(sm.getString("contextConfig.applicationUrl"));
}
}
else {//public static final String ApplicationWebXml = "/WEB-INF/web.xml";
stream = servletContext.getResourceAsStream
(Constants.ApplicationWebXml);
try {
url = servletContext.getResource(
Constants.ApplicationWebXml);
} catch (MalformedURLException e) {
log.error(sm.getString("contextConfig.applicationUrl"));
}
}
}
if (stream == null || url == null) {
if (log.isDebugEnabled()) {
log.debug(sm.getString("contextConfig.applicationMissing") + " " + context);
}
if (stream != null) {
try {
stream.close();
} catch (IOException e) {
// Ignore
}
}
} else {
source = new InputSource(url.toExternalForm());
source.setByteStream(stream);
} return source;
}
ContextConfig.webConfig()的step9解析到servlets包装成wrapper对象:ContextConfig.webConfig()—>configureContext(webXml) :
for (ServletDef servlet : webxml.getServlets().values()) {
Wrapper wrapper = context.createWrapper();
// Description is ignored
// Display name is ignored
// Icons are ignored // jsp-file gets passed to the JSP Servlet as an init-param if (servlet.getLoadOnStartup() != null) {
wrapper.setLoadOnStartup(servlet.getLoadOnStartup().intValue());
}
if (servlet.getEnabled() != null) {
wrapper.setEnabled(servlet.getEnabled().booleanValue());
}
wrapper.setName(servlet.getServletName());
Map<String,String> params = servlet.getParameterMap();
for (Entry<String, String> entry : params.entrySet()) {
wrapper.addInitParameter(entry.getKey(), entry.getValue());
}
wrapper.setRunAs(servlet.getRunAs());
Set<SecurityRoleRef> roleRefs = servlet.getSecurityRoleRefs();
for (SecurityRoleRef roleRef : roleRefs) {
wrapper.addSecurityReference(
roleRef.getName(), roleRef.getLink());
}
wrapper.setServletClass(servlet.getServletClass());
MultipartDef multipartdef = servlet.getMultipartDef();
if (multipartdef != null) {
if (multipartdef.getMaxFileSize() != null &&
multipartdef.getMaxRequestSize()!= null &&
multipartdef.getFileSizeThreshold() != null) {
wrapper.setMultipartConfigElement(new MultipartConfigElement(
multipartdef.getLocation(),
Long.parseLong(multipartdef.getMaxFileSize()),
Long.parseLong(multipartdef.getMaxRequestSize()),
Integer.parseInt(
multipartdef.getFileSizeThreshold())));
} else {
wrapper.setMultipartConfigElement(new MultipartConfigElement(
multipartdef.getLocation()));
}
}
if (servlet.getAsyncSupported() != null) {
wrapper.setAsyncSupported(
servlet.getAsyncSupported().booleanValue());
}
wrapper.setOverridable(servlet.isOverridable());
context.addChild(wrapper);
}
StandardContext.startInternal()->最终会调用 StandardContext#loadOnStartup(findChildren())) ----> wrapper.load()
详细流程请查阅官网:https://tomcat.apache.org/tomcat-8.0-doc/architecture/startup/serverStartup.pdf
Tomcat8.0.11优化相关的更多相关文章
- Kafka 0.11.0.0 实现 producer的Exactly-once 语义(中文)
很高兴地告诉大家,具备新的里程碑意义的功能的Kafka 0.11.x版本(对应 Confluent Platform 3.3)已经release,该版本引入了exactly-once语义,本文阐述的内 ...
- Redis-4.0.11集群配置
版本:redis-3.0.5 redis-3.2.0 redis-3.2.9 redis-4.0.11 参考:http://redis.io/topics/cluster-tutorial. 集群 ...
- tomcat8.0 基本参数调优配置
1.优化内核及TCP连接: fs.file-max = 655350 # 系统文件描述符总量 net.ipv4.ip_local_port_range = 1024 65535 # 打开端口范围 ...
- Apache Kafka 0.11版本新功能简介
Apache Kafka近日推出0.11版本.这是一个里程碑式的大版本,特别是Kafka从这个版本开始支持“exactly-once”语义(下称EOS, exactly-once semantics) ...
- Web 前端性能优化相关内容解析
Web 前端性能优化相关内容,来源于<Google官方网页载入速度检测工具PageSpeed Insights 使用教程>一文中PageSpeed Insights 的相关说明.大家可以对 ...
- Web 前端性能优化相关内容解析[转]
Web 前端性能优化相关内容,来源于<Google官方网页载入速度检测工具PageSpeed Insights 使用教程>一文中PageSpeed Insights 的相关说明.大家可以对 ...
- Mysql8.0.11简介,新特性
MySQL 8.0 正式版 8.0.11 已发布,官方表示 MySQL 8 要比 MySQL 5.7 快 2 倍,还带来了大量的改进和更快的性能! 注意:从 MySQL 5.7 升级到 MySQL 8 ...
- 【 Tomcat 】tomcat8.0 基本参数调优配置-----(1)
Tomcat 的缺省配置是不能稳定长期运行的,也就是不适合生产环境,它会死机,让你不断重新启动,甚至在午夜时分唤醒你.对于操作系统优化来说,是尽可能的增大可使用的内存容量.提高CPU 的频率,保证文件 ...
- CentOS 7.2.1511编译安装Nginx1.10.1+MySQL5.7.14+PHP7.0.11
准备篇 一.防火墙配置 CentOS 7.x默认使用的是firewall作为防火墙,这里改为iptables防火墙. 1.关闭firewall: systemctl stop firewalld.se ...
随机推荐
- 【BZOJ3999】[TJOI2015]旅游(Link-Cut Tree)
[BZOJ3999][TJOI2015]旅游(Link-Cut Tree) 题面 BZOJ 洛谷 题解 一道不难的\(LCT\)题(用树链剖分不是为难自己吗,这种有方向的东西用\(LCT\)不是方便那 ...
- 【题解】Hanoi塔问题
题目描述 有三根柱A,B,C.在柱A上有N块盘片,所有盘片都是大的在下面,小片能放在大片上面.并依次编好序号,现要将A上的N块片移到C柱上,每次只能移动一片,而且在同一根柱子上必须保持上面的盘片比下面 ...
- 【简】题解 AWSL090429 【噪音】
因为每次加上一头奶牛 是什么不重要 牛棚之间贡献除清空操作外无影响 就只要考虑 每个牛棚清空分x次 的贡献 x之和为k 求贡献和最小 一个牛棚清空x次 显然平均清空贡献最小 再用等差数列的 ...
- django restframework Serializer field
SerializerMethodField 这是一个只读字段.它通过调用它所连接的序列化类的方法来获得它的值.它可用于将任何类型的数据添加到对象的序列化表示中. 签名: SerializerMetho ...
- @WebFilter注解
@WebFilter @WebFilter 用于将一个类声明为==过滤器==,该注解将会在部署时被容器处理,容器将根据具体的属性配置将相应的类部署为过滤器.该注解具有下表给出的一些常用属性 ( 以下所 ...
- pytest 13 使用自定义标记mark
前言: pytest可以规定那些要跑,那些不跑,跑特定的哪些?比如以下的这个例子: #!/usr/bin/env/python # -*-coding:utf-8-*- import pytest @ ...
- cucumber学习笔记
来源于cucumber官网 学习完了之后全部整理一遍
- 【caffe】caffe在linux环境下的安装与编译
网上的caffe的安装教程繁杂而散乱,对初学者很不友好,尤其对该框架理解不深的童鞋.总的来说,caffe的安装不外乎几个固定的步骤,对每一步有了一定的理解,安装只是time-consuming的问题! ...
- Linux Centos6.9下安装部署VNC的实操详述
VNC (Virtual Network Console)是虚拟网络控制台的缩写.它 是一款优秀的远程控制工具软件,由著名的AT&T的欧洲研究实验室开发的.VNC 是在基于 UNIX和 Lin ...
- cannot update the cursor rep,since it is read-only
操作DBF文件,开发机器读写都OK,但部署到服务器上后报:cannot update the cursor rep,since it is read-only 网上寻找解决方案英文答案比较多,也没有给 ...