From a monolith to clients and servers
----------------------------------------
At the moment, your program is a monolithic piece of code. It
consists of a game administrator and a bunch of players that
communicate via method calls:
+-------------------------------------+
|\ /|
| \ / |
| \ P1 / |
| \ / |
| \ |^ / |
| +-----||------------------+ |
| | v| | |
| | | |
| P2 | Admin | P4 |
| | | |
| ---> ---> |
| <--- <--- |
| | |^ | |
| +-----||------------------+ |
| / v| \ |
| / \ |
| / P3 \ |
| / \ |
|/ \|
+-------------------------------------+
In this day and age, however, software runs on the net. The
administrator in such a game is a service that someone offers, and
the players are separate programs that run somewhere else and
communicate with this service. In other words, we have a picture
that looks more like this:
+ +-------------------------------------+ +
|\ \ / /|
| \ \ / / |
| \ \ P1 / / |
| \ \ / / |
| \ +---------------------------+ / |
| | ^| + |
| | |v | |
| | +--------------------------+ | |
| | | | | |
| | | | | |
| P2 | | Admin | | P4 |
| | | | | |
| |-->| |-->| |
| |<--| |<--| |
| | | | | |
| | +--------------------------+ | |
| | ^| | |
| | |V | |
| + +-------------------------+ + |
| / / \ \ |
| / / \ \ |
| / / P3 \ \ |
| / / \ \ |
|/ / \ \|
+ +-------------------------------------+ +
The code is not linked into one program. Instead there are five
different programs. They don't communicate through direct calls to
each others methods, but through messages across the network.
What does the conversion take?
----------------------------------------
The conversion from a monolithic piece of software to a networked
system of programs requires careful planning.
(1) The resulting programs need to run on different machines. If
you don't have several machines to test your system or if you
just want to start the program a lot of times so that you can
observe and debug, you need to simulate the independence that
comes with several machines.
Here are some possibilities:
(a) write an OS script that forks off five independent
OS-level processes
(b) rewrite the main program that links the five components
into a program that spawns five language-level threads
(2) In many cases, the players and the administrator can no longer
communicate via method calls. Even though some languages
provide remote procedure calls (rpc) or remote method
invocations (rmi), we cannot assume in this age that all
programs are written in one and the same language. For
example, the server may be written in Java, one player in C++,
and another one in Scheme.
Hence, we need to replace all method call sequences with
message sending sequences. The exchange of a sequence of
messages is called a protocol. Designing and implementing a
protocol is beyond the scope of this course, but we will
discuss the rudimentary steps below.
(3) Point~2 implies that we can't possibly just separate the
components into five independent programs and leave it at
that. If we replace method calls with message sending, we must
also modify the programs.
At first glance, this would mean modifying "perfectly"
designed and tested code. Worse, the new code would have to
implement -- at some level -- the same functionality as the
code we have.
Instead of modifying the code, we should supplement it with an
additional layer of code that translates method calls on the
caller side into message send actions and that translates the
reception of a message into a method call on the caller side.
This is called the *proxy pattern* [have students look it up]
in the pattern literature. For example, a turn in Bazaar now
acts as follows:
(a) the administrator creates an instance of ITurn
(b) the instance is sent to a proxy of the player on the
admin's machine; the proxy was created as the player
registered with the admin
(c) the proxy translates the call into a message and sends it
to the player's machine
(d) there a proxy for the server receives the message
translates it into a method call, and calls the takeTurn
method of the player proper
Conversely, when the player invokes one of the methods in a turn,
(a) it actually calls a method in a proxy instance of turn
(b) the proxy translates the callback (say tradePebbles) into
a message and sends the message to the proxy of the player
on the server side
(c) the proxy player decodes the message and turns it into a
method call for the actual instance of the turn
(d) the result of the method call is sent back in a similar
manner.
an illustration
[Note: if your code doesn't satisfy the specified interface
precisely, you may also have to write an adapter and layer it
around the player.]
The above suggests that the protocol is the critical step.
How to design the protocol
----------------------------------------
The protocol represents method calls as messages. So the first
thing we need to do is identify all those method calls we need to
replace and agree on a textual representation.
To do that, it is best to consult the interfaces of the player and
the administrator and the sequence diagrams that show how the two
interact.
The interface specifies what a method call communicates from the
administrator to the player and what a method return communicates
back from the player to the administrator. The same is true for
method calls such as takeTurn and callbacks during the dynamic
extent of takeTurn.
Example:
To register, Main sends the administrator an instance of the
player. The instance uniquely identifies the player. The admin
then tells the player what his initial pebble is and what the
bartering equations are.
Main Administrator
| |
| |
| p = new p% |
| ---------------------------------> Player
| | |
| register(p) | |
| -----------------> | |
| | |
| | tell |
| | -------------> | % inform player of name,
| | | first pebble
| | | trading equations
| | |
This suggests two method calls:
- from the player program to the admin
- from the admin to the player
The first must say hello to the admin. The specific tcp
connection will identify the player to the admin, but an
additional identifying string won't be bad.
Client to Server messages is one of:
<string> % identifies a new player by name
Similarly, the administrator sends the player information about
the pebble and the bartering information:
Server to Client messages is one of:
(<string> <pebble> <equations>)
The message consists of several components, each of which may
consist of other components. We use parentheses to group these
components. This leads to what is known as 1958-XML.
The additional string is just an acknowledgment of the server
that the name has been recognized.
Both methods don't communicate anything upon return so we just
send a token back that things are okay. So here are the first few
lines of the message format specifications:
Client to Server messages is one of:
<string> % identifies a new player by name
(okay)
Server to Client messages is one of:
(<string> <pebble> <equations>)
(done)
All we need to do now is translate the method sequence diagram
into a message sending diagram and we're done.