Spring Boot与Micronaut性能比较

文章转载出处:微信公众号——锅外的大佬 链接:https://mp.weixin.qq.com/s/MdBByJ0ju-rROKg7jsWygA

今天我们将比较两个在JVM上构建微服务的框架:Spring BootMicronautSpring BootJVM世界中最受欢迎和最具代表性的框架。Micronaut作为Spring Boot的竞争对手,通过构建无服务器功能或低内存占用微服务,迅速流行起来。我们将比较Spring Boot2.1.4版本和Micronaut1.0.0.RC1。比较标准是:

  • 内存使用情况(堆和非堆)
  • 生成fat JAR文件的MB数
  • 应用程序启动时间
  • 应用程序的性能,在样本负载测试期间REST端口的平均响应时间的含义

为了使测试尽可能准确,我们将收集两个几乎相同的应用程序的统计数据。当然,唯一的区别在于构建的框架。示例应用程序非常简单。它为一个实体公开了一些在内存中操作CRUD的端口。它还公开了infohealth端点,以及通过Swagger API自动生成所有端点的文档。

我将在JDK 11上测试示例应用程序性能。在启动和负载测试期间使用Yourkit分析和监视内存使用情况,并使用Gatling来构建性能API测试。首先,简要概述一下示例应用程序。

1. 源代码

我已经实现了非常简单的内存存储bean,它将新对象添加到列表中,并提供了通过在add方法中生成的id搜索对象的find方法。

public class PersonRepository { 
        List<Person> ersons = new ArrayList<>(); 
        public Person add(Person person) {
            person.setId(persons.size()+1); 
            persons.add(person); 
            return person; 
        }
        public Person findById(Long id) { 
        Optional<Person> person = persons.stream().filter(a -> a.getId().equals(id)).findFirst(); 
        if (person.isPresent()) return person.get(); else return null; } public List<Person> findAll() { return persons; }
        }

  

Repository Bean被注入到controller(控制器)。Controller公开了两种HTTP方法。其中第一个(POST)用于添加新对象,而第二个(GET)用于通过id搜索它。这是Spring Boot应用程序中的控制器实现:

@RestController@RequestMapping("/persons")public class PersonsController {private static final Logger LOGGER = LoggerFactory.getLogger(PersonsController.class);@AutowiredPersonRepository repository;@PostMappingpublic Person add(@RequestBody Person person) {LOGGER.info("Person add: {}", person);return repository.add(person);}@GetMapping("/{id}")public Person findById(@PathVariable("id") Long id) {LOGGER.info("Person find: id={}", id);return repository.findById(id);}@GetMappingpublic List<Person> findAll() {LOGGER.info("Person find");return repository.findAll();}}

接下来是Micronaut的类似实现。为了实现REST端点:healthcheckSwagger API,我们需要添加一些依赖项。以下是Spring Boot的依赖项列表:

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.1.4.RELEASE</version>
</parent>
<groupId>pl.piomin.services</groupId>
<artifactId>sample-app</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
    <java.version>11</java.version>
    <maven.compiler.source>${java.version}</maven.compiler.source>
    <maven.compiler.target>${java.version}</maven.compiler.target>
</properties>
<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>
    <dependency>
        <groupId>io.springfox</groupId>
        <artifactId>springfox-swagger2</artifactId>
        <version>2.9.2</version>
    </dependency>
</dependencies>

  


以下是Micronaut所需的类似依赖列表:
<dependency>
    <groupId>io.micronaut</groupId>
    <artifactId>micronaut-http-server-netty</artifactId>
</dependency>
<dependency>
    <groupId>io.micronaut</groupId>
    <artifactId>micronaut-inject</artifactId>
</dependency>
<dependency>
    <groupId>io.micronaut</groupId>
    <artifactId>micronaut-runtime</artifactId>
</dependency>
<dependency>
    <groupId>io.micronaut</groupId>
    <artifactId>micronaut-management</artifactId>
</dependency>
<dependency>
    <groupId>io.micronaut</groupId>
    <artifactId>micronaut-inject-java</artifactId>
    <scope>provided</scope>
</dependency>
<dependency>
    <groupId>io.swagger.core.v3</groupId>
    <artifactId>swagger-annotations</artifactId>
</dependency>
<dependency>
    <groupId>ch.qos.logback</groupId>
    <artifactId>logback-classic</artifactId>
    <version>1.2.3</version>
    <scope>runtime</scope>
</dependency>

  

application.yml中必须使用一些额外的配置来启用Swaggerhealthchecks
micronaut: router: static-resources: swagger: paths: classpath:META-INF/swagger mapping: /swagger/**endpoints: info: enabled: true sensitive: false

2.开启应用

首先,使用Intellij开始应用程序。基于Spring Boot构建的示例应用程序大约需要6-7秒。如下所示,正好需要6.344s.11cb8e8ffbade5edc7e805bc9b3bc149.png
建立在Micronaut之上的类似应用程序开启大约3-4秒。如下所示,正好占用3.463。
无论如何,当我通过公共代理来启动应用程序时,都必须设置VM选项Dmicronaut.cloud.platform=BARE_METAL来规避环境的影响。8db765373921e74264057e0db1918705.png
这是Spring BootMicronaut启动时间差异的图表。07231b35066ff47cf195aaf1a60b1540.png

3. 应用构建

我们还将检查应用程序fat JAR的大小。为此,推荐使用mvn clean install命令构建应用程序。对于Spring Boot,我们使用了两个标准的starter包: WebActuatorSwagger SpringFox库。因此,包含了50多个库。当然,我们可以做一些去除或不使用starter包,但我选择了最简单的方法来构建应用程序。fat JAR的大小为24.2 MB。同样的,基于Micronaut的应用程序就要小很多。fat JAR的大小为12.1 MB。我在pom.xml中包含了更多的库,最后包含了37个库。Spring Boot在标准配置中包含更多库,但在另一方面它比Micronaut有更多的功能和自动配置。这是Spring BootMicronaut目标JAR大小差异的图表。51f7f031e897d11960639c487fa3c243.png
4. 内存管理在启动之后,Spring Boot应用程序为堆分配了305 MB,为non-heap(非堆)分配了81 MB。我没有使用Xmx或任何其他选项设置任何内存限制。在堆中,old gen(老年代)消耗了8 MB,eden区消耗了60 MB,survivor消耗了15 MB。大多数non-heap(非堆)由metaspace消耗 - 52 MB。运行性能负载测试后,堆分配增加到369 MB,non-heap(非堆)到87 MB。这是性能测试之前和测试期间的CPU和RAM使用情况的截图。83f2834db80942200e130a9b9ed65c0e.png
在启动之后,Micronaut应用程序为堆分配了254 MB,为非堆分配了51 MB。我没有使用Xmx或任何其他选项设置任何内存限制 - 与Spring Boot应用程序相同。在堆中,old gen(老年代)消耗了2.5 MB,eden区消耗了20 MB,survivor消耗了7 MB。大多数non-heap非堆内存由metaspace消耗 - 35 MB。运行性能负载测试后,堆分配没有改变,非堆增加到63 MB。这是性能测试之前和测试期间CPU和RAM使用情况的截图。39de67223a934cecca811b55dd51c023.png
这是Spring BootMicronaut启动之后堆内存使用情况比较。ae84bf06ad9b8bafd2bde8c5195a6f00.png
non-heap:252b6f710719ccdc4618cb75dc48101e.png
5. 性能测试使用Gatling来构建性能负载测试。此工具允许在Scala中创建测试方案。我们使用20个线程同时发送40k样本请求。这是为POST方法实现的测试类。

class SimpleTest extends Simulation {val scn = scenario("AddPerson").repeat(2000, "n") {exec(http("Persons-POST").post("http://localhost:8080/persons").header("Content-Type", "application/json").body(StringBody("""{"name":"Test${n}","gender":"MALE","age":100}""")).check(status.is(200)))}setUp(scn.inject(atOnceUsers(20))).maxDuration(FiniteDuration.apply(10, TimeUnit.MINUTES))}

这是为GET方法实现的测试类。

class SimpleTest2 extends Simulation {val scn = scenario("GetPerson").repeat(2000, "n") {exec(http("Persons-GET").get("http://localhost:8080/persons/${n}").check(status.is(200)))}setUp(scn.inject(atOnceUsers(20))).maxDuration(FiniteDuration.apply(10, TimeUnit.MINUTES))}

post/person方法的性能测试结果如下图所示。一秒钟内平均处理请求数是1176。74359735395676aa428b27bda3b3360a.png
下面的截图显示了响应时间随时间变化的百分位数的直方图。21467fbc4b6d0b29318121dee0c6f52b.png
GET / persons / {id}方法的性能测试结果如下图所示。一秒钟内平均处理请求数是1428。ce28eb46aa3ebf9ca33c461c860c7d2a.png
以下截图显示了响应时间随时间变化的百分位数的直方图。a6e81c6e469e58dcb4c0291a758c1308.png
现在,我们为Micronaut应用程序进行相同的Gatling负载测试。 POST /person 方法的性能测试结果如下图所示。在一秒钟内平均处理请求数是1290。129bc56c1ddbdfa02dbb83478364c270.png
以下截图显示了响应时间随时间变化的百分位数的直方图。0a3732d90508f50c257bbc5343122841.png
GET / persons / {id}方法的性能测试结果如下图所示。一秒钟内平均处理请求数是1538。f0e74d646c4a3d2cbb71259b63cc06c7.png
以下截图显示了响应时间随时间变化的百分位数的直方图。

image

Spring Boot和Micronaut的处理时间没有太大差别。时间上的微小差异可能与框架无关,而与基础的服务器有关。默认情况下,Spring Boot使用Tomcat,而Micronaut使用Netty。

 

posted @ 2019-05-21 09:30  锅外的大佬  阅读(8140)  评论(0编辑  收藏