micronaut_Micronaut教程:如何使用基于JVM的框架构建微服务

micronaut

重要要点

  • Micronaut是基于JVM的现代全栈框架,用于构建模块化,易于测试的微服务应用程序。
  • Micronaut具有100%的编译时间,无反射,依赖项注入和AOP。
  • 框架开发团队与Grails框架背后的团队相同。
  • Micronaut将云技术集成到框架中,并将微服务模式(例如服务发现,分布式跟踪,断路器)烘焙到框架中。
  • 在本教程中,您将创建三种使用不同语言的微服务:Java,Kotlin和Groovy。 您还将学习使用Micronaut HTTP Client使用其他微服务有多么容易,以及如何创建运行速度很快的功能测试。

与使用传统JVM框架构建的应用程序不同, Micronaut具有100%的编译时,无反射,依赖项注入和AOP。 因此,Micronaut应用程序很小,内存占用很少。 使用Micronaut,您可以开发可以部署到AWS Lambda的大型组件或小型功能。 您不受框架的约束。

Micronaut还将云技术集成到框架中,并将微服务模式(例如服务发现,分布式跟踪,断路器)烘焙到框架中。

Micronaut于2018年5月以开源形式发布,计划于2018年底发布其1.0.0版本。您可以立即试用Micronaut,因为它具有里程碑和候选版本。

Micronaut开发团队与Grails Framework背后的团队相同。 最近刚迎来10周年庆的Grails继续帮助开发人员使用许多提高生产力的方法来制作Web应用程序。 Grails 3构建在Spring Boot之上。 您很快就会发现,对于来自Grails和Spring Boot两种框架的开发人员来说,Micronaut都有一条容易学习的曲线。

教程概述

在本系列文章中,我们将创建一个由几个微服务组成的应用程序:

  • books微服务; 用Groovy编写。
  • inventory微服务; 用Kotlin写的。
  • gateway微服务; 用Java编写。

你会:

  • 编写端点并使用编译时DI。
  • 编写功能测试。
  • 配置这些micronaut应用程序以向Consul注册。
  • 使用Micronaut声明性HTTP客户端在它们之间进行通信。

下图说明了该应用程序,您将构建:

微服务#1-Groovy微服务

创建Micronaut应用程序的最简单方法是使用其命令行界面( Micronaut CLI ),该界面可通过SDKMan轻松安装。

Micronaut应用程序可以用Java,Kotlin和Groovy编写 。 让我们首先创建一个Groovy Micronaut应用程序:

mn create-app example.micronaut.books --lang groovy

上一个命令使用默认包example.micronaut创建一个名为books的应用程序。

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.CompileStatic
import io.micronaut.http.annotation.Controller
import io.micronaut.http.annotation.Get

@CompileStatic
@Controller("/api")
class BooksController {

    private final BooksRepository booksRepository

    BooksController(BooksRepository booksRepository) {
        this.booksRepository = booksRepository
    }

    @Get("/books")
    List<Book> list() {
        booksRepository.findAll()
    }
}

前面的代码中有几件事值得提及。

  • Controller公开了可以通过GET请求调用的route / api / books。
  • @Get和@Controller批注的值是RFC-6570 URI模板。
  • 通过构造函数注入,Micronaut提供了一个协作者。 BooksRepository。
  • 默认情况下,Micronaut控制器使用并生成JSON。

前一个控制器使用一个接口和一个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.CompileStatic
import groovy.transform.TupleConstructor

@CompileStatic
@TupleConstructor
class Book {
    String isbn
    String name
}

Micronaut 在编译时连接一个实现BooksRepository接口的bean。

对于此应用程序,我们创建一个单例,并使用javax.inject.Singleton批注定义。

books/src/main/groovy/example/micronaut/BooksRepositoryImpl.groovy

package example.micronaut

import groovy.transform.CompileStatic
import javax.inject.Singleton

@CompileStatic
@Singleton
class BooksRepositoryImpl implements BooksRepository {

    @Override
    List<Book> findAll() {
        [
            new Book("1491950358", "Building Microservices"),
            new Book("1680502395", "Release It!"),
        ]
    }
}

由于功能测试可以对应用程序进行完整的测试,因此它们可以带来最大的价值。 但是,在其他框架中,很少使用功能和集成测试。 通常,因为它们涉及整个应用程序的启动,所以它们很慢。

但是,用Micronaut编写功能测试很高兴。 因为他们很快; 真的很快。

下面列出了对先前控制器的功能测试:

books/src/test/groovy/example/micronaut/BooksControllerSpec.groovy

package example.micronaut

import io.micronaut.context.ApplicationContext
import io.micronaut.core.type.Argument
import io.micronaut.http.HttpRequest
import io.micronaut.http.client.RxHttpClient
import io.micronaut.runtime.server.EmbeddedServer
import spock.lang.AutoCleanup
import spock.lang.Shared
import spock.lang.Specification

class BooksControllerSpec extends Specification {

    @Shared
    @AutoCleanup
    EmbeddedServer embeddedServer = ApplicationContext.run(EmbeddedServer)

    @Shared @AutoCleanup RxHttpClient client = embeddedServer.applicationContext.createBean(RxHttpClient, embeddedServer.getURL())

    void "test books retrieve"() { 
        when:
        HttpRequest request = HttpRequest.GET('/api/books')
        List<Book> books = client.toBlocking().retrieve(request, Argument.of(List, Book))

        then:
        books books.size() == 2
    }
}

以前的测试中有几件事值得一提:

  • 使用EmbeddedServer接口从单元测试中运行应用程序很容易。
  • 您可以轻松创建一个HTTP Client bean来使用嵌入式服务器。
  • Micronaut Http Client使将JSON解析为Java对象变得容易。

第二个微服务。 Kotlin微服务

运行下一个命令以创建另一个名为库存的微服务。 这次,我们使用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.HttpResponse 
import io.micronaut.http.MediaType 
import io.micronaut.http.annotation.Controller 
import io.micronaut.http.annotation.Get 
import io.micronaut.http.annotation.Produces
import io.micronaut.security.annotation.Secured

@Controller("/api") 
class BooksController {

    @Produces(MediaType.TEXT_PLAIN) 
    @Get("/inventory/{isbn}") 
    fun inventory(isbn: String): HttpResponse<Int> {
        return when (isbn) { 
            "1491950358" -> HttpResponse.ok(2) 
            "1680502395" -> HttpResponse.ok(3) 
            else -> HttpResponse.notFound()
        }
    }
}

第三微服务。 Java微服务

创建一个同时使用书籍和清单微服务的Java网关应用。

mn create-app example.micronaut.gateway

如果未指定lang标记,则默认情况下会选择Java。

gateway微服务内部,创建一个声明性HTTP客户端以与书籍微服务通信。

首先创建一个接口:

gateway/src/main/java/example/micronaut/BooksFetcher.java

package example.micronaut;

import io.reactivex.Flowable;

public interface BooksFetcher { 
    Flowable<Book> fetchBooks(); 
}

然后创建一个声明性的HTTP客户端; 用@Client.注释的接口@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 {

    @Override @Get("/api/books") Flowable<Book> fetchBooks();

}

Micronaut声明式HTTP客户端方法将在编译时为您实现,从而大大简化了HTTP客户端的创建。

而且,Micronaut支持应用程序环境的概念。 在前面的代码清单中,您可以看到使用@Requires批注为特定环境禁用某些bean的加载是多么容易。

此外,从前面的代码示例中可以看到,非阻塞类型是Micronaut中的一等公民。 BooksClient::fetchBooks()方法返回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;
     
     public Book() {}

     public Book(String isbn, String name) { 
         this.isbn = isbn; 
         this.name = name; 
     }

     public String getIsbn() { return isbn; }

     public void setIsbn(String isbn) { this.isbn = isbn; }

     public String getName() { return name; }

     public void setName(String name) { this.name = name; }
     
     public Integer getStock() { return stock; }

     public void setStock(Integer stock) { this.stock = stock; }
}

创建另一个声明性HTTP客户端以与inventory微服务通信。

首先创建一个接口:

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并创建React式响应的控制器。

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 {

    private final BooksFetcher booksFetcher; 
    private final InventoryFetcher inventoryFetcher;

    public BooksController(BooksFetcher booksFetcher, InventoryFetcher inventoryFetcher) {
        this.booksFetcher = booksFetcher;
        this.inventoryFetcher = inventoryFetcher; 
    }

    @Get("/books") Flowable<Book> findAll() { 
        return booksFetcher.fetchBooks()
                   .flatMapMaybe(b -> inventoryFetcher.inventory(b.getIsbn())
                        .filter(stock -> stock > 0)
                        .map(stock -> { 
                            b.setStock(stock); 
                            return b; 
                        })
                    );

    }
}

在为控制器创建功能测试之前,我们需要在测试环境中为( BooksFetcherInventoryFetcher )创建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 {
    @Override
    public 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 {

    @Override 
    public Maybe<Integer> inventory(String isbn) { 
        if (isbn.equals("1491950358")) { 
            return Maybe.just(2); 
        } 
        if (isbn.equals("1680502395")) { 
            return Maybe.just(0); 
        } 
        return Maybe.empty();
    } 
}

创建一个功能测试。 在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 {

    private static EmbeddedServer server; 
    private static HttpClient client;

    @BeforeClass 
    public static void setupServer() {
        server = ApplicationContext.run(EmbeddedServer.class); 
        client = server .getApplicationContext() .createBean(HttpClient.class, server.getURL());
    }

    @AfterClass 
    public static void stopServer() {
        if (server != null) { 
            server.stop();
        }
        if (client != null) { 
            client.stop();
        }
     }

     @Test 
     public void retrieveBooks() { 
         HttpRequest request = HttpRequest.GET("/api/books");         
         List<Book> books = client.toBlocking().retrieve(request, Argument.of(List.class, Book.class)); 
         assertNotNull(books); 
         assertEquals(1, books.size());
     } 
}

发现服务

我们将配置我们的micronaut微服务以向Consul Service发现注册。

Consul是一个分布式服务网格,用于跨任何运行时平台和公共或私有云连接,保护和配置服务

整合Micronaut和Consul很简单。

首先添加到每本微服务booksnventorygateway发现客户端依赖性:

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: gateway 
    server:
        port: 8080
consul:
    client:
        registration: 
            enabled: true
        defaultZone: "${CONSUL_HOST:localhost}:${CONSUL_PORT:8500}"



books/src/main/resources/application.yml
micronaut:
    application:
        name: books
    server:
        port: 8082
consul:
    client:
        registration: 
            enabled: true
        defaultZone: "${CONSUL_HOST:localhost}:${CONSUL_PORT:8500}"



inventory/src/main/resources/application.yml
micronaut:
    application:
        name: inventory
    server:
        port: 8081
consul:
    client:
        registration: 
            enabled: true
        defaultZone: "${CONSUL_HOST:localhost}:${CONSUL_PORT:8500}"

每个服务在Consul中注册时,都将属性icronaut.application.name用作服务id。 这就是为什么我们在先前的@Client批注中使用这些确切名称的@Client

先前的代码清单说明了Micronaut的另一个功能,即在配置文件中带有默认值的环境变量插值。 看到:

defaultZone: "${CONSUL_HOST:localhost}:${CONSUL_PORT:8500}"

另外,在Micronaut中,您可以具有特定于环境的配置文件。 我们将在每个环境中创建一个名为application-test.yml的文件,以在测试阶段咨询注册。

gateway/src/test/resources/application-test.yml
consul:
    client:
        registration: enabled: false


books/src/test/resources/application-test.yml
consul:
    client:
        registration: enabled: false


inventory/src/test/resources/application-test.yml
consul:
    client:
        registration: enabled: false

运行应用

开始使用Consul的最简单方法是通过Docker。 现在,运行一个Docker实例。

docker run -p 8500:8500 consul

使用Gradle创建一个多项目构建 。 在根文件夹中创建一个settings.gradle文件:

settings.gradle
include 'books'

include 'inventory'

include 'gateway'

现在,您可以并行运行每个应用程序。 Gradle为此提供了一个方便的标记(-parallel):

./gradlew -parallel run

每个微服务都从以下配置的端口开始:8080、8081和8082。

Consul带有HTML UI。 在浏览器中打开http:// localhost:8500 / ui ,您将看到:

每个Micronaut微服务均已向领事注册。

您可以使用下一个cURL命令调用网关微服务:

$ 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的所有内容。

和我一起参加本系列的第二部分,即将推出。 同时,请随时通过下面的评论部分提出问题。

翻译自: https://www.infoq.com/articles/micronaut-tutorial-microservices-jvm/?topicPageSponsorship=c1246725-b0a7-43a6-9ef9-68102c8d48e1

micronaut

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
你可以使用 Micronaut 框架与 Swagger 集成来生成和展示 API 文档。下面是使用 Micronaut 和 Swagger 的步骤: 1. 添加依赖:在你的构建工具(如 Maven 或 Gradle)中添加 Micronaut Swagger 的依赖。例如,对于 Maven,可以在 `pom.xml` 文件中添加以下依赖: ```xml <dependency> <groupId>io.micronaut.configuration</groupId> <artifactId>micronaut-openapi</artifactId> <scope>compile</scope> </dependency> ``` 2. 配置 Swagger:在 `application.yml` 或 `application.properties` 文件中添加以下配置: ```yaml micronaut: router: static-resources: swagger: paths: classpath:META-INF/swagger mapping: /swagger/** ``` 3. 编写 API 控制器:创建一个 Micronaut 控制器类,使用 `@Controller` 注解标记,同时使用 `@Api` 和 `@ApiOperation` 注解来定义接口和操作。例如: ```java import io.micronaut.http.annotation.Controller; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; @Controller("/api") @Api("/api") @Tag(name = "Hello") public class HelloController { @Get("/hello") @Operation(summary = "Say hello") public String hello() { return "Hello, Micronaut!"; } } ``` 4. 运行应用程序:启动你的 Micronaut 应用程序,可以使用 Gradle 或 Maven 命令运行。例如,对于 Gradle,可以使用以下命令: ``` ./gradlew run ``` 5. 查看 Swagger 文档:在浏览器中访问 `http://localhost:8080/swagger`,你将能够看到自动生成的 Swagger 文档,其中包含了你的 API 接口和操作的详细描述、参数和响应模型等信息。 通过上面的步骤,你就可以在 Micronaut 应用程序中集成 Swagger,并通过 Swagger 自动生成和展示 API 文档了。希望对你有所帮助!如果你有任何进一步的问题,请随时提问。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值