Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

Micronaut tutorial: how to build microservices using a JVM-based framework?

2025-01-18 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Internet Technology >

Share

Shulou(Shulou.com)06/03 Report--

The main points of this article:

Micronaut is a modern full-stack framework based on jvm for building modular and easy-to-test micro-service applications. Micronaut provides full compile-time, reflection-independent dependency injection and AOP. The development team of the framework is the same as the development team of the Grails framework. The Micronaut framework integrates cloud technology, and micro-service patterns such as service discovery, distributed tracking and circuit breakers are also built into the framework. In this tutorial, you will create three microservices in different languages: Java, Kotlin, and Groovy. You'll also learn how easy it is to consume other microservices using the Micronaut HTTP client and how to create functional tests that execute quickly.

Unlike applications built using traditional JVM frameworks, Micronaut provides 100% compile-time, reflection-independent dependency injection and AOP. As a result, Micronaut applications are small and memory footprint is low. With Micronaut, you can develop a large monolithic application or a small function that can be deployed to AWS Lambda. The framework doesn't limit you.

The Micronaut framework also integrates cloud technology, and micro-service patterns such as service discovery, distributed tracking and circuit breakers are also built into the framework.

Micronaut was released as open source software in May 2018 and plans to release version 1.0.0 by the end of 2018. Now you can try Micronaut because milestone versions and release candidates are already available.

The development team of the Micronaut framework is the same as that of the Grails framework. Grails recently celebrated its 10th anniversary and continues to use many productivity boosters to help developers write Web applications. Grails 3 builds on top of Spring Boot. You will soon find that Micronaut has a simple learning curve for developers using the Grails and Spring Boot frameworks.

Introduction to the tutorial

In this series of articles, we will create an application using several microservices:

A books microservice, written in Groovy, an inventory microservice, written in Kotlin, and a gateway microservice, written in Java.

You will do the following: write endpoints, use compile-time dependency injection, write functional tests, configure those Micronaut applications, register with Consul; and use the Micronaut declarative HTTP client to communicate between them.

The following figure illustrates the application you are going to build:

Microservice # 1 Groovy microservice

The easiest way to create a Micronaut application is to use its command line interface (Micronaut CLI), which can be easily installed using SDKMan.

Micronaut applications can be written in Java, Kotlin, and Groovy. First, let's create a Groovy Micronaut application:

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

The above command creates an application called books, and the default package is example.micronaut.

Micronaut is independent of the test framework. It chooses a default test framework based on the language you use. By default, Java uses JUnit. If you choose Groovy, by default, Spock will be used. You can use different languages and testing frameworks together. For example, test a Java Micronaut application with Spock.

Moreover, Micronaut is independent of the build tool. You can use Maven or Gradle. Gradle is used by default.

The generated application includes a Netty-based non-blocking HTTP server.

Create a controller to expose your first Micronaut endpoint:

Books/src/main/groovy/example/micronaut/BooksController.groovypackage example.micronautimport groovy.transform.CompileStaticimport io.micronaut.http.annotation.Controllerimport io.micronaut.http.annotation.Get@CompileStatic@Controller ("/ api") class BooksController {private final BooksRepository booksRepository BooksController (BooksRepository booksRepository) {this.booksRepository = booksRepository} @ Get ("/ books") List list () {booksRepository.findAll ()}

In the above code, there are several things worth mentioning:

The controller exposes an route/api/books endpoint that can be called using GET requests; note that the values of @ Get and @ Controller are a RFC-6570 URI template; and through constructor injection, Micronaut provides a collaborative class: the BooksRepository;Micronaut controller consumes and generates JSON by default.

The above controller uses an interface and a POGO:books/src/main/groovy/example/micronaut/BooksRepository.groovypackage example.micronautinterface BooksRepository {List findAll ()} books/src/main/groovy/example/micronaut/Book.groovypackage example.micronautimport groovy.transform.CompileStaticimport groovy.transform.TupleConstructor@CompileStatic@TupleConstructorclass Book {String isbn String name}

Micronaut connects a bean that implements the BooksRepository interface at compile time.

For this application, we created a singleton, which we defined using javax.inject.Singleton annotations.

Books/src/main/groovy/example/micronaut/BooksRepositoryImpl.groovypackage example.micronautimport groovy.transform.CompileStaticimport javax.inject.Singleton@CompileStatic@Singletonclass BooksRepositoryImpl implements BooksRepository {@ Override List findAll () {[new Book ("1491950358", "Building Microservices"), new Book ("1680502395", "Release It!"),]}}

Functional tests are of the greatest value because they test the entire application. However, for other frameworks, functional testing and integration testing are rarely used. In most cases, they are slow because they involve the startup of the entire application.

However, writing functional tests in Micronaut is a pleasure. Because they're fast, very fast.

The functional tests of the above controller are as follows:

Books/src/test/groovy/example/micronaut/BooksControllerSpec.groovypackage example.micronautimport 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.Specificationclass 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 books = client.toBlocking (). Retrieve (request, Argument.of (List, Book)) then: books books.size () = 2}}

In the above tests, there are several points worth mentioning:

With the EmbeddedServer interface, it's easy to run applications from unit tests; it's easy to create a HTTP client bean to consume embedded servers; and Micronaut Http clients can easily parse JSON into Java objects. Microservices # 2 Kotlin microservices

Run the following command to create another microservice called inventory. This time, we use the Kotlin language.

> mn create-app example.micronaut.inventory-- lang kotlin

This new microservice controls the inventory of each book.

Create a Kotlin data class that encapsulates attribute fields:

Inventory/src/main/kotlin/example/micronaut/Book.ktpackage example.micronautdata class Book (val isbn: String, val stock: Int)

Create a controller to return the inventory of a book.

Inventory/src/main/kotlin/example/micronaut/BookController.ktpackage example.micronautimport 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@Controller ("/ api") class BooksController {@ Produces (MediaType.TEXT_PLAIN) @ Get ("/ inventory/ {isbn}") fun inventory (isbn: String): HttpResponse {return when (isbn) {"1491950358" -> HttpResponse.ok (2) "1680502395"-> HttpResponse.ok (3) else-> HttpResponse.notFound ()}} Micro Services # 3 Java Micro Services

Create a Java gateway application that consumes two microservices, books and inventory.

Mn create-app example.micronaut.gateway

If you do not specify a lang identity, Java is chosen by default.

In the gateway microservice, create a declarative HTTP client to communicate with the books microservice.

First, create an interface:

Gateway/src/main/java/example/micronaut/BooksFetcher.javapackage example.micronaut;import io.reactivex.Flowable;public interface BooksFetcher {Flowable fetchBooks ();}

Then, create a declarative HTTP client, which is an interface using the @ Client annotation.

Gateway/src/main/java/example/micronaut/BooksClient.javapackage 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.Flowable;@Client (books) @ Requires (notEnv = Environment.TEST) public interface BooksClient extends BooksFetcher {@ Override @ Get ("/ api/books") Flowable fetchBooks ();}

The Micronaut declarative HTTP client method will be implemented at compile time, greatly simplifying the creation of HTTP clients.

In addition, Micronaut supports the concept of an application environment. In the code listing above, you can see that using the @ Requires annotation, it is easy to disable some bean from being loaded in a particular environment.

And, as you can see in the previous code example, non-blocking types are first-class citizens in Micronaut. The BooksClient::fetchBooks () method returns Flowable, where Book is a Java POJO:

Gateway/src/main/java/example/micronaut/Book.javapackage 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;}}

Create another declarative HTTP client to communicate with the inventory microservice.

First, create an interface:

Gateway/src/main/java/example/micronaut/InventoryFetcher.javapackage example.micronaut;import io.reactivex.Maybe;public interface InventoryFetcher {Maybe inventory (String isbn);}

Then, a HTTP declarative client:

Gateway/src/main/java/example/micronaut/InventoryClient.javapackage 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.Flowable;import io.reactivex.Maybe; import io.reactivex.Single Client ("inventory") @ Requires (notEnv = Environment.TEST) public interface InventoryClient extends InventoryFetcher {@ Override @ Get ("/ api/inventory/ {isbn}") Maybe inventory (String isbn);}

Now, create a controller, inject two bean, and create a reactive reply.

Gateway/src/main/java/example/micronaut/BooksController.javapackage example.micronaut;import io.micronaut.http.annotation.Controller; import io.micronaut.http.annotation.Get; import io.reactivex.Flowable;@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 findAll () {return booksFetcher.fetchBooks () .flatMapMaybe (b-> inventoryFetcher.inventory (b.getIsbn ()) .filter (stock-> stock > 0) .map (stock-> {b.setStock (stock); return b;});}}

Before creating functional tests for the controller, we need to create bean implementations for (BooksFetcher and InventoryFetcher) in the test environment.

Create a bean that conforms to the BooksFetcher interface, which is only applicable to the test environment; see the @ Requires annotation.

Gateway/src/test/java/example/micronaut/MockBooksClient.javapackage 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 fetchBooks () {return Flowable.just (new Book ("1491950358", "Building Microservices"), new Book ("1680502395", "Release It!"), new Book ("0321601912", "Continuous Delivery:");}}

Create a bean that conforms to the InventoryFetcher interface, which is only applicable to the test environment

Gateway/src/test/java/example/micronaut/MockInventoryClient.javapackage 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 inventory (String isbn) {if (isbn.equals ("1491950358")) {return Maybe.just (2);} if (isbn.equals ("1680502395")) {return Maybe.just (0) } return Maybe.empty ();}}

Create a functional test. In the Groovy microservice, we wrote a Spock test, and this time, we wrote a JUnit test.

Gateway/src/test/java/example/micronaut/BooksControllerTest.javapackage 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 books = client.toBlocking (). Retrieve (request, Argument.of (List.class, Book.class); assertNotNull (books); assertEquals (1, books.size ());}} Service Discovery

We will configure our Micronaut micro service to register with Consul service discovery.

Consul is a distributed service grid that connects, protects, and configures services across any runtime platform and public or private cloud.

The integration of Micronaut and Consul is simple.

First, add service discovery client dependencies to each of the three microservices, books, inventory, and gateway:

Gateway/build.gradleruntime "io.micronaut:discovery-client" books/build.gradleruntime "io.micronaut:discovery-client" inventory/build.gradleruntime "io.micronaut:discovery-client"

We need to make some changes to the configuration of each application so that the application is registered with Consul when it starts.

Gateway/src/main/resources/application.ymlmicronaut: application: name: gateway server: port: 8080consul: client: registration: enabled: true defaultZone: ${CONSUL_HOST:localhost}: ${CONSUL_PORT:8500} "books/src/main/resources/application.ymlmicronaut: application: name: books server: port: 8082consul: client: registration: enabled: true defaultZone:" ${CONSUL_HOST:localhost}: ${CONSUL_PORT:8500} "inventory/src/main/resources/application.ymlmicronaut: application: Name: inventory server: port: 8081consul: client: registration: enabled: true defaultZone: "${CONSUL_HOST:localhost}: ${CONSUL_PORT:8500}"

Each service uses the attribute microaut.application. Name as the service id when registering in Consul. That's why we used those explicit names in the previous @ Client annotation.

The previous code listing shows another feature of Micronaut, which has environment variable interpolation with default values in the configuration file, as shown below:

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

In addition, you can have environment-specific configuration files in Micronaut. We will create a file called application-test.yml in each environment for Consul registration during the test phase.

Gateway/src/test/resources/application-test.ymlconsul: client: registration: enabled: falsebooks/src/test/resources/application-test.ymlconsul: client: registration: enabled: falseinventory/src/test/resources/application-test.ymlconsul: client: registration: enabled: false

* * run the application

The easiest way to start using Consul is through Docker. Now, run an instance of Docker.

Docker run-p 8500 8500 consul

Create a multi-project build using Gradle. Create a settings.gradle file in the root directory.

Settings.gradleinclude 'books' include' inventory' include 'gateway'

Now you can run each application in parallel. Gradle provides a convenient logo (- parallel) for this purpose:

. / gradlew-parallel run

Each microservice starts on a configured port: 8080, 8081, and 8082.

Consul provides a HTML UI. Open http://localhost:8500/ui in the browser and you will see:

Every Micronaut microservice has been registered with Consul.

You can use the following curl command to invoke the gateway microservice:

$curl http://localhost:8080/api/books [{"isbn": "1680502395", "name": "Release It!", "stock": 3}, {"isbn": "1491950358", "name": "Building Microservices", "stock": 2}]

Congratulations on creating your first Micronaut microservice network!

Summary

In this tutorial, you have created three microservices in different languages: Java, Kotlin, and Groovy. You also learned how easy it is to consume other microservices using the Micronaut HTTP client and how to create functional tests that execute quickly.

In addition, everything you create can take advantage of complete reflection-independent dependency injection and AOP.

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 0

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

Internet Technology

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report