Как Quarkus объединяет MicroProfile и Spring

    Всем привет, и с вами третий пост из серии про Quarkus! (Кстати, смотрите наш вебинар «Это Quarkus – Kubernetes native Java фреймворк». Покажем, как начать «с нуля» или перенести готовые решения)



    В предыдущем посте мы поговорили о нативной компиляции в Quarkus – и узнали, почему это важно.

    При разработке Java-микросервисов часто считается, что Eclipse MicroProfile и Spring Boot – это отдельные и независимые друг от друга API. По умолчанию, программисты, как правило, используют те API, к которым они уже привыкли, поскольку изучение новых фреймворков и runtime-компонентов требует много времени. Сегодня мы попробуем упростить освоение некоторых популярных MicroProfile API для Spring-разработчиков и покажем, как одновременно задействовать Spring API и новые полезные возможности Quarkus.

    Если чуть подробнее, то сначала мы рассмотрим область применения и детали того, как Quarkus поддерживает API-интерфейсы Spring, чтобы показать Spring-разработчикам, как можно применять MicroProfile API в своей повседневной работе. Затем мы расскажем о MicroProfile API, которые пригодятся Spring-разработчикам при создании микросервисов.

    Почему именно Quarkus? Во-первых, это кодирование вживую (live coding), то есть автоматическая перезагрузка любых изменений в MicroProfile API, Spring API и других Java API, которая выполняется всего одной командой: mvn quarkus:dev. Во-вторых, рассматриваемый в нашем примере сервис Person (он компилируется из API-интерфейсов Spring, MicroProfile и JPA в двоичный файл с использованием нативного образа GraalVM) запускается всего за 0.055 секунды и занимает около 90 МБ в оперативной памяти (RSS) на конечной точке приложения RESTful. Причем сама его компиляция выполняется всего одной командой: mvn package -Pnative.

    Мы не будем углубляться в подробности MicroProfile, а лишь постараемся помочь Spring-разработчикам понять, как в Quarkus можно использовать API-интерфейсы Spring вместе с API-интерфейсами MicroProfile.

    Контейнеры и Kubernetes


    Чтобы не перегружать эту статью, мы рассмотрим здесь лишь высокоуровневые аспекты поддержки Kubernetes, поскольку это важно понимать. Quarkus позиционируется как Java-стек для Kubernetes, он призван минимизировать расход памяти и время запуска Java-приложений и сервисов, и, как следствие, повысить плотность их размещения на хосте и снизить общие затраты.

    Quarkus также поддерживает автогенерацию ресурсов Kubernetes и предлагает руководства по развертыванию на платформах Kubernetes и Red Hat OpenShift. Кроме того, Quarkus автоматически генерирует файлы Dockerfile.jvm (JVM packaging) и Dockerfile.native (native binary packaging), необходимые для создания контейнеров.

    И наконец, ориентируясь на Kubernetes как на целевую среду развертывания, Quarkus не использует Java-фреймворки в тех случаях, когда аналогичный функционал реализован на уровне самой платформы Kubernetes. В таблице 1 приводится карта функционального соответствия Kubernetes и типовых Java-фреймворков, применяемых Spring-разработчиками.

    Таблица 1. Карта функционального соответствия Java-фреймворков и Kubernetes.
    Функционал Традиционный Spring Boot Kubernetes
    Service discovery Eureka DNS
    Configuration Spring Cloud Config Config Maps / Secrets
    Load balancing Ribbon (на стороне клиента) Service, Replication Controller(на стороне сервера)

    Компиляция и запуск кода из примера


    В этой статье мы ссылаемся на пример проекта, где совместно используются API-интерфейсы Spring и MicroProfile и даже тот самый Java-класс. Код из этого примера можно скомпилировать и запустить из командной строки, подробнее см. файл README.md.

    API-интерфейсы Spring Framework


    Dependency Injection


    Quarkus поддерживает целый ряд API-интерфейсов Contexts and Dependency Injection (CDI) и API-интерфейсов Spring Dependency Injection (Spring DI). Если вы работаете с MicroProfile, Java EE и Jakarta EE, то уже хорошо знакомы с CDI. С другой стороны, Spring-разработчики могут использовать Quarkus Extension for Spring DI API для обеспечения совместимости с Spring DI. Примеры использования поддерживаемых Spring DI API приводятся в таблице 2.

    В проекте из нашего примера используется как CDI, так и Spring Dependency Injection. Дополнительные сведения и примеры на эту тему можно найти в руководстве Quarkus, которое называется Spring DI Guide.

    Таблица 2. Примеры использования поддерживаемых API-интерфейсов Spring DI.
    Поддерживаемые функции Spring DI Примеры
    Constructor Injection
    public PersonSpringController(
       PersonSpringRepository personRepository,  // injected      
       PersonSpringMPService personService) {    // injected
          this.personRepository = personRepository;
          this.personService = personService;
    }
    
    Field Injection
    Autowired
    Value
    @Autowired
    @RestClient
    SalutationRestClient salutationRestClient;
    
    @Value("${fallbackSalutation}")
    String fallbackSalutation;
    
    Bean
    @Configuration
    @Configuration
    public class AppConfiguration {
       @Bean(name = "capitalizeFunction")
       public StringFunction capitalizer() {
          return String::toUpperCase;
       }
    }
    
    Component
    @Component("noopFunction")
    public class NoOpSingleStringFunction implements StringFunction {
       @Override
       public String apply(String s) {
          return s;
       }
    }
    
    Service
    @Service
    public class MessageProducer {
       @Value("${greeting.message}")
       String message;
    
       public String getPrefix() {
          return message;
       }
    }
    

    Веб-фреймворк


    Пользователям MicroProfile понравится, что Quarkus поддерживает JAX-RS, MicroProfile Rest Client, JSON-P и JSON-B в качестве основной модели веб-программирования. Spring-разработчиков приятно порадует недавно появившаяся в Quarkus поддержка Spring Web API, в частности интерфейсов, отвечающих за REST. По аналогии со Spring DI главная цель поддержки Spring Web API заключается в том, чтобы Spring-разработчики могли использовать API-интерфейсы Spring Web вместе с API-интерфейсами MicroProfile. Примеры использования поддерживаемых Spring Web API приводятся в таблице 3, а дополнительные сведения и примеры на эту тему можно найти в руководстве Quarkus, которое называется Spring Web Guide.

    Таблица 3. Примеры использования поддерживаемых API-интерфейсов Spring Web.
    Поддерживаемые функции Spring Web Примеры
    @RestController
    @RequestMapping
    @RestController
    @RequestMapping("/person")
    public class PersonSpringController {
       ...
       ...
       ...
    }
    
    @GetMapping
    @PostMapping
    @PutMapping
    @DeleteMapping
    @PatchMapping
    @RequestParam
    @RequestHeader
    @MatrixVariable
    @PathVariable
    @CookieValue
    @RequestBody
    @ResponseStatus
    @ExceptionHandler
    @RestControllerAdvice (partial)
    @GetMapping(path = "/greet/{id}",
       produces = "text/plain")
       public String greetPerson(
       @PathVariable(name = "id") long id) {
       ...
       ...
       ...
    }
    

    Spring Data JPA


    Пользователям MicroProfile также придется по душе, что Quarkus поддерживает JPA с использованием Hibernate ORM. Для Spring-разработчиков тоже есть хорошая новость: Quarkus поддерживает общепринятые аннотации и типы Spring Data JPA. Примеры использования поддерживаемых Spring Data JPA API приводятся в таблице 4.
    В проекте из нашего примера используются API-интерфейсы Spring Data JPA, а дополнительная информация доступна в руководстве Quarkus под названием Spring Data JPA Guide.

    Таблица 4. Примеры использования поддерживаемых API-интерфейсов Spring Data JPA.
    Поддерживаемые функции Spring Data JPA Примеры
    CrudRepository
    public interface PersonRepository
             extends JpaRepository,
                     PersonFragment {
       ...
    }
    
    Repository
    JpaRepository
    PagingAndSortingRepository
    public class PersonRepository extends 
    
        Repository {
    
        Person save(Person entity);
    
        Optional findById(Person entity);
    }
    
    Repository Fragments
    public interface PersonRepository
             extends JpaRepository,
                     PersonFragment {
       ...
    }
    
    Derived query methods
    public interface PersonRepository extends CrudRepository {
    
        List findByName(String name);
        
        Person findByNameBySsn(String ssn);
        
        Optional 
           findByNameBySsnIgnoreCase(String ssn);
    
        Boolean existsBookByYearOfBirthBetween(
                Integer start, Integer end);
    }
    
    User-defined queries
    public interface MovieRepository
             extends CrudRepository {
    
        Movie findFirstByOrderByDurationDesc();
    
        @Query("select m from Movie m where m.rating = ?1")
        Iterator findByRating(String rating);
    
        @Query("from Movie where title = ?1")
        Movie findByTitle(String title);
    }
    

    API-интерфейсы MicroProfile


    Отказоустойчивость (Fault tolerance)


    Конструкции Fault tolerance очень важны для предотвращения каскадных отказов и создания надежных микросервисных архитектур. Spring-разработчики уже много лет используют для отказоустойчивости circuit-breaker’ы Hystrix. Однако Hystrix давно не обновлялся, а вот MicroProfile’евский Fault Tolerance сейчас активно развивается и имеет за плечами уже несколько лет продакшн-использования. Поэтому для повышения надежности сервисов в Quarkus рекомендуется применять API-интерфейсы MicroProfile Fault Tolerance, примеры использования которых приводятся в таблице 5. Дополнительные сведения об этом можно найти в руководстве Quarkus Fault Tolerance Guide.

    Таблица 5. Примеры использования поддерживаемых API-интерфейсов MicroProfile Fault Tolerance.
    Функции MicroProfile Fault Tolerance Описание Примеры
    @Asynchronous
    Выполнение логики в отдельном потоке
    @Asynchronous
    @Retry
    public Future<String> getSalutation() {
       ...
       return future;
    }
    
    @Bulkhead
    Ограничение количества одновременных запросов
    @Bulkhead(5)
    public void fiveConcurrent() {
       makeRemoteCall(); //...
    }
    
    @CircuitBreaker
    Умная обработка сбоев и восстановление после сбоев
    @CircuitBreaker(delay=500   // milliseconds
       failureRatio = .75,
       requestVolumeThreshold = 20,
       successThreshold = 5)
    @Fallback(fallbackMethod = "fallback")
    public String getSalutation() {
       makeRemoteCall(); //...
    }
    
    @Fallback
    Вызов альтернативной логики в случае сбоя
    @Timeout(500) // milliseconds
    @Fallback(fallbackMethod = "fallback")
    public String getSalutation() {
       makeRemoteCall(); //...
    }
    
    public String fallback() {
       return "hello";
    }
    
    Retry
    Повтор при сбое запроса
    @Retry(maxRetries=3)
    public String getSalutation() {
       makeRemoteCall(); //...
    }
    
    Timeout
    Контрольное время ожидания при сбое
    @Timeout(value = 500 )   // milliseconds
    @Fallback(fallbackMethod = "fallback")
    public String getSalutation() {
       makeRemoteCall(); //...
    }
    

    Проверка сервисов (Service Health)


    Kubernetes-платформы отслеживают исправность контейнеров с помощью специальных сервисов. Чтобы нижележащая платформа могла мониторить сервисы, Spring-разработчики обычно используют настраиваемые HealthIndicator и Spring Boot Actuator. В Quarkus это можно сделать с помощью MicroProfile Health, которые по умолчанию выполняют проверку работоспособности (liveness check), но могут быть настроены и на одновременную проверку liveness и readiness (готовности). Примеры использования поддерживаемых MicroProfile Health API приводятся в таблице 6, а дополнительная информация представлена в руководстве Quarkus Health Guide.

    Таблица 6. Примеры использования поддерживаемых API-интерфейсов MicroProfile Health.
    Функции MicroProfile Health Описание Примеры
    @Liveness
    Платформа выполняет перезагрузку неисправных контейнеризованных приложений
    Endpoint:
    host:8080/health/live
    @Liveness
    public class MyHC implements HealthCheck {
      public HealthCheckResponse call() {
    
       ...
       return HealthCheckResponse
         .named("myHCProbe")
         .status(ready ? true:false)
         .withData("mydata", data)
         .build();  
    }
    
    @Readiness
    Платформа не будет отправлять трафик на контейнеризованное приложений в случае его неготовности
    Endpoint:
    host:8080/health/ready
    @Readiness
    public class MyHC implements HealthCheck {
      public HealthCheckResponse call() {
    
       ...
       return HealthCheckResponse
         .named("myHCProbe")
         .status(live ? true:false)
         .withData("mydata", data)
         .build();  
    }
    

    Метрики


    Приложения предоставляют метрики или в операционных целях (для контроля SLA-показателей производительности), или в неоперационных (бизнес-показатели SLA). Spring-разработчики предоставляют метрики с помощью Spring Boot Actuator и Micrometer. В свою очередь, Quarkus использует MicroProfile Metrics для предоставления базовых метрик (JVM и операционная система), вендор-метрик (Quarkus) и метрик приложений. MicroProfile Metrics требует, чтобы реализация поддерживала выходные форматы JSON и OpenMetrics (Prometheus). Примеры использования MicroProfile Metrics API приводятся в таблице 7.

    В проекте из нашего примера MicroProfile Metrics используются для предоставления метрик приложения. Дополнительные сведения можно найти в руководстве Quarkus Metrics Guide.

    Таблица 7. Примеры использования API-интерфейсов MicroProfile Metrics.
    Функции MicroProfile Metrics Описание Примеры
    @Counted
    Обозначает counter-счетчик, подсчитывающий количество вызовов аннотированного объекта
    @Counted(name = "fallbackCounter", 
      displayName = "Fallback Counter", 
      description = "Fallback Counter")
    public String salutationFallback() {
       return fallbackSalutation;
    }
    
    @ConcurrentGauge
    Обозначает gauge-датчик, подсчитывающий количество параллельных вызовов аннотированного объекта
    @ConcurrentGuage(
      name = "fallbackConcurrentGauge", 
      displayName="Fallback Concurrent", 
      description="Fallback Concurrent")
    public String salutationFallback() {
       return fallbackSalutation;
    }
    
    @Gauge
    Обозначает gauge-датчик, замеряющий значение аннотированного объекта
    @Metered(name = "FallbackGauge",
       displayName="Fallback Gauge",
       description="Fallback frequency")
    public String salutationFallback() {
       return fallbackSalutation;
    }
    
    @Metered
    Обозначает meter-датчик, отслеживающий частоту вызова аннотированного объекта
    @Metered(name = "MeteredFallback",
       displayName="Metered Fallback",
       description="Fallback frequency")
    public String salutationFallback() {
       return fallbackSalutation;
    }
    
    Metric
    Аннотация, содержащая информацию о метаданных, при поступлении запроса на внесение или производство метрики
    @Metric
    @Metered(name = "MeteredFallback",
       displayName="Metered Fallback",
       description="Fallback frequency")
    public String salutationFallback() {
       return fallbackSalutation;
    }
    
    Timed
    Обозначает таймер, отслеживающий длительность аннотированного объекта
    @Timed(name = "TimedFallback",
       displayName="Timed Fallback",
       description="Fallback delay")
    public String salutationFallback() {
       return fallbackSalutation;
    }
    

    Конечные точки метрик (Metrics Endpoints)

    Метрики приложений localhost:8080/metrics/application
    Базовые метрики localhost:8080/metrics/base
    Вендор-метрики localhost:8080/metrics/vendor
    Все метрики localhost:8080/metrics

    Rest-клиент MicroProfile


    Микросервисы часто предоставляют конечные точки RESTful, для работы с которыми требуются соответствующие клиентские API. Чтобы использовать конечные точки RESTful, Spring-разработчики обычно применяют RestTemplate. Quarkus же предлагает для решения этой задачи API-интерфейсы MicroProfile Rest Client, примеры использования которых приводятся в таблице 8.

    В проекте из нашего примера использование конечных точек RESTful выполняется с помощью MicroProfile Rest Client. Дополнительные сведения и примеры на эту тему можно найти в руководстве Quarkus Rest Client Guide.

    Таблица 8. Примеры использования API-интерфейсов MicroProfile Rest Client.
    Функции MicroProfile Rest Client Описание Примеры
    @RegisterRestClient
    Регистрирует типизированный Java-интерфейс в качестве клиента REST
    @RegisterRestClient
    @Path("/")
    public interface MyRestClient {
        @GET
        @Produces(MediaType.TEXT_PLAIN)
        public String getSalutation();
    }
    
    @RestClient
    Отмечает внедрение экземпляра типизированного интерфейса REST-клиента
    @Autowired // or @Inject
    @RestClient
    MyRestClient restClient;
    
    Invocation
    Вызывает конечную точку REST
    System.out.println(
       restClient.getSalutation());
    
    mp-rest/url
    Задает конечную точку REST
    application.properties:
    org.example.MyRestClient/mp-rest/url=
       http://localhost:8081/myendpoint
    

    Итоги


    В этом блоге, который в первую очередь пригодится Spring-разработчикам, мы кратко рассмотрели, как в Quarkus использовать API-интерфейсы Spring вместе API-интерфейсами MicroProfile, чтобы разрабатывать микросервисы Java и затем компилировать их в нативный двоичный код, который экономит сотни мегабайт оперативной памяти и запускается за считанные миллисекунды.

    Как вы уже поняли, дополнительные сведения о поддержке API-интерфейсов Spring и MicroProfile, а также массу другой полезной информации можно найти в руководствах Quarkus.

    В нашем следующем – четвертом – посте из серии про Quarkus расскажем, как модернизировать приложения на примере helloworld из JBoss EAP Quickstart. Уже по ссылке: habr.com/ru/company/redhatrussia/blog/499524
    Red Hat
    Программные решения с открытым исходным кодом

    Комментарии 2

      +2
      Спасибо за упоминание, мне это льстит :)
        +1

        Глядя на постоянное упоминание Spring, я думаю очень важно включить в статью следующее замечание из этого гайда:


        Important Technical Note

        Please note that the Spring support in Quarkus does not start a Spring Application Context nor are any Spring infrastructure classes run. Spring classes and annotations are only used for reading metadata and / or are used as user code method return types or parameter types.

        Т.е. используются только аннотации Spring, вся реализация выполнена самим Quarkus и не обязательно совместима.

        Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

        Самое читаемое