springboot-内置Tomcat
一、springboot的特性之一
基于springboot的特性 自动装配@Configuretion 注解
二、springboot内置Tomcat步骤
直接看SpringApplication方法的代码块
总纲:
1、在SpringApplication.run 初始化了一个上下文ConfigurableApplicationContext configurableApplicationContext = AnnotationConfigServletWebServerApplicationContext,这里是通过Class.forName获取到的。
2、在调用AbstartApplicationContext中调用了onRefresh()方法。
3、继承onRefresh()有5个子类
...
ServletWebServerApplicationContext
..
4、为什么说是在ServletWebServerApplicationContext实现的呢。
5、可以去查看AnnotationConfigServletWebServerApplicationContext这个类是继承那个类。可以看到,这个类继承了ServletWebServerApplicationContext,而onRefresh()方法在ServletWebServerApplicationContext实现了。衔接
6、在这个createWebServer()方法中。获取一个ServletWebServerFactory,创建一个服务生成工厂;而这里就比较有意思。这里是通过自动装配,ServletWebServerFactoryAutoConfiguretion;在这里创建了具体服务的工厂。
7、而我们引入spring-boot-starter的时候,依赖的spring-boot-starter-web中依赖的spring-boot-starter-tomcat; 所以在自动装配创建的TomcatServletWebServiceFactory;在这里个工厂中创建WebServer,也就是Tomcat;
8、既然Tomcat已经创建了,那么怎么跟SpringMvc中的DispatherServlet进行关联呢?第三章。
// 1、第一步从SpringAppplicaton.run开始
/*** Static helper that can be used to run a {@link SpringApplication} from the* specified source using default settings.* @param primarySource the primary source to load* @param args the application arguments (usually passed from a Java main method)* @return the running {@link ApplicationContext}*/
public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {return run(new Class<?>[] { primarySource }, args);
}
// ....中间的就省略
// 第二步
try {ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);configureIgnoreBeanInfo(environment);Banner printedBanner = printBanner(environment);context = createApplicationContext();exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,new Class[] { ConfigurableApplicationContext.class }, context);prepareContext(context, environment, listeners, applicationArguments, printedBanner);// 从这里进入refreshContext(context);afterRefresh(context, applicationArguments);stopWatch.stop();if (this.logStartupInfo) {new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);}listeners.started(context);callRunners(context, applicationArguments);
}
catch (Throwable ex) {handleRunFailure(context, ex, exceptionReporters, listeners);throw new IllegalStateException(ex);
}// 第三步
private void refreshContext(ConfigurableApplicationContext context) {
// 看这里
refresh(context);
if (this.registerShutdownHook) {try {context.registerShutdownHook();}catch (AccessControlException ex) {// Not allowed in some environments.}
}
}
查看refresh(context) 在使用ApplicationContext 类贯彻整条启动的链路 AbstratApplicationContext
而AnnotationConfigServletWebServerApplicationContext继承了SerlvetWebServerApplicationContext
所以在onRefresh(), 是使用SerlvetWebServerApplicationContext
try {// Allows post-processing of the bean factory in context subclasses.postProcessBeanFactory(beanFactory);// Invoke factory processors registered as beans in the context.invokeBeanFactoryPostProcessors(beanFactory);// Register bean processors that intercept bean creation.registerBeanPostProcessors(beanFactory);// Initialize message source for this context.initMessageSource();// Initialize event multicaster for this context.initApplicationEventMulticaster();// Initialize other special beans in specific context subclasses.// 在这里做内置Tomcat的内置,及加载到spring容器中。// 在这里实现一些自定义的实现。onRefresh();// Check for listener beans and register them.registerListeners();// Instantiate all remaining (non-lazy-init) singletons.finishBeanFactoryInitialization(beanFactory);// Last step: publish corresponding event.finishRefresh();}// 父类什么都没有?
// 那就看下父类有哪些实现了? 但这里是怎么实现在子类实现的?
// 子类里面是怎么关联到tomcat上的?
// 在
// 这里描述下为什么选择SerlvetWebServerApplicationContext
// 那是因为在启动创建spring的上下文的,AnnotationConfigServletWebServerApplicationContext
// 而AnnotationConfigServletWebServerApplicationContext继承了SerlvetWebServerApplicationContext
// 所以在onRefresh(), 是使用SerlvetWebServerApplicationContext
protected ConfigurableApplicationContext createApplicationContext() {Class<?> contextClass = this.applicationContextClass;if (contextClass == null) {try {switch (this.webApplicationType) {case SERVLET:// 这里创建的是org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContextcontextClass = Class.forName(DEFAULT_SERVLET_WEB_CONTEXT_CLASS);break;case REACTIVE:contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);break;default:contextClass = Class.forName(DEFAULT_CONTEXT_CLASS);}}catch (ClassNotFoundException ex) {throw new IllegalStateException("Unable create a default ApplicationContext, please specify an ApplicationContextClass", ex);}}return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);}
在SerlvetWebServerApplicationContext实现了onRefresh()方法
@Override
protected void onRefresh() {super.onRefresh();try {// 在这里创建一个web服务,先看代码createWebServer();}catch (Throwable ex) {throw new ApplicationContextException("Unable to start web server", ex);}
}// 创建web服务
private void createWebServer() {WebServer webServer = this.webServer;ServletContext servletContext = getServletContext();// 如果已经创建为了web服务,这里就不在创建,但是服务刚启动,肯定都是==nullif (webServer == null && servletContext == null) {// 这里获取一个创建服务的工厂。这里就很有意思。这里用的springboot自动装配ServletWebServerFactory factory = getWebServerFactory();this.webServer = factory.getWebServer(getSelfInitializer());}else if (servletContext != null) {try {getSelfInitializer().onStartup(servletContext);}catch (ServletException ex) {throw new ApplicationContextException("Cannot initialize servlet context", ex);}}initPropertySources();
}// 在容器中获取ServletWebServerFactory。 而这个类的子类是有三个
// 1、TomcatServletWebServerFactory (默认spring-boot-starter-web,依赖spring-boot-starter-tomcat)
// 2、JettyServletWebServerFactory
// 3、UndertowServletWebServerFactory
/*** Returns the {@link ServletWebServerFactory} that should be used to create the* embedded {@link WebServer}. By default this method searches for a suitable bean in* the context itself.* @return a {@link ServletWebServerFactory} (never {@code null})*/protected ServletWebServerFactory getWebServerFactory() {// Use bean names so that we don't consider the hierarchyString[] beanNames = getBeanFactory().getBeanNamesForType(ServletWebServerFactory.class);if (beanNames.length == 0) {throw new ApplicationContextException("Unable to start ServletWebServerApplicationContext due to missing "+ "ServletWebServerFactory bean.");}if (beanNames.length > 1) {throw new ApplicationContextException("Unable to start ServletWebServerApplicationContext due to multiple "+ "ServletWebServerFactory beans : " + StringUtils.arrayToCommaDelimitedString(beanNames));}return getBeanFactory().getBean(beanNames[0], ServletWebServerFactory.class);}// 从头看起,我们在第一章的时候也说明了。这里用到springboot的自动装配。
// 这里要从@EnableAutoConfiguration ; 而这个注解是在@SpringBootApplication中引用
// 自动装备会读取starter注解下spring-factory中加载。
// 而创建服务工厂是ServletWebServerFactoryAutoConfiguration这个配置类
/*** {@link EnableAutoConfiguration Auto-configuration} for servlet web servers.** @author Phillip Webb* @author Dave Syer* @author Ivan Sopov* @author Brian Clozel* @author Stephane Nicoll* @since 2.0.0*/
@Configuration(proxyBeanMethods = false)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
// 这个配置类生效,必须是有ServletRequest这个类
@ConditionalOnClass(ServletRequest.class)
@ConditionalOnWebApplication(type = Type.SERVLET)
// 注入ServerProperties属性进来, 这是server服务的配置项。
// 端口。超时情况。在第四会详情介绍Tomcat配置详解。
@EnableConfigurationProperties(ServerProperties.class)
// 导入如下配置
@Import({ ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class,// 嵌入tomcatServletWebServerFactoryConfiguration.EmbeddedTomcat.class,// 嵌入JettyServletWebServerFactoryConfiguration.EmbeddedJetty.class,// 嵌入Undertow,nettyServletWebServerFactoryConfiguration.EmbeddedUndertow.class })
public class ServletWebServerFactoryAutoConfiguration {
}// 来看下嵌入的这些代码都干了什么?
// 先来看下EmbeddedTomcat这个类
// 初始化TomcatServletWebServerFactory是前提条件Servlet,Tomcat等类是要存在才生效
// 而我们spring-boot-web-starter主键里面默认是引入了spring-boot-starter-tomcat。
// 这些类是存在,反之我们看jetty的。
@Configuration(proxyBeanMethods = false)@ConditionalOnClass({ Servlet.class, Tomcat.class, UpgradeProtocol.class })@ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)static class EmbeddedTomcat {@BeanTomcatServletWebServerFactory tomcatServletWebServerFactory(ObjectProvider<TomcatConnectorCustomizer> connectorCustomizers,ObjectProvider<TomcatContextCustomizer> contextCustomizers,ObjectProvider<TomcatProtocolHandlerCustomizer<?>> protocolHandlerCustomizers) {TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory();factory.getTomcatConnectorCustomizers().addAll(connectorCustomizers.orderedStream().collect(Collectors.toList()));factory.getTomcatContextCustomizers().addAll(contextCustomizers.orderedStream().collect(Collectors.toList()));factory.getTomcatProtocolHandlerCustomizers().addAll(protocolHandlerCustomizers.orderedStream().collect(Collectors.toList()));return factory;}}// 反观jetty的初始化Factory。这里是server都是标红了。
@Configuration(proxyBeanMethods = false)@ConditionalOnClass({ Servlet.class, Server.class, Loader.class, WebAppContext.class })@ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)static class EmbeddedJetty {@BeanJettyServletWebServerFactory JettyServletWebServerFactory(ObjectProvider<JettyServerCustomizer> serverCustomizers) {JettyServletWebServerFactory factory = new JettyServletWebServerFactory();factory.getServerCustomizers().addAll(serverCustomizers.orderedStream().collect(Collectors.toList()));return factory;}}
// 获取到了WebServer在操作Tomcat操作。
如图:
来查看下TomcatServletWebSeverFactory下创建WebServer
@Overridepublic WebServer getWebServer(ServletContextInitializer... initializers) {if (this.disableMBeanRegistry) {Registry.disableRegistry();}// 创建一个Tomcat服务,这里的就是apache的代码块了。// 如下图Tomcat的结果图进行对比代码。// 这里就是一个Server// 这里默认的一个Service是StandardServiceTomcat tomcat = new Tomcat();// 文件的路径,这里要获取jar文件目录,部署的目录。File baseDir = (this.baseDirectory != null) ? this.baseDirectory : createTempDir("tomcat");tomcat.setBaseDir(baseDir.getAbsolutePath());// 创建一个连接协议,这里传入的HttpNioProtocol// 一个Server可以有多个Connector。一种协议只能有一个Connector。Connector connector = new Connector(this.protocol);connector.setThrowOnFailure(true);tomcat.getService().addConnector(connector);customizeConnector(connector);tomcat.setConnector(connector);// 设置host,如果有新的web应用进来,可以自动发布应用。tomcat.getHost().setAutoDeploy(false);// StandardEngineconfigureEngine(tomcat.getEngine());// 这里可以自定义连接协议,比如非http等;for (Connector additionalConnector : this.additionalTomcatConnectors) {tomcat.getService().addConnector(additionalConnector);}prepareContext(tomcat.getHost(), initializers);return getTomcatWebServer(tomcat);}
1、Tomcat回归
- 一个Tomcat只会有一个Server
- 一个Server有多个service
- 一个Service有多个Connector连接协议,有http,https,ajp等协议。
- 一个Service只有一个Engine(引擎)
- 一个Engine,可以有多个Host,每个Host代表一个虚拟主机,他可以包含多个Web应用。
- 每一个context表示运行在Tomcat的web应用
2、Tomcat的结构图
三、SpringMvc的DispatcherServlet关联Tomcat
还是原汁原味自动装配。
总纲:
1、还是回到@EnableAutoConfiguration这个注解,会自动装配一个叫 @DispatcherServletAutoConfiguration;
2、这个配置类会初始化1:DispatherServlet
初始2:DispatcherServletRegistrationConfiguration,在这里面初始化了DispatcherServletRegistrationBean,来看看这个类都继承了什么。
这个类很熟悉了吧。在初始化Tomcat。屡次出现;
这个类就能拿到ServletContext上下文了。在看看Tomcat的结构图。是不是就能对得起一些东西了。
3、DispatcherServletRegistrationBean从这个类来往上翻,看在那一层上实现了ServletContextInitializer,最终是RegistrationBean类实现ServletContextInitializer的onStartUp(),
在DynamicRegistrationBean上实现RegistrationBean的register功能。
4、直接上代码流程
代码如下:
// 先看自动装配
@Configuration(proxyBeanMethods = false)
@Conditional(DefaultDispatcherServletCondition.class)
@ConditionalOnClass(ServletRegistration.class)
@EnableConfigurationProperties({ HttpProperties.class, WebMvcProperties.class })
protected static class DispatcherServletConfiguration {@Bean(name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)public DispatcherServlet dispatcherServlet(HttpProperties httpProperties, WebMvcProperties webMvcProperties) {DispatcherServlet dispatcherServlet = new DispatcherServlet();dispatcherServlet.setDispatchOptionsRequest(webMvcProperties.isDispatchOptionsRequest());dispatcherServlet.setDispatchTraceRequest(webMvcProperties.isDispatchTraceRequest());dispatcherServlet.setThrowExceptionIfNoHandlerFound(webMvcProperties.isThrowExceptionIfNoHandlerFound());dispatcherServlet.setPublishEvents(webMvcProperties.isPublishRequestHandledEvents());dispatcherServlet.setEnableLoggingRequestDetails(httpProperties.isLogRequestDetails());return dispatcherServlet;}@Bean@ConditionalOnBean(MultipartResolver.class)@ConditionalOnMissingBean(name = DispatcherServlet.MULTIPART_RESOLVER_BEAN_NAME)public MultipartResolver multipartResolver(MultipartResolver resolver) {// Detect if the user has created a MultipartResolver but named it incorrectlyreturn resolver;}}@Configuration(proxyBeanMethods = false)
@Conditional(DispatcherServletRegistrationCondition.class)
@ConditionalOnClass(ServletRegistration.class)
@EnableConfigurationProperties(WebMvcProperties.class)
@Import(DispatcherServletConfiguration.class)
protected static class DispatcherServletRegistrationConfiguration {@Bean(name = DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME)@ConditionalOnBean(value = DispatcherServlet.class, name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)public DispatcherServletRegistrationBean dispatcherServletRegistration(DispatcherServlet dispatcherServlet,WebMvcProperties webMvcProperties, ObjectProvider<MultipartConfigElement> multipartConfig) {DispatcherServletRegistrationBean registration = new DispatcherServletRegistrationBean(dispatcherServlet,webMvcProperties.getServlet().getPath());registration.setName(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME);registration.setLoadOnStartup(webMvcProperties.getServlet().getLoadOnStartup());multipartConfig.ifAvailable(registration::setMultipartConfig);return registration;}
}// 在看这个DispatcherServletRegistrationBean的族谱,上图已经明了,会继承到ServletContextInitializer
// 实现onStartup()
@Override
public final void onStartup(ServletContext servletContext) throws ServletException {String description = getDescription();if (!isEnabled()) {logger.info(StringUtils.capitalize(description) + " was not registered (disabled)");return;}register(description, servletContext);
}@Override
protected final void register(String description, ServletContext servletContext) {//servlet注册在这里完成 该方法由子类ServletRegistrationBean实现//servlet注册完后会返回一个registration对象,用于完成servlet-mapping的配置D registration = addRegistration(description, servletContext);if (registration == null) {logger.info(StringUtils.capitalize(description) + " was not registered (possibly already registered?)");return;}// servlet的mapping配置在这里完成 该方法由子类ServletRegistrationBean实现configure(registration);
}
四、SpringBoot的Tomcat及access配置项
Tomcat配置类:ServerProperties,以server开头。这个类在ServletWebServerFactoryAutoConfiguration中开启注入进来。
直接撸代码,在代码做注释
@ConfigurationProperties(prefix = "server", ignoreUnknownFields = true)
public class ServerProperties {// 这里就好解释,就是端口咯private Integer port;// 绑定网络ip,填写这个了,那么访问只能这个ip才能访问,亲测可以的;当然也是随便填写服务的ip,// 这个属性目前不是很明确要做什么,服务私有? 而且不是本机的ip,会报错的。private InetAddress address;// 配置爆出Exception后,跳转到指定的报错页面 -> BasicErrorController (这个类也是springboot实现的)// 尝试了下,没起作用,就放弃,现在更多的都是前后端分离,都使用@ControllerAdice// 具体放在yml中注明@NestedConfigurationPropertyprivate final ErrorProperties error = new ErrorProperties();// 看着意思是设置转发头部策略,好像这个不建议使用,没有尝试过// 有三种Native framework noneprivate ForwardHeadersStrategy forwardHeadersStrategy;// private String serverHeader;// 请求头部最大的size设置,默认8Kbprivate DataSize maxHttpHeaderSize = DataSize.ofKilobytes(8);// 连接器在关闭连接之前等待另一个HTTP请求的时间。不设置无效等待// 好像也什么用了。private Duration connectionTimeout;// 请求https-ssl证书加密,这个也没有用过,没尝试过@NestedConfigurationPropertyprivate Ssl ssl;@NestedConfigurationPropertyprivate final Compression compression = new Compression();@NestedConfigurationPropertyprivate final Http2 http2 = new Http2();// servlet配置,结构图可以参照下Tomcat结构图private final Servlet servlet = new Servlet();// tomcat配置类, 在yml上署名,这里重点说明下Tomcat配置。private final Tomcat tomcat = new Tomcat();// jetty配置类, 在yml上署名private final Jetty jetty = new Jetty();// netty配置类, 在yml上署名private final Netty netty = new Netty();// Undertow配置类, 在yml上署名private final Undertow undertow = new Undertow();// 这里是Servlet配置。public static class Servlet {// Servlet参数private final Map<String, String> contextParameters = new HashMap<>();// 路径 例如:/tk 请求路径http://xx:xx/tk/xxprivate String contextPath;// Servlet应用名称private String applicationDisplayName = "application";// jsp属性,这里都不展示,因为现在的框架基本都是前后端分离@NestedConfigurationPropertyprivate final Jsp jsp = new Jsp();// session会话配置@NestedConfigurationPropertyprivate final Session session = new Session();}/*** Tomcat配置项*/public static class Tomcat {// tomcat的accesslog配置,这里在下面具体配置上说明private final Accesslog accesslog = new Accesslog();/*** Regular expression that matches proxies that are to be trusted.*/private String internalProxies = "10\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}|" // 10/8+ "192\\.168\\.\\d{1,3}\\.\\d{1,3}|" // 192.168/16+ "169\\.254\\.\\d{1,3}\\.\\d{1,3}|" // 169.254/16+ "127\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}|" // 127/8+ "172\\.1[6-9]{1}\\.\\d{1,3}\\.\\d{1,3}|" // 172.16/12+ "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";/*** Header that holds the incoming protocol, usually named "X-Forwarded-Proto".*/// 看着是协议请求头设置private String protocolHeader;private String protocolHeaderHttpsValue = "https";// 端口请求头private String portHeader = "X-Forwarded-Port";/*** Name of the HTTP header from which the remote IP is extracted. For instance,* `X-FORWARDED-FOR`.*/private String remoteIpHeader;/*** Name of the HTTP header from which the remote host is extracted.*/private String hostHeader = "X-Forwarded-Host";/*** Tomcat base directory. If not specified, a temporary directory is used.*/// 根目录,存放一些日志使用的,一般就是 ".", 根目录private File basedir;/*** Delay between the invocation of backgroundProcess methods. If a duration suffix* is not specified, seconds will be used.*/@DurationUnit(ChronoUnit.SECONDS)private Duration backgroundProcessorDelay = Duration.ofSeconds(10);/*** Maximum amount of worker threads.*/// 最大线程数,默认是200,可以任务是临时工,有活来了,就要干。private int maxThreads = 200;// 最小工作线程,可以认为这是正式工,每天都要干活private int minSpareThreads = 10;// post请求,最大报文大小,默认2Mprivate DataSize maxHttpFormPostSize = DataSize.ofMegabytes(2);// 最大正文大小。这里说明下。这里跟spring里面也有一个文件大小设置的// 这两个是没有什么上下关系private DataSize maxSwallowSize = DataSize.ofMegabytes(2);private Boolean redirectContextRoot = true;private boolean useRelativeRedirects;// unicode设置private Charset uriEncoding = StandardCharsets.UTF_8;// 提问:Tomcat最大能承接多少连接?// 最大的请求连接设置private int maxConnections = 8192;// 如果请求数量超过了最大的请求连接,就会把连接存放在队列中。private int acceptCount = 100;// 所以Tomcat最大能承接的连接是 maxConnections + acceptCount - 连接使用完的。private int processorCache = 200;private List<String> additionalTldSkipPatterns = new ArrayList<>();/*** Comma-separated list of additional unencoded characters that should be allowed* in URI paths. Only "< > [ \ ] ^ ` { | }" are allowed.*/private List<Character> relaxedPathChars = new ArrayList<>();/*** Comma-separated list of additional unencoded characters that should be allowed* in URI query strings. Only "< > [ \ ] ^ ` { | }" are allowed.*/private List<Character> relaxedQueryChars = new ArrayList<>();// 连接超时时间,如果不设置,或者设置-1,那么就会无限时间连接。private Duration connectionTimeout;private final Resource resource = new Resource();private final Mbeanregistry mbeanregistry = new Mbeanregistry(); // Tomcat的accesslog设置public static class Accesslog {// 是否开启accesslog日志,默认不开启private boolean enabled = false;// # 在Servlet.getAttribute("token"),存在的时候才输出日志, 这里不在输出,一般应该不会选择这个。private String conditionIf;// ServletRequest.getAttribute(conditionUnless) 是否存在整个class输出private String conditionUnless;// 内容输出正则配置// "%{yyyy-MM-dd HH:mm:ss.SSS}t %h %A %l %user \"%r\" %s %b %D"// 这里的正则与logback差不多一致private String pattern = "common";// tomcat日志目录private String directory = "logs";// 文件输出前缀protected String prefix = "access_log";// 文件输出后缀private String suffix = ".log";// private String encoding;/*** Locale used to format timestamps in log entries and in log file name* suffix. Default to the default locale of the Java process.*/private String locale;// 检查日志文件是否存在,存在是否重新创建;private boolean checkExists = false;// 是否开启根据时间轮转日志,比如今天access.2023-06-14.log,明天access.2023-06-15.logprivate boolean rotate = true;// 是否推迟在文件名上加上时间;等轮转到第二天的时候,在加上;private boolean renameOnRotate = false;// 日志文件天数多少天删除private int maxDays = -1;// 文件名格式化时间戳private String fileDateFormat = ".yyyy-MM-dd";private boolean ipv6Canonical = false;// 请求是否带上request的属性,ip,端口,协议等private boolean requestAttributesEnabled = false;// 是否启用缓存,定时刷新到日志文件中private boolean buffered = true;}
更直观的通yml在展示下;
server:# 端口设置port: 5051# 绑定网络ip,填写这个了,那么访问只能这个ip才能访问,亲测可以的;当然也是随便填写服务的ip,# 这个属性目前不是很明确要做什么,服务私有?而且不是本机的ip,会报错的。address: 127.0.0.1# 开启设置请求content-type支持类型比如application/json;text/html;application/xml等compression:enabled: true# 设置context-path,http://localhost:5051/tk/test/getservlet:context-path: /tk# 设置会话,这块就不在做说什么。
# session:
# cookie:
# comment:# 指定报错后,跳转/error页面,/error的实现Controller -> BasicErrorController# 没有尝试,好像没什么用。不过前后端都分离,更多都是用@ControllerAdiceerror:path: /errorinclude-exception: truewhitelabel:enabled: true# 重定向请求头部使用策略。没有对比过# forward-headers-strategy: native# 头部最大size,这里是kbmax-http-header-size: 8KB# 连接器最大超时时间,这里是指connector这个连接器connection-timeout: 100stomcat:# 存放一些日志等的目录,一般都是设置根目录,当然也不一定是basedir: .# 服务的接受和处理最大的连接数,默认:8192,如果超过这个数据,那么就会进入队列,accept-count# 这里很容易被面试,Tomcat最大能承接连接数???max-connections: 8192# 当请求的连接都接受和处理,那么传入进来的连接就会进入队列,这个是设置队列使用accept-count: 100# 最大工作线程数,默认200;并不是所有的服务线程都是使用 8191max-threads: 200# 最小的工作线程数,默认10min-spare-threads: 10# url解码字符编码,默认utf-8uri-encoding: utf-8# post请求最大内容大小设置,默认2M,如果设置-1,则不限制post请求大小max-http-form-post-size: 2MB# 可吞下的请求正文的最大数量,默认2MBmax-swallow-size: 2MB# 请求连接的最大超时时间connection-timeout: 60000ms# tomcat的accesslog配置accesslog:# 是否开启accesslog设置enabled: true# 是否把请求日志缓存起来,在定时缓存刷新,这里应该要设置false,为什么要设置false,这里不做讨论buffered: false# 存在日志文件夹下directory: logs# 放置文件拼接的文件名file-date-format: .yyyy-MM-dd# 格式化格式pattern: "%{yyyy-MM-dd HH:mm:ss.SSS}t %h %A %l %user \"%r\" %s %b %D"# 文件名前缀prefix: access# 文件名后缀suffix: .log# 是否推迟文件名中包含时间戳,知道轮换时间;意思就是先不在文件名上加时间,知道日志做分割的时候在做;rename-on-rotate: false# 请求是否带上request的请求属性,比如ip,端口,主机名等request-attributes-enabled: true# 是否启用日志分割; 未看到按照日志文件大小分割。这里应该按照时间来分割。随时间进行新建文件# 设置为true,比如今天是access.2023-06-14.log 明天就是access.2023-06-15.log;# 设置为false,那么就只会有一个文件rotate: true# 在Servlet.getAttribute("token"),存在的时候才输出日志, 这里不在输出,一般应该不会选择这个。condition-if: token# 删除日志保留文件前N天的accesslog日志,-1,不删除; 默认不删除max-days: -1# 检查日志文件是否存在,要是存在,是否重建;设置true就重建;设置false就不重建。check-exists: false
五、SpringBoot的Tomcat的优化建议
从第四章的tomcat配置了解到
能接收到连接数是通过server.tomcat.max-connections和accept-count来控制;
而处理这些连接线程控制是:server.tomcat.max-threads 和 server.tomcat.min-spare-threads
总结:
假设1:把连接数max-connections和accept-count设置过大;但是线程数max-threads不变情况;
但是功能可能处理过慢,线程数处理连接就过慢(硬件资源等问题)所以连接就会积压;要是连接上限触发connection-timeout;假设1:连接数设置过大,线程数处理不过来;第一:这里的线程数跟硬件资源;比如cpu。第二:跟代码程序原因;
假设2:把连接数max-connections和accept-count设置小;线程数设置大;
如果请求量大,所以就会触发连接数上限后,就触发连接拒绝;所以支撑不了更多的请求量。
总结:
1、设置最小工作线程:
最小工作线程:server.tomcat.min-spare-threads
这个本上是跟硬件资源有关,比如多少核,算力更快;4核,8核的;都不一样;所以这里建议是不做变更或者设置到10-50之间即可
2、设置最大线程数:
最大工作线程:server.tomcat.max-threads
这个跟上面的也是一样的意思;一般都是跟硬件和代码本身相关;比如程序需要耗费很多cpu资源等;需要很多算力的资源;所以这里就不能设置过大,一般是server.tomcat.min-spare-threads 的20倍左右,也就是在200-1000的左右。
3、设置最大连接数:
这个就没什么可讲的,一般现在做分布式集群服务,基本很满足业务;默认即可;
相关文章:

springboot-内置Tomcat
一、springboot的特性之一 基于springboot的特性 自动装配Configuretion 注解 二、springboot内置Tomcat步骤 直接看SpringApplication方法的代码块 总纲: 1、在SpringApplication.run 初始化了一个上下文ConfigurableApplicationContext configurableApplica…...

Flink流批一体计算(2):Flink关键特性
目录 Flink关键特性 流式处理 丰富的状态管理 丰富的时间语义支持 Data pipeline 容错机制 Flink SQL CEP in SQL Flink 应用程序可以消费来自消息队列或分布式日志这类流式数据源(例如 Apache Kafka 或 Kinesis)的实时数据,也可以从各…...

2023软件工程中各种图在现代企业级开发中的使用频率
概览 系统流程图 ✔ 数据流图 不常用 ER图 ✔ 状态转换图 ✔ Warnier图 不常用 IPO图 不常用 Petri网 不常用 层次方框图 不常用 层次图 a.k.a. H图 ✔ 1,层次图描绘软件的层次结构.层层次方框图描绘的是数据结构。 2,层次图的方框表示模块或子模块。层次方框图的方框表示数据结…...

macOS Big Sur 11.7.8 (20G1351) 正式版 ISO、PKG、DMG、IPSW 下载
macOS Big Sur 11.7.8 (20G1351) 正式版 ISO、PKG、DMG、IPSW 下载 本站下载的 macOS 软件包,既可以拖拽到 Applications(应用程序)下直接安装,也可以制作启动 U 盘安装,或者在虚拟机中启动安装。另外也支持在 Window…...
【C++案例】一个项目掌握C++基础-通讯录管理系统
文章目录 1、系统需求2、菜单功能3、退出功能4、添加联系人4.1 设计联系人结构体4.2 设计通讯录结构体4.3 main函数中创建通讯录4.4 封装添加联系人函数4.5 测试添加联系人功能 5、显示联系人5.1 封装显示联系人函数5.2 测试显示联系人功能 6、删除联系人6.1 封装检测联系人是否…...

Triton教程 --- 动态批处理
Triton教程 — 动态批处理 Triton系列教程: 快速开始利用Triton部署你自己的模型Triton架构模型仓库存储代理模型设置优化动态批处理 Triton 提供了动态批处理功能,将多个请求组合在一起执行同一模型以提供更大的吞吐量。 默认情况下,只有当每个输入在…...
Python的并行(持续更新)
0. 参考: 《Python并行编程 中文版》https://python-parallel-programmning-cookbook.readthedocs.io/zh_CN/latest/index.html 1. 线程和进程: 进程可以包含多个并行运行的线程;通常,操作系统创建和管理线程比进程更省CPU资源&am…...

chatgpt赋能python:Python实现Fibonacci数列
Python实现Fibonacci数列 Fibonacci数列是一个非常经典的数列,定义如下: F ( 0 ) 0 , F ( 1 ) 1 F(0)0, F(1)1 F(0)0,F(1)1 F ( n ) F ( n − 1 ) F ( n − 2 ) F(n)F(n-1)F(n-2) F(n)F(n−1)F(n−2) 也就是说,第n个数等于前两个数之和…...

开环模块化多电平换流器仿真(MMC)N=6
模型简介: 运行环境MATLAB2021a 开环模块化多电平换流器仿真(MMC)N=6,连接负载,采用载波移相调制。 可以得到换流器输出N+1=7电平的相电压波形。可考虑线路阻抗。 子模块采用半桥结…...

java springboot整合MyBatis联合查询
前面文章 java springboot整合MyBatis做数据库查询操作写了springboot整合MyBatis的方法 并演示了基础查询的语法 根据id查 那么 我们这次来演示联合查询 我们staff 表 内容如下 每条数据 对应的都有一个departmentid 这是 department部门表的外键id department表内容如下 如…...

windows2022证书配置.docx
Windows证书的配置 要求两台主机,一台作为域,一台进入域 按要求来选择角色服务 确认之后安装 安装完以后配置证书服务 选择服务 按要求配置 注:此处不用域用户登陆无法使用企业CA 按要求来 创建新的私钥 这几处检查无误后默认即可 有效期…...

HCIP网络笔记分享——IA回顾及OSPF协议
第一部分 HCIA回顾1、网络基础2、动态路由协议3、路由认证4、路由控制(AD metric ) 一、知识巩固二、场景模拟1、获取IP地址1.1 DHCP --- 动态主机配置协议1.1.1 DHCP客户端1.1.2 DHCP服务器1.1.3 DHCP客户端1.1.4 DHCP服务器 2、打开浏览器3、路由器进行…...

网络:IP地址、子网掩码、网络地址、广播地址、网段、网关
目录 一、IP地址 二、子网掩码 三、网络地址 四、广播地址 五、网段 六、网关 七、IP地址、子网掩码、网络地址、广指地址、网殷、网关的关系 参考链接 一、IP地址 IP地址是因特网协议(IP)中使用的一种数字标识符,用于唯一地标识网络…...

编程的未来 - 还有未来么?
缘起 唐门教主上个月某天深夜写了一篇博客 --《编程的未来》,要我谈谈感想。 这也是最近软件工程师们聊得比较多的问题,上周,在上海的 “关东小磨” 和十多位 CSDN 博主聚会的时候,大家也稍微谈了一下这个话题,但是谈…...
从零开始搭建群众权益平台(二)
这篇文章我们要建立的群众权益维护平台需要提供用户注册、登录、提交和查看问题或建议的功能,并且支持电话短信登录。在这个过程中,我们需要存储用户的登录信息。 我们将使用Node.js和Express.js作为后端框架,MongoDB作为数据库,并且使用Twilio服务发送短信验证码来实现手…...
Mysql之数据备份
一.日志 1.MySQL 的日志默认保存位置为 /usr/local/mysql/data2.修改日志配置文件 vim /etc/my.cnf [mysqld] ##错误日志,用来记录当MySQL启动、停止或运行时发生的错误信息,默认已开启 log-error/usr/local/mysql/data/mysql_error.log #指定日志的…...

【数据库数据恢复】SQL Server数据表结构损坏的数据恢复案例
数据库故障&分析: SQL server数据库数据无法读取。 经过初检,发现SQL server数据库文件无法被读取的原因是因为底层File Record被截断为0,无法找到文件开头,数据表结构损坏。镜像文件的前面几十M空间和中间一部分空间被覆盖掉…...

C语言/C++ 之 打飞机游戏
【项目简介】 1、设计思想:本项目主要是为了实现打飞机游戏,主要包括5个函数模块,和1个主函数框架。分别是chu_shi_hua();、you_cao_zuo;、wu_cao_zuo();、show();、main();等。项目完成过程中主要运用了C/C中的输入输…...
在 Docker 中部署 Mino 并挂载配置文件
创建本地目录 首先,在主机上创建一个目录,用于存放 Mino 的配置文件。例如,创建一个名为 mino 的目录,用于存放 Mino 的配置文件。 mkdir mino拉取 Mino 镜像 使用以下命令从 Docker Hub 上拉取 Mino 的最新镜像: …...

无限脉动:释放音乐和区块链在音乐领域的力量
音乐是一种永恒的通用语言,它将人们聚集在一起,超越了边界,在我们灵魂深处产生共鸣,创造联系。在当今数字时代,随着区块链技术和去中心化网络的出现,音乐世界正在经历一场深刻的变革。 我们在与艺术家合作&…...
基于算法竞赛的c++编程(28)结构体的进阶应用
结构体的嵌套与复杂数据组织 在C中,结构体可以嵌套使用,形成更复杂的数据结构。例如,可以通过嵌套结构体描述多层级数据关系: struct Address {string city;string street;int zipCode; };struct Employee {string name;int id;…...

CTF show Web 红包题第六弹
提示 1.不是SQL注入 2.需要找关键源码 思路 进入页面发现是一个登录框,很难让人不联想到SQL注入,但提示都说了不是SQL注入,所以就不往这方面想了 先查看一下网页源码,发现一段JavaScript代码,有一个关键类ctfs…...

python/java环境配置
环境变量放一起 python: 1.首先下载Python Python下载地址:Download Python | Python.org downloads ---windows -- 64 2.安装Python 下面两个,然后自定义,全选 可以把前4个选上 3.环境配置 1)搜高级系统设置 2…...

安宝特方案丨XRSOP人员作业标准化管理平台:AR智慧点检验收套件
在选煤厂、化工厂、钢铁厂等过程生产型企业,其生产设备的运行效率和非计划停机对工业制造效益有较大影响。 随着企业自动化和智能化建设的推进,需提前预防假检、错检、漏检,推动智慧生产运维系统数据的流动和现场赋能应用。同时,…...

376. Wiggle Subsequence
376. Wiggle Subsequence 代码 class Solution { public:int wiggleMaxLength(vector<int>& nums) {int n nums.size();int res 1;int prediff 0;int curdiff 0;for(int i 0;i < n-1;i){curdiff nums[i1] - nums[i];if( (prediff > 0 && curdif…...

SpringBoot+uniapp 的 Champion 俱乐部微信小程序设计与实现,论文初版实现
摘要 本论文旨在设计并实现基于 SpringBoot 和 uniapp 的 Champion 俱乐部微信小程序,以满足俱乐部线上活动推广、会员管理、社交互动等需求。通过 SpringBoot 搭建后端服务,提供稳定高效的数据处理与业务逻辑支持;利用 uniapp 实现跨平台前…...
Robots.txt 文件
什么是robots.txt? robots.txt 是一个位于网站根目录下的文本文件(如:https://example.com/robots.txt),它用于指导网络爬虫(如搜索引擎的蜘蛛程序)如何抓取该网站的内容。这个文件遵循 Robots…...

EtherNet/IP转DeviceNet协议网关详解
一,设备主要功能 疆鸿智能JH-DVN-EIP本产品是自主研发的一款EtherNet/IP从站功能的通讯网关。该产品主要功能是连接DeviceNet总线和EtherNet/IP网络,本网关连接到EtherNet/IP总线中做为从站使用,连接到DeviceNet总线中做为从站使用。 在自动…...

Mysql中select查询语句的执行过程
目录 1、介绍 1.1、组件介绍 1.2、Sql执行顺序 2、执行流程 2.1. 连接与认证 2.2. 查询缓存 2.3. 语法解析(Parser) 2.4、执行sql 1. 预处理(Preprocessor) 2. 查询优化器(Optimizer) 3. 执行器…...
A2A JS SDK 完整教程:快速入门指南
目录 什么是 A2A JS SDK?A2A JS 安装与设置A2A JS 核心概念创建你的第一个 A2A JS 代理A2A JS 服务端开发A2A JS 客户端使用A2A JS 高级特性A2A JS 最佳实践A2A JS 故障排除 什么是 A2A JS SDK? A2A JS SDK 是一个专为 JavaScript/TypeScript 开发者设计的强大库ÿ…...