CS6650 Building Scalable Distributed Systems
You work for Upic - a global acquirer of ski resorts that is homogenezing skiing around the world. Upic ski resorts all use RFID lift ticket readers so that every time a skiier gets on a ski lift, the time of the ride and the skier ID are recorded.
In this course, through a series of assignments, we’ll build a scalable distributed cloud-based system that can record all lift rides from all Upic resorts. This data can then be used as a basis for data analysis, answering such questions as:
In Assignment 1, we’ll build a client that generates and sends lift ride data to a server in the cloud. The server will simply accept and validate requests, and send an HTTP 200/201 response. In Assignment 2, we’ll add the processing and storage logic to the server, and send a richer set of requests. In Assignments 3 and 4, we’ll get a little crazy, so make sure you lay solid code and design foundations in the first two assignments!
The initial server API is specified using Swagger
In this assignment you need to implement this API using a Java servlets. Each API should:
Good simple illustrations of how to handle JSON request payloads are here and here.
This should be a pretty simple task. Test each servlet API with POSTMAN or an equivalent HTTP testing tools. Deploy your server on us-west2 on AWS Academy.
Make sure you can load the resulting .war file onto your EC2 free tier instance you have created and configured in lab 1 and call the APIs successfully.
This is the major part of this assignment. We want a multithreaded Java client we can configure to upload a day of lift rides to the server and exert various loads on the server.
First you need to get a Java client to call your server APIs. You can generate a client API from the Swagger specification. Look at:
Export-Client SDK-Java
Unzip the client and follow the instructions in the README to incorporate the generated code in your client project.
The generated code contains thread-safe methods for calling the server APIs. Write a simple test that calls the API before proceeding, to establish that you have connectivity. The example in the README is your friend ;). You just need to look at the ApiClient methods to figure out how to point the example at your server.
If you don’t want to figure out the Swagger client, you can use the Apache Java HTTP API.
Once you have your client calling the API, it’s time to build the multithreaded client fun time!!
Your client should accept a set of parameters from the command line (or a parameter file) at startup. These are:
In addition, each ski day is of length 420 minutes (7 hours - 9am-4pm) from when the lifts open until they all close.
Based on these values, your client will start up and execute 3 phases, with each phase sending a large number of lift ride events to the server API.
Phase 1, the startup phase, will launch numThreads/4 threads, and each thread will be passed:
For example if numThreads=64 and numSkiers=1024, we will launch 16 threads, with thread 0 passed skierID range 1 to 64, thread 1 range 65 to 128, and so on.
Once each thread has started it should send (numRunsx0.2)x(numSkiers/(numThreads/4)) POST requests to the server. Each POST should randomly select:
With our example, if numRuns=20, each thread will send 4x(1024/16) POST requests.
The server will return an HTTP 201 response code for a successful POST operation. As soon as the 201 is received, the client should immediately send the next request until it has exhausted the number of requests to send.
If the client receives a 5XX response code (Web server error), or a 4XX response code (from your servlet), it should retry the request up to 5 times before counting it as a failed request.
Once 20% (rounded up) of the threads in Phase 1 have completed, Phase 2, the peak phase should begin. Phase 2 behaves like Phase 1, except:
As above, each thread will randomly select a skierID, liftID and time from the ranges provided and sends a POST request. It will do this (numRunsx0.6)x(numSkiers/numThreads) times. Back to our example above, this means phase 2 would create 64 threads, and and each sends 12*(1024/64) POSTs.
Finally, once 20% of the threads in Phase 2 complete, Phase 3 should begin. Phase 3, the cooldown phase, is identical to Phase 1, starting 10% of numThreads, with each thread sending (0.1xnumRuns) POST requests, and with a time interval range of 361 to 420.
When all threads from all phases are complete, the programs should print out:
You should run the client on your laptop. Thus means each request will incur latency depending on where your server resides. You should test how long a single request takes to estimate this latency. Run a simple test and send eg 10000 requests from a single thread to do this.
You can then calculate the expected throughput your client will see using Little’s Law.
If your throughput is not close to this estimate for each of the test runs, you probably have a bug in your client.
With your load generating client working wonderfully, we want to now instrument the client so we have deeper insights into the performance of the system. To this end, for each POST request:
Once all phases have completed, we need to calculate:
You may want to do all the processing of latencies in your client after the test completes, or you may want to write a separate program to run after the test has completed that generates the results. Check your results against those calculated in a spreadsheet.
The client should calculate these and display them in the output window in addition to the output from the previous step, and then cleanly terminate.
Submit your work to Blackboard Assignment 1 as a pdf document. The document should contain:
It is usually interesting to plot average latencies over the whole duration of a test run. To do this you will have to capture timestamps of when the request occurs, and then generate a plot that shows latencies against time (there’s a good example in the percentile article earlier). You might want to plot every request, or thinking ahead for assignment 2, put them in buckets of, for example, a second interval, and plot the average response time for that time interval bucket.
You need to modify your POM, add the following dependencies:
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<version>2.2.11</version>
</dependency>
<dependency>
<groupId>com.sun.xml.bind</groupId>
<artifactId>jaxb-core</artifactId>
<version>2.2.11</version>
</dependency>
<dependency>
<groupId>com.sun.xml.bind</groupId>
<artifactId>jaxb-impl</artifactId>
<version>2.2.11</version>
</dependency>
<dependency>
<groupId>javax.activation</groupId>
<artifactId>activation</artifactId>
<version>1.1.1</version>
</dependency>
<dependency>
<groupId>javax.annotation</groupId>
<artifactId>javax.annotation-api</artifactId>
<version>1.3.2</version>
</dependency>
Sometimes there’s an issue building the GSON jar file into a servlet, such that when the servlet is deployed it fails because GSON is missing.
Try adding the gson jar to your project from the Maven website
For IntelliJ you also need to:
Compile and deploy … and cross your fingers and toes!!
For more details check this out
Worth a read for random number generation in your client ;)