写点什么

🏆【SpringBoot 技术专题】「Tomcat 技术专区」用正确的姿势如何用外置 tomcat 配置及运行(Tomcat 优化分析)

发布于: 9 小时前
🏆【SpringBoot 技术专题】「Tomcat技术专区」用正确的姿势如何用外置tomcat配置及运行(Tomcat优化分析)

前提概要

在特别特殊的时候,我们可能需要外置 tomcat 去运行程序,例如 alitomcat 等特殊场景,方便我们去定时化开发项目或者其他特殊场景。

外置 tomcat 执行

pom.xml 文件首先更改打包方式 war,再排除 springboot 内置的 web 项目下 tomcat 依赖

Maven 配置调整

移除 tomcat 依赖或者将 tomcat 依赖 scope 改为 provide,移除 tomcat 依赖


<dependency>  <groupId>org.springframework.boot</groupId>    <artifactId>spring-boot-starter-web</artifactId>    <!-- 移除嵌入式tomcat插件 -->    <exclusions>      <exclusion>        <groupId>org.springframework.boot</groupId>        <artifactId>spring-boot-starter-tomcat</artifactId>      </exclusion>    </exclusions></dependency>
复制代码

再引入 tomcat 依赖:

 <dependency>    <groupId>org.springframework.boot</groupId>   <artifactId>spring-boot-starter-tomcat</artifactId>     <!--打包的时候可以不用包进去,别的设施会提供。事实上该依赖理论上可以参与编译,测试,运行等周期。                相当于compile,但是打包阶段做了exclude操作-->     <scope>provided</scope>  </dependency>
复制代码

将打包方式修改为 war

<packaging>war</packaging>
复制代码

调整 springboot 的启动类

继承 org.springframework.boot.web.servlet.support.SpringBootServletInitializer,实现 configure 方法:为什么继承该类,SpringBootServletInitializer 源码注释:


  • Note that a WebApplicationInitializer is only needed if you are building a war file and deploying it. If you prefer to run an embedded web server then you won't need this at all.

  • 注意,如果您正在构建 WAR 文件并部署它,则需要 WebApplicationInitializer。如果你喜欢运行一个嵌入式 Web 服务器,那么你根本不需要这个。


DemoApplication,让其实现 SpringBootServletInitializer,然后重写 configure()方法:

方式一,启动类继承 SpringBootServletInitializer 实现 configure:
@SpringBootApplicationpublic class Application extends SpringBootServletInitializer {    public static void main(String[] args) {        SpringApplication.run(Application.class, args);    }    @Override    protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {        return builder.sources(Application.class);    }}
复制代码
方式二,新增加一个类继承 SpringBootServletInitializer 实现 configure:
public class ServletInitializer extends SpringBootServletInitializer {    @Override    protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {        //此处的Application.class为带有@SpringBootApplication注解的启动类        return builder.sources(Application.class);    }}
复制代码



注意事项
  • 使用外部 Tomcat 部署访问的时候,application.properties(或者 application.yml)中配置的将失效,请使用 tomcat 的端口,tomcat,webapps 下项目名进行访问。


server.port=server.servlet.context-path=
复制代码


  • 为了防止应用上下文所导致的项目访问资源加载不到的问题,建议 pom.xml 文件中<build></build>标签下添加<finalName></finalName>标签:


<build>    <!-- 应与application.properties(或application.yml)中context-path保持一致 -->    <finalName>war包名称</finalName>    <plugins>        <plugin>            <groupId>org.springframework.boot</groupId>            <artifactId>spring-boot-maven-plugin</artifactId>        </plugin>    </plugins></build>
复制代码




  1. IDEA 下,运行 mvn clean,--> mvn package ,等到项目打包成功,在该项目文件位置找到 target 下的 demo.war,拷贝到 tomcat/webapps 目录下,

  2. 使用 windows 命令行,启动 tomcat 服务器,项目启动成功会出现, spring 的标志。

  3. 访问路径:localhost:8080/${打包文件名}/请求 url

  4. 如何在访问时去掉 war 包名?


原理:Tomcat 的默认根目录是 ROOT,实际上 ROOT 这个项目在实际生产环境是没有用的,所以我们可以用我们的项目覆盖 ROOT 项目

操作过程

  1. 删除 ROOT 下所有文件及文件夹.

  2. 把我们项目的 war 包解压后,项目目录下的所有文件和子目录都拷贝到 ROOT 目录下即可或者有更狠的一招:直接删掉 ROOT 目录,然后把我们的项目打包名称改成 ROOT.war,放到 webapps 下就行.


原理:Tomcat 本身可以配置虚拟目录。方法就是在 Server.xml 中<Engine><Host>节点下加入 Context 信息。


如我们可以配置<Context path="/abc" docBase="D:\app\abc" ... />,那我们可以通过地址 http://localhost:8080/abc 来访问我们放在 D:\app\下面的 abc 项目。我们可以把这个 path="/abc"修改为 path=""。意思就是把 abc 映射到根目录,访问路径就会变成 http://localhost:8080/。


按照配置虚拟目录的方式,在<Engine><Host>下添加一个 Context 节点,具体配置如下:


  <Engine name="Catalina" defaultHost="localhost">            <Host name="localhost"  appBase="webapps" unpackWARs="true" autoDeploy="true">              <Context path="" docBase="Interface" reloadable="true" />          <!--注:我这里使用的是相对路径,Interface项目是放在Tomcat的webapps目录下的,当然也可以改为绝对路径-->           </Host>          </Engine>
复制代码


Spring Boot 也提供了对 JMX 监控的支持。JMX 监控对外暴露的信息相同,不过是使用 MBeans 容器将应用数据封装管理。


  • Springboot 的 jmx 是默认开启的,如果 tomcat 部署两个原 springboot 打成的 war 包。

  • 需要将每一个项目的 jmx 关闭 Application.properties 配置中 添加 spring.jmx.enabled=false;

  • 在各自项目中都添加:spring.jmx.default-domain=project1 以及 spring.jmx.default-domain=project2 保证 domain 是不一样的。

tomcat 相关配置修改

修改默认的 http 访问端口:8080 为 8181,在 tomcat8.5/conf/server.xml 文件里修改:示例


<Connector port="8181" protocol="HTTP/1.1"               connectionTimeout="20000"               redirectPort="8443" />
复制代码

修改默认的 session 有效期

在 tomcat/conf/web.xml 文件里修改,范围是具体项目:示例


    <session-config>       <session-timeout>30</session-timeout>    </session-config>
复制代码


在 tomcat/conf/server.xml


<Context path="/test" docBase="/test"        defaultSessionTimeOut="3600" isWARExpanded="true"        isWARValidated="false" isInvokerEnabled="true"        isWorkDirPersistent="false"/>
复制代码


在 java 后台代码里配置,具体页面的 session 有效时间


session.setMaxInactiveInterval(30*60);
复制代码


生效优先级: 3 > 2 > 1,

(彩蛋) tomcat 调优

tomcat 修改最大线程,在 tomcat/conf/server.xml 文件里修改:示例




  • maxThreads=“X” 表示最多同时处理 X 个连接

  • minSpareThreads=“X” 初始化 X 个连接

  • maxSpareThreads=“X” 表示如果最多可以有 X 个线程,一旦超过 X 个,则会关闭不在需要的线程

  • acceptCount=“X” 当同时连接的人数达到 maxThreads 时,还可以排队,队列大小为 X.超过 X 就不处理

tomcat 内存优化

  • Windows 下的 catalina.bat

  • Linux 下的 catalina.sh 如:


JAVA_OPTS=’-Xms256m -Xmx512m’-Xms JVM 初始化堆的大小-Xmx JVM 堆的最大值 实际参数大小根据服务器配置或者项目具体设置.

Tomcat IO 优化
  1. 同步阻塞 IO(JAVA BIO) 同步并阻塞,服务器实现模式为一个连接一个线程(one connection one thread 想想都觉得恐怖,线程可是非常宝贵的资源),当然可以通过线程池机制改善.

  2. BIO 方式适用于连接数目比较小且固定的架构,这种方式对服务器资源要求比较高,并发局限于应用中,JDK1.4 以前的唯一选择,但程序直观简单易理解.

  3. JAVA NIO:又分为同步非阻塞 IO,异步阻塞 IO 与 BIO 最大的区别 one request one thread.可以复用同一个线程处理多个 connection(多路复用)

  4. NIO 方式适用于连接数目多且连接比较短(轻操作)的架构,比如聊天服务器,并发局限于应用中,编程比较复杂,JDK1.4 开始支持.

  5. 异步非阻塞 IO(Java NIO2 又叫 AIO) 主要与 NIO 的区别主要是操作系统的底层区别.可以做个比喻:比作快递,NIO 就是网购后要自己到官网查下快递是否已经到了(可能是多次),然后自己去取快递;AIO 就是快递员送货上门了(不用关注快递进度)。

  6. AIO 方式使用于连接数目多且连接比较长(重操作)的架构,比如相册服务器,充分调用 OS 参与并发操作,编程比较复杂,JDK7 开始支持.

在 server.xml 中

实现对 Tomcat 的 IO 切换


APR 是从操作系统级别来解决异步的 IO 问题,大幅度的提高性能. (http://apr.apache.org/).


  1. APR(Apache Portable Runtime)是一个高可移植库,它是 Apache HTTP Server 2.x 的核心.能更好地和其它本地 web 技术集成,总体上让 Java 更有效率作为一个高性能 web 服务器平台而不是简单作为后台容器.

  2. 在产品环境中,特别是直接使用 Tomcat 做 WEB 服务器的时候,应该使用 Tomcat,Native 来提高其性能.如果不配 APR,基本上 300 个线程狠快就会用满,以后的请求就只好等待.但是配上 APR 之后,并发的线程数量明显下降,从原来的 300 可能会马上下降到只有几十,新的请求会毫无阻塞的进来.

  3. 在局域网环境测,就算是 400 个并发,也是一瞬间就处理/传输完毕,但是在真实的 Internet 环境下,页面处理时间只占 0.1%都不到,绝大部分时间都用来页面传输.如果不用 APR,一个线程同一时间只能处理一个用户,势必会造成阻塞。所以生产环境下用 apr 是非常必要的.

发布于: 9 小时前阅读数: 6
用户头像

🏆2021年InfoQ写作平台-签约作者 🏆 2020.03.25 加入

👑【酷爱计算机技术、醉心开发编程、喜爱健身运动、热衷悬疑推理的”极客狂人“】 🏅 【Java技术领域,MySQL技术领域,APM全链路追踪技术及微服务、分布式方向的技术体系等】 我们始于迷惘,终于更高水平的迷惘

评论

发布
暂无评论
🏆【SpringBoot 技术专题】「Tomcat技术专区」用正确的姿势如何用外置tomcat配置及运行(Tomcat优化分析)