介绍Micronaut:一个云原生的Java框架

1,965 阅读7分钟

介绍Micronaut:一个云原生的Java框架

Micronaut为微服务和无服务器开发提供提前编译、反应式NIO和云原生支持。它能成为你的下一个Java框架吗?

长期以来,Spring框架一直主导着后端Java开发,但有几个新的框架挑战了这种现状。Micronaut 是其中最引人注目的一个。由构建Grails的团队开发,Micronaut是为现代架构而生的。

这篇文章是对Micronaut的实践介绍。我们将从一个简单的基于RESTful API的应用开始,为反应式非阻塞IO(反应式NIO)进行重构,然后快速浏览Micronaut对微服务和无服务器架构中云原生开发的支持。

Micronaut的特殊之处

Micronaut提供了一系列从Spring和Grails等旧框架中收集到的好处。它被称为 "原生云",这意味着它是为云环境而建立的。它的云原生能力包括环境检测、服务发现和分布式跟踪。

Micronaut还提供了一个新的反转控制(IoC)容器,它使用提前编译(AoT)以加快启动。AoT编译意味着启动时间不会随着代码库的大小而增加。这对于无服务器和基于容器的部署尤其重要,因为在这些部署中,节点经常被关闭,并根据需求而启动。

Micronaut是一个多语言JVM框架,目前支持Java、Groovy和Kotlin,对Scala的支持也在进行中。

最后,Micronaut支持反应式编程。开发人员可以在框架内使用ReactiveXReactor。从2021年7月发布的Micronaut 3来看,Reactor是推荐的方法。(请注意,在新的版本中,没有Reactive库被包含为过渡性依赖。)

开始使用Micronaut

通过SDKMan,Micronaut可以简单地安装在任何基于Unix的系统上,包括Linux和macOS。如果你是在Windows上,下载Micronaut二进制文件并将其添加到你的路径中。

一旦安装完成,你会发现mn 工具在你的命令行上可用。

打开一个外壳,找到一个方便的位置。输入mn create-app micronaut-idg --build maven 。Micronaut通过包装器支持Gradle和Maven,所以你不需要安装构建工具本身。我更喜欢Maven。如果你喜欢Gradle,可以不使用前面命令中的--build maven 标志。

如果你用mvnw mn:run 来运行服务器,那么你可以在浏览器中点击*http://localhost:8080/*,它会给你一个默认的 "未找到 "JSON响应。

如果你探索一下示例项目的布局,它是一个标准的Maven项目,在src/main/java/micronaut/idg/Application.java ,有一个main 类。注意,main 类运行一个嵌入式服务器。当你进行代码修改时,Micronaut开发服务器会自动更新运行中的应用程序。

添加一个Micronaut控制器

就像在Spring MVC中,你可以添加控制器类来映射URL到代码处理程序。在src/main/java/micronaut/idg/controller/SimpleController 添加一个类。让我们使用这个控制器来创建一个文本响应,如清单1所示。

清单1.使用Micronaut控制器


package micronaut.idg.controller; 

import io.micronaut.http.MediaType; 
import io.micronaut.http.annotation.Controller; 
import io.micronaut.http.annotation.Get; 

@Controller("/simple") 
public class SimpleController { 

    @Get(produces = MediaType.TEXT_PLAIN) 
    public String index() { 
        return "A Simple Endpoint"; 
    } 
} 

现在你可以看到返回一个JSON格式的响应是多么容易,如清单2所示。

清单2.JSON格式的响应


package micronaut.idg.controller; 

import io.micronaut.http.MediaType; 
import io.micronaut.http.annotation.Controller; 
import io.micronaut.http.annotation.Get; 

import java.util.Map; 
import java.util.HashMap; 

@Controller("/simple") 
public class SimpleController { 
 
    @Get(produces = MediaType.APPLICATION_JSON) 
    public Map index() { 
      Map msg = new HashMap(); 
      msg.put("message", "A simple message"); 
      return msg;   

    } 
} 

清单2展示了Micronaut对@Get 注释的produces 参数的智能处理。在这种情况下,它发出了我们设定的JSON格式的响应。

添加一个Micronaut服务层

Micronaut的IoC实现是独一无二的,因为它是事先运行的,但它仍然是CDI(Contexts and Dependency Injection)规范的一个完整实现。这意味着你可以使用所有你可能从Spring中知道的熟悉的DI注解(如@Inject )。

在清单3中,我们将连接一个服务层Bean来提供一个消息。在一个真实的应用中,这个类可以通过数据访问Bean调用到一个数据存储或其他远程API。

创建一个src/main/java/micronaut/idg/service 文件夹,并添加清单3中看到的两个文件--接口(Simple )和它的实现(SimpleService )。

清单3.创建一个简单的服务层Bean


// Simple.java 
package micronaut.idg.service; 

public interface Simple { 
  public String getMessage(); 

} 

// SimpleService.java 
package micronaut.idg.service; 

import jakarta.inject.Singleton; 

@Singleton 
public class SimpleService implements Simple { 
  public String getMessage(){ 
    return "A simple service message"; 

  } 
} 

现在你可以通过将服务注入你在清单1中创建的SimpleController 来使用你的新服务层。清单4显示了Constructor 的注入。

清单4.将服务Bean注入到控制器中


@Controller("/simple") 
public class SimpleController { 

  @Inject 
  private final Simple simpleService; 

  public SimpleController(@Named("simpleService") Simple simple) {  // (1) 
    this.simpleService = simple; 

  } 

  @Get(produces = MediaType.APPLICATION_JSON) 
  public Map index() { 
    Map msg = new HashMap(); 
    msg.put("message", simpleService.getMessage()); 
    return msg; 
  } 
} 

关键的工作是在注释"(1) "这一行完成的,在这里,服务Bean的名字被连接进来。现在,如果你访问*http://localhost:8080/simple,*你会看到服务层的响应:`{"message":"A simple service message"}` 。

使用Micronaut的反应式NIO

接下来,让我们来探索如何使用Micronaut和Reactor。在这种情况下,我们只是重构我们当前的应用程序,以使用Reactor和非阻塞IO。该应用程序将执行相同的任务,但在引擎盖下使用非阻塞堆栈-Reactor和Netty。

正如我之前提到的,Micronaut 3默认不包含反应式库,所以首先要把Reactor核心添加到你的Maven POM中,如清单5所示。

清单5.将Reactor添加到pom.xml中


<dependency> 
    <groupId>io.projectreactor</groupId> 
    <artifactId>reactor-core</artifactId> 
    <version>3.4.11</version> 
</dependency> 

现在你可以回到SimpleController ,并按清单6所示修改它。

清单6.使控制器成为非阻塞式


import reactor.core.publisher.Mono; 

//... 

@Get 
  public Mono<map> index() { 
    Map msg = new HashMap(); 
    msg.put("message", simpleService.getMessage()); 
    return Mono.just(msg); 
  } 
} 

正如你所看到的,我们只是用ReactorMono 类来包装相同的返回类型(一个string/string 的映射)。

类似的支持也存在于以反应式方式消费远程服务,所以你可以完全在非阻塞的IO上运行一个应用程序。

使用Micronaut的CLI来创建新的组件

你可以使用Micronaut的命令行工具来存根出组件。例如,如果你想添加一个新的控制器,你可以使用命令mn add-controller MyController 。这将输出一个新的控制器和它的测试,如清单7所示。

清单7.使用Micronaut命令行创建一个新的控制器


mn create-controller MyController 
| Rendered controller to src/main/java/micronaut/idg/MyControllerController.java 
| Rendered test to src/test/java/micronaut/idg/MyControllerControllerTest.java 

使用Micronaut进行云原生开发

我在前面提到,Micronaut是为云原生微服务和无服务器开发从头开始建立的。Micronaut支持的一个云原生概念是联盟。联盟的概念是,几个较小的应用程序共享相同的设置,并可以串联部署。如果这听起来非常像一个微服务架构,你是对的。其目的是使微服务开发更简单,并保持其可管理性。请参阅Micronaut的文档,了解更多关于联合服务的信息。

Micronaut也可以很容易地针对云环境进行部署。作为一个例子,你可以针对谷歌云平台的Docker注册表,如清单8所示。

清单8.使用GCP的Docker注册表部署一个Micronaut应用程序


./mvnw deploy \ 
     -Dpackaging=docker \ 
     -Djib.to.image=gcr.io/my-org/my-project:latest 

在这种情况下,该项目将作为一个Docker镜像被推送到GCP的Docker注册中心。注意,我们使用了Jib Maven插件,它可以将一个Java项目变成一个Docker镜像,而不需要你创建一个实际的Docker文件。

还注意到,我们用-Dpackaging=docker ,将Docker确定为打包工具。一旦打包完成,你就可以用GCP命令行工具部署你的项目了,如清单9所示。

清单9.从命令行运行Docker镜像


gcloud run deploy \ 
    --image=gcr.io/my-org/my-project:latest \ 
    --platform managed \ 
    --allow-unauthenticated

追踪是Micronaut支持的另一个云原生功能。例如,Micronaut通过注释使Jaeger分布式跟踪变得相当简单。

清单10配置了Jaeger来追踪一个微服务应用的application.xml 文件中的所有请求。

清单10.应用程序.xml中的Jaeger配置


tracing: 
  jaeger: 
    enabled: true 
    sampler: 
      probability: 1 

总结

Micronaut提供了一系列的功能,对于云原生和微服务开发来说是非常好的。同时,该框架使更多传统的基于API的开发变得直接和简单。它与Reactor和Netty的反应式NIO整合得很好。