Java
Groovy & Grails
Distributed systems
July 2018 24

Введение в Micronaut Framework

Original author: Michael Pratt
Translation


1. Что такое Micronaut


Micronaut — это фреймворк на JVM для построения легковесных модульных приложений. Он разработан компанией OCI, той же компанией, что подарила нам Grails. Micronaut это современный фреймворк, призванный сделать создание микросервисных приложений быстрым и простым.

Micronaut содержит возможности похожие на существующие фреймворки, такие как Spring, но в то же время он реализует некоторые новые идеи, которые являются его отличительными чертами. Вместе с поддержкой Java, Groovy и Kotlin он предлагает множество путей создания приложений.

2. Основные возможности


Одна из самых захватывающих возможностей Micronaut это внедрение зависимостей (DI) во время компиляции. Большинство фреймворков используют рефлексию и прокси-объекты для внедрения зависимостей во время исполнения. Micronaut же собирает данные для внедрения зависимостей на этапе компиляции. Результат этого — быстрое время старта приложений и меньшее потребление памяти.

Следующая его возможность это первоклассная поддержка реактивного программирования, как для клиентов, так и для серверов. Выбор специфичной реализации реактивного подхода оставлен за разработчиками решений, а RxJava и Project Reactor поддерживаются из коробки.

Плюс, у Micronaut есть несколько возможностей, которые делают его отличным фреймворком для разработки облачных приложений (cloud-native). Он поддерживает множество механизмов обнаружения сервисов (service discovery), такие как Eureka и Consul, а также работает с различными системами распределённой трассировки (distributed tracing), такими как Zipkin и Jaeger.

Дополнительно, он предоставляет поддержку создания лямбда функций AWS, позволяя легко создавать бессерверные приложения (serverless).

3. Начало работы


Самый простой способ начать — использовать SDKMAN:

> sdk install micronaut 1.0.0.M2

SDKMAN установит все бинарные файлы, которые понадобятся вам для сборки, тестирования и развёртывания Micronaut приложений. Кроме того, вы получите консольное приложение Micronaut CLI, позволяющее с лёгкостью начать новый проект.

Бинарные артефакты также доступны в Sonatype и на Github.

В следующих разделах мы посмотрим на некоторые возможности Micronaut.

4. Внедрение зависимостей (DI)


Как упоминалось ранее, Micronaut обрабатывает внедрение зависимостей на этапе компиляции, что отличает его от большинства IoC контейнеров.

Однако, он полностью поддерживает аннотации JSR-330, так что работа с бинами похожа на другие IoC фреймворки.

Чтобы инжектировать бин в нашем коде мы используем @Inject:

@Inject
private EmployeeService service;

Аннотация @Inject работает также как @Autowired и может использоваться с полями, методами, конструкторами и параметрами.

По умолчанию, все бины имееют область видимости (scope) — prototype. Мы можем быстро создать синглтоны, используя аннотацию @Singleton. Если множество бинов реализуют один и тот же интерфейс, мы можем использовать аннотацию @Primary чтобы разрешить конфликт:

@Primary
@Singleton
public class BlueCar implements Car {}

Аннотация @Requires может использоваться, когда бины опциональны, или чтобы выполнить инъекцию при выполнении определённых условий.

В этом плане, он ведёт себя так же, как аннотация Spring Boot — @Conditional.

@Singleton
@Requires(beans = DataSource.class)
@Requires(property = "enabled")
@Requires(missingBeans = EmployeeService)
@Requires(sdk = Sdk.JAVA, value = "1.8")
public class JdbcEmployeeService implements EmployeeService {}

5. Создаём HTTP сервер


Теперь, давайте попробуем создать простое приложение HTTP сервер. Чтобы начать, мы будем использовать SDKMAN:

> mn create-app hello-world-server -build maven

Так мы создадим новый Java проект с Maven в каталоге с именем hello-world-server. Внутри этого каталога мы найдём основной код приложения, POM файл Maven и другие файлы проекта.

Простейшее приложение выглядит так:

public class ServerApplication {
    public static void main(String[] args) {
        Micronaut.run(ServerApplication.class);
    }
}

5.1 Блокирующий HttpRequest


Само по себе приложение не делает почти ничего. Давайте добавим контроллер с двумя обработчиками. Оба будут возвращать приветствие, но один будет отвечать на GET запросы, а другой на POST.

@Controller("/greet")
public class GreetController {

    @Inject
    private GreetingService greetingService;

    @Get("/{name}")
    public String greet(String name) {
        return greetingService.getGreeting() + name;
    }

    @Post(value = "/{name}", consumes = MediaType.TEXT_PLAIN)
    public String setGreeting(@Body String name) {
        return greetingService.getGreeting() + name;
    }
}

От переводчика: ваш покорный слуга пошёл и проделал всё, что рассказывается в этой статье. Если вы на этом этапе собрались запустить приложение и посмотреть, работает ли оно, то не забудьте включить Annotation Processing в Eclipse / IntelliJ IDEA.

5.2 Реактивный IO


По умолчанию, Micronaut реализует эти обработчики в виде традиционного блокирующего I/O. Однако, мы можем быстро реализовать неблокирующие обработчики, всего то изменив возвращаемый тип на любой из реактивных неблокирующих типов.

Например, с RxJava мы можем использовать Observable. Похожим образом, с Reactor мы можем вернуть типы Mono или Flux:

@Get("/{name}")
public Mono<String> greet(String name) {
    return Mono.just(greetingService.getGreeting() + name);
}

От переводчика: для этого примера вам понадобится Project Reactor в зависимостях Maven:

<dependency>
    <groupId>io.projectreactor</groupId>
    <artifactId>reactor-core</artifactId>
    <version>3.1.8.RELEASE</version>
</dependency>

Как для блокирующих, так и для неблокирующих обработчиков используется HTTP сервер Netty.

Обычно, запросы обрабатываются в основном I/O пуле потоков, который создаётся при запуске, что делает их блокирующимися.

Однако, если обработчик возвращает неблокирующие типы данных, то Micronaut использует цикл обработки событий Netty, делая весь запрос неблокирующим.

6. Создаём HTTP клиент


Теперь, давайте создадим клиентское приложение для обработчиков, которые только что создали. Micronaut предоставляет два способа создания HTTP клиентов:

— декларативный
— программный

6.1 Декларативное создание HTTP клиента


Первый и простейший способ создания использует декларативный подход:

@Client("/greet")
public interface GreetingClient {
    @Get("/{name}")
    String greet(String name);
}

Заметьте, мы не реализовали ни строчки кода для вызова сервиса. Вместо этого, Micronaut понимает как вызвать сервис из сигнатуры метода и аннотаций.

Чтобы протестировать этот клиент, мы можем создать тест на JUnit, который использует серверный API для запуска встроенного сервера:

public class GreetingClientTest {
    private EmbeddedServer server;
    private GreetingClient client;

    @Before
    public void setup() {
        server = ApplicationContext.run(EmbeddedServer.class);
        client = server.getApplicationContext().getBean(GreetingClient.class);
    }

    @After
    public void cleanup() {
        server.stop();
    }

    @Test
    public void testGreeting() {
        assertEquals(client.greet("Mike"), "Hello Mike");
    }
}

От переводчика: для ленивых любознательных читателей есть готовый проект на Github: github.com/jreznot/micronaut-introduction

6.2 Программное создание HTTP клиента


Есть вариант создания традиционного HTTP клиента, если требуется больше контроля над его поведением и реализацией:

@Singleton
public class ConcreteGreetingClient {
   private RxHttpClient httpClient;

   public ConcreteGreetingClient(@Client("/") RxHttpClient httpClient) {
      this.httpClient = httpClient;
   }

   public String greet(String name) {
      HttpRequest<String> req = HttpRequest.GET("/greet/" + name);
      return httpClient.retrieve(req).blockingFirst();
   }

   public Single<String> greetAsync(String name) {
      HttpRequest<String> req = HttpRequest.GET("/async/greet/" + name);
      return httpClient.retrieve(req).first("An error as occurred");
   }
}

Клиент по умолчанию использует RxJava, так что вы легко можете использовать блокирующие и неблокирующие вызовы.

7. Micronaut CLI


Мы уже видели, как работает утилита Micronaut CLI, когда создавали приложение.

В нашем случае это было отдельное приложение, но эта утилита поддерживает несколько других возможностей.

7.1 Проекты из множества приложений (Federation)


В Micronaut ферерация — просто группа отдельных приложений, которые разрабатываются в одном проекте. Используя федерацию мы можем легко управлять все их вместе и убедиться, что они используют одинаковые настройки.

Когда мы используем CLI для генерации федерации, утилита принимает такие же аргументы как команда create-app. Она создаст основную директорию проекта и поместит каждое приложение в поддиректорию.

7.2 Возможности


При создании приложения или федерации мы можем выбрать, какие возможности необходимы нашему приложению. Это позволяет использовать минимальный набор зависимостей в проекте.

Мы указываем возможности в видел аргумента -features, разделяя их запятыми.

Можно вывести список доступных возможностей при помощи следующей команды:

> mn profile-info service

Provided Features:
--------------------
* annotation-api - Adds Java annotation API
* config-consul - Adds support for Distributed Configuration with Consul
* discovery-consul - Adds support for Service Discovery with Consul
* discovery-eureka - Adds support for Service Discovery with Eureka
* groovy - Creates a Groovy application
[...] More features available

От переводчика: ну а тут не удивляйтесь, команду надо запускать вне каталога проекта. В каталоге проекта не работает, может в .M3 версии починили. Она уже вышла.

7.3 Существующие Проекты


Мы можем использовать CLI для модификации существующих проектов. Это позволяет нам создавать бины, клиенты, контроллеры, и т.д. Когда мы запускаем команду «mn» в каталоге проекта, у нас будут доступны следующие команды:

> mn help
| Command Name         Command Description
-----------------------------------------------
create-bean            Creates a singleton bean
create-client          Creates a client interface
create-controller      Creates a controller and associated test
create-job             Creates a job with scheduled method

8. Заключение


В этом кратком введении в Micronaut мы посмотрели, насколько просто создавать блокирующие и неблокирующие HTTP сервера и клиенты. Также мы взглянули на пару возможностей CLI.

Это всего лишь небольшой кусочек пирога, который предлагает Micronaut. Под капотом у него поддержка функций serverless, обнаружения сервисов, распределённой трассировки, мониторинга и метрик, распределённых конфигураций и много чего ещё.

Вместе с тем, что многие возможности Micronaut были заимствованы из существующих фреймворков, таких как Grails и Spring, он предлагает уникальные возможности, которые его выделяют на фоне других.
+12
6.5k 40
Comments 14
Top of the day