Spring Boot与Micronaut性能比较

点击左上角,关注:“锅外的大佬”

专注分享国外最新技术内容,帮助每一个技术人更优秀地成长


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

  • 内存使用情况(堆和非堆)

  • 生成 fat JAR文件的MB数

  • 应用程序启动时间

  • 应用程序的性能,在样本负载测试期间REST端口的平均响应时间的含义

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

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

1.源代码

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

public class PersonRepository {	
    List<Person> persons = 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;	
    }	
}

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

@RestController	
@RequestMapping("/persons")	
public class PersonsController {	
    private static final Logger LOGGER = LoggerFactory.getLogger(PersonsController.class);	
    @Autowired	
    PersonRepository repository;	
    @PostMapping	
    public 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);	
    }	
    @GetMapping	
    public List<Person> findAll() {	
        LOGGER.info("Person find");	
        return repository.findAll();	
    }	
}

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

<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开始应用程序。基于 SpringBoot构建的示例应用程序大约需要6-7秒。如下所示,正好需要6.344秒。 

640?wx_fmt=png

 建立在 Micronaut之上的类似应用程序开启大约3-4秒。如下所示,正好占用3.463。

640?wx_fmt=png

无论如何,当通过公共代理来启动应用程序时,都必须设置 VM选项 Dmicronaut.cloud.platform=BARE_METAL来规避环境的影响。 这是 SpringBootMicronaut启动时间差异的图表。

640?wx_fmt=png

3.构建应用

我们还将检查应用程序 fat JAR的大小。为此,推荐使用 mvn clean install命令构建应用程序。对于 SpringBoot,我们使用了两个标准的 starter包: WebActuatorSwaggerSpringFox库。因此,它包含了50多个库。当然,我们可以做一些去除或不使用 starter包,但我选择了最简便的方式来构建, fat JAR的大小为24.2 MB。同样的,基于 Micronaut的应用程序就要小很多, fat JAR的大小为12.1 MB。我在 pom.xml中包含了更多的库,最后包含了37个库。

SpringBoot在标准配置中包含更多库,但在另一方面它比 Micronaut有更多的功能和自动配置。这是 SpringBootMicronaut目标 JAR大小差异的图表。

640?wx_fmt=png

4.内存管理

在启动之后, SpringBoot应用程序为堆分配了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使用情况的截图。

640?wx_fmt=png

在启动之后, Micronaut应用程序为堆分配了254 MB,为非堆分配了51 MB。与 SpringBoot应用程序相同,我没有使用 Xmx或任何其他选项设置任何内存限制。在堆中, old gen(老年代)消耗了2.5 MB, eden区消耗了20 MB, survivor消耗了7 MB。大多数 non-heap非堆内存由 metaspace消耗,约35 MB。运行性能负载测试后,堆分配没有改变,非堆增加到63 MB。这是性能测试之前和测试期间CPU和RAM使用情况的截图。

640?wx_fmt=png

这是 SpringBootMicronaut启动之后堆内存使用情况比较:

640?wx_fmt=png

非堆内存(non-heap)使用比较:

640?wx_fmt=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。

640?wx_fmt=png

下面的截图显示了响应时间随时间变化的百分位数的直方图。

640?wx_fmt=png

GET/persons/{id}方法的性能测试结果如下图所示。一秒钟内平均处理请求数是1428。

640?wx_fmt=png

以下截图显示了响应时间随时间变化的百分位数的直方图。

640?wx_fmt=png

现在,我们为 Micronaut应用程序进行相同的 Gatling负载测试。 POST /person 方法的性能测试结果如下图所示。

640?wx_fmt=png

在一秒钟内平均处理请求数是1290。以下截图显示了响应时间随时间变化的百分位数的直方图。

640?wx_fmt=png

GET/persons/{id}方法的性能测试结果如下图所示。一秒钟内平均处理请求数是1538。

640?wx_fmt=png

以下截图显示了响应时间随时间变化的百分位数的直方图。

640?wx_fmt=png

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

原文链接:https://piotrminkowski.wordpress.com/2019/04/09/performance-comparison-between-spring-boot-and-micronaut/

作者:Piotr Mińkowski

译者:Emma


640?wx_fmt=png


动手扫一扫关注,帮你不断突破技术壁垒

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值