使用 Micronaut 框架构建一个微服务网络.
与使用传统 JVM 框架构建的应用程序不同,Micronaut 具有 100%的编译时间、无反射、依赖注入和 AOP。因此,Micronaut 应用程序体积小,内存占用小。使用 Micronaut,您可以开发一个大的整体或一个可以部署到 AWS Lambda 的小功能。您不受框架的约束。
Micronaut 还将云技术集成到框架中,并将服务发现、分布式跟踪、断路器等微服务模式烘焙到框架中。
Micronaut 于 2018 年 5 月以开源形式发布,并计划于 2018 年底发布其 1.0.0 版本。您可以在今天尝试 Micronaut,因为里程碑和发布候选版本都可用。
Micronaut 开发团队就是 Grails 框架背后的同一个团队。Grails 最近迎来了它的十周年纪念日,它继续帮助开发人员使用许多生产力提升工具来设计 web 应用程序。Grails3 构建在 Spring Boot 之上。您很快就会发现,Micronaut 为来自 Grails 和 Spring Boot 这两个框架的开发人员提供了一个简单的学习曲线。
在本系列文章中,我们将创建一个由多个微服务组成的应用程序:
图书微服务;用 Groovy 编写。库存微服务;用 Kotlin 写的。网关微服务;用 Java 编写。你会:
编写端点并使用 compiletime DI。编写功能测试。将这些 micronaut 应用程序配置为向 Consul 注册。它们之间通过 Micronaut 声明性 HTTP 客户端进行通信。下一个图表说明了应用程序,您将构建:
使用 Micronaut 框架构建一个微服务网络微服务-一个非常棒的微服务创建 Micronaut 应用程序的最简单方法是使用其命令行界面(Micronaut CLI),该界面可以通过 SDKMan 轻松安装。
Micronaut 应用程序可以用 Java、Kotlin 和 Groovy 编写。让我们首先创建一个 Groovy Micronaut 应用程序:
mn create-app example.micronaut.books --lang groovy 前面的命令创建一个名为 books 的应用程序,其默认包为 example.micronaut 。
Micronaut 是测试框架不可知论者。它根据您使用的语言选择默认测试框架。默认情况下,Java 使用 JUnit。如果选择 Groovy,默认情况下将使用 Spock。您可以混合使用不同的语言和测试框架。例如,使用 Spock 测试的 Java Micronaut 应用程序。
此外,Micronaut 是构建工具可以使用 Maven 或 Gradle。默认情况下,将使用 Gradle。
生成的应用程序包括一个基于 Netty 的非阻塞 HTTP 服务器。
创建控制器以显示第一个 Micronaut 端点:
books/src/main/groovy/example/micronaut/BooksController.groovy
package example.micronaut
import groovy.transform.CompileStaticimport io.micronaut.http.annotation.Controllerimport io.micronaut.http.annotation.Get
@CompileStatic@Controller("/api")class BooksController {
}在前面的代码中有几点值得一提。
Controller 公开一个路由 /api/books ,可以通过 GET 请求调用它。@Get 和 @Controller 注释的值是 RFC-6570 URI 模板。通过构造函数注入,Micronaut 提供一个 collaborator : BooksRepository 。默认情况下,Micronaut 控制器使用并生成 JSON。前一个 Controller 使用接口和 POGO:
books/src/main/groovy/example/micronaut/BooksRepository.groovy
package example.micronaut
interface BooksRepository {List<Book> findAll()}
books/src/main/groovy/example/micronaut/Book.groovy
package example.micronaut
import groovy.transform.CompileStaticimport groovy.transform.TupleConstructor
@CompileStatic@TupleConstructorclass Book {String isbnString name}Micronaut 在编译时连接一个实现 BooksRepository 接口的 bean。
对于这个应用程序,我们创建了一个 singleton,我们用 javax.inject.singleton 注释定义它。
books/src/main/groovy/example/micronaut/BooksRepositoryImpl.groovy
package example.micronaut
import groovy.transform.CompileStaticimport javax.inject.Singleton
@CompileStatic@Singletonclass BooksRepositoryImpl implements BooksRepository {
}功能测试增加了最大的价值,因为它们测试整个应用程序。然而,对于其他框架,很少使用功能测试和集成测试。主要是因为它们涉及整个应用程序的启动,所以速度很慢。
然而,用 Micronaut 编写功能测试是一件乐事。因为他们很快;真的很快。
下表列出了先前 controller 的功能测试:
books/src/test/groovy/example/micronaut/BooksControllerSpec.groovy
package example.micronaut
import io.micronaut.context.ApplicationContextimport io.micronaut.core.type.Argumentimport io.micronaut.http.HttpRequestimport io.micronaut.http.client.RxHttpClientimport io.micronaut.runtime.server.EmbeddedServerimport spock.lang.AutoCleanupimport spock.lang.Sharedimport spock.lang.Specification
class BooksControllerSpec extends Specification {
}上一次测试中有几点值得一提:
EmbeddedServer 第二个微服务-Kotlin 微服务运行下一个命令创建另一个名为 inventory 的微服务。这一次,我们使用 Kotlin 作为语言。
mn create-app example.micronaut.inventory --lang kotlin 这项新的微服务控制着每本书的库存。
创建 Kotlin 数据类以封装域:
inventory/src/main/kotlin/example/micronaut/Book.kt
package example.micronaut
data class Book(val isbn: String, val stock: Int)创建一个控制器,用于返回书籍的库存。
inventory/src/main/kotlin/example/micronaut/BookController.kt
package example.micronaut
import io.micronaut.http.HttpResponseimport io.micronaut.http.MediaTypeimport io.micronaut.http.annotation.Controllerimport io.micronaut.http.annotation.Getimport io.micronaut.http.annotation.Producesimport io.micronaut.security.annotation.Secured
@Controller("/api")class BooksController {
}第三个微服务-Java 微服务创建一个使用书籍和库存微服务的 Java 网关应用程序。
mn create-app example.micronaut.gateway 默认情况下,如果不指定语言,则选择 Java。
在网关微服务内部,创建一个声明性 HTTP 客户端以与图书微服务通信。
首先创建一个接口:
gateway/src/main/java/example/micronaut/BooksFetcher.java
package example.micronaut;
import io.reactivex.Flowable;
public interface BooksFetcher {Flowable<Book> fetchBooks();}然后创建一个声明性 HTTP 客户端;用 @Client 注释的接口。
gateway/src/main/java/example/micronaut/BooksClient.java
package example.micronaut;
import io.micronaut.context.annotation.Requires;import io.micronaut.context.env.Environment;import io.micronaut.http.annotation.Get;import io.micronaut.http.client.annotation.Client;import io.reactivex.Flowable;
@Client("books")
@Requires(notEnv = Environment.TEST)
public interface BooksClient extends BooksFetcher {
}Micronaut 声明性 HTTP 客户端方法将在编译时为您实现,从而大大简化 HTTP 客户端的创建。
此外,Micronaut 支持应用程序环境的概念。在前面的代码清单中,您可以看到为带有 @Requires 注释的特定环境禁用某些 bean 的加载是多么容易。
此外,从前面的代码示例中可以看出,非阻塞类型是 Micronaut 中的一级公民。 BooksClient::fetchBooks() 方法返回 Flowable<Book> ,其中 Book 是 Java POJO:
gateway/src/main/java/example/micronaut/Book.java
package example.micronaut;
public class Book {private String isbn;private String name;private Integer stock;
}创建另一个声明性 HTTP 客户端以与 inventory microservice 通信。
首先创建一个接口:
gateway/src/main/java/example/micronaut/InventoryFetcher.java
package example.micronaut;
import io.reactivex.Maybe;
public interface InventoryFetcher {Maybe<Integer> inventory(String isbn);}然后,HTTP 声明性客户端:
gateway/src/main/java/example/micronaut/InventoryClient.java
package example.micronaut;
import io.micronaut.context.annotation.Requires;import io.micronaut.context.env.Environment;import io.micronaut.http.annotation.Get;import io.micronaut.http.client.Client;import io.reactivex.Maybe;
@Client("inventory")@Requires(notEnv = Environment.TEST)public interface InventoryClient extends InventoryFetcher {@Override@Get("/api/inventory/{isbn}")Maybe<Integer> inventory(String isbn);}现在,创建一个控制器,它注入两个 bean 并创建一个反应响应。
gateway/src/main/java/example/micronaut/BooksController.java
package example.micronaut;
import io.micronaut.http.annotation.Controller;import io.micronaut.http.annotation.Get;import io.micronaut.security.annotation.Secured;import io.reactivex.Flowable;import java.util.List;
@Controller("/api")public class BooksController {
}在为控制器创建功能测试之前,我们需要在测试环境中为( BooksFetcher 和 InventoryFetcher )创建 bean 实现。
创建一个符合 BooksFetcher 接口的 bean,只对测试环境可用;请参阅 @Requires 注释。
gateway/src/test/java/example/micronaut/MockBooksClient.java
package example.micronaut;
import io.micronaut.context.annotation.Requires;import io.micronaut.context.env.Environment;import io.reactivex.Flowable;import javax.inject.Singleton;
@Singleton@Requires(env = Environment.TEST)public class MockBooksClient implements BooksFetcher {@Overridepublic Flowable<Book> fetchBooks() {return Flowable.just(new Book("1491950358", "Building Microservices"), new Book("1680502395", "Release It!"), new Book("0321601912", "Continuous Delivery:"));}}创建一个符合 InventoryFetcher 接口的 bean,该接口仅适用于测试环境。
gateway/src/test/java/example/micronaut/MockInventoryClient.java
package example.micronaut;
import io.micronaut.context.annotation.Requires;import io.micronaut.context.env.Environment;import io.reactivex.Maybe;import javax.inject.Singleton;
@Singleton@Requires(env = Environment.TEST)public class MockInventoryClient implements InventoryFetcher {
}创建一个功能测试。在 Groovy 微服务中,我们编写了一个 Spock 测试,这次我们编写了 JUnit 测试。
gateway/src/test/java/example/micronaut/BooksControllerTest.java
package example.micronaut;
import io.micronaut.context.ApplicationContext;import io.micronaut.core.type.Argument;import io.micronaut.http.HttpRequest;import io.micronaut.http.client.HttpClient;import io.micronaut.runtime.server.EmbeddedServer;import org.junit.AfterClass;import org.junit.BeforeClass;import org.junit.Test;import static org.junit.Assert.assertEquals;import static org.junit.Assert.assertNotNull;import java.util.List;
public class BooksControllerTest {
}发现服务我们将配置我们的 micronaut 微服务,以便向 Consul 服务发现注册。
Consul 是一个分布式服务网格,用于跨任何运行时平台和公共或私有云连接、保护和配置服务
集成 Micronaut 和 Consul 很简单。
首先,将 discovery 客户端依赖项添加到每个 microservice books、inventory 和 gateway 中:
gateway/build.gradle
runtime "io.micronaut:micronaut-discovery-client"
books/build.gradle
runtime "io.micronaut:micronaut-discovery-client"
inventory/build.gradle
runtime "io.micronaut:micronaut-discovery-client"我们需要对每个应用程序进行一些配置更改,以便当应用程序启动时,它可以向 Consul 注册。
gateway/src/main/resources/application.yml
micronaut:application:name: gatewayserver:port: 8080consul:client:registration:enabled: truedefaultZone: "{CONSUL_PORT:8500}"
books/src/main/resources/application.ymlmicronaut:application:name: booksserver:port: 8082consul:client:registration:enabled: truedefaultZone: "{CONSUL_PORT:8500}"
inventory/src/main/resources/application.ymlmicronaut:application:name: inventoryserver:port: 8081consul:client:registration:enabled: truedefaultZone: "{CONSUL_PORT:8500}"在 Consul 中注册时,每个服务都使用属性 micronaut.application.name 作为服务 id。这就是为什么我们在前面的 @Client 注释中使用这些确切的名称。
前面的代码清单说明了 Micronaut 的另一个功能,即配置文件中带有默认值的环境变量插值。见:
defaultZone: "{CONSUL_PORT:8500}"此外,在 Micronaut 中,您可以拥有环境特定配置。我们将在每个环境中创建一个名为 application-test.yml 的文件,以便在测试阶段协商注册。
gateway/src/test/resources/application-test.ymlconsul:client:registration: enabled: false
books/src/test/resources/application-test.ymlconsul:client:registration: enabled: false
inventory/src/test/resources/application-test.ymlconsul:client:registration: enabled: false 运行应用程序开始使用 Consul 最简单的方法是通过 Docker。现在,运行一个 Docker 实例。
docker run -p 8500:8500 consul 使用 Gradle 创建多项目构建。在根文件夹中创建 settings.gradle 文件:
settings.gradleinclude 'books'include 'inventory'include 'gateway'现在您可以并行运行每个应用程序。Gradle 有一个方便的界面( -parallel ):
./gradlew -parallel run 每个微服务在配置的端口 8080 、 8081 和 8082 中启动。
Consul 提供了一个 HTML 用户界面。打开 http://localhost:8500/ui 在浏览器中,您将看到:
使用 Micronaut 框架构建一个微服务网络 $ curl http://localhost:8080/api/books [{"isbn":"1680502395","name":"Release It!","stock":3}, {"isbn":"1491950358","name":"Building Microservices","stock":2}]祝贺您创建了第一个 Micronaut 微服务网络!
总结在本文中,您已经用不同的语言创建了三个微服务:Java、Kotlin 和 Groovy。您还了解了使用 Micronaut HTTP 客户端使用其他微服务是多么容易,以及如何创建快速运行的功能测试。此外,您还创建了享受完全无反射的依赖项注入和 AOP 的一切。
如果您觉得文章对您有帮助,可以点赞评论转发支持一下~蟹蟹!
评论