Micronaut Tutorial: How to use micro-services-based framework to build the JVM?

This article points:

Micronaut jvm is based on a modern full-stack framework for building modular and easy to test micro-service applications. When Micronaut provide complete compilation, irrespective of the reflected dependency injection and AOP. Grails framework of the development team and development team of the framework is the same. Micronaut framework integrates cloud technology, service discovery, distributed tracking, circuit breakers and other micro-services model is also built into the framework. In this tutorial, you will create three micro-services in different languages: Java, Kotlin and Groovy. You will also learn to use Micronaut HTTP client service consumption of other micro how easy it is and how to create functional tests quickly performed.

Application framework constructed using different conventional JVM, when compiled Micronaut 100% reflective dependencies-injection and AOP. Therefore, Micronaut application is very small, very low memory footprint. Use Micronaut, you can develop a great application or a monomer can be deployed to AWS Lambda little function. Framework does not limit you.

Micronaut framework also integrates cloud technology, service discovery, distributed tracking, circuit breakers and other micro-services model is also built into the framework.

Micronaut as open source software released in May 2018, plans to release version 1.0.0 before the end of 2018. Now you can try Micronaut, because the milestone version and release candidate version is already available.

Micronaut framework Grails framework of the development team and development team is the same. Grails has recently celebrated its 10th anniversary, it continues in many Productivity helps developers to write Web applications. Grails 3 is built on Spring Boot. You will soon find that using Grails and Spring Boot framework of these two developers who, Micronaut has a simple learning curve.

Tutorial

In this series of articles, we will use the service to create a few micro applications:

  • A micro-service books, written in Groovy;
  • A micro-service inventory, use Kotlin write;
  • A micro gateway service, written in Java.
    You will do the following:
  • Write endpoint, dependency injection when using the compiler;
  • Write functional tests;
  • Micronaut configure those applications to register Consul;
  • Use Micronaut declarative HTTP client communicate therebetween.
    The following figure illustrates the application you want to build:
    Micronaut Tutorial: How to use micro-services-based framework to build the JVM?

Micro Services # 1 Groovy microService

The easiest way to create Micronaut application is using its command line interface (Micronaut CLI), using SDKMan easy installation.
Micronaut application can use Java, Kotlin and written in Groovy. First, let's create a Groovy Micronaut application:

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

The above command creates a named books application, the default package is example.micronaut.

Micronaut testing framework is irrelevant. It is based on the language you choose to use a default testing framework. By default, Java using JUnit. If you choose Groovy, by default, we will use Spock. You can use with different language and testing framework. For example, a Java Micronaut Spock test application.

Moreover, Micronaut is unrelated to build tools. You can use Maven or Gradle. Default Gradle.

Application generated based Netty comprises a non-blocking HTTP server.

Create a controller expose your first Micronaut endpoints:

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()
 }
}

In the above code, there are a few places worth mentioning:

  • The controller exposing a route / api / books endpoint, call GET request may be used;
  • Notes @Get @Controller value and is a RFC-6570 URI template;
  • By constructor injection, Micronaut provides a collaborative categories: BooksRepository;
  • Micronaut controller default consumption and generate JSON.
    Said controller using an interface and a 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 at compile time to a realization of the bean BooksRepository interface connected.

For this application, we have created a single example, we are using javax.inject.Singleton annotation defined.

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!"),
 ]
 }
}

The maximum value function tests because they test the entire application. However, for other frameworks, rarely used functional testing and integration testing. In most cases, as they relate to start the entire application, so the speed is very slow.

However, write functional tests in Micronaut is a pleasure. Because they are fast, very fast.

Function of the controller tests are as follows:

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
 }
}

In the test, there are a few places worth mentioning:

  • Interface means EmbeddedServer easily from the unit test run the application;
  • It is easy to create an HTTP client to bean consumption embedded server;
  • Micronaut Http client is easy to parse JSON into Java objects.

    Micro Service # 2 Kotlin microService

    Run the following command to create a micro-called inventory of additional services. This time, we use Kotlin language.

&gt; mn create-app example.micronaut.inventory --lang kotlin
The new micro-services control the inventory of each book.
Kotlin create a data class attribute field Package:

inventory/src/main/kotlin/example/micronaut/Book.kt
package example.micronaut
data class Book(val isbn: String, val stock: Int)

Create a controller, return to stock a book.

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
@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()
 }
 }
}

# 3 Java Micro Services Micro Services

Creating a Java gateway application, the application will consume books and inventory both micro service.

mn create-app example.micronaut.gateway

If you do not specify lang logo, it will default to the choice of Java.

In the micro gateway service, create a declarative HTTP client communications services and micro books.

First, create an interface:

gateway/src/main/java/example/micronaut/BooksFetcher.java
package example.micronaut;
import io.reactivex.Flowable;
public interface BooksFetcher { 
 Flowable<Book> fetchBooks(); 
}

Then, create a declarative HTTP client, this is a use of @Client annotation interface.

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.Client; 
import io.reactivex.Flowable;
@Client("books") 
@Requires(notEnv = Environment.TEST) 
public interface BooksClient extends BooksFetcher {
 @Override @Get("/api/books") Flowable<Book> fetchBooks();
}

Micronaut declarative method to implement HTTP client at compile time, which greatly simplifies the creation of HTTP clients.

In addition, Micronaut support the concept of application environments. In the above code listing, you can see, it is easy to use annotation @Requires ban certain bean loaded in a specific environment.

And, as you can see in the code as in the previous example, non-blocking type is a first-class citizens in Micronaut in. BooksClient :: fetchBooks () method returns Flowable <Book>, wherein Book is a 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; }
}

Create another declarative HTTP client, micro service to communicate with the inventory.

First, create an interface:

gateway/src/main/java/example/micronaut/InventoryFetcher.java
package example.micronaut;
import io.reactivex.Maybe;
public interface InventoryFetcher { 
 Maybe<Integer> inventory(String isbn); 
}

Then, an HTTP client declarative:

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.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<Integer> inventory(String isbn);
}

Now, create a controller, into two bean, create a reactive response.

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.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<Book> findAll() { 
 return booksFetcher.fetchBooks()
 .flatMapMaybe(b -> inventoryFetcher.inventory(b.getIsbn())
 .filter(stock -> stock > 0)
 .map(stock -> { 
 b.setStock(stock); 
 return b; 
 })
 );
 }
}

Before you create a functional test for the controller, we need to create the bean is implemented as (BooksFetcher and InventoryFetcher) in a test environment.

Creation bean in line with BooksFetcher interface is only available in test environment; see @Requires comment.

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:"));
 } 
}

Create a line with InventoryFetcher interface bean, applies only to the test environment;

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();
 } 
}

Creating functional testing. In Groovy micro services, we wrote a Spock test, this time, we write JUnit tests.

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());
 } 
}

Service Discovery

We will configure our Micronaut micro-services, registered with the Consul service discovery.

Consul is a distributed service grid for the time across any platform and run public or private cloud connection, protection and configuration services.

Micronaut and Consul integration is very simple.

First of all the books, inventory and three micro gateway service each added service discovery client dependencies:

gateway/build.gradle
runtime "io.micronaut:discovery-client"
books/build.gradle
runtime "io.micronaut:discovery-client"
inventory/build.gradle
runtime "io.micronaut:discovery-client"

We need to make some changes to the configuration of each application for registration to the Consul when the application starts.

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}"

Each service as a service using the property microaut.application .name id when registering at the Consul. That's why we used those precise name in front of @Client annotation.

Listing foregoing illustrates another characteristic of Micronaut, the configuration file with a default value of the environment variable interpolation, as follows:

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

In addition, there may be Micronaut environment-specific configuration files. We will create a file named application-test.yml in each environment, Consul for the testing phase of registration.


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

** Run the application
easiest way to start is by using Consul Docker. Now, run a Docker instance.

docker run -p 8500:8500 consul

Use Gradle to create a multi-project build. Settings.gradle create a file in the root directory.

settings.gradle
include 'books' 
include 'inventory' 
include 'gateway'

Now, you can run in parallel to each app. Gradle aims to provide a convenient identification (-parallel):

./gradlew -parallel run

Each micro services are configured to start on port: 8080,8081 and 8082.

Consul provides an HTML UI. Open your browser http: // localhost: 8500 / ui , you will see :
Micronaut Tutorial: How to use micro-services-based framework to build the JVM?

Each micro Micronaut services have registered with the Consul.

You can use the following curl command invokes the Gateway Micro services:


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

Congratulations, you have created the first Micronaut micro-service network!

summary

In this tutorial, you created three micro-services in different languages: Java, Kotlin and Groovy. You also learned how easy it is to use Micronaut HTTP client consumption of other micro service is and how to create functional tests quickly performed.

In addition, everything you create can use completely unrelated reflection of dependency injection and AOP.

Guess you like

Origin blog.51cto.com/14541438/2437748