SpringBoot默认集成了tomcat、jetty、undertow 三种容器,本文讲解内置Tomcat的定制和优化
application.properties
在org.springframework.boot.autoconfigure.web.ServerProperties
类中有Server的相关配置,源码如下:
@ConfigurationProperties(
prefix = "server",
ignoreUnknownFields = true
)
public class ServerProperties {
private Integer port;
private InetAddress address;
@NestedConfigurationProperty
private final ErrorProperties error = new ErrorProperties();
private Boolean useForwardHeaders;
private String serverHeader;
private DataSize maxHttpHeaderSize = DataSize.ofKilobytes(8L);
private Duration connectionTimeout;
@NestedConfigurationProperty
private Ssl ssl;
@NestedConfigurationProperty
private final Compression compression = new Compression();
@NestedConfigurationProperty
private final Http2 http2 = new Http2();
private final ServerProperties.Servlet servlet = new ServerProperties.Servlet();
//tomcat
private final ServerProperties.Tomcat tomcat = new ServerProperties.Tomcat();
//jetty
private final ServerProperties.Jetty jetty = new ServerProperties.Jetty();
//undertow
private final ServerProperties.Undertow undertow = new ServerProperties.Undertow();
public ServerProperties() {
}
}
其中port和address是三个容器的公共配置
server.port = 8001是绑定端口号
server.address = 192.168.0.1是绑定IP地址
Tomcat的日志配置在ServerProperties.Tomcat.Accesslog内部类中,
Tomcat的配置在ServerProperties.Tomcat内部类中,具体源码如下:
//Tomcat的配置
public static class Tomcat {
private final ServerProperties.Tomcat.Accesslog accesslog = new ServerProperties.Tomcat.Accesslog();
private String internalProxies = "10\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}|192\\.168\\.\\d{1,3}\\.\\d{1,3}|169\\.254\\.\\d{1,3}\\.\\d{1,3}|127\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}|172\\.1[6-9]{1}\\.\\d{1,3}\\.\\d{1,3}|172\\.2[0-9]{1}\\.\\d{1,3}\\.\\d{1,3}|172\\.3[0-1]{1}\\.\\d{1,3}\\.\\d{1,3}|0:0:0:0:0:0:0:1|::1";
private String protocolHeader;
private String protocolHeaderHttpsValue = "https";
private String portHeader = "X-Forwarded-Port";
private String remoteIpHeader;
private File basedir;
@DurationUnit(ChronoUnit.SECONDS)
private Duration backgroundProcessorDelay = Duration.ofSeconds(10L);
private int maxThreads = 200;
private int minSpareThreads = 10;
private DataSize maxHttpPostSize = DataSize.ofMegabytes(2L);
private DataSize maxHttpHeaderSize = DataSize.ofBytes(0L);
private DataSize maxSwallowSize = DataSize.ofMegabytes(2L);
private Boolean redirectContextRoot = true;
private Boolean useRelativeRedirects;
private Charset uriEncoding;
private int maxConnections;
private int acceptCount;
private List<String> additionalTldSkipPatterns;
private final ServerProperties.Tomcat.Resource resource;
public Tomcat() {
this.uriEncoding = StandardCharsets.UTF_8;
this.maxConnections = 10000;
this.acceptCount = 100;
this.additionalTldSkipPatterns = new ArrayList();
this.resource = new ServerProperties.Tomcat.Resource();
}
}
//tomcat的访问日志配置
public static class Accesslog {
private boolean enabled = false;
private String pattern = "common";
private String directory = "logs";
protected String prefix = "access_log";
private String suffix = ".log";
private boolean rotate = true;
private boolean renameOnRotate = false;
private String fileDateFormat = ".yyyy-MM-dd";
private boolean requestAttributesEnabled = false;
private boolean buffered = true;
public Accesslog() {
}
}
参考配置如下:
详细参数配置可以参考 SpringBoot项目详细配置文件修
# ===============================
# Tomcat Access Log
# ===============================
# Buffer output such that it is only flushed periodically.
server.tomcat.accesslog.buffered=true
# ${HOME}/gome/log/job/
server.tomcat.accesslog.directory=d:/log/
# Enable access log.
server.tomcat.accesslog.enabled=true
# Date format to place in log file name.
server.tomcat.accesslog.file-date-format=.yyyy-MM-dd
# Format pattern for access logs.
server.tomcat.accesslog.pattern=common
# Log file name prefix.
server.tomcat.accesslog.prefix=job_access_log
# Defer inclusion of the date stamp in the file name until rotate time.
server.tomcat.accesslog.rename-on-rotate=false
# Set request attributes for IP address, Hostname, protocol and port used for the request.
server.tomcat.accesslog.request-attributes-enabled=false
# Enable access log rotation.
server.tomcat.accesslog.rotate=true
# Log file name suffix.
server.tomcat.accesslog.suffix=.log
EmbeddedServletContainerCustomizer
接口在SpringBoot 1.5.10.RELEASE 版本之前,使用该方式进行Tomcat的定制,具体实现方式就是实现EmbeddedServletContainerCustomizer接口,并且把自定义实现类放入到SpringIOC容器中。
package com.feiyue.config;
import org.apache.catalina.connector.Connector;
import org.apache.catalina.valves.AccessLogValve;
import org.apache.coyote.http11.Http11NioProtocol;
import org.springframework.boot.web.embedded.tomcat.TomcatConnectorCustomizer;
import org.springframework.stereotype.Component;
import java.io.File;
@Component
public class MyEmbeddedServletContainerCustomizer implements EmbeddedServletContainerCustomizer {
@Override
public void customize(ConfigurableEmbeddedServletContainer servletContainer) {
TomcatEmbeddedServletContainerFactory factory = (TomcatEmbeddedServletContainerFactory)servletContainer;
//启动端口号
factory.setPort(8001);
//日志目录
factory.setBaseDirectory(new File("d:/tomcat"));
//设置日志
factory.addContextValves(getAccessLogValve());
//初始化
factory.addInitializers((servletContext) -> {
System.out.println("========= servletContext startup ============");
//添加 监听器
// servletContext.addListener(className);
//添加 过滤器
// servletContext.addFilter(filterName, filter);
//可以设置 全局变量
servletContext.setAttribute("startup", "true");
System.out.println("========= servletContext startup finished ============");
});
//设置连接器
factory.addConnectorCustomizers(new MyTomcatConnectorCustomizer());
}
/**
* access访问日志设置
* @return
*/
private AccessLogValve getAccessLogValve() {
AccessLogValve log = new AccessLogValve();
log.setDirectory("d:/tomcat/logs");
log.setEnabled(true);
log.setPattern("common");
log.setPrefix("springboot-access-log");
log.setSuffix(".txt");
return log;
}
}
/**
* tomcat connectot 连接器
*/
class MyTomcatConnectorCustomizer implements TomcatConnectorCustomizer {
@Override
public void customize(Connector arg0) {
Http11NioProtocol protocol=(Http11NioProtocol)arg0.getProtocolHandler();
//最大连接数
protocol.setMaxConnections(2000);
//最大线程数
protocol.setMaxThreads(500);
//超时时间 1s
protocol.setConnectionTimeout(1000);
}
}
EmbeddedServletContainerFactory
对象使用@Bean在SpringIOC容器中装配一个EmbeddedServletContainerFactory
对象
package com.feiyue.config;
import org.apache.catalina.valves.AccessLogValve;
import org.springframework.boot.web.server.ErrorPage;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpStatus;
@Configuration
public class WebServerConfiguration {
@Bean
public EmbeddedServletContainerFactory createEmbeddedServletContainerFactory() {
TomcatEmbeddedServletContainerFactory factory = new TomcatEmbeddedServletContainerFactory();
factory.setPort(8001);
factory.addErrorPages(new ErrorPage(HttpStatus.NOT_FOUND, "/error/404.html"));
//初始化
factory.addInitializers((servletContext) -> {
System.out.println("========= servletContext startup ============");
//添加 监听器
//servletContext.addListener(className);
//添加 过滤器
//servletContext.addFilter(filterName, filter);
//可以设置 全局变量
servletContext.setAttribute("startup", "true");
System.out.println("========= servletContext startup finished ============");
});
factory.addContextValves(getAccessLogValve());
return factory;
}
/**
* access访问日志设置
* @return
*/
private AccessLogValve getAccessLogValve() {
AccessLogValve log = new AccessLogValve();
log.setDirectory("d:/tomcat/logs");
log.setEnabled(true);
log.setPattern("common");
log.setPrefix("springboot-access-log");
log.setSuffix(".txt");
return log;
}
}
(参照org.springframework.boot.autoconfigure.web.EmbeddedServletContainerAutoConfiguration
)
spring容器中如果 没有 EmbeddedServletContainerFactory ,则会根据使用的Web容器创建不同的EmbeddedServletContainerFactory实现类。
spring容器中如果 有 EmbeddedServletContainerFactory ,则不创建。
EmbeddedServletContainerAutoConfiguration是Servlet容器自动配置类,源码如下
package org.springframework.boot.autoconfigure.web;
import io.undertow.Undertow;
import javax.servlet.Servlet;
import org.apache.catalina.startup.Tomcat;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.util.Loader;
import org.eclipse.jetty.webapp.WebAppContext;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.boot.autoconfigure.AutoConfigureOrder;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.boot.autoconfigure.condition.SearchStrategy;
import org.springframework.boot.context.embedded.EmbeddedServletContainerCustomizerBeanPostProcessor;
import org.springframework.boot.context.embedded.EmbeddedServletContainerFactory;
import org.springframework.boot.context.embedded.jetty.JettyEmbeddedServletContainerFactory;
import org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedServletContainerFactory;
import org.springframework.boot.context.embedded.undertow.UndertowEmbeddedServletContainerFactory;
import org.springframework.boot.web.servlet.ErrorPageRegistrarBeanPostProcessor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.util.ObjectUtils;
import org.xnio.SslClientAuthMode;
@AutoConfigureOrder(-2147483648)
@Configuration
@ConditionalOnWebApplication
@Import({EmbeddedServletContainerAutoConfiguration.BeanPostProcessorsRegistrar.class})
public class EmbeddedServletContainerAutoConfiguration {
public EmbeddedServletContainerAutoConfiguration() {
}
public static class BeanPostProcessorsRegistrar implements ImportBeanDefinitionRegistrar, BeanFactoryAware {
private ConfigurableListableBeanFactory beanFactory;
public BeanPostProcessorsRegistrar() {
}
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
if (beanFactory instanceof ConfigurableListableBeanFactory) {
this.beanFactory = (ConfigurableListableBeanFactory)beanFactory;
}
}
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
if (this.beanFactory != null) {
if (ObjectUtils.isEmpty(this.beanFactory.getBeanNamesForType(EmbeddedServletContainerCustomizerBeanPostProcessor.class, true, false))) {
registry.registerBeanDefinition("embeddedServletContainerCustomizerBeanPostProcessor", new RootBeanDefinition(EmbeddedServletContainerCustomizerBeanPostProcessor.class));
}
if (ObjectUtils.isEmpty(this.beanFactory.getBeanNamesForType(ErrorPageRegistrarBeanPostProcessor.class, true, false))) {
registry.registerBeanDefinition("errorPageRegistrarBeanPostProcessor", new RootBeanDefinition(ErrorPageRegistrarBeanPostProcessor.class));
}
}
}
}
//Undertow容器
@Configuration
@ConditionalOnClass({Servlet.class, Undertow.class, SslClientAuthMode.class})
@ConditionalOnMissingBean(
value = {EmbeddedServletContainerFactory.class},
search = SearchStrategy.CURRENT
)
public static class EmbeddedUndertow {
public EmbeddedUndertow() {
}
@Bean
public UndertowEmbeddedServletContainerFactory undertowEmbeddedServletContainerFactory() {
return new UndertowEmbeddedServletContainerFactory();
}
}
//Jetty容器
@Configuration
@ConditionalOnClass({Servlet.class, Server.class, Loader.class, WebAppContext.class})
@ConditionalOnMissingBean(
value = {EmbeddedServletContainerFactory.class},
search = SearchStrategy.CURRENT
)
public static class EmbeddedJetty {
public EmbeddedJetty() {
}
@Bean
public JettyEmbeddedServletContainerFactory jettyEmbeddedServletContainerFactory() {
return new JettyEmbeddedServletContainerFactory();
}
}
//Tomcat容器
@Configuration
@ConditionalOnClass({Servlet.class, Tomcat.class})
@ConditionalOnMissingBean(
value = {EmbeddedServletContainerFactory.class},
search = SearchStrategy.CURRENT
)
public static class EmbeddedTomcat {
public EmbeddedTomcat() {
}
@Bean
public TomcatEmbeddedServletContainerFactory tomcatEmbeddedServletContainerFactory() {
return new TomcatEmbeddedServletContainerFactory();
}
}
}