How Quarkus combines imperative and reactive programming

This year we plan to seriously develop the topics of containers, Cloud Native Java ΠΈ Kubernetes. A logical continuation of these topics will be a story about the Quarkus framework, already reviewed on Habr. Today's article is not so much about the design of "subatomic ultra-fast Java" as it is about the promise that Quarkus brings to Enterprise.

How Quarkus combines imperative and reactive programming

Java and the JVM are still extremely popular, but when working with serverless technologies and cloud-based microservices, Java and other languages ​​for the JVM are being used less and less because they take up too much memory space and are too slow to load, which makes them poorly suited for use with short-lived containers. Fortunately, this situation is now starting to change thanks to Quarkus.

Ultra-fast subatomic Java has reached a new level!

42 releases, 8 months of community work, and 177 amazing developers, all of which resulted in a release in November 2019 Quarkus 1.0, a release that marks an important milestone in the development of the project and offers a ton of cool features and functionality (you can read more about them in announcement).

Today we will show you how Quarkus combines imperative and reactive programming models based on a single reactive core. We'll start with a brief history, and then we'll go into detail about what Quarkus jet core dualism is and how Java-developers can take advantage of these benefits.

Microservices, event-driven architectures ΠΈ serverless-functions - all this today, as they say, is on the rise. Recently, creating cloud-based architectures has become much easier and more accessible, but problems remain - especially for Java developers. For example, in the case of serverless functions and microservices, there is an urgent need to reduce startup time, reduce memory consumption, and still make their development more convenient and enjoyable. Java has made several improvements in recent years, such as improved ergonomics functionality for containers, and so on. However, getting Java to work properly in a container is still not easy. So we'll start by looking at some of Java's inherent complexities that are especially poignant when developing container-oriented Java applications.

First, let's look at history.

How Quarkus combines imperative and reactive programming

Streams and Containers

Starting with version 8u131, Java has more or less supported containers through improvements to the ergonomics functionality. In particular, the JVM now knows how many processor cores it is running on and can set up thread pools accordingly - typically fork/join pools. Of course, this is great, but let's say we have a traditional web application that uses HTTP servlets and runs on Tomcat, Jetty, and so on. As a result, this application will give each request a separate thread and allow it to block this thread while waiting for I / O operations, for example, when accessing a database, files, or other services. That is, the size of such an application does not depend on the number of available cores, but on the number of simultaneous requests. In addition, this means that quotas or limits in Kubernetes on the number of cores will not help much here, and the matter will eventually end up in throttling.

Memory exhaustion

Streams are memory. And in-container memory restrictions are by no means a panacea. Just start increasing the number of applications and threads, and sooner or later you will encounter a critical increase in switching frequency and, as a result, performance degradation. Also, if your application uses traditional microservice frameworks or connects to a database or uses caching or otherwise wastes memory, you obviously need a tool that allows you to look inside the JVM and see how it manages memory without killing the JVM itself (for example, XX:+UseCGroupMemoryLimitForHeap). And even though, since Java 9, the JVM has learned to accept cgroups and adapt accordingly, reserving and managing memory remains a rather complicated matter.

Quotas and limits

Java 11 introduced support for CPU quotas (like PreferContainerQuotaForCPUCount). Kubernetes also offers support for limits and quotas. Yes, it all makes sense, but if the application again goes beyond the allocated quota, we again come to the conclusion that the size - as in the case of traditional Java applications - is determined by the number of cores and with the allocation of a separate thread for each request, then there is little sense in all this.
In addition, using quotas and limits or using the scale-out features of the platform underlying Kubernetes does not solve the problem by itself. We simply spend more resources on solving the original problem, or end up with resource overruns. And if it's a heavily loaded system in a public public cloud, we almost certainly end up using more resources than we really need.

And what to do with all this?

In a simple way, then use asynchronous and non-blocking I / O libraries and frameworks like Netty, Vert.x or Akka. They are much better suited to work in containers due to their reactive nature. Thanks to non-blocking I/O, the same thread can process multiple simultaneous requests at once. While one request is waiting for I/O results, the thread processing it is released and taken on another request. And when the I/O results finally arrive, processing of the first request continues. By interleaving request processing within the same thread, you can reduce the total number of threads and reduce the overhead of request processing.

With non-blocking I/O, the number of cores becomes a key parameter, since it determines the number of I/O threads that can be executed in parallel. When used correctly, this allows you to effectively distribute the load between the cores and handle higher loads with fewer resources.

How is that all?

No, there is more. Reactive programming helps you make better use of resources, but it also comes at a price. In particular, the code will have to be rewritten to be non-blocking and avoid blocking I/O streams. And this is a completely different model of development and execution. And although there are a lot of useful libraries here, it is still a radical change in the usual way of thinking.

First, you need to learn how to write code that runs asynchronously. As soon as you start using non-blocking I/O, you need to explicitly state what should happen when a response to a request is received. Just block and wait no more. Instead, you can pass callbacks, use reactive programming, or use continuations. But that's not all: to use non-blocking I/O, you need both non-blocking servers and clients, and preferably everywhere. In the case of HTTP, everything is simple, but there is also a database, and file systems, and much more.

And although total end-to-end reactivity gives maximum efficiency, such a shift can be difficult to digest in practice. Therefore, the ability to combine reactive and imperative code becomes a necessary condition in order to:

  1. Efficient use of resources in the most loaded areas of the software system;
  2. Use simpler style code in other parts of it.

Introducing Quarkus

Actually, this is the essence of Quarkus - to combine the reactive and imperative models within the same runtime environment.

At the heart of Quarkus are Vert.x and Netty, on top of which there are a number of reactive frameworks and extensions designed to help the developer. Quarkus is designed to build not only HTTP microservices, but also event-driven architectures. Due to its reactive nature, it works very effectively with messaging systems (Apache Kafka, AMQP, etc).

The trick is how to use the same reactive engine for both imperative and reactive code.

How Quarkus combines imperative and reactive programming

Quarkus does this brilliantly. The choice between imperative and reactive is obvious - use the reactive kernel for both. And what it helps a lot with is fast non-blocking code that processes almost everything that goes through the event-loop thread (aka IO thread). But if you have classic REST or client-side applications, Quarkus has an imperative programming model at the ready. For example, HTTP support in Quarkus is based on the use of a non-blocking and reactive engine (Eclipse Vert.x and Netty). All HTTP requests received by your application first pass through the event loop (IO Thread) and then are sent to the part of the code that manages the requests. Depending on the destination, the request control code can be called within a separate thread (the so-called worker thread, used in the case of servlets and Jax-RS) or use the original I/O thread (the reactive route).

How Quarkus combines imperative and reactive programming

Messaging connectors use non-blocking clients running on top of the Vert.x engine. Therefore, you can efficiently send, receive, and process messages from messaging middleware class systems.

The site Quarkus.io We've put together a few good tutorials to help you get started with Quarkus:

In addition, we have prepared online practical lessons to introduce various aspects of reactive programming, and all you need to go through them is a browser, no IDE is required for this, and a computer is not required. You can find these tutorials here.

Useful resources

10 Quarkus video tutorials to get you started

As they write on the site Quarkus.io, quarkus - is KubernetesJava-oriented stack tuned for GraalVM and OpenJDK HotSpot and assembled from the best Java libraries and standards.

To help you understand the topic, we have selected 10 video tutorials that highlight various aspects of Quarkus and examples of its use:

1. Introducing Quarkus: Next-Generation Java Framework for Kubernetes

Authors: Thomas Qvarnstrom and Jason Greene
The goal of the Quarkus project is to create a Java platform for Kubernetes and serverless environments, and to combine reactive and imperative programming models within a single runtime environment so that developers can flexibly vary their approach when working with a wide range of distributed application architectures. Learn more from the introductory lecture below.

2. Quarkus: Superfast Subatomic Java

Author: Burr Sutter
A video tutorial from the DevNation Live online lecture demonstrates how to use Quarkus to optimize enterprise Java applications, APIs, microservices, and serverless functions in a Kubernetes/OpenShift environment, making them much smaller, faster, and more scalable.

3. Quarkus and GraalVM: overclocking Hibernate to super speeds and shrinking it to subatomic size

Author: Sanne Grinovero
From the presentation, you will learn how Quarkus appeared, how it works, and how it allows you to make complex libraries like Hibernate ORM compatible with GraalVM native images.

4. Learning to develop serverless applications

Author: Martin Luther
The video below shows how to create a simple Java application using Quarkus and deploy it as a serverless application on Knative.

5. Quarkus: Code for fun

Author: Edson Yanaga
A video guide to building your first Quarkus project to see why Quarkus is winning the hearts of developers.

6. Java and containers - what will be their joint future

Author: Mark Little
This presentation introduces the history of Java and explains why Quarkus is the future of Java.

7. Quarkus: Superfast Subatomic Java

Author: Dimitris Andreadis
An overview of Quarkus' developer-acclaimed benefits: simplicity, ultra-fast speeds, best libraries and standards.

8. Quarkus and subatomic jet systems

Author: Clement Escoffier
By integrating with GraalVM, Quarkus provides an ultra-fast development experience and a subatomic execution environment. The author talks about the reactive side of Quarkus and how to use it to build reactive and streaming applications.

9. Quarkus and rapid application development in Eclipse MicroProfile

Author: John Clingan
By combining Eclipse MicroProfile and Quarkus, developers can create full-featured MicroProfile containerized applications that launch in as little as tens of milliseconds. The video goes into detail on how to code a containerized MicroProfile application for deployment on the Kubernetes platform.

10. Java Turbo Version

Author: Marcus Biel
The author shows you how to use Quarkus to create super small and super fast Java containers that allow for real breakthroughs, especially in serverless environments.



Source: habr.com

Add a comment