ankitagrawa / foodOrderRestaurantApp

Other
1 stars 0 forks source link

// Copyright (c) 2017, 2019 IBM Corporation and others. // Licensed under Creative Commons Attribution-NoDerivatives // 4.0 International (CC BY-ND 4.0) // https://creativecommons.org/licenses/by-nd/4.0/ // // Contributors: // IBM Corporation // :projectid: foodOrderRestaurantApp :page-layout: guide-multipane :page-duration: 30 minutes :page-releasedate: 2020-01-15 :page-guide-category: MicroProfile :page-description: Learn how to use MicroProfile Reactive Messaging to implement reactive architecture application :guide-author: Open Liberty :page-tags: ['MicroProfile', 'Jakarta EE', 'Microservices'] :page-related-guides: [] :page-permalink: /guides/{projectid} :imagesdir: /img/guide/{projectid} :page-seo-title: Creating a MicroProfile Reactive Messaging REST service :page-seo-description: A tutorial on how to create a microservices in Open Liberty using MicroProfile Reactive Messaging :common-includes: https://raw.githubusercontent.com/OpenLiberty/guides-common/master :source-highlighter: prettify = Creating asynchronous reactive microservices using MicroProfile Reactive Messaging

[.hidden] NOTE: This repository contains the guide documentation source. To view the guide in published form, view it on the https://openliberty.io/guides/{projectid}.html[Open Liberty website].

Learn how to use MicroProfile Reactive Messaging to implement reactive architecture application.

== What you'll learn

You will learn how to build reactive microservices using MicroProfile Reactive Messaging. You'll also learn how to send messages between these microservices using Kafka broker.

=== What is MicroProfile Reactive Messaging?

Asynchronous communication allows temporal decoupling of services in a microservice-based architecture. MicroProfile Reactive Messaging delivers a way to build systems of microservices promoting responsive, non-blocking, location transparency, elastic, resiliency to failure and temporal decoupling, enforcing asynchronous message passing between the different parts of the system. https://download.eclipse.org/microprofile/microprofile-reactive-messaging-1.0/microprofile-reactive-messaging-spec.html#_microprofile_reactive_messaging[View the MicroProfile Reactive Messaging Specification^]

=== What is Kafka?

https://kafka.apache.org/[Apache Kafka^] is a stream-processing platform that manages communication in distributed systems. Communication is message-oriented, and follows the publish-subscribe model. Kafka allows for real-time stream processing and distributed, replicated storage of streams and messages. Kafka Producer is a client or a program, which produces the message and pushes it to the Topic. Whereas Kafka Consumer is a client or a program, which consumes the published messages from the Producer.

=== What is asynchronous programming? Imagine asynchronous programming as a restaurant. After you're seated, a waiter takes your Order. Then, you must wait a few minutes for your food to be prepared. While your food is being prepared, your waiter may take more orders or serve other tables. After your food is ready, your waiter brings out the food to your table. However, in a synchronous model, the waiter must wait for your food to be prepared before serving any other customers. This method blocks other customers from placing orders or receiving their food.

The Restaurant application is designed with Reactive Architecture. The application that you'll be working with consists of five microservices namely order, kitchen, bar, servingWindow and restaurantBFF. And uses Kafka broker to enable reactive messaging communication between the Producer and Consumer microservices over the messaging channels.

You'll update the order, kitchen, bar, and servingWindow microservices to use MicroProfile Reactive Messaging for message passing. These microservices run on Open Liberty.

image::reactive-messaging-restaurant.png[Reactive restaurant,align="center"]

The restaurantBFF microservice is a https://microservices.io/patterns/apigateway.html#variation-backends-for-frontends[backend for frontend^] service. It communicates with the backend microservices on the caller's behalf.

The waiter places a request using the restaurantBFF microservice.

The order microservice consumes the request, produces Order messages, and sends them to Kafka on the food or beverage channel depending on the type.

An Order begins with a NEW status. The kitchen and bar microservices consume and process the Order and update the status to IN_PROGRESS and READY consecutively. There’s a sleep operation in between each status to represent the Order processing time, and the status updates are reported back to the order microservice via reactive messages on Kafka.

The servingWindow microservice contains a list of all ready to serve food and beverages. It consumes these statuses from kitchen and bar microservices. Once the Order is served, it’s marked as COMPLETED and the status is sent back to the order microservice as a message.

// ================================================================================================= // Prerequisites // ================================================================================================= == Additional prerequisites

You will build and run the microservices in Docker containers. You can learn more about containerizing microservices with Docker in the https://openliberty.io/guides/containerize.html[Containerizing microservices^] guide.

Install Docker by following the instructions on the official https://docs.docker.com/engine/installation[Docker documentation^]. Start your Docker environment.

// ================================================================================================= // Getting started // ================================================================================================= [role='command'] include::{common-includes}/gitclone.adoc[]

USE this Github Link for the time being:

https://github.com/ankitagrawa/foodOrderRestaurantApp

== Creating a Reactive Messaging application

=== Key Concepts

==== @Outgoing Outgoing is an annotation indicating that the method feeds a channel. The name of the channel is given as attribute.

==== @Incoming Incoming is an annotation indicating that the method consumes a channel. The name of the channel is given as attribute.

==== Channel A channel is a name indicating which source or destination of messages is used. Channels are opaque Strings.

==== Connector Reactive messaging uses Connectors to attach one end of a channel  and are configured using MicroProfile Config. Open Liberty includes the liberty-kafka connector for sending and receiving messages from an Apache Kafka broker.

Navigate to the start directory to begin. Most of the code is already provided for use.

[role=command] include::{common-includes}/devmode-start.adoc[]

=== Building the order microservice

The order microservice uses MicroProfile reactive messaging to send messages to kitchen and bar microservices over kafka.

[role="code_command hotspot", subs="quotes"]

Replace the OrderResource class.

order/src/main/java/io/openliberty/guides/order/OrderResource.java

OrderResource.java [source, Java, linenums, role='code_column hide_tags=copyright']

include::finish/order/src/main/java/io/openliberty/guides/order/OrderResource.java[]

order microservice creates an Order and sends it to the kafka topic on [hotspot=OutgoingFood file=0]@Outgoing("food") or [hotspot=OutgoingBev file=0]@Outgoing("beverage") channel. The [hotspot=IncomingStatus file=0]@Incoming("updateStatus") channel receives an updated Order status from the kafka.

=== Building the kitchen microservice

If the order is a food, kitchen service receives an Order, processes it and sends back all the statuses to kafka on MicroProfile Reactive Messaging channels.

//Kitchen [role="code_command hotspot", subs="quotes"]

Replace the KitchenResource class.

kitchen/src/main/java/io/openliberty/guides/kitchen/KitchenResource.java

KitchenResource.java [source, Java, linenums, role='code_column hide_tags=copyright']

include::finish/kitchen/src/main/java/io/openliberty/guides/kitchen/KitchenResource.java[]

=== Building the bar microservice

If the Order is a drink, bar service receives an Order, processes it and sends back all the statuses to kafka on MicroProfile Reactive Messaging channels.

//bar [role="code_command hotspot", subs="quotes"]

Replace the BarResource class.

bar/src/main/java/io/openliberty/guides/bar/BarResource.java

BarResource.java [source, Java, linenums, role='code_column hide_tags=copyright']

include::finish/bar/src/main/java/io/openliberty/guides/bar/BarResource.java[]

=== Building the servingWindow microservice

servingWindow microservice receives the Ready Order from the bar and kitchen microservices over kafka topics. Once the Order is served, it's marked as COMPLETED and the status is sent across to order microservice using the MicroProfile Reactive Messaging.

//ServingWindow [role="code_command hotspot", subs="quotes"]

Replace the ServingWindowResource class.

servingWindow/src/main/java/io/openliberty/guides/servingWindow/ServingWindowResource.java

ServingWindowResource.java [source, Java, linenums, role='code_column hide_tags=copyright']

include::finish/servingWindow/src/main/java/io/openliberty/guides/servingWindow/ServingWindowResource.java[]

=== Configuring the MicroProfile Reactive Messaging

Each of the four microservices order, kitchen, bar and servingWindow contains microprofile-config.properties file, which configures kafka connector with the MicroProfile Reactive Messaging channels.

[role="code_command hotspot", subs="quotes"]

microprofile-config.properties example from the kitchen microservice.

finish/kitchen/src/main/resources/META-INF/microprofile-config.properties

microprofile-config.properties [source, Java, linenums, role='code_column hide_tags=copyright']

include::finish/kitchen/src/main/resources/META-INF/microprofile-config.properties[]

Rest all the microservices use similar microprofile-config.properties configuration.

== Configuring the server

To get the service running, the Open Liberty server needs to be correctly configured.

Replace the bar server.xml to include [hotspot=featureMP file=0]mpReactiveMessaging-1.0 feature element.

[role="code_command hotspot file=0", subs="quotes"]

Replace the bar server configuration file.

bar/src/main/liberty/config/server.xml

server.xml [source,xml,linenums,role="code_column"]

include::finish/bar/src/main/liberty/config/server.xml[]

Rest all the microservices have server.xml already configured.

.The configuration does the following actions:

[role="code_command hotspot file=1", subs="quotes"]

pom.xml configuration

bar/pom.xml


pom.xml [source, XML ,linenums,role="code_column"]

include::finish/bar/pom.xml[]

== Building the application

You will build and run the order, kitchen, bar, servingWindow and restaurantBFF microservices in Docker containers. You can learn more about containerizing microservices with Docker in the https://openliberty.io/guides/containerize.html[Containerizing microservices^] guide.

Start your Docker environment.

To build the application, run the Maven install goal from the command line in the start directory:

[role='command']

mvn -pl models install
mvn package

Run the following commands to containerize the application:

[role='command']

docker build -t order:1.0-SNAPSHOT order/.
docker build -t kitchen:1.0-SNAPSHOT kitchen/.
docker build -t bar:1.0-SNAPSHOT bar/.
docker build -t servingWindow:1.0-SNAPSHOT servingWindow/.
docker build -t restaurantBFF:1.0-SNAPSHOT restaurantBFF/.

Next, use the provided script to start the application in Docker containers. The script creates a network for the containers to communicate with each other. It also creates containers for Kafka, Zookeeper, and all of the microservices in the project.

include::{common-includes}/os-tabs.adoc[]

[.tab_content.mac_section.linux_section]

[role='command']

./scripts/startContainers.sh

--

[.tab_content.windows_section]

[role='command']

.\scripts\startContainers.bat

--

== Trying the application

You can access the application by making requests to the restaurantBFF endpoint using OpenAPI.

The services take some time to become available. Check out the service that you created at the http://localhost:9080/openapi/ui[^] URL.

=== Place Orders

Expand the /api/orders POST request to post an Order and click Try it out. Copy the following example input into the text box:

[source, role='command']

{ "tableID": "1", "foodList": [ "burger" ], "beverageList": [ "coke" ] }

Click Execute and you will receive the 200 response.

=== Check IN_PROGRESS Order Status

Now Expand the /api/orders GET request to get the Order status and click Try it out.

Click Execute and you will see the response with IN_PROGRESS status.

[source, role='command']

[ { "item": "burger", "orderID": "0001", "status": "IN_PROGRESS", "tableID": "1", "type": "FOOD" }, { "item": "coke", "orderID": "0002", "status": "IN_PROGRESS", "tableID": "1", "type": "BEVERAGE" } ]

=== Check READY Order Status

Click Execute again and you will see the response with READY status.

[source, role='command']

[ { "item": "burger", "orderID": "0001", "status": "READY", "tableID": "1", "type": "FOOD" }, { "item": "coke", "orderID": "0002", "status": "READY", "tableID": "1", "type": "BEVERAGE" } ]

=== Complete an Order

Expand the /api/servingWindow/complete/{orderID} POST request to complete an Order and click Try it out. Copy the following example input into the text box:

[source, role='command']

0002

Click Execute and you will receive the 200 response.

Now Expand the /api/orders GET request to get the Order status and click Try it out.

Click Execute and you will see the response with COMPLETED status for orderID 0002. This updated status is cascaded to the order microservice.

[source, role='command']

[ { "item": "burger", "orderID": "0001", "status": "IN_PROGRESS", "tableID": "1", "type": "FOOD" }, { "item": "coke", "orderID": "0002", "status": "COMPLETED", "tableID": "1", "type": "BEVERAGE" } ]

== Testing the service

You will create an unit tests to test the basic functionality of the microservices. If a test failure occurs, then you may have introduced a bug into the code.

// You can test this service manually by starting a server and pointing a web browser at the // http://localhost:9080/LibertyProject/System/properties[^] URL. Automated tests are a much better // approach because they trigger a failure if a change introduces a bug. JUnit and the JAX-RS Client // API provide a simple environment to test the application.

// You can write tests for the individual units of code outside of a running application server, or they // can be written to call the application server directly. In this example, you will create a test that // does the latter.

// [role="code_command hotspot", subs="quotes"] // ---- // #Create the EndpointIT class.# // src/test/java/it/io/openliberty/guides/rest/EndpointIT.java // ----

// EndpointIT.java // [source, Java, linenums, role="code_column hide_tags=comment"] // ---- // include::finish/src/test/java/it/io/openliberty/guides/rest/EndpointIT.java[] // ----

// This test class has more lines of code than the resource implementation. This situation is common. // The test method is indicated with the [hotspot=test file=0]@Test annotation.

// pom.xml // [source , xml, linenums,role="code_column"] // ---- // include::finish/pom.xml[] // ----

// The test code needs to know some information about the application to make requests. The server port and the application context root are key, and are dictated by the server configuration. While this information can be hardcoded, it is better to specify it in a single place like the Maven [hotspot=defaultHttpPort hotspot=defaultHttpsPort hotspot=appContextRoot file=1]pom.xml file. Refer to the [hotspot file=1]pom.xml file to see how the application information such as the [hotspot=defaultHttpPort file=1]default.http.port, [hotspot=defaultHttpsPort file=1]default.https.port and [hotspot=appContextRoot file=1]app.context.root elements are provided in the file.

// These Maven properties are then passed to the Java test program as the [hotspot=testsysprops file=1]<systemPropertyVariables/> element in the [hotspot file=1]pom.xml file.

// Getting the values to create a representation of the URL is simple. The test class uses the [hotspot=systemProperties file=0]getProperty method // to get the application details.

// To call the JAX-RS service using the JAX-RS client, first create a WebTarget object by calling // the [hotspot=target file=0]target method that provides the URL. To cause the HTTP request to occur, the [hotspot=requestget file=0]request().get() method // is called on the WebTarget object. The [hotspot=requestget file=0]get method // call is a synchronous call that blocks until a response is received. This call returns a [hotspot=requestget file=0]Response // object, which can be inspected to determine whether the request was successful.

// The first thing to check is that a 200 response was received. The JUnit [hotspot=assertequals file=0]assertEquals method can be used for this check.

// Check the response body to ensure it returned the right information. Since the client and the server // are running on the same machine, it is reasonable to expect that the system properties for the local // and remote JVM would be the same. In this case, an [hotspot=assertosname file=0]assertEquals assertion is made so that the os.name system property // for both JVMs is the same. You can write additional assertions to check for more values.

=== Running the tests

Run tests

Navigate to the restaurantBFF directory, then verify that the tests pass by using the Maven verify goal:

[role='command']

mvn verify

When the tests succeed, you see output similar to the following example: // Since you started Open Liberty in development mode at the start of the guide, press // enter/return key to run the tests. You will see the following output:

// [source,role="no_copy"] // ---- // ------------------------------------------------------- // T E S T S // ------------------------------------------------------- // Running it.io.openliberty.guides.rest.EndpointIT // Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 2.884 sec - in it.io.openliberty.guides.rest.EndpointIT

// Results :

// Tests run: 1, Failures: 0, Errors: 0, Skipped: 0 // ----

// To see whether the tests detect a failure, add an assertion that you know fails, or change the existing // assertion to a constant value that doesn't match the os.name system property.

// When you are done checking out the service, exit development mode by typing q in the shell session where // you ran the server and then pressing the enter/return key.

== Tearing down the environment

Navigate back to the start directory.

Finally, use the following script to stop the application:

include::{common-includes}/os-tabs.adoc[]

[.tab_content.mac_section.linux_section]

[role='command']

./scripts/stopContainers.sh

--

[.tab_content.windows_section]

[role='command']

.\scripts\stopContainers.sh

--

== Great work! You're done!

You have just developed an application using MicroProfile Reactive Messaging, Open Liberty and Kakfa.

== Related Links

Learn more about MicroProfile.

https://microprofile.io/[See the MicroProfile specs^]

https://openliberty.io/docs/ref/microprofile[View the MicroProfile API^]

https://download.eclipse.org/microprofile/microprofile-reactive-messaging-1.0/microprofile-reactive-messaging-spec.html#_microprofile_reactive_messaging[View the MicroProfile Reactive Messaging Specification^]

include::{common-includes}/attribution.adoc[subs="attributes"]