The third project is to build an event-driven pub/sub library (ie.
libps_client.a
) using POSIX threads and network sockets. In a pub/sub
system there is usually one server and multiple clients:
As shown above, a typical pub/sub system has a server who maintains a collection of topics, which serve as endpoints for messages. Clients connect to this server and perform the following operations:
IDENTIFY
: This identifies and authenticates the client to the server.
PUBLISH
: This posts a message to a particular topic queue.
SUBSCRIBE
: This notifies the server that a client is interested in a
particular topic.
RETRIEVE
: This fetches all the messages destined for the client.
DISCONNECT
: This notifies the server that the client is disconnecting.
In the diagram, Client A connects to the Server and first performs
an IDENTIFY
command with its name and nonce or key. It then publishes
a message to the TOPIC_A
endpoint and then disconnects.
Client B, on the other hand, also performs an IDENTIFY
operation and
the proceeds to SUBSCRIBE
to TOPIC_A
and then RETRIEVE
any messages
for it. If Client B had subscribed to TOPIC_A
before Client A
published its message, then Client B would receive the message during
this RETRIEVE
operation.
Working in groups of one or two people, you are to create a library that utilizes POSIX threads and concurrent data structures to implement a client for the described pub/sub system by midnight on Tuesday, October 10, 2017. Additionally, you are to utilize this library to build and demonstrate a distributed and parallel application of your choosing by Thursday, October 26, 2017.
More details about this project and your deliverables are described below.
As mentioned in class, pub/sub systems are used on many real-world platforms such as Google Cloud and Amazon Web Services. These systems allow developers to construct distributed and parallel applications that operate concurrently by utilizing both message passing and event-driven programming paradigms.
Due to the limited time frame, the server is provided to you and located in AFS:
# Usage $ /afs/nd.edu/user15/pbui/pub/bin/ps_server -h Usage: /afs/nd.edu/user15/pbui/pub/bin/ps_server [options] General Options: -h Print this help message -p PORT Port to listen on (default: 9611) # Start Server on port 9411 $ /afs/nd.edu/user15/pbui/pub/bin/ps_server -p 9411 [1507080093] INFO Listening on port 9411
The communication between the client and server follows the simple text-based request and respond protocol described below:
Client Requests | Server Responds |
---|---|
IDENTIFY $USER_ID $NONCE Example IDENTIFY pbui 123 |
|
SUBSCRIBE $TOPIC Example SUBSCRIBE hot_topic |
|
UNSUBSCRIBE $TOPIC Example UNSUBSCRIBE hot_topic |
|
PUBLISH $TOPIC $LENGTH $BODY Example PUBLISH hot_topic 12 hello, world |
|
RETRIEVE $USER_ID Example RETRIEVE pbui |
|
DISCONNECT $USER_ID $NONCE Example DISCONNECT pbui 123 |
|
Some notes about this protocol:
Communication should be done via streaming network sockets.
Each message should be terminated with a newline character (ie. \n
),
except for the $BODY
of the PUBLISH
action.
Unknown operations will receive the following response: 400 Unknown command: $OPERATION
.
Clients must IDENTIFY
before performing any other operations.
Otherwise the result of any unauthorized operations will be: "401 Not
identified yet
.
Clients should maintain a connection to the server and send and receive messages on this one connection.
When a client disconnects, the server removes any subscriptions and messages associated with that client.
Furthermore, in this pub/sub system, the server will handle as many clients as system resources allow and will continuously process requests until the client disconnects (implicitly or explicitly).
The main goal of this project is to create a client library (ie.
lib/libps_client.a
) that communicates to a pub/sub server as described
above. This library will be used by a variety of test programs and an user
application.
The client library must implement a Client
class with the following
methods:
This constructs a Client
object with the specified server host
,
server port
, and client identify cid
.
Client::Client(const char *host, const char *port, const char *cid)
This schedules a message with the specified topic
, message
, and
length
to be published to the server.
void Client::publish(const char *topic, const char *message, size_t length)
This schedules a subscription to the topic
be sent to the server,
and records a Callback
for that particular topic
.
void Client::subscribe(const char *topic, Callback *callback)
This schedules an unsubscribe operation from the topic
to be sent to
the server, and removes all Callback
s for that particular topic
.
void Client::unsubscribe(const char *topic)
This schedules a disconnect operation and causes the client to shutdown.
void Client::disconnect()
This starts any required background messaging threads and then processes
incoming messages by calling the appropriate Callback
on any messages
until it is time to shutdown.
void Client::run()
This returns whether or not the client should shutdown.
bool Client::shutdown()
Additionally, the library must provide a Callback
class with the
following methods:
Callback
should be be used to process a Message
.void Callback::run(Message &m);
For instance, the library should provide the following EchoCallback
as
used in the bin/echo_test
program:
class EchoCallback : public Callback { public: void run(Message &m) { ... } };
Likewise, the library must provide a Message
struct with the following
fields:
std::string type // Message type (MESSAGE, IDENTIFY, SUBSCRIBE, UNSUBSCRIBE, RETRIEVE, DISCONNECT) std::string topic // Message topic std::string sender // Message sender size_t nonce // Sender's nonce std::string body // Message body
Finally, the library must provide a Thread
class with the following
methods:
This starts the thread with the specified func
and arg
.
void Thread::start(thread_func func, void *arg);
This joins the thread and stores the return value in the result
.
void Thread::join(void **result);
This detaches the thread so it can run without needing to be joined.
void Thread::detach();
The client library should utilize multiple POSIX threads to enable concurrent publishing and retrieving messages. This means that at any one time, the library should be able to do the following things concurrently:
Publishing: The library should be able to publish any messages that have been queued up.
Retrieving: The library should be able to retrieve any messages that the server has available to the client.
Callbacks: The library should able to process any retrieved messages by passing the retrieved message to any matching callback handlers.
Because of this, you should have at least 3
POSIX threads. To pass
messages between these threads, you should utilize concurrent data
structures.
Hide most of the complexity of multi-threaded programming by using concurrent data structures such as the queue we created in Lecture 11: Condition Variables. Note, you can use any combination of locks, condition variables, and semaphores to synchronize the threads in your client library.
To test the client library, we have provided the echo_test
program, which
is located in AFS:
# Run echo client test on pub/sub server on port 9611 $ /afs/nd.edu/user15/pbui/pub/bin/echo_test localhost 9611 `whoami`
To further test your client library, you will need to implement unit tests using the Google Test framework as described below.
Once you have implemented the client library and are confident in your testing, you must create a distributed and parallel application that utilizes the pub/sub system. Here are some possible applications:
Real-time message service (ie. chat)
System monitoring service (ie. nagios)
Parallel data processing service (ie. filtering, aggregation)
Internet-of-Things messaging (eg. send messages between some Raspberry Pi's)
Web crawler.
The exact user application is up to you. However, the application must utilize your client library and must utilize multiple threads correctly.
As noted above, you are to work in groups of one or two (three
is permitted, but discouraged) to implement pq
. You must use C++ as
the implementation language. Any test scripts or auxillary tools can be
written in any reasonable scripting language.
Here is a timeline of events related to this project:
Date | Event |
---|---|
Tuesday, October 03 | Project description and repository are available. |
Tuesday, October 10 | Client library is due (pushed to GitLab). |
Tuesday, October 24 | User application is due (pushed to GitLab). |
Thursday, October 26 | Demonstrations of application must be completed. |
To start this project, one group member must fork the Project 03 repository on GitLab:
https://gitlab.com/nd-cse-30341-fa17/cse-30341-fa17-project03
Once this repository has been forked, follow the instructions from Reading 00 to:
Make the repository private
Configure access to the repository
Make sure you add all the members of the team in addition to the instructional staff.
As you can see, the base Project 03 repository contains a README.md
file and the following folder hierarchy:
project03 \_ Makefile # This is the project Makefile \_ bin # This contains the binary executables \_ contrib \_ gtest # This contains the Google Test library \_ include \_ ps_client # This contains the ps_client header files \_ lib # This contains the ps_client library \_ src \_ client # This contains the ps_client source code \_ tests # This contains any test source code / scripts \_ units
You must maintain this folder structure for your project and place files in their appropriate place.
To help you get started, we have provided you with a Makefile
with all
the necessary targets:
$ make # Builds lib/libps_client.a bin/echo_test bin/client_unit $ make bin/echo_test # Builds echo_test executable $ make build/gtest # Explicitly build Google Test framework $ make clean # Removes all targets and intermediate objects
You will need to modify this Makefile
to support your user application.
While the exact organization of the project code is up to you, keep in mind that you will be graded in part on coding style, cleaniness, and organization. This means your code should be consistently formatted, not contain any dead code, have reasonable comments, and appropriate naming among other things:
Break long functions into smaller functions.
Make sure each function does one thing and does it well.
Abstract, but don't over do it.
Please refer to these Coding Style slides for some tips and guidelines on coding style expectations.
As part of your grade, you will need to present your user application to a TA where you will demonstrate the correctness of your pub/sub client library and the implementation of your user application.
While creating your user application, you should be creating tests in
the form of functional tests (ie. bin/echo_test
) and unit tests (ie.
bin/client_unit
).
The point of these tests is to ensure your application works correctly and to allow you to catch any possible bugs in your code. For instance, you may wish to test any concurrent data structures you created or to check how your client library handles message parsing and communication.
Ideally, each method or function you write would have a corresponding unit test or is covered by a functional test application.
To help with testing, we have included the Google Test framework in the Project 03 repository and have include some stubs you can expand to write your unit tests.
For this project, you must have both functional and unit tests and utilize them regularly during your application development process.
Writing all the tests at the end of your development process defeats the purpose of the test-driven development. What you should do is try to implement one feature or function at a time and test it as you build it. Rather than trying to implement multiple pieces all at once, focus on one thing at a time and incrementally build your application.
As part of your demonstration, you must provide a presentation (between 5
- 10
slides) with the following content:
Design: An overview of the design of your user application.
Implementation: A summary of how you implemented the user application and utilized the pub/sub system.
Testing: A discussion on how you tested your client library and your end user application.
Paradigms: A discussion on how you used three different concurrency programming paradigms: message passing, threading, and events and what the challenge of each model is.
Summary: A summary of what you learned.
Note, you should incorporate images, graphs, diagrams and other visual elements as part of your presentation where reasonable.
Please upload these slides to Google Drive and place a link in your
README.md
.
Be prepared to be asked about different aspects of your project, as the TA may ask you questions to probe your understanding of the material and your work.
As noted above, the Project 03 repository includes a README.md
file
with the following sections:
Members: This should be a list of the project members.
Design: This is a list of design questions that you should answer before you do any coding as they will guide you towards the resources you need.
Demonstration: This is where you should provide a Google Drive link to your demonstration slides.
Errata: This is a section where you can describe any deficiencies or known problems with your implementation.
You must complete this document report as part of your project.
You should look at the design questions in the README.md
file of the
Project 03 repository. The questions there will help you design and plan
your process queue implementation.
Feel free to ask the TAs or the instructor for some guidance on these questions before attempting to implement this project.
Once you have completed your project, you may extend your implementation of
libps_client
by performing either (or both) of the following
modifications:
Graphical User Interface: Create a graphical application using a
toolkit such as Qt that utilizes your libps_client
.
Scripting Interface: Create a language binding to your
libps_client
so that you can utilize the library from a scripting
language such as [Python] or [Ruby] to replicate the echo_test
program.
Each of these modifications is worth 1 Point of extra credit each.
Your project will be graded on the following metrics:
Metric | Points |
---|---|
Source Code
|
18.0
|
Demonstration
|
4.0
|
Documentation
|
2.0
|