A4 - Concurrency and Discovery
Overview
This assignment will lay down a few more key capabilities needed to run in a distributed system. First, you will modify the main loop of your selver to handle multiple clients in an event driven manner. Second, you will modify the client and server to discover each other via a common naming service.
Part 1: Multiple Clients per Server
So far, your server has only permitted one client to connect and interact at a time. Now is the time to fix that. You are going to use an event-driven approach which will permit multiple connections. Here is how to think about it:
Your server will keep track of the master socket on which it is listening
for new connections. And, it will also have a dictionary of sockets, each one
representing a currently-connected client. In the server’s main loop, it
should use select
to determine which socket is readable.
If the master socket is ready for reading, then the server should accept a new connection
and add it to the set. If any other socket is ready for reading, then the
server should read a request from the socket, perform the action, send a response,
and then go back to waiting.
This approach strikes a balance between concurrency and complexity. The server can handle multiple clients connected at once, and can interleave multiple requests to satisfy all comers. Each request still executes one at a time, so we say that the request stream is serialized. You won’t get the performance benefit of running multiple processes/threads concurrently, but you won’t have the synchronization problems from that either. (We will make up for it soon by running multiple servers concurrently.)
Be sure to test your server carefully before moving on. Try various combinations of clients connecting and leaving, and ensure that the server does not crash, no matter when or how the clients connect and disconnect. Add some debugging output to assure yourself that the server is in fact interleaving requests from multiple clients.
Part 2: Discovery via a Name Server
To this point, your server has listened on a manually-selected port number in a fixed location. This is ok for testing purposes, but becomes a problem when running multiple servers across many machines. The client needs a better way of locating the server that it wants to access.
We will address this problem by making use of a simple name server that we have running here at Notre Dame: take a look at catalog.cse.nd.edu:9097. Here is how the name server works:
Various services running at ND (and around the world) periodically register
themselves with the name server by sending a UDP packet to catalog.cse.nd.edu:9097
,
typically once every five minutes. The UDP packet contains a JSON document
that describes the essential properties of the service: name, port, location, memory, disk, etc.
Some of the services are quite simple, while others are very complex.
The name server publishes the set of known services via a web page. You can browse an HTML representation of the web page manually, or you can access a JSON representation programmatically, like this:
curl http://catalog.cse.nd.edu:9097/query.json | json_pp
The name server periodically discards records from services that have not sent an update in the last 15 minutes. This is merely a garbage collection measure, and so the user must accept the fact that any data in the name server is necessarily “stale”.
For this assignment, you must modify your client and server to use the name server as follows:
- First, the server should listen on any available TCP port
(indicated by port zero) and then determine what port was actually allocated.
Then, at startup and once per minute thereafter, it should send an update
describing itself to the name server. This is done by sending a UDP message
containing a text JSON document to
catalog.cse.nd.edu:9097
. Thetype
field should behashtable
, theport
field should be the port the server is listening on, theowner
field should be your netid, and theproject
field should be a personalized server name that you pass in on the command line. (Note that thename
field is automatically filled in with the host name by the name server.) As an example:
{
"type" : "hashtable",
"owner" : "YOURNETID",
"port" : 1234,
"project" : "YOURNETID-a4-test5"
}
- Second, your client should locate the desired server by name.
To do so, make an HTTP connection to the catalog server, download
the JSON representation of the service information, and find the
entry that has
type=="hashtable"
andproject
equal to your server name. Then, connect to the indicated host and port number.
Invocation
Once name lookups are working, you should modify your client and server to make use of a project name on the command line. So, start your server like this:
python HashTableServer.py YOURNETID-a4-test5
And then start your client using the same project name:
python TestPerf.py YOURNETID-a4-test5
Your client and server will now be able to find each other, no matter where they are located.
Take Care: To avoid collisions between students, please make sure to use a project name that contains your netid. (Honor system.)
Testing and Measurement
Evaluate the performance of your service in the presence of mulitple clients, as follows.
Start a single instance of your server on one of the student machines. Then, on a different student machine,
run a single instance of the TestPerf.py
program.
python TestPerf.py YOURNETID-a4-test
Capture the output of that test, then run two clients simultaneously:
python TestPerf.py YOURNETID-a4-test & python TestPerf.py YOURNETID-a4-test & wait
And continue in this way until the performance “levels out”. Hint: Look at the sum of the throughputs obtained by simultaneous clients.
What to Turn In
Please review the general instructions for submitting assignments.
Turn in all of your source code, along with a lab report titled REPORT
that describes the following in detail:
- The raw data from your throughput measurements, all laid out in a nice tabular form. (Plain text is fine.)
- Consider your performance data closely, and explain what what it tells you about the system as a whole. Take your time to look at the data from multiple perspectives: consider individual clients, and the sum of simultaneous clients; consider differences between operation types; consider what happens as the number of clients increase.