Another example of STM and Vert.x integration. The quickstart is divided into 3 parts each introducing a different facet of STM and Vert.x integration.
Use Case 1 shows a single JVM running multiple vertx instances sharing the same volatile STM object.
Use Case 2 uses the same STM object but makes it persistent and shows how to share it across address spaces.
These two examples illustrate the different ways in which an application can be scaled whilst maintaining data consistency:
-
verticle scaling by using better hardware so that more threads can do be used to service the workload;
-
horizontal scaling by using more servers so that the workload can be distributed to multiple JVMs
Use case 3 shows a single JVM hosting multiple STM objects and a Trip verticle which listens for trip, theatre and taxi booking requests. STM is used manage concurrent access to the various STM objects.
Use case 4 is an optional final stress test to check that STM correctly manages concurrent access to STM objects.
First build a fat jar that contains all the classes needed to run both demos in a single jar:
mvn clean package
Characteristics:
-
RECOVERABLE and EXCLUSIVE
-
creates multiple vertx instances each instance using a handle to the same STM object
What it’s good for:
-
vertical scaling where adding better h/w is an option in order to support more threads in one JVM
Features:
This example shows a Theatre booking service:
-
an STM object is used to maintain all Theatre bookings
-
the STM object is volatile
-
-
multiple vertx instances each listening on the same HTTP endpoint
-
each vertx instance shares the same STM object
-
all vertx instances run in the same address space
-
concurrency is managed by the STM runtime
-
-
shows vertical scaling
Start a Vert.x verticle to represent the theatre service:
mvn exec:java -Pdemo1
or
java -cp target/stm-vertx-demo-5.6.0.Final-SNAPSHOT-fat.jar demo.demo12.VolatileTheatreVerticle
This command starts the 10 instances of the theatre verticle which all listen for requests/messages on HTTP port 8080. The port and number of instances are configurable. If run the example directly from the java command line notice that the version of the jar (5.6.0.Final-SNAPSHOT in this example) will change depending upon which narayana release you have.
The kind of output that you should expect to see is as follows:
[mmusgrov@dev1 devoxxUK2017](mucon)$ mvn exec:java -Pdemo1 [INFO] Scanning for projects... [INFO] [INFO] ------------------------------------------------------------------------ [INFO] Building Narayana - STM and vertx demo 5.7.2.Final-SNAPSHOT [INFO] ------------------------------------------------------------------------ [INFO] [INFO] --- exec-maven-plugin:1.4.0:java (default-cli) @ stm-vertx-demo --- Nov 22, 2017 6:16:53 PM com.arjuna.ats.arjuna.recovery.TransactionStatusManager start INFO: ARJUNA012170: TransactionStatusManager started on port 37255 and host 127.0.0.1 with service com.arjuna.ats.arjuna.recovery.ActionStatusService vert.x-eventloop-thread-3: theatre service listening on http://localhost:8080/api/theatre vert.x-eventloop-thread-0: theatre service listening on http://localhost:8080/api/theatre vert.x-eventloop-thread-5: theatre service listening on http://localhost:8080/api/theatre vert.x-eventloop-thread-4: theatre service listening on http://localhost:8080/api/theatre vert.x-eventloop-thread-8: theatre service listening on http://localhost:8080/api/theatre vert.x-eventloop-thread-6: theatre service listening on http://localhost:8080/api/theatre vert.x-eventloop-thread-2: theatre service listening on http://localhost:8080/api/theatre vert.x-eventloop-thread-9: theatre service listening on http://localhost:8080/api/theatre vert.x-eventloop-thread-7: theatre service listening on http://localhost:8080/api/theatre vert.x-eventloop-thread-1: theatre service listening on http://localhost:8080/api/theatre
Create 2 bookings:
First booking execute:
curl -X POST http://localhost:8080/api/theatre/Apollo
Expected output should look similar to:
{ "serviceName" : "theatre", "threadId" : "vert.x-eventloop-thread-3", "message" : "", "bookingCount" : 1, "altBookingCount" : 0 }
Second booking execute:
curl -X POST http://localhost:8080/api/theatre/Savoy
Expected output should look similar to:
{ "serviceName" : "theatre", "threadId" : "vert.x-eventloop-thread-2", "message" : "", "bookingCount" : 2, "altBookingCount" : 0 }
Here the theatre service is listening on the REST endpoint http://localhost:8080/api/theatre and the final path component (Apollo and Savoy in this example) is interpreted as an input parameter. Although this is not a RESTful design it suffices to show the intent of the demo which is to
Observe how each request is serviced on a different verticle instance
Similarly performing GETs will show the same booking counts regardless of which verticle services it:
curl -X GET http://localhost:8080/api/theatre curl -X GET http://localhost:8080/api/theatre
Where you should see something similar to the following two outcomes:
{ "serviceName" : "theatre", "threadId" : "vert.x-eventloop-thread-9", "message" : "", "bookingCount" : 2, "altBookingCount" : 0 }
and
{ "serviceName" : "theatre", "threadId" : "vert.x-eventloop-thread-8", "message" : "", "bookingCount" : 2, "altBookingCount" : 0 }
At this point we are finished with the first use case and you can kill the running process.
Use Case 2: Scaling by distributing the workload across JVMs
Similar to use case 1 but uses persistent STM objects spread across different JVMs.
Characteristics:
-
PERSISTENT and SHARED
-
theatre service verticles running in different JVMs sharing the same STM object
-
each JVM hosting multiple vertx instances each instance using a handle to the same STM object
What it’s good for:
-
horizontal scaling by using better hardware so that more threads can do be used to service the workload;
Usage:
Start a Vert.x verticle to represent the theatre service: mvn exec:java -Pdemo2 or java -cp target/stm-vertx-demo-5.6.0.Final-SNAPSHOT-fat.jar demo.demo12.PersistentSharedTheatreVerticle Start a second verticle that will share the same STM object. In the output of the above command look for the line that reports the uid of the STM object, for example: Theatre STM uid: 0:ffffac1182c6:9197:5912d4ff:1 The actual uid may change from run to run. Pass this uid the second JVM using a java system property called uid: mvn exec:java -Pdemo2 -Duid=0:ffffac1182c6:9197:5912d4ff:1 or java -cp target/stm-vertx-demo-5.6.0.Final-SNAPSHOT-fat.jar demo.demo12.PersistentSharedTheatreVerticle 0:ffffac1182c6:9197:5912d4ff:1 Create two bookings using services running in the two different JVMs (on endpoints 8080 and 8082 respectively): curl -X POST http://localhost:8080/api/theatre/Apollo which should give output like: { "serviceName" : "theatre", "threadId" : "vert.x-eventloop-thread-0", "message" : "", "bookingCount" : 1, "altBookingCount" : 0 } and then: curl -X POST http://localhost:8082/api/theatre/Savoy which should give output similar to: { "serviceName" : "theatre", "threadId" : "vert.x-eventloop-thread-5", "message" : "", "bookingCount" : 2, "altBookingCount" : 0 } Check that each JVM reports the correct number of bookings (namely 2): curl -X GET http://localhost:8080/api/theatre curl -X GET http://localhost:8082/api/theatre And you should see the following outputs: { "serviceName" : "theatre", "threadId" : "vert.x-eventloop-thread-1", "message" : "", "bookingCount" : 2, "altBookingCount" : 0 } and { "serviceName" : "theatre", "threadId" : "vert.x-eventloop-thread-8", "message" : "", "bookingCount" : 2, "altBookingCount" : 0 } At this stage you can kill both running processes before moving on to the next Use Case. Use Case 3: Managing shared state across different STM objects =============================================== Characteristics: - RECOVERABLE and EXCLUSIVE - trip service verticle (multiple instances) using STM objects to maintain theatre and taxi bookings. - providing HTTP endpoints for making trip, theatre and taxi bookings. What it's good for: - composing transactional operations across different STM objects The trip service fulfils booking requests by updating shared STM objects representing the theatre and taxi booking services respectively. Start a Vert.x verticle to represent trip booking service: mvn exec:java -Pdemo3 or java -cp target/stm-vertx-demo-5.6.0.Final-SNAPSHOT-fat.jar demo.demo3.TripSTMVerticle Make two trip bookings (specifying the required hotel and taxi company for the trip): First execute: curl -X POST http://localhost:8080/api/trip/Savoy/ABC to obtain: { "serviceName" : "trip", "threadId" : "vert.x-eventloop-thread-8", "message" : "1 bookings with alt taxi service", "bookingCount" : 1, "altBookingCount" : 1 } Then execute: curl -X POST http://localhost:8080/api/trip/Apollo/XYZ Here the trip booking service is listening on the REST endpoint http://localhost:8080/api/trip and the final two path components (Savoy and ABC in the first example) are interpreted as an input parameters corresponding to the name of the theatre and taxi companies to use for the trip booking. This is not a RESTful design but it serves the purpose of the demo for showing STM in action. In a more realistic example the trip booking details would be specified in the POST request body using a dedicated media format for booking trips. The expected output should be similar to: { "serviceName" : "trip", "threadId" : "vert.x-eventloop-thread-0", "message" : "2 bookings with alt taxi service", "bookingCount" : 2, "altBookingCount" : 2 } Also make a single theatre booking via the theater booking service: curl -X POST http://localhost:8080/api/theatre/Savoy giving the following output: { "serviceName" : "theatre", "threadId" : "vert.x-eventloop-thread-9", "message" : "", "bookingCount" : 3, "altBookingCount" : 0 } Again observe that each booking is serviced by a different verticle. Check that number of theatre (3) and the number of taxi (2) bookings are correct: curl -X GET http://localhost:8080/api/theatre curl -X GET http://localhost:8080/api/taxi Which will give the following outputs: { "serviceName" : "theatre", "threadId" : "vert.x-eventloop-thread-5", "message" : "", "bookingCount" : 3, "altBookingCount" : 0 } and { "serviceName" : "taxi", "threadId" : "vert.x-eventloop-thread-2", "message" : "", "bookingCount" : 2, "altBookingCount" : 0 } respectively. Again, kill the demo process before proceeding to the next use case. Use Case 4: Stress Testing: -------------------------- None of the previous use cases demonstrate contention of the STM objects. This next case will start use case 1 (the theatre booking example) and then make lots of concurrent trip bookings and validate that the expected number of bookings are made. Usage:
Start the theatre service in one window or in the background:
mvn exec:java -Pdemo1
and now make lots of concurrent bookings:
mvn exec:java -Pstress
The stress test will make 1000 requests to the theatre service spreading the load using 10 threads. Provided there are no failures you should see output similar to the folllowing which indicates successful completion:
[INFO] Scanning for projects... [INFO] [INFO] ------------------------------------------------------------------------ [INFO] Building Narayana - STM and vertx demo 5.7.2.Final-SNAPSHOT [INFO] ------------------------------------------------------------------------ [INFO] [INFO] --- exec-maven-plugin:1.4.0:java (default-cli) @ stm-vertx-demo --- Waiting for 1000 requests 0 out of 1000 requests failed in 6227 ms [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESS [INFO] ------------------------------------------------------------------------ [INFO] Total time: 2.611s [INFO] Finished at: Wed Nov 22 19:22:44 GMT 2017 [INFO] Final Memory: 16M/362M [INFO] ------------------------------------------------------------------------