1 概述

1.0 关键依赖包

  • spring-boot-autoconfigure : 2.3.12.RELEASE
  • spring-boot : 2.3.12.RELEASE
  • spring-context : 5.2.15.RELEASE
  • spring-webmvc : 5.2.15.RELEASE
  • tomcat-embed-core:9.0.46
  • tomcat-embed-jasper:9.0.46

1.1 内嵌 Web Server 的优势

我们在使用 springboot 开发 web 项目时,大多数时候采用的是内置的 Tomcat (当然也可配置支持内置的 jett y),内置 Tomcat 有什么好处呢?

  • 方便微服务部署,减少繁杂的配置
  • 方便项目启动,不需要单独下载web容器,如Tomcat,jetty等。

1.2 Web Server 的优化思路

针对目前的容器优化,可以从以下几点考虑:

  • 1、线程数
  • 2、超时时间
  • 3、JVM优化

1.3 Tomcat Web Server的核心配置参数

min-spare-threads

max-threads

accept-count

max-connections

connection-timeout

keepAliveTimeout

http keep-alive与tcp keep-alive,不是同一回事,意图不一样。

http keep-alive是为了让tcp活得更久一点,以便在同一个连接上传送多个http,提高socket的效率。

而tcp keep-alive是TCP的一种检测TCP连接状况的保鲜机制。

tcp keep-alive保鲜定时器,支持三个系统内核配置参数:
	echo 1800 > /proc/sys/net/ipv4/tcp_keepalive_time
	echo 15 > /proc/sys/net/ipv4/tcp_keepalive_intvl
	echo 5 > /proc/sys/net/ipv4/tcp_keepalive_probes
	
keepalive是TCP保鲜定时器,当网络两端建立了TCP连接之后,闲置idle(双方没有任何数据流发送往来)了tcp_keepalive_time后,服务器内核就会尝试向客户端发送侦测包,来判断TCP连接状况(有可能客户端崩溃、强制关闭了应用、主机不可达等等)。如果没有收到对方的回答(ack包),则会在 tcp_keepalive_intvl后再次尝试发送侦测包,直到收到对对方的ack,如果一直没有收到对方的ack,一共会尝试 tcp_keepalive_probes次,每次的间隔时间在这里分别是15s, 30s, 45s, 60s, 75s。如果尝试tcp_keepalive_probes,依然没有收到对方的ack包,则会丢弃该TCP连接。TCP连接默认闲置时间是2小时,一般设置为30分钟足够了。
总结一下,实际上tcp keep-alive是一个协议级别的心跳检测实现,当超过规定的时间,tcp就断开,而这边是讨论的http的keepalive,描述的http高层多次tcp链接共享,根本不是一个网络层级的东西,一定注意不要混淆。

1.4 springboot --> tomcat 源码分析

spring-boot-autoconfigure : 2.3.12.RELEASE

-> org.springframework.boot.autoconfigure.web.embedded.EmbeddedWebServerFactoryCustomizerAutoConfiguration
    @ConditionalOnClass({Tomcat.class, UpgradeProtocol.class})
    public static class TomcatWebServerFactoryCustomizerConfiguration { [*]
        @Bean
        public TomcatWebServerFactoryCustomizer tomcatWebServerFactoryCustomizer(Environment environment, ServerProperties serverProperties){
            return new TomcatWebServerFactoryCustomizer(environment, serverProperties);
        }
    }
    
-> org.springframework.boot.autoconfigure.web.embedded.TomcatWebServerFactoryCustomizer
    + 关系: public class TomcatWebServerFactoryCustomizer implements WebServerFactoryCustomizer<ConfigurableTomcatWebServerFactory>, Ordered { /** ... **/ }
    + 属性:
        private final Environment environment;
        private final org.springframework.boot.autoconfigure.web.ServerProperties serverProperties; [*]
    + 方法:
        public void customize(ConfigurableTomcatWebServerFactory factory) { 
            ServerProperties properties = this.serverProperties;
            ServerProperties.Tomcat tomcatProperties = properties.getTomcat();
                --> Tomcat { // 内部类
                    private final Threads threads = new Threads();
                    ...
                    private int maxConnections;
                    private int acceptCount;
                    ...
                    private Duration connectionTimeout;
                    ...
                    private Charset uriEncoding;
                    --> Threads { // 内部类
                        private int max = 200;
                        private int minSpare = 10;
                    }
                }
            PropertyMapper propertyMapper = PropertyMapper.get();
            
            ServerProperties.Tomcat.Threads threadProperties = tomcatProperties.getThreads();
            ...
            propertyMapper.from(threadProperties::getMax).when(this::isPositive).to((maxThreads) -> {
                this.customizeMaxThreads(factory, threadProperties.getMax());
            });
            ...
            propertyMapper.from(threadProperties::getMinSpare).when(this::isPositive).to((minSpareThreads) -> {
                this.customizeMinThreads(factory, minSpareThreads);
            });
            ...
            propertyMapper.from(tomcatProperties::getMaxHttpFormPostSize).asInt(DataSize::toBytes).when((maxHttpFormPostSize) -> {
                return maxHttpFormPostSize != 0;
            }).to((maxHttpFormPostSize) -> {
                this.customizeMaxHttpFormPostSize(factory, maxHttpFormPostSize);
            });
            ...
            propertyMapper.from(tomcatProperties::getAccesslog).when(ServerProperties.Tomcat.Accesslog::isEnabled).to((enabled) -> {
                this.customizeAccessLog(factory);
            });
            ...
            propertyMapper.from(tomcatProperties::getUriEncoding).whenNonNull().to(factory::setUriEncoding);
            ...
            propertyMapper.from(tomcatProperties::getConnectionTimeout).whenNonNull().to((connectionTimeout) -> {
                this.customizeConnectionTimeout(factory, connectionTimeout);
            });
            ...
            propertyMapper.from(tomcatProperties::getMaxConnections).when(this::isPositive).to((maxConnections) -> {
                this.customizeMaxConnections(factory, maxConnections);
            });
            ...
            propertyMapper.from(tomcatProperties::getAcceptCount).when(this::isPositive).to((acceptCount) -> {
                this.customizeAcceptCount(factory, acceptCount);
            });
        }
        
        private void customizeAcceptCount(ConfigurableTomcatWebServerFactory factory, int acceptCount) {
            factory.addConnectorCustomizers(new TomcatConnectorCustomizer[]{(connector) -> {
                ProtocolHandler handler = connector.getProtocolHandler();
                if (handler instanceof AbstractProtocol) {
                    AbstractProtocol<?> protocol = (AbstractProtocol)handler;
                    protocol.setAcceptCount(acceptCount);
                }
    
            }});
        }
        ...
        private void customizeMaxConnections(ConfigurableTomcatWebServerFactory factory, int maxConnections) {
            factory.addConnectorCustomizers(new TomcatConnectorCustomizer[]{(connector) -> {
                ProtocolHandler handler = connector.getProtocolHandler();
                if (handler instanceof AbstractProtocol) {
                    AbstractProtocol<?> protocol = (AbstractProtocol)handler;
                    protocol.setMaxConnections(maxConnections);
                }
    
            }});
        }
        ...

X 参考文献

04-08 00:52