MicroProfile и его экосистема

Aug 27, 2021

Совсем недавно вышла новая версия MicroProfile - 4.1. Я бы хотел рассказать, что вообще такое MicroProfile, для чего он применяется, из каких компонентов состоит и чем привлекает к себе разработчиков.

Это перевод статьи MicroProfile® and its Ecosystem, автор David Dahlin.

В этой статье я покажу несколько примеров того, как он может использоваться на практике на примере работы с Quarkus, фреймворком, который является одной из множества реализаций спецификаций MicroProfile.

Что такое MicroProfile?

Так что же это всё-таки такое и какие проблемы на самом деле решает? Некоторое время назад глядя на медленное развитие Java EE, несколько лидеров отрасли собрались вместе, чтобы обсудить дальнейшие пути развития этой технологии в сфере энтерпрайз разработки. 14 июня 2016 года IBM, Red Hat, Payara, LJC, Tomitribe и другие независимые компании основали сообщество MicroProfile и зарегистрировали домен https://microprofile.io. 27 июня 2016 года на презентации DevNation в Сан-Франциско, Калифорния, было официально объявлено о создании MicroProfile.

В экосистеме Java уже существует множество «микросервисных» платформ, фреймворков и решений из мира энтерпрайз. Эти проекты предоставляют возможности для работы с микросервисными архитектурами с использованием Jakarta EE / Java EE и других технологий. Целью проекта MicroProfile является создание новых общих API и спецификаций функций, которые будут приняты сообществом. Также одной из целей является скорость развитие этих общих API и спецификаций в ногу с современными подходами к разработке микросервисных приложений. MicroProfile в основном предоставляет согласованную спецификацию API, которой может следовать любой проект, реализовать её и продвигать на рынке.

По сути, MicroProfile это просто набор спецификаций для создания микросервисов. В данный момент он включает в себя 13 спецификаций

Microprofile specification

Реализации

В настоящее время существуют следующие реализации MicroProfile от разных поставщиков:

Кто-то может спросить, почему, например, здесь не был упомянут Micronaut? Micronaut также поддерживает шаблоны Retry, Fallback и Circuit Breaker, но больше полагается на реализации аспектно-ориентированного программирования и избегает использования механизма рефлексии для решения своих задач, а также Micronaut использует собственные подходы, вдохновленные другими фреймворками, такими как Spring.

Eclipse MicroProfile Starter

Каждая версия MicroProfile предлагает набор спецификаций и реализуется несколькими вендорами. Различные спецификации могут быть созданы с помощью так называемого стартера MicroProfile - https://start.microprofile.io. MicroProfile Starter помогает разработчикам начать процесс разработки микросервисов, выбирая наиболее удобную реализацию из списка доступных для выбранной версии MicroProfile. Это означает, что вы можете выбрать реализацию от любого вендора и посмотреть, какая версия, совместимая с MicroProfile, доступна вместе со спецификациями, которые вы хотите использовать для своего приложения. Для примера на изображении ниже я выбрал несколько, чтобы продемонстрировать, что будет предоставлено для различных спецификаций. Все они выбраны с Quarkus как конкретной реализацией поставщика.

Microprofile starter

Далее я рассмотрю некоторые из специйикаций MicroProfile.

Microprofile Fault Tolerance

https://github.com/eclipse/microprofile-fault-tolerance

Для обеспечения отказоустойчивости ваших сервисов Microprofile предоставляет спецификацию Microprofile Fault Tolerance, которая предоставляет такие возможности как @Retry, @Timeout, @Fallback и @CircuitBreaker. Существует множество вариантов для конфигурации отказоустойчивости сервиса и того, как это всё может быть применено. Ниже приведён небольшой пример кода:

package com.dvddhln.demo.microprofile.with.quarkus.resilient;

import org.eclipse.microprofile.faulttolerance.Fallback;
import org.eclipse.microprofile.faulttolerance.Timeout;

import javax.enterprise.context.ApplicationScoped;
import javax.ws.rs.GET;
import javax.ws.rs.Path;

@Path("/resilience")
@ApplicationScoped
public class ResilienceController {

    @Fallback(fallbackMethod = "fallback") // better use FallbackHandler
    @Timeout(500)
    @GET
    public String checkTimeout() {
        try {
            Thread.sleep(700L);
        } catch (InterruptedException e) {
            //
        }
        return "Never from normal processing";
    }

    public String fallback() {
        return "Fallback answer due to timeout";
    }
}

Microprofile Metrics

https://github.com/eclipse/microprofile-metrics

Эта спецификация создана для унифицированного способа предоставления различных телеметрических данных сервисами на базе Microprofile. Microprofile Metrics предоставляет способ регистрации метрик, специфичных для приложения, которые впоследствии могут быть отправлены, например, в Promethus. Ниже приведён небольшой пример использования:

package com.dvddhln.demo.microprofile.with.quarkus.metric;

import org.eclipse.microprofile.metrics.Counter;
import org.eclipse.microprofile.metrics.MetricUnits;
import org.eclipse.microprofile.metrics.annotation.Gauge;
import org.eclipse.microprofile.metrics.annotation.Metric;
import org.eclipse.microprofile.metrics.annotation.Timed;

import javax.enterprise.context.ApplicationScoped;
import javax.inject.Inject;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import java.util.Random;

@Path("/metric")
@ApplicationScoped //Required for @Gauge
public class MetricController {

    @Inject
    @Metric(name = "endpoint_counter")

    // https://quarkus.io/guides/cdi-reference#private-members
    // - private Counter counter;
    Counter counter;

    @Path("timed")
    @Timed(name = "timed-request")
    @GET
    public String timedRequest() {
        // Demo, not production style
        int wait = new Random().nextInt(1000);
        try {
            Thread.sleep(wait);
        } catch (InterruptedException e) {
            // Demo
            e.printStackTrace();
        }
        return "Request is used in statistics, check with the Metrics call.";
    }


    @Path("increment")
    @GET
    public long doIncrement() {
        counter.inc();
        return counter.getCount();
    }

    @Gauge(name = "counter_gauge", unit = MetricUnits.NONE)
    private long getCustomerCount() {
        return counter.getCount();
    }
}

Microprofile Open API

https://github.com/eclipse/microprofile-open-api

Эта спецификация MicroProfile нацелена на предоставление унифицированного Java API для документирования ваших сервисов по спецификации OpenAPI v3. Вот небольшой пример использования этого:

package com.dvddhln.demo.microprofile.with.quarkus.openapi;

import org.eclipse.microprofile.openapi.annotations.OpenAPIDefinition;
import org.eclipse.microprofile.openapi.annotations.info.Info;
import org.eclipse.microprofile.openapi.annotations.media.Content;
import org.eclipse.microprofile.openapi.annotations.media.Schema;
import org.eclipse.microprofile.openapi.annotations.responses.APIResponse;
import org.eclipse.microprofile.openapi.annotations.responses.APIResponses;

import javax.enterprise.context.ApplicationScoped;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;

@Path("/booking")
@ApplicationScoped
@OpenAPIDefinition(info = @Info(title = "Booking endpoint", version = "1.0"))
public class BookingController {

    @APIResponses(value = {
            @APIResponse(
                    responseCode = "200",
                    description = "Booking for id",
                    content = @Content(
                            mediaType = MediaType.APPLICATION_JSON,
                            schema = @Schema(ref = "Booking"))
            ),
            @APIResponse(
                    responseCode = "404",
                    description = "No booking found for the id.")
    })
    @GET
    @Produces(MediaType.APPLICATION_JSON)
    @Path("{bookingId}")
    public Response getBooking(@PathParam("bookingId") String bookingId) {
        return Response
                .status(Response.Status.OK)
                .entity(Booking.booking(bookingId, Destination.destination("New Rearendia", "Wheeli")))
                .build();
    }
}

Microprofile Health

https://github.com/eclipse/microprofile-health

Эта спецификация предоставляет возможность проверки работоспособности и исправности вашего сервиса. Для этого используется две аннотации:

  • для проверки готовности сервиса используется аннотация @Readiness;
  • для проверки того, что сервис жив, используется аннотация @Liveness.
package com.dvddhln.demo.microprofile.with.quarkus.health;

import org.eclipse.microprofile.health.HealthCheck;
import org.eclipse.microprofile.health.HealthCheckResponse;
import org.eclipse.microprofile.health.Liveness;

import javax.enterprise.context.ApplicationScoped;

@Liveness
@ApplicationScoped
public class ServiceLiveHealthCheck implements HealthCheck {

    @Override
    public HealthCheckResponse call() {
        return HealthCheckResponse.named(ServiceLiveHealthCheck.class.getSimpleName())
          .withData("live", true)
          .up()
          .build();
    }
}

Стоит отметать, что компоненты проверки работоспособности сервиса являются компонентами CDI, поэтому их также можно определить с помощью CDI продюсеров. Таким образом, приведенный выше пример может выглядеть примерно так:

@ApplicationScoped
class MyChecks {

    @Produces
    @Liveness
    HealthCheck check1() {
        return () -> HealthCheckResponse.named("heap-memory").status(getMemUsage() < 0.9).build();
    }

    @Produces
    @Readiness
    HealthCheck check2() {
        return () -> HealthCheckResponse.named("cpu-usage").status(getCpuUsage() < 0.9).build();
    }

    @Produces
    @Startup
    HealthCheck check3() {
        return () -> HealthCheckResponse.named("initial-heap-memory").status(getMemUsage() < 0.95).build();
    }
}

Для самой простой проверки работоспособности и готовности сервиса обе проверки могут помечать один и тот же класс:

@ApplicationScoped
@Liveness
@Readiness
public class MyCheck implements HealthCheck {

    public HealthCheckResponse call() {
        [...]
    }
}

Microprofile Rest Client

https://github.com/eclipse/microprofile-rest-client

REST клиент MicroProfile обеспечивает типобезопасный подход для вызова RESTful сервисов через HTTP. Насколько это возможно, клиент пытается использовать Jakarta RESTful Web Services 2.1 для обеспечения согласованности и упрощения повторного использования. Вот небольшой пример:

package com.dvddhln.demo.microprofile.with.quarkus.client;

import org.eclipse.microprofile.rest.client.inject.RegisterRestClient;
import javax.enterprise.context.ApplicationScoped;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;

@RegisterRestClient
@ApplicationScoped
public interface Service {
    @GET
    @Path("/{parameter}")
    String doSomething(@PathParam("parameter") String parameter);
}

Microprofile Config

https://github.com/eclipse/microprofile-config

MicroProfile Config использует внедрение контекстов и зависимостей (CDI) для внедрения значений свойств конфигурации непосредственно в приложение не требуя написания дополнительного пользовательского кода для их извлечения. Значения конфигурации определяются как статические, поскольку они устанавливаются только при запуске приложения. Microprofile Config позволяет объединять значения конфигурации из нескольких источников, каждый из которых объявлен как ConfigSource. Каждый ConfigSource имеет определенный приоритет, определяемый его порядковым номером. Более высокий порядковый номер означает, что значения, взятые из этого ConfigSource, переопределят значения из ConfigSources с более низким порядковым номером. Ниже приведён небольшой пример использования Microprofile Config:

package com.dvddhln.demo.microprofile.with.quarkus.config;

import org.eclipse.microprofile.config.Config;
import org.eclipse.microprofile.config.ConfigProvider;
import org.eclipse.microprofile.config.inject.ConfigProperty;

import javax.enterprise.context.RequestScoped;
import javax.inject.Inject;
import javax.ws.rs.GET;
import javax.ws.rs.Path;

@Path("/config")
@RequestScoped
public class ConfigTestController {

    @Inject
    @ConfigProperty(name = "injected.value")
    // https://quarkus.io/guides/cdi-reference#private-members
    // - private String injectedValue;
    String injectedValue;

    @Path("/injected")
    @GET
    public String getInjectedConfigValue() {
        return "Config value as Injected by CDI " + injectedValue;
    }

    @Path("/lookup")
    @GET
    public String getLookupConfigValue() {
        Config config = ConfigProvider.getConfig();
        String value = config.getValue("value", String.class);
        return "Config value from ConfigProvider " + value;
    }
}

Заключение

В этой статье были обзорно рассмотрены некоторые из спецификаций, которые предоставляются Microprofile. Если вам интересна эта тема, а также тема реализаций этих спецификаций, я приглашаю вас присоединяться к моему телеграм-каналу о Quarkus, где можно найти дополнительную информацию по этой теме и конечно же узнать гораздо больше о Quarkus как одной из реализаций Microprofile.