Tomcat,Jetty和Undertow:Spring Boot嵌入式Servlet容器的比較

1.源碼

隨着微服務的普及,我們已經看到帶有嵌入式servlet容器的應用的普及也有類似的增長。Spring Boot是基於Java的框架,支持應用程序服務。它可以作爲帶有嵌入式servlet容器的獨立jar或容器內的WAR文件運行。

使用SpringBoot時,首先引人注意的便是其啓動方式,我們熟知的web項目都是需要部署到服務容器上,例如tomcat、weblogic、widefly(以前叫JBoss),然後啓動web容器真正運行我們的系統。而SpringBoot搭建的系統卻是運行***Application.class中的main方法啓動。原因是SpringBoot除了高度集成封裝了Spring一系列框架之外,還封裝了web容器,SpringBoot啓動時會根據配置啓動相應的上下文環境,查看EmbeddedServletContainerAutoConfiguration源碼可知。

@AutoConfigureOrder(-2147483648)
@Configuration
@ConditionalOnWebApplication
@Import({EmbeddedServletContainerAutoConfiguration.BeanPostProcessorsRegistrar.class})
public class EmbeddedServletContainerAutoConfiguration {
    ...
    ...(中間省略部分)

    @Configuration
    @ConditionalOnClass({Servlet.class, Undertow.class, SslClientAuthMode.class})//Undertow配置判斷
    @ConditionalOnMissingBean(
        value = {EmbeddedServletContainerFactory.class},
        search = SearchStrategy.CURRENT
    )
    public static class EmbeddedUndertow {
        public EmbeddedUndertow() {
        }

        @Bean
        public UndertowEmbeddedServletContainerFactory undertowEmbeddedServletContainerFactory() {
            return new UndertowEmbeddedServletContainerFactory();
        }
    }

    @Configuration
    @ConditionalOnClass({Servlet.class, Server.class, Loader.class, WebAppContext.class})//Jetty配置判斷
    @ConditionalOnMissingBean(
        value = {EmbeddedServletContainerFactory.class},
        search = SearchStrategy.CURRENT
    )
    public static class EmbeddedJetty {
        public EmbeddedJetty() {
        }

        @Bean
        public JettyEmbeddedServletContainerFactory jettyEmbeddedServletContainerFactory() {
            return new JettyEmbeddedServletContainerFactory();
        }
    }

    @Configuration
    @ConditionalOnClass({Servlet.class, Tomcat.class})//Tomcat配置判斷,默認爲Tomcat
    @ConditionalOnMissingBean(
        value = {EmbeddedServletContainerFactory.class},
        search = SearchStrategy.CURRENT
    )
    public static class EmbeddedTomcat {
        public EmbeddedTomcat() {
        }

        @Bean
        public TomcatEmbeddedServletContainerFactory tomcatEmbeddedServletContainerFactory() {
            return new TomcatEmbeddedServletContainerFactory();
        }
    }
}


該自動配置類表明SpringBoot支持封裝Tomcat、Jetty和Undertow三種web容器,查看spring-boot-starter-web的pom.xml(如下),其默認配置爲Tomcat。

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starters</artifactId>
        <version>1.5.8.RELEASE</version>
    </parent>
    <artifactId>spring-boot-starter-web</artifactId>
    <name>Spring Boot Web Starter</name>
    <description>Starter for building web, including RESTful, applications using Spring
        MVC. Uses Tomcat as the default embedded container</description>
    <url>http://projects.spring.io/spring-boot/</url>
    <organization>
        <name>Pivotal Software, Inc.</name>
        <url>http://www.spring.io</url>
    </organization>
    <properties>
        <main.basedir>${basedir}/../..</main.basedir>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-tomcat</artifactId>
        </dependency>
        ...
        ...


若我們使用其他容器,該如何配置

Tomcat vs. Jetty vs. Undertow: Comparison of Spring Boot Embedded Servlet Containers詳細比較了SpringBoot中三種容器的性能、穩定性等,結果證明了Undertow在性能和內存使用上是最好的。

1.設置Spring Boot應用程序

1.1 Setup Spring Boot Dependencies

默認的嵌入式Servlet容器是Tomcat。 此版本的Spring Web 1.4.3引入了Tomcat版本8.5.6。

pom.xml

01

02

03

04

05

06

07

08

09

10

11

12

13

<parent><font></font>

   <groupId>org.springframework.boot</groupId><font></font>

   <artifactId>spring-boot-starter-parent</artifactId><font></font>

   <version>1.4.3.RELEASE</version><font></font>

</parent><font></font>

<font></font>

<dependencies><font></font>

   <!-- TOMCAT --><font></font>

   <dependency><font></font>

      <groupId>org.springframework.boot</groupId><font></font>

      <artifactId>spring-boot-starter-web</artifactId><font></font>

   </dependency><font></font>

</dependencies><font></font>

1.2設置Spring Boot主要應用程序和控制器
要設置Spring Boot應用程序,您可以在Main類中包含@SpringBootApplication批註。 @SpringBootApplication批註引入了@ SpringBootConfiguration,@ EnableAutoConfiguration和@ComponentScan批註。

Application.java

1

2

3

4

5

6

@SpringBootApplication

@ConfigurationProperties

public class Application {

public static void main(String[] args) {

   SpringApplication.run(Application.class, args);

}

 

You may choose to eliminate this annotation and add the @SpringBootConfiguration alone or to another class that allows you to customize the configuration. The @ComponentScan will scan your application for items like the @Controller you will need to setup a RESTful service. The following controller will return a simple “Hello World” string from a HTTP GET request. We have also included in the bundled example another endpoint mapping that returns a complex object type.

SampleController.java

01

02

03

04

05

06

07

08

09

10

11

@Controller

public class SampleController {

 

@Autowired

private ResourceLoader resourceLoader;

 

@RequestMapping("/")

@ResponseBody

public String home() {

   return "Hello World!";

}

1.3 Key Configuration Parameters

The default properties for all the embedded servlet containers are the same. Some of the most important properties to consider are the properties for configuring startup information like ports and application name, TSL, access logs, compression and many more.

For example, to configure SSL add the following to key value pairs to the application.properties.

application.properties

1

2

3

4

server.port=8443

server.ssl.key-store=classpath:keystore.jks

server.ssl.key-store-password=secret

server.ssl.key-password=another-secret

1.4 How to Find Additional Parameters

To explore the parameters for Spring boot applications you can add the Spring actuator dependency and the @ConfigurationProperties annotation to your Main class. You then visit the /configprops endpoint on your application to get a list of the available properties.

Application.java

1

2

3

@SpringBootApplication

@ConfigurationProperties

public class Application {

pom.xml

1

2

3

4

<dependency>

   <groupId>org.springframework.boot</groupId>

   <artifactId>spring-boot-starter-actuator</artifactId>

</dependency>

1

http://localhost:8080/jcg/service/configprops

1.5 Change version of Embedded Servlet Containers

The embedded servlet container versions are defined in the following parent dependency from the pom. You can change the version of the embedded servlet container by explicitly including the dependency and identifying a new version in the pom. We will show you how in the examples below.

pom.xml

1

2

3

4

5

<dependency>

   <groupId>org.springframework.boot</groupId>

   <artifactId>spring-boot-dependencies</artifactId>

   <version>1.3.7.RELEASE</version>

</dependency>

2. Tomcat

As Tomcat is the default embedded servlet container there is nothing you need to do to the default implementation to use Tomcat. You can change the version of Tomcat you are using or change properties in the pom.xml or application.properties files.

2.2 Change Version of Tomcat

pom.xml

01

02

03

04

05

06

07

08

09

10

11

12

13

14

15

16

17

<properties><tomcat.version>8.5.6</tomcat.version></properties>

 

<dependency>

   <groupId>org.apache.tomcat.embed</groupId>

   <artifactId>tomcat-embed-core</artifactId>

   <version>${tomcat.version}</version>

</dependency>

<dependency>

   <groupId>org.apache.tomcat.embed</groupId>

   <artifactId>tomcat-embed-el</artifactId>

   <version>${tomcat.version}</version>

</dependency>

<dependency>

   <groupId>org.apache.tomcat.embed</groupId>

   <artifactId>tomcat-embed-websocket</artifactId>

   <version>${tomcat.version}</version>

</dependency>

3. Jetty

To change the embedded servlet container to Jetty you need to edit the pom file to remove the Tomcat dependency and add Jetty.

3.1 Change to Jetty (version 9.3.14)

pom.xml

01

02

03

04

05

06

07

08

09

10

11

12

13

14

<dependency>

   <groupId>org.springframework.boot</groupId>

   <artifactId>spring-boot-starter-web</artifactId>

   <exclusions>

      <exclusion>

         <groupId>org.springframework.boot</groupId>

         <artifactId>spring-boot-starter-tomcat</artifactId>

      </exclusion>

   </exclusions>

</dependency>

<dependency>

   <groupId>org.springframework.boot</groupId>

   <artifactId>spring-boot-starter-jetty</artifactId>

</dependency>

4. Undertow

To change the embedded servlet container to Undertow you need to edit the pom file to remove the Tomcat dependency and add Undertow.

4.1 Change to Undertow (version 1.3.24 final)

Notice the undertow version included in the spring boot starter is incorrect, referring to 1.3.25. You’ll need to change it to 1.3.24.Final for this to work at the time of this article.

pom.xml

01

02

03

04

05

06

07

08

09

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

<dependency>

   <groupId>org.springframework.boot</groupId>

   <artifactId>spring-boot-starter-web</artifactId>

   <exclusions>

      <exclusion>

         <groupId>org.springframework.boot</groupId>

         <artifactId>spring-boot-starter-tomcat</artifactId>

      </exclusion>

   </exclusions>

</dependency>

<dependency>

   <groupId>org.springframework.boot</groupId>

   <artifactId>spring-boot-starter-undertow</artifactId>

</dependency>

<dependency>

   <groupId>io.undertow</groupId>

   <artifactId>undertow-core</artifactId>

   <version>1.3.24.Final</version>

</dependency>

<dependency>

   <groupId>io.undertow</groupId>

   <artifactId>undertow-servlet</artifactId>

   <version>1.3.24.Final</version>

</dependency>

5. Performance and Load

In this example, we will analyze both the peformance of HTTP requests and the memory footprint at startup of all three embedded servlet containers. We used JMeter to measure performance by simulating load and JVisualVM to look at the memory footprint.

5.1 Measure Performance

In this example, we will analyze both the peformance of simple RESTFul GET requests that return a string and more complex GET requests that return complex JSON objects. JMeter is the tool used to measure the performance of the the three different types of containers. The key to setting up this test was establishing thread groups with the appropriate load, a counter to dynamically update the input to the API and report viewers to display or aggregate the results. For the simple string examples, we used a thread group with 1000 threads that would loop 3 times through the sequence. It also used a ramp up time of 10 seconds. For the complex object examples, we used the same parameters but did not loop.

7.結論
數字表明Undertow在性能和內存使用方面是最好的。 令人鼓舞的是,Undertow正在擁抱最新功能並默認使用持久連接。 這些數字並不表示此示例中使用的負載會導致性能出現顯着差異,但是我可以想象它們會擴展,並且如果性能是最重要的因素,Undertow將是您的應用程序的正確選擇。 考慮到組織的功能,可以合理地認爲一個組織可能偏愛嵌入式Servlet容器。 由於應用程序要求(包括性能,內存使用情況和功能),許多默認設置將不得不更改。

連接地址:https://examples.javacodegeeks.com/enterprise-java/spring/tomcat-vs-jetty-vs-undertow-comparison-of-spring-boot-embedded-servlet-containers/

更換內置容器,能提高SpringBoot項目的性能,由於SpringBoot插拔式的模塊設計,配置Undertow只需要兩步,如下。

1.第一步,去除原容器依賴,加入Undertow依賴。

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    <exclusions>
        <exclusion>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-tomcat</artifactId>
        </exclusion>
    </exclusions>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-undertow</artifactId>
</dependency>


2.第二步,在application.yml中配置Undertow。

server.undertow.accesslog.dir= # Undertow access log directory.
server.undertow.accesslog.enabled=false # Enable access log.
server.undertow.accesslog.pattern=common # Format pattern for access logs.
server.undertow.accesslog.prefix=access_log. # Log file name prefix.
server.undertow.accesslog.rotate=true # Enable access log rotation.
server.undertow.accesslog.suffix=log # Log file name suffix.
server.undertow.buffer-size= # Size of each buffer in bytes.
server.undertow.buffers-per-region= # Number of buffer per region.
server.undertow.direct-buffers= # Allocate buffers outside the Java heap.
server.undertow.io-threads= # Number of I/O threads to create for the worker.
server.undertow.max-http-post-size=0 # Maximum size in bytes of the HTTP post content.
server.undertow.worker-threads= # Number of worker threads.

到這裏,肯定會有很多人有疑惑,非得用SpringBoot集成的容器作爲運行環境嗎?

SpringBoot同樣提供了像往常一樣打war包部署的解決方案。

1.將項目的啓動類Application.java繼承SpringBootServletInitializer並重寫configure方法。

@SpringBootApplication
public class Application extends SpringBootServletInitializer {

    @Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
        return application.sources(Application.class);
    }

    public static void main(String[] args) throws Exception {
        SpringApplication.run(Application.class, args);
    }

}


2.在pom.xml文件中,< project >標籤下面添加war包支持的< package >標籤,或者將原標籤值jar改成war。

 

<packaging>war</packaging>


3.在pom.xml文件中,去除tomcat依賴,或者將其標記爲provided(打包時排除),provided方式有一點好處是調試是可以用內置tomcat。

<dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-tomcat</artifactId>
        <scope>provided</scope>
</dependency>


至此,以上3個配置便可以完成war方式部署,注意war包部署後訪問時需要加上項目名稱
 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章