Statistics
5
Views
1
Downloads
0
Donations
Support
Share
Uploader

高宏飞

Shared on 2026-04-29
Support Statistics
¥.00 · 0times
Text Preview (First 20 pages)
Registered users can read the full content for free

Register as a Gaohf Library member to read the complete e-book online for free and enjoy a better reading experience.

(This page has no text content)
Quarkus for Spring Developers | 2 Contents 05 Foreword 06 Acknowledgments 08 Chapter 1–Introducing Quarkus Brief History of Java Introduction of Spring Emergence of Microservices Spring Boot Challenges of Microservices Challenge #1: Composition of Distributed Applications Challenge #2: Infrastructure and Operations Quarkus: The Solution to Today’s Challenges Container First Built on Standards Developer Productivity and Joy Unifying Reactive and Imperative Summary 19 Chapter 2–Getting Started with Quarkus Prerequisites IDEs Extensions Spring API Extensions Creating a New Project Generating a Project Project Structure Controlling Dependency Versions Quarkus Dev Mode and Live Coding Dev UI Configuration Single Property Values Type-Safe Configuration Profiles Dependency Injection Scopes Example Lifecycle Callbacks Native Image Resident Set Size
Quarkus for Spring Developers | 3 Contents Testing Continuous Testing Unit Testing Native Image Testing Summary 45 Chapter 3–RESTful Applications Underlying Runtime Reactive Libraries HTTP Method Routing HTTP Requests Building RESTful Endpoints RESTful Endpoint Class Structure RESTful Endpoint Examples Exception Handling Testing RESTful Endpoints RESTful Endpoint Test Class Structure RESTful Endpoint Test Examples Testing Exception Handling Server-Sent Event Endpoints Testing Server-Sent Event Endpoints OpenAPI Documentation Summary 71 Chapter 4–Persistence Evolution of the Java Persistence API JPA Abstractions Spring Data JPA Quarkus Hibernate with Panache JPA in Action JPA Testing Reactive Data Access Spring Data R2DBC Quarkus Hibernate Reactive with Panache Reactive Data Access in Action Reactive Data Access Testing Summary
Quarkus for Spring Developers | 4 Contents 98 Chapter 5–Event-Driven Services Event Message Handling Spring Events and Integration Quarkus Event Bus Event Message Handling in Action Testing Event Message Handling Reactive Messaging and Streams Reactive Messaging and Streams in Action Reactive Messaging and Streams Testing Knative Events Binding Knative Events Source and Sink Patterns Knative Events Binding in Action Knative Events Binding Testing Deploying Knative Events to Kubernetes Enabling Knative Events to Kafka Event Source Summary 122 Chapter 6–Building Applications for the Cloud Prerequisites Preparing Your Application for the Cloud Building a Container Image Generation of Kubernetes Manifests Deployment Routing Health Checks Service Binding Remote Coding and Debugging Remote Live Coding Remote Debugging Configuration Management Environment Variables ConfigMaps and Secrets Spring Cloud Config Monitoring Metrics Distributed Tracing and Logs Summary 147 Appendix 149 About the Authors
Quarkus for Spring Developers | 5 Foreword The year is 2021, and for the 21st year in a row, the Java ecosystem once more is confronted with the “Java is Dead” meme. This time, industry observers state that the Java ecosystem will not pivot to efficient runtimes and frameworks for microservices deployed on resource-constrained containers. Naturally, the Java community has ignored the meme and responded with a wave of new runtimes and frameworks. One of the firm leaders in this new wave of “runtime plus framework” stacks is Quarkus. Quarkus combines standardized enterprise Java APIs (from Java Enterprise Edition, Jakarta Enterprise Edition, and MicroProfile) to build your services and then run as a tightly packed, precompiled, and resource-efficient native image (via GraalVM). An example of this is Eclipse Adoptium (previously AdoptOpenJDK). We moved our API to use Quarkus, and it’s been brilliant both in developer productivity and performance, serving up to 300 million requests in the past two years from a single small instance. Given the title of the book, and especially if you’re a Spring develop- er, I suspect you might be thinking “marketing hype” right about now, right? For me, the paradigm that Quarkus brings is a major one for Java. This is one of these moments in your career when you should take stock and explore a new technology in-depth! This book makes it easy to do that, providing like-for-like examples of your favorite Spring development patterns mapped to their Quarkus equivalents (don’t panic, there are many similarities) and giving you an in-depth understanding of the fundamentals at play under the hood. — Martijn Verburg (aka “The Diabolocal Developer”) Principal Group Manager (Java), Microsoft Eclipse Adoptium Steering Committee member August 2021
Quarkus for Spring Developers | 6 Acknowledgments Eric Deandrea I’ve spent most of my software development and architecture career building Java ap- plications and application frameworks. Over the last ten years, I have focused on building opinionated frameworks based on Spring and architecting DevOps solutions. My experi- ence with Spring led my employer, Red Hat, to approach me about writing this book. There are many similarities and differences between Quarkus and Spring. When getting started with Quarkus, I found many Quarkus books, guides, and tutorials, but not many explicitly tailored to developers familiar with Spring concepts, constructs, and con- ventions. I was excited to use my Spring experience to help showcase and explain the benefits of Quarkus while also showing, through examples, that Spring developers can learn and understand Quarkus with relatively little effort. Still, I was somewhat terrified at the notion of writing a book. I had never written anything more than a blog post or short article before. I didn’t even know how to get started. Luckily, I work with many talented people who were there to help along the way. Each and every one of the following people played a key role. This book would not have been possible without the contributions of each person. First, thank you, Syed M. Shaaf, for getting me involved in this project and helping me get started. You helped me get organized and develop a table of contents. The initial table of contents was aggressive and needed to be scaled back, but it helped drive the overall topics and structure that we wanted to cover in this book. A big thank you is also needed for helping with decisions around publisher selection. Thank you, Daniel Oh, for your initial reviews of the chapters I authored. You were always there to help me with ideas in the writing and the examples, as well as for your help testing all the examples. A huge thank you for authoring Chapter 5: Event-Driven Services. The book would not have been a success without your time and contributions. Thank you, Charles Moulliard, for your countless hours of time and effort reviewing chapters for technical accuracy and suggesting improvements. Thank you as well for organizing reviews of all the example projects. A huge thank you for authoring Chapter 6: Building Applications for the Cloud. I don’t know what I would have done without your involvement and dedication to this project, especially since all of your contributions fall outside your daily responsibilities. Your time and effort are very much appreciated! Thank you, Georgios Andrianakis, for your help reviewing the examples used in the chapters and pushing raised GitHub issues and pull requests through the process. Thank you as well for being a sounding board for ideas and questions. I really appreciate you dealing with my constant nagging, even when it was late at night for you. Thank you, Aurea Munoz and Gytis Trikleris, for your help reviewing and improving the quality of the examples used in the chapters. A fresh set of eyes is always a good thing. Your contributions made the examples clear and easy to follow. Thank you, Clement Escoffier and Stephane Epardaud, for your help reviewing and improving Chapter 4. I am extremely grateful that you were willing and able to step up and help with something that was outside your normal activities.
Quarkus for Spring Developers | 7 Acknowledgments Thank you, Thomas Qvarnström, for all your support in helping to find reviewers and the fielding of questions. This was a long project, and I could always count on you to assist in whatever capacity was needed. Thank you, Martijn Verburg, for graciously agreeing to write the foreword for the book. Having someone of your stature within the community embracing Quarkus says a lot about where Quarkus stands today. Thank you to the Red Hat Developer team for all of your help and support, from ideation to the production of this finished product. Thank you, Colleen Lobner, for managing the process so smoothly, and thank you, Andy Oram, for the hours of editing and help to organize the content into its current form. The book would not have been a success without both of you. Finally, thank you to my wife, Rachel, my daughter, Emily, and my son, Ethan, for supporting me throughout this process. You might not understand what I wrote, but you were always there to listen to me talk about it. You were excited when I was excited, which helped drive me to the finish line. I couldn’t have done it without your loving support. Daniel Oh I would like to first and foremost thank Eric Deandrea for allowing me to write Chapter 5 and review the other chapters. This opportunity enabled me to take many technical deep-dives into the Spring and Quarkus frameworks. I would also like to thank my lov- ing and patient wife and kids for their continued support, patience, and encouragement throughout the writing of this book. Charles Moulliard At the beginning of this book’s adventure, Eric Deandrea and Syed Shaaf contacted me to review the book content and examples. Ultimately they convinced me to write the cloud material in Chapter 6. This has been a fantastic experience. I would like to thank them for giving me this opportunity, as speaking about the cloud is not the easiest task, nor is writing a chapter of a book as a non-native English speaker. Thank you to the Snowdrop team and my associates, who took the time to play with the different examples and made proposals to improve the code and wording. Special thanks to Aurea Munoz Hernandez and Georgios Andrianakis for their support and encouragement. Thank you, Ioannis Canellos, for quickly releasing a new version of Dekorate to fix a blocking problem discovered when a change was made by Kubernetes to move the API of the Ingress resource. Thank you, Dimitri and Bruno, for giving me as a manager the time needed to be part of this project and to accept some delays when my weekly reports were not ready on time.
Quarkus for Spring Developers | 8 As interest grows in microservices and containers, Java developers have been struggling to make applications smaller and faster to meet today’s requirements. In the modern computing environment, applications must respond to requests quickly and efficiently, be suitable for running in volatile environments such as virtual machines or containers, and support rapid development. Java, as well as popular Java runtimes, are sometimes considered inferior to runtimes in other languages such as Node.js and Go. But with addi- tions to the Java frameworks over the past few years, Java can proudly retake its role as the primary language for enterprise applications. One of these boundary-pushing frameworks is Quarkus, an open source project introduced by Red Hat, which has taken the Java community by storm. Quarkus combines developer productivity and joy similar to Node.js with the speed and performance of Go, enabling Java developers to build solutions targeting modern platforms and architectures. This chapter will introduce Quarkus and highlight the key drivers behind its creation. Because many Java developers already know Spring and see Quarkus as an alternative, we’ll showcase fundamental differences between Quarkus and Spring while also high- lighting similarities. These differences make Quarkus an ideal runtime for Java applica- tions that target modern platforms and architectures, such as microservices architec- tures, event-driven architectures, serverless, functions-as-a-service, edge computing, and IoT. The remaining chapters will help developers familiar with Spring Framework learn how to do familiar things with Quarkus while highlighting key fundamental differences. Brief History of Java In today’s world, application architects and developers have many technology choices to solve a business or technical problem. Java remains one of the most widely used programming languages to build applications today. Within Java, there are many tools and frameworks to help developers build applications. Java was created when the cloud, containers, and container orchestration systems such as Kubernetes [1.1] did not exist. The historical Java stack consisted of applications deployed into Java application servers. In that architecture, each application server hosted multiple applications and provided additional services, such as transaction management, caching, connection pooling, and more. These application servers focused on providing the Java 2 Enterprise Edition (J2EE) Specification implementations, many of which developers deemed “heavyweight.” These application servers took minutes to start up and consumed large amounts of memory, while their response times were measured in seconds. Some time later, other application servers supporting a smaller subset of the J2EE specification emerged. Apache Tomcat, one of the most established open source projects and web containers, catered to web applications not requiring the full J2EE specification. These web applications still needed to deliver the dynamic web capabili- ties made possible by the Java Servlet API and Java Server Pages (JSP) specifications, part of the larger J2EE specification. These runtimes became increasingly popular because they required fewer system resources to run. The past 15-20 years have seen many optimizations to the Java application stack and the Java Virtual Machine (JVM) to support running large heaps and highly dynamic frame- works that make decisions at runtime, particularly at application startup. Chapter 1 Introducing Quarkus Eric Deandrea Note: We use the general term “Spring” to cover Spring Framework and any other modules within the Spring ecosystem, including Spring Boot. Source code for all examples used throughout this book is located at https:// github.com/quarkus-for- spring- developers/ examples. This book was written using Quarkus version 2.1.4.Final and Spring Boot version 2.5.4.
Chapter 1 – Introducing Quarkus Quarkus for Spring Developers | 9 Introduction of Spring Spring was introduced to the open source community in 2003. At that time, the J2EE specifications moved rather slowly, sometimes with years in between releases. Devel- opers needed a faster release cadence for features. Spring brought in another popular trend where developers could build the same enterprise applications using a light- er-weight stack based on “plain old Java objects” (POJOs) while still complimenting some of the J2EE specifications. Such a stack did not require the overhead and complexity of many of the J2EE specifi- cations, such as Enterprise Java Beans (EJBs) or Message Driven Beans (MDBs). This stack made Spring Framework an ideal choice for building “lighter” applications. These applications did not require the same amount of memory and CPU resources needed by a Java application server because they could be deployed into any runtime support- ing the Servlet specification. For many years after, Spring continued to evolve and grow, providing new features that supported and simplified capabilities such as access to a database, asynchronous mes- saging, authentication and authorization of users, and web services. Many architects and developers have promoted Spring as arguably one of the most popular frameworks to build applications. Emergence of Microservices Over time, large, monolithic applications became harder to scale. Many teams needed to work in parallel on the same project in the same source code repository. Any change to one part of an application affected other developers working on other parts of the application, while also forcing testing and redeployment of the entire application. A sin- gle application deployment may have consisted of tens of servers and required hours of offline maintenance. The entire application needed additional instances deployed to handle increased loads that might affect only a subset of its capabilities. Development teams were also becoming more agile, wanting to deliver business capa- bilities faster. Gone were the days of waterfall design, where an entire system needed to be designed before a line of code was written. Agile development helps enable teams to deliver smaller subsets of business capabilities faster. It also allows for smaller development teams, where each team can focus on a small subset of the capabilities an overall system provides. These technical and nontechnical issues partly influenced the rise of microservices architectures. A microservices architecture brings together many small applications to form a larger system, communicating using HTTP, TLS, or asynchronous messaging. A microservices architecture allows for the decoupling of functionality around specific business capa- bilities, where each capability can be owned, maintained, tested, and deployed inde- pendently. Additionally, microservices can more efficiently consume the available CPU and memory, which is not always possible in a monolithic application. A single business capability can scale independently of other capabilities. Spring Boot The shift towards microservices also led to the introduction of Spring Boot in 2014. One of the most powerful features of Spring Boot was its ability to package all the needed libraries within a single JAR file. This feature allowed quick bootstrapping of an application without deploying it into a separate Servlet container. Instead, Spring Boot embeds a web container (i.e., Apache Tomcat, Eclipse Jetty, or Undertow) inside the
Chapter 1 – Introducing Quarkus Quarkus for Spring Developers | 10 application’s JAR. All that was required was a JVM. This packaging mechanism helped reduce the operational overhead of installing and configuring a Java application server. Coupled with this self-encapsulated runtime was the standardization of configuration across all of the underlying Spring modules. Additionally, Spring Boot gave developers and operations teams a standard convention for providing application configuration when moving from one environment to another, such as from integration testing (IT) to quality assurance (QA) to production. Challenges of Microservices A microservices architecture, while solving some challenges, introduces others. Micro- services create complexity for developers and operations teams alike. The distributed architecture gives rise to challenges that need to be addressed. Microservices are also harder to operate efficiently. Challenge #1: Composition of Distributed Applications The composition of distributed applications communicating with each other introduc- es a new set of challenges. New and modern applications need to be designed around scalable distributed patterns to deal with these challenges. Users today expect near-instant response times and 100% uptime. The proliferation of distributed appli- cations communicating amongst each other to fulfill a user’s request goes directly against these expectations. A set of design principles, known as Reactive Systems or Reactive Principles, attempt to address some of these challenges. Reactive systems Reactive Systems are flexible, loosely-coupled, scalable, and highly responsive, making them more maintainable and extensible. Reactive Systems are also resilient when faced with failure. The Reactive Manifesto [1.2] was introduced in 2014 as a set of guidelines and principles for building Reactive Systems. It defines Reactive Systems as: 1. Responsive - The system responds in a timely manner if at all possible. Respon- siveness is the cornerstone of usability and utility, but more than that, responsive- ness means that problems may be detected quickly and dealt with effectively. Responsive systems focus on providing rapid and consistent response times, establishing reliable upper bounds so they deliver a consistent quality of service. This consistent behaviour in turn simplifies error handling, builds end user confi- dence, and encourages further interaction. 2. Resilient - The system stays responsive in the face of failure. This applies not only to highly-available, mission-critical systems — any system that is not resilient will be unresponsive after a failure. Resilience is achieved by replication, containment, isolation and delegation. Failures are contained within each component, isolating components from each other and thereby ensuring that parts of the system can fail and recover without compromising the system as a whole. Recovery of each component is delegated to another (external) component and high-availability is ensured by replication where necessary. The client of a component is not bur- dened with handling its failures. 3. Elastic - The system stays responsive under varying workload. Reactive Systems can react to changes in the input rate by increasing or decreasing the resources allocated to service these inputs. This implies designs that have no contention points or central bottlenecks, resulting in the ability to shard or replicate components and distribute inputs among them. Reactive Systems support predictive, as well as Reactive, scaling
Chapter 1 – Introducing Quarkus Quarkus for Spring Developers | 11 algorithms by providing relevant live performance measures. They achieve elasticity in a cost-effective way on commodity hardware and software platforms. 4. Message Driven - Reactive Systems rely on asynchronous message-passing to establish a boundary between components that ensures loose coupling, isolation and location transparency. This boundary also provides the means to delegate failures as messages. Employing explicit message-passing enables load manage- ment, elasticity, and flow control by shaping and monitoring the message queues in the system and applying back-pressure when necessary. Location transparent messaging as a means of communication makes it possible for the management of failure to work with the same constructs and semantics across a cluster or with- in a single host. Non-blocking communication allows recipients to only consume resources while active, leading to less system overhead. Following the reactive guidelines in each application helps the system as a whole, since the entire system is composed of all of the applications within the system. Reactive applications can scale much more efficiently than blocking applications. As load increases on a blocking application, so does the amount of memory and CPU resources needed by the application. More details about imperative/blocking and reactive/non-blocking can be found in Appendix section A.1. Challenge #2: Infrastructure and Operations Another major challenge in a microservices architecture is managing an increased number of applications: there are more applications to build, deploy, and manage. The increase in the number of applications increases the pressure on the system as a whole. Each application in the system therefore must optimize compute and memory resources. Reduction in boot-up time for applications is also more of a concern than ever before. Organizations continue to optimize their existing infrastructure to sup- port these microservices while utilizing on-premise virtualization and cloud resources to reduce operating costs. Optimization of resources is not the only problem operations teams face. Many addi- tional concerns arise when running applications on a bare-metal server or in a virtual machine (VM). Each server or VM needs: • To have a supported operating system installed. • To be patched with updates continually. • Installation and continuous maintenance for appropriate tools, libraries, and appli- cation runtimes, such as a JVM or other operating-system-level libraries. How do operations teams manage multiple applications sharing the same system resources? What happens when updating the version of a shared library? An update to a shared library could affect all running applications. Furthermore, if one application misbehaves and consumes all available system resources, the other applications can become starved, potentially causing a crash. A microservices architecture exacerbates these challenges further. Containers to the Rescue Adopting a containerized runtime can help address many of these challenges by allowing isolation between applications. A container host can place resource restrictions on the running containers, ensuring that one container cannot consume all the resources avail- able on a system for other containers running on the same host. All of an application’s required libraries and dependencies and their configurations are encapsulated inside a container. This encapsulation eliminates the need for these depen-
Chapter 1 – Introducing Quarkus Quarkus for Spring Developers | 12 dencies to be installed, maintained, and configured on the host, allowing the application to provide everything it needs to operate. Additionally, container images are portable, deployable on multiple hosts without all the necessary setup and configuration. Deploying a new version of an application may imply changes to the application itself or other pieces of the application runtime environment, such as the JVM or any of its required runtime dependencies. This process is greatly simplified when using container images, as everything is packaged within a versioned container image. This versioning also reinforces the concept of immutability: Container images should not be modified at runtime. If a running container instance is modified and the con- tainer is subsequently restarted, all changes made are lost. Each instance of the same version of a container image should be the same as every other instance. Containers on their own aren’t a complete solution to all problems. Containers intro- duce additional concerns, such as operating and managing many containers at scale across many environments, both on-premises and in the cloud. Kubernetes, made open source in 2015 by Google, has become an industry-standard container orches- tration platform for automating deployment, scaling, and operations of application containers across clusters of hosts. Challenges of Java in containers Some challenges of Java in the cloud, container, and Kubernetes world stem from Java’s tendency to consume lots of memory and to suffer slower startup times than other languages and runtimes. The immutable nature of containers forces developers to rethink the design and maintenance of Java applications. Highly reconfigurable and long-running applications with large heap sizes are no longer necessary. Moreover, essential parts of an applica- tion, such as the database driver or logging framework, are known in advance. In such situations, it is unnecessary to use frameworks that rely heavily on Java reflection [1.3]. Instead, application boot times can be accelerated and runtime footprints reduced by performing as much work at build time as possible. At their core, many Java-based frameworks embrace the dynamic flexibility Java pro- vides. A lot happens in the first few seconds of Java application startup. For example, a Spring application can alter its runtime behavior by changing a few configuration properties. The dynamic runtime binding allowed by Java enables this adaptability. How does a Java-based framework optimize itself to cater to today’s reality? These frameworks need to rethink their underlying architectures. It is certainly not an easy task, considering the number of applications built on these frameworks and the inability to introduce breaking changes. Quarkus: The Solution to Today’s Challenges The Java language and the Java platform have been very successful over the years, preserving Java as the predominant language in current use. Analysts have estimated the global application server market size at $15.84 billion in 2020, with expectations of growing at a rate of 13.2% from 2021 to 2028 [1.4]. Additionally, tens of millions of Java developers worldwide work for organizations that run their businesses using Java. Faced with today’s challenges, these organizations need to adapt and adopt new ways of building and deploying applications. Forgoing Java for other application stacks isn’t a choice for many organizations, as it would involve retraining their devel- opment staff and reimplementing processes to release and monitor applications in production. Reflection in Java allows the application, at runtime, to inspect, manipulate, invoke, and even create new classes, interfaces, constructors, and methods. It also allows an ap- plication to invoke methods at runtime regardless of the access specifier (i.e., pub- lic/private/ protected) used. Reflection contributes to startup time and mem- ory usage because these inspections and invoca- tions are being performed at runtime rather than at compile time. All the classes’ metadata is retained in the JVM for the application’s lifetime.
Chapter 1 – Introducing Quarkus Quarkus for Spring Developers | 13 Something needed to be done to accommodate rapid development, microservices architectures, and container runtimes, given Java’s market size and the number of developers and organizations invested in Java. In late 2017, the previously listed challenges prompted Red Hat, a recognized leader in the open source community, to rethink how Java applications should be developed and deployed in today’s cloud and containerized world. Red Hat’s goal was to design a framework centered on Java but suited to today’s operational concerns while not forcing developers to learn a completely new framework. In March of 2019, after more than a year of internal development, Quarkus was introduced to the open source community. Quarkus is a Java framework tailored for OpenJDK HotSpot and GraalVM [1.5], boasting extremely fast boot times and low memory utilization. Many organizations since the initial release have seen the immediate value of Quarkus and joined the development effort. Quarkus also offers near-instant scale-up and high-density utilization in container or- chestration platforms such as Kubernetes. Many more application instances can be run within the same hardware resources used by previous applications. After its initial debut, Quarkus underwent many enhancements over the next few months, culminating in a 1.0 release within the open source community in October 2019. As a new framework, Quarkus doesn’t need to attempt to retrofit new patterns and principles into an existing codebase. Instead, it can focus on innovation. That innovation continues even today, with a 2.0 community release in June 2021. Red Hat offers enterprise support for Quarkus. Visit https://access.redhat.com/ products/quarkus for more information. Container First From the beginning, Quarkus was designed around container-first and Kubernetes- native philosophies, optimizing for low memory usage and fast startup times. As much processing as possible is done at build time, including taking a closed world assumption approach to building and running applications. This optimization means that, in most cases, the resulting JVM contains only code that has an execution path at runtime. In Quarkus, classes used only at application startup are invoked at build time and not loaded into the runtime JVM. Quarkus also avoids reflection as much as possible, instead favoring static class binding. These design principles reduce the size, and ultimately the memory footprint, of the application running on the JVM while also enabling Quarkus to be “natively native.” Quarkus’s design accounted for native compilation from the onset. It was optimized for using the native image capability of GraalVM [1.8] to compile JVM bytecode to a native machine binary. GraalVM aggressively removes any unreachable code found within the application’s source code as well as any of its dependencies. Combined with Linux containers and Kubernetes, a Quarkus application runs as a native Linux executable, eliminating the JVM. A Quarkus native executable starts much faster and uses far less memory than a traditional JVM. Similar native image capabilities in Spring are still considered experimental or beta as of this writing. Spring’s effort to support native compilation doesn’t provide all the same compile-time optimizations and design choices that make Quarkus extremely fast and memory-efficient when running on the JVM or within a native image. GraalVM is a Java Virtual Machine for compiling and running applications written in different languages to a native machine binary. There are three distributions of GraalVM: GraalVM Commu- nity Edition (CE), GraalVM Enterprise Edition (EE), and Mandrel. GraalVM CE and EE have varying support and licensing requirements [1.6]. Mandrel [1.7] is a downstream distribution of GraalVM CE, supporting the same capa- bilities to build native execut- ables but based on the open source OpenJDK. Mandrel makes GraalVM easy to consume by Quarkus appli- cations by including only the GraalVM CE components that Quarkus needs. Red Hat offers commercial support for using Mandrel to build native Quarkus applications since the Quarkus 1.7 release in October 2020.
Chapter 1 – Introducing Quarkus Quarkus for Spring Developers | 14 As Figure 1.1 shows, Quarkus boasts incredibly fast boot times coupled with extremely low resident set size (RSS) memory usage. It is an optimal choice for building highly scalable applications that require low CPU and memory resources. IDC performed a study [1.9] confirming that Quarkus can save as much as 64% of cloud resources. Coupled with a runtime platform like Kubernetes, more Quarkus applications can be deployed within a given set of resources than other Java applications. These applications can be tradi- tional web applications, serverless applications, or even functions as a service. Figure 1.1: Quarkus RSS and times to first response. Built on Standards Quarkus rests on a vast ecosystem of technologies, standards, libraries, and APIs. Developers don’t have to spend lots of time learning an entirely new set of APIs and technologies to take advantage of the benefits Quarkus brings to the JVM or native images. Among the specifications and techno logies underlying Quarkus are Eclipse MicroProfile, Eclipse Vert.x, Contexts & Dependency Injection (CDI), Jakarta RESTful Web Services (JAX-RS), the Java Persistence API (JPA), the Java Transaction API (JTA), Apache Camel, and Hibernate, just to name a few. Quarkus is also an ahead-of-time compilation (AOT) platform, optimizing code for the JVM as well as compiling to native code for improved performance. All of the underlying technologies are AOT-enabled, and Quarkus continually incorporates new AOT-enabled technologies, standards, and libraries. All the technologies and capabilities needed for building microservices are available and optimized for Quarkus. These technologies, such as Keycloak for authentica- tion and authorization, or Infinispan for distributed caching, take advantage of all the compile-time and runtime optimizations that have already been discussed. The tech- nologies are optimized for use within Quarkus, regardless of whether they run in the HotSpot JVM or as a native image. Third-party framework and library developers can take advantage of this AOT platform to optimize them for Quarkus. Eclipse MicroProfile Eclipse MicroProfile [1.10] is an open source community specification for Enterprise Java microservices. It brings together a community of individuals, organizations, and vendors collaborating to iterate and innovate in short cycles to propose new standard APIs and functionality around capabilities that Java microservices need. Some of the organizations contributing to the MicroProfile specification process include Red Hat, IBM, Payara, Tomitribe, Oracle, Microsoft, Fujitsu, and Lightbend.
Chapter 1 – Introducing Quarkus Quarkus for Spring Developers | 15 MicroProfile also incorporates concepts and efforts from other communities, such as Istio [1.11] and Netflix OSS [1.12]. Some MicroProfile capabilities [1.13] include application-level metrics, health checks, distributed tracing, standardized external configuration, fault tolerance, JSON Web Token (JWT) propagation, OpenAPI integration, and CDI. MicroProfile is different from Java Enterprise Edition (Java EE) and Jakarta EE because it doesn’t require a Java EE application server. Additionally, because MicroProfile is not a monolithic architecture, it allows developers to start small and build a minimal micros- ervice or cut down existing Java EE applications into core components. Each core com- ponent may become its own microservice. This approach can be a useful modernization approach when moving from a monolithic architecture to microservices. The MicroProfile specification process is lightweight to facilitate faster innovation with- in the community and contains no reference implementation. It is up to the community to provide implementations of the MicroProfile specification. Quarkus, Wildfly, Red Hat JBoss Enterprise Application Platform (via expansion packs), Open Liberty, Payara Micro, and Apache TomEE are some of the current MicroProfile implementations. Various modules within the Spring and Spring Cloud ecosystems also provide many ca- pabilities similar to those within the MicroProfile specification. These modules evolved because developers wanted higher-level abstractions than the ones provided in the different Java EE specifications. Developers familiar with these Spring modules will find many similarities in the Eclipse MicroProfile specifications. Developer Productivity and Joy Quarkus focuses on more than just delivering sets of features; every feature should be simple, have little-to-no configuration, and be as intuitive as possible to use. It should be trivial to do trivial things while relatively easy to do more complex things, allowing developers more time to focus on their domain expertise and business logic. One major productivity issue that most Java developers face today is the traditional Java development workflow. For most developers, this process looks like Figure 1.2. Figure 1.2: Typical developer workflow.
Chapter 1 – Introducing Quarkus Quarkus for Spring Developers | 16 The issue is that the Compile and Deploy/Run cycles can take time, sometimes up to a minute or more. This delay is wasted time where a developer could be doing something productive. Quarkus’s Live Coding feature solves this issue. Quarkus will automatically detect changes made to Java files, including class or method refactorings, application configuration, static resources, or even classpath dependency changes. When such a change is detected, Quarkus transparently recompiles and redeploys the changes. Quarkus redeployments typically happen in under a second. Using Quarkus, the devel- opment workflow now looks like Figure 1.3. Figure 1.3: Quarkus developer workflow. Quarkus enables this workflow out of the box for Java files, application configu- ration, and static resources. Any changes made are immediately reflected in the running application. Quarkus also provides its Live Coding feature for Quarkus applications running on remote hosts. A developer can connect their local envi- ronment to a remote application and see changes immediately reflected in the remote application. Quarkus’s Dev Mode provides an additional capability, called Dev Services, that will automatically bootstrap various containers an application might need, such as a data- base container or other middleware component, while also setting all of the required configuration properties for running Dev Mode or when running tests. This capability grants developers a tight feedback loop and removes the additional overhead of having to start services manually and provide configurations while quickly iterating on code they’re writing. As of this writing, Quarkus Dev Services supports database containers (discussed in Chapter 4), Apache Kafka (discussed in Chapter 5), OpenID Connect (Keycloak), Advanced Message Queuing Protocol (AMQP), Redis, HashiCorp Vault, and Apicurio Registry. Future Quarkus versions will extend this capa- bility to include other middleware components as well. Additionally, Quarkus takes this concept a step further by introducing continuous testing [1.14] to further facilitate test-driven development. Quarkus understands which tests are affected by classes and methods within the application. As changes are made to the application source code, Quarkus can automatically rerun affected tests in the background, giving developers instant feedback about the code they are writing or modifying. Following the philosophy of simplicity and enhancing developer productivity, building an application into a native image is extremely simple. All the heavy-lifting and integration to consume GraalVM is done for you by the Quarkus build tools via Maven or Gradle. Developers or CI/CD systems simply need to run a build, just like any other Java build, to produce a native executable. Tests can even be run against the built artifact.
Chapter 1 – Introducing Quarkus Quarkus for Spring Developers | 17 Unifying Reactive and Imperative With Spring, a developer needs to decide up front, before writing a line of code, which architecture to follow for an application. This choice determines the entire set of librar- ies that a developer uses in a Spring application. In some cases, these libraries vary de- pending on the chosen architecture. Spring has always remained backward-compatible with all the imperative APIs built over the years. On the reactive side, Spring was able to start fresh while trying to reuse pieces of the framework where it could. As a result, Spring is left trying to maintain two versions of its APIs and its documentation. Quarkus does not have such limitations, since it was born in the reactive era. Quarkus, at its core, is based on a fully reactive and non-blocking architecture pow- ered by Eclipse Vert.x [1.15]. Eclipse Vert.x is a Java toolkit for building extremely re- source-efficient reactive applications on the JVM. Quarkus integrates deeply with Vert.x, allowing developers to utilize both blocking (imperative) and non-blocking (reactive) libraries and APIs. In most cases, developers can use both imperative and reactive APIs within the same classes. Quarkus ensures that the imperative APIs will block appropriately while the reactive APIs remain non-blocking. The Quarkus reactive architecture [1.16] implements a proactor pattern [1.17] that switches to a worker thread when needed. Listing 1.1 illustrates this. Listing 1.1: Unifying imperative and reactive. @Path(“/resources”) public class MyResourceClass { @Inject SayService say; @Inject @Stream(“kafka-topic”) Publisher<String> sseSay; @GET @Path(“/blocking/{name}”) @Produces(MediaType.TEXT_PLAIN) @Blocking public String helloBlocking(@PathParam(“name”) String name) { return say.hello(name); } @GET @Path(“/reactive/{name}”) @Produces(MediaType.TEXT_PLAIN) public Uni<String> helloReactive(@PathParam(“name”) String name) { return Uni.createFrom() .item(name) .onItem().transform(say::hello); } @GET @Path(“/sse”) @Produces(MediaType.SERVER_SENT_EVENTS) public Publisher<String> sseHello() { return sseSay; } } Note: Don’t worry if you are unfamiliar with the code in this listing. It is shown mere- ly to illustrate that you can mix and match blocking and non-blocking operations within a single Java class. Later chapters will dive deeper into these specific topics and the libraries used.
Chapter 1 – Introducing Quarkus Quarkus for Spring Developers | 18 As you can see, the class MyResourceClass contains both blocking and non-blocking methods. The helloBlocking method calls a method on the injected SayService and returns some result as a String. This method blocks until the SayService.hello() method completes. Quarkus ensures that when the method blocks, it is moved off onto another Thread where it can wait for the operation to complete before returning the result on the main application Thread. The helloReactive method returns an asynchronous reactive pipeline that will produce a single String value at some point in the future. Finally, the sseHello method returns an asynchronous pipeline that will emit Strings as server-sent events as new events arrive on the kafka-topic Apache Kafka topic. Summary Java has evolved since its introduction in 1995, but more importantly, the environ- ments that host applications and the applications’ requirements for responsiveness and scaling have evolved as well. Some trade-offs made in Java’s design at the onset affect how Java is perceived today. Things that were important in those days aren’t as important anymore. Quarkus was invented to address today’s challenges and problems, and aims to solve those challenges without forcing developers to learn an entirely new programming language. References [1.1] “Kubernetes”: https://kubernetes.io [1.2] “The Reactive Manifesto,” September 2014: https://www.reactivemanifesto.org [1.3] “Using Java Reflection,” January 1998: https://www.oracle.com/technical-resources/articles/java/javareflection.html [1.4] “Application Server Market Size & Share Report, 2016-2028”: https://www.grandviewresearch.com/industry-analysis/application-server-market [1.5] “GraalVM”: https://www.graalvm.org/docs/introduction [1.6] “GraalVM FAQ”: https://www.graalvm.org/faq [1.7] “Mandrel: A community distribution of GraalVM for the Red Hat build of Quarkus,” June 2020: https://developers.redhat.com/blog/2020/06/05/mandrel-a-com- munity-distribution-of-graalvm-for-the-red-hat-build-of-quarkus [1.8] “GraalVM Native Image”: https://www.graalvm.org/reference-manual/native-image [1.9] Dayaratna, Arnal. “Red Hat Quarkus Lab Validation.” IDC, May 2020: https://red.ht/idc-quarkus-study [1.10] “Eclipse MicroProfile”: https://microprofile.io [1.11] “Istio”: https://istio.io [1.12] “Netflix OSS”: https://github.com/netflix [1.13] “Eclipse MicroProfile Projects”: https://microprofile.io/projects [1.14] “Quarkus 2.0.0.Alpha1: Continuous Testing”: https://quarkus.io/blog/quarkus-2-0-0-final-released/#continuous-testing [1.15] “Eclipse Vert.x”: https://vertx.io [1.16] “Quarkus Reactive Architecture”: https://quarkus.io/version/main/guides/quarkus-reactive-architecture [1.17] “Proactor Pattern”: https://en.wikipedia.org/wiki/Proactor_pattern
Quarkus for Spring Developers | 19 Chapter 2 Getting Started with Quarkus Eric Deandrea The tools available in the Spring ecosystem make it easy for a developer to start building applications. Additionally, Spring developers are familiar with this ecosystem’s many conventions, some of which are very similar in Quarkus. A developer familiar with Spring should quickly be able to become familiar with similar concepts and conventions in Quarkus. This chapter showcases some of the tools in Quarkus, while also explaining the key differences between Quarkus and Spring capabilities. Prerequisites Quarkus requires a setup similar to any other typical Java application: an Integrat- ed Development Environment (IDE), a JDK 11+ distribution, and a build tool such as Apache Maven 3.6.3+ (Maven) or Gradle. Unless otherwise stated in a specific example, all examples in the book and sample repository (https://github.com/quarkus- for-spring- developers/examples) use Java 11 and Apache Maven 3.8.1. The examples use Maven’s wrapper feature, so a preinstalled version of Maven is not required. Each example project contains mvnw scripts for Windows and Unix/Linux/macOS. Building a native executable requires a distribution of GraalVM. Chapter 1 discussed GraalVM and its different distributions. You need to install either a container runtime environment or one of those GraalVM distributions to run the native image examples throughout this book. Additionally, both Quarkus and Spring support using Kotlin as the programming language to build an application. IDEs Quarkus tooling is available for most major IDEs, including Microsoft Visual Studio Code (VSCode), JetBrains IntelliJ IDEA (IntelliJ), and Eclipse, shown in Table 2.1. IDE Quarkus tooling available Microsoft VSCode (and web variants, such as Eclipse Che and Red Hat CodeReady Workspaces) Quarkus Tools for Visual Studio Code [2.1] JetBrains IntelliJ Quarkus Framework support (Ultimate Edition only) [2.2] Quarkus Tools [2.3] (all editions) Eclipse Quarkus Tools (part of JBoss Tools) [2.4] [2.5] Table 2.1: Quarkus tooling for major IDEs. Extensions Spring Boot applications discover various types of annotated classes and methods, known as beans, at runtime. Dependency injection mechanisms inject beans into other beans. The same is true for Quarkus. Quarkus and Spring applications are made up of beans that perform various functions, such as bootstrapping an embedded HTTP serv- er. So where do these beans come from? In Spring, a developer provides some beans as part of the application, but other beans are part of the underlying framework’s Spring Boot Starters [2.6]. Starters contain bean definitions and configurations that are added to the application’s runtime classpath and are automatically discovered and applied at runtime during application startup. In Quarkus, an extension provides similar behavior, adding new functionality or config- uration to an application. An extension, similar to a Starter, is a dependency included in an application, enriching an application and enforcing opinions and sensible defaults. Note: An IDE is not re- quired but assists in the development of Quarkus applications. No particular IDE is assumed for any of the examples in this book. More information about the tooling can be found in Appendix A.2. The Red Hat Developer Sandbox (http://red.ht/ dev-sandbox) is a Red Hat OpenShift environment and developer IDE that is available in the cloud, free to use. Once you register, navigating to https://work- spaces. openshift.com/ f?url=https://github.com/ quarkus-for- spring- de- velopers/examples in your browser will load an IDE with all necessary tooling installed.
Chapter 2 – Getting Started with Quarkus Quarkus for Spring Developers | 20 There is, however, one fundamental difference between a Spring Boot Starter and a Quarkus extension. A Quarkus extension consists of two distinct parts: build-time augmentation, referred to as the deployment module, and the runtime container, re- ferred to as the runtime module. The majority of an extension’s work is done within the deployment module when an application is built. A Quarkus extension loads and scans the compiled application’s configuration and byte- code and all of its dependencies during build time. At this stage, the extension can read configuration files, scan classes for annotations, parse descriptors, and even generate additional code. Once all metadata has been collected, the extension can preprocess bootstrap actions, such as a dependency injection framework or REST endpoint con- figuration. The bootstrap result is directly recorded into bytecode and made part of the final application package. The work performed by the deployment module is what makes Quarkus super fast and memory-efficient, while also making native image support trivial. Does this mean that Quarkus applications can’t supply configuration at runtime? They certainly can, but to let Quarkus best optimize the application, careful consideration needs to be taken to decide which configuration options should be read at build time versus leaving them to be overridable at runtime. Configurations such as a database host, port, username, and password should be overridable at runtime because those settings are part of the environment and change from one deployment to another. Many other configuration properties, such as whether to enable caching or setting JDBC drivers to use, should be fixed at build time because they are design choices and are not subject to change. Quarkus, like Spring Boot, has a vast ecosystem of extensions for many of today’s commonly-used technologies. Table 2.2 lists some of the most common Quarkus extensions and the Spring Boot Starters providing similar functionality. A list of all available Quarkus extensions can be found at https://code.quarkus.io or by running the quarkus:list-extensions Maven goal or the listExtensions Gradle task. As of this writing, Quarkus has more than 400 extensions. Quarkus extension Spring Boot Starter quarkus-resteasy-jackson spring-boot-starter-web spring-boot-starter-webflux quarkus-resteasy-reactive-jackson spring-boot-starter-web spring-boot-starter-webflux quarkus-hibernate-orm-panache spring-boot-starter-data-jpa quarkus-hibernate-orm-rest-data- panache spring-boot-starter-data-rest quarkus-hibernate-reactive-panache spring-boot-starter-data-r2dbc quarkus-mongodb-panache spring-boot-starter-data-mongodb spring-boot-starter-data-mongodb-reactive quarkus-hibernate-validator spring-boot-starter-validation quarkus-qpid-jms spring-boot-starter-activemq quarkus-artemis-jms spring-boot-starter-artemis quarkus-cache spring-boot-starter-cache quarkus-redis-client spring-boot-starter-data-redis spring-boot-starter-data-redis-reactive quarkus-mailer spring-boot-starter-mail quarkus-quartz spring-boot-starter-quartz quarkus-oidc spring-boot-starter-oauth2-resource-server quarkus-oidc-client spring-boot-starter-oauth2-client quarkus-smallrye-jwt spring-boot-starter-security Table 2.2: Common Quarkus extensions.