介绍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支持反应式编程。开发人员可以在框架内使用ReactiveX或Reactor。从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整合得很好。