How To Build ADistributed Fish Tank. Design Document For A Java-based Distributed Problem Solver With The Graphical Interface Of A Fish Tank.
|
|
|
1.0 Introduction
|
|
|
Our problem is the creation a network consisting of nodes of a computation. These nodes move through the network to and from entities that accept and process these nodes. We have a graphical presentation for this problem. Fish serve as nodes, as they contain a part of the computation problem, and the fish tanks that accept them, allow these nodes to interact with the computers on the network. While these fish compute, some of the processor time will go to doing graphics instead of solving the problem.
The solution is a peer-to-peer network system that follows the principles of encapsulation, data hiding, and code reuse. Naturally, an object-oriented language is best suited for such a design. We chose to implement the design in Java, instead of C++ or SmallTalk, because of Java's cross-platform abilities combined with Remote Machine Instructions (RMI). Using RMI both makes communication across the network easier and preserves an object-oriented paradigm by allowing us to pass messages directly to objects across a network. The design goals are to create a straight-forward object-oriented design that is reasonable to fully implement in two months. We also placed some criterion upon our design.
|
|
|
2.0 Overview
|
|
|
The networked fish tank can be viewed as three distinct high-level modules: the server, the fish shipper, and the actual fish themselves.
The server is a process running on a machine that serves two purposes. It handles registration of new fish shippers and sends out messages to have fish sent to the new shipper. It also directs the distributed computation carried out by the fish swimming in the network. Each fish shipper runs on a different user's machine on a network. The shipper is responsible for directing the sending fish from one tank to another. So, there is a 1-1 correspondence between fish shippers and tanks. While a fish is responsible for shipping itself, it uses the fish shipper to do so. In general, a fish tells a fish shipper: "Scoop me out of your local tank and arrange for me to arrive at another fish shipper running on another machine. That new fish shipper should then place me in its personal tank." The fish are what actually wanders through the network. They know how to draw themselves, have a message attached to them, and carry out a distributed computation. Also, each fish has behavior that governs when the fish will ship itself to another tank. Many provisions have been made so that the network of shippers will not be brought down by any reasonable number of machines being removed from the network, be it from crashing or some other reason, including the server. The network is organized as a web with edges connecting shippers constantly changing. This is in contrast to a token ring, which could not recover as easily from multiple machines being removed unexpectedly.
| |
|
2.1 Network Organization
|
|
|
We must ensure that new users can be added to the network and find out about other fish tanks while still maintaining a robust and decentralized network. To do so, we place the server as the entryway to the network. When a new shipper registers with the server, it instructs existing fish shippers to send the new shipper some fish to fill the tank. When the new fish arrive, the shippers will derive information about the location of other shippers from the fish. It will then add these locations to a list containing its most recent shippers. Fish will send themselves out to different shippers on that list. Because of this cross-pollination, the network should resemble a web instead of a token loop. The only times the fish will have to contact the server is to get a piece of the computation and when their section of the computation is done.
|
|
|
2.2 Communication
|
|
|
All communication between objects across the network is carried out by objects using RMI. This reduces the amount of coding required for communications across the network while keeping an object oriented design. While RMI is slow, it is reliable, which is much more important for our purposes.
|
|
|
2.3 Advantages
|
|
|
There are several advantages to this design, the most important is that the system will be able to function with multiple machine or network failures. While no shippers can be able to be added if the tank server crashes, the shippers already running can continue to operate. If the fish server crashes, no fish will be given a part of the computation to do nor will a fish be able to report back the results of its computation; however, the fish will still be able to be created and swim between tanks.
The division between the communication classes, the graphics classes, and the calculation methods make developing the system much easier. Do-nothing code modules can be substituted during development and then, as classes become available, they are substituted in.
|
|
|
2.4 Disadvantages
|
|
|
The major problem is the dependence between the Fish and FishShipper. This was necessary to allow the fish to have more control over its own behavior, while not setting up an extensive callback system. Testing the system without the basic components of at least the FishTank, FishList, FirstFish, NullFish, Fish, and a specific fish is very difficult.
Implementing the system involves a thorough understanding of threads and synchronization problems, but it is inherent in this type of system.
|
|
|
3.0 Modules
|
|
|
The modules section describes the projects in lower-level terms than the Overview section. Therefore, it breaks the above-mentioned Server, Fish Shipper, and Fish into smaller pieces.
|
|
|
3.1 Servers
|
|
|
This module is responsible for getting a tank up on the network and for keeping track of the computation. This module is split into two sub-modules, the tank server and the fish server, that are different processes running on possibly two different machines. The tank server is responsible for telling some tanks already existing on the network that a new tank has been added and that those veteran tanks need to send fish to the new tank. The tank server will periodically write tanks locations to disk, so that if it crashes and is restarted, it does not lose all of its data. The fish server keeps track of the computation. This server portions out parts of the computation to fish and puts all the parts together when it receives the results. Each fish will contact the server when it is created to get a piece of the computation, then it will contact the server again when the computation is done. The server will need to notify whomever is supervising the computation when a solution is found or when the search space is exhausted.
|
|
|
3.1.1 Tank Server
|
|
Responsibilities
|
|
|
3.1.2 Fish Server
|
|
Responsibilities
|
|
|
3.2 TankLocationBag
|
|
|
This module is an abstraction of having an unordered bag containing the locations of other tanks. The location itself, in reality an URL, is represented as a string. The bag module has certain properties. It can only hold a specific number of locations. When it is full, the oldest tank locations are thrown away first. There is a method to get a random location from the bag, as well as to remove a specific location from the bag. There is also method to add a location to the bag if it is not already in there and there is a method to compare two bags and add the locations that are not already in the bag "foo" to bag "bar." This set of modules abstracts the idea of a bag of locations that can easily be transferred to tanks and fish. Responsibilities
|
|
|
3.3 Tank
|
|
|
The tank is responsible for drawing itself on the screen and managing the list of fish in its tank. It has a list head and forwards messages from the fish shipper (such as, sending a fish to a specific tank) to the list. The tank will be responsible for clearing the tank of fish before it is destroyed. It will also have a list of all known tanks. Responsibilities
|
|
|
3.4 Fish
|
|
|
The fish module and all of its sub-components tries to abstract a real fish as much as possible. Each kind of fish has some common functionality, such as shipping themselves. On the other hand, each specific fish has different behavior, such as visual appearance and swimming speed. This is accomplished through inheritance and method overriding. The rest of the modules are a framework that allow the fish to swim through the network while performing their computation. The fish module can have many sub modules, some of them corresponding to specific kinds of fish. The fish class itself has the code to ship itself. It also has a generic method to draw itself on the screen, which allows the tank to treat each fish the same, while having all of the different types of fish act differently. Also, each fish is responsible for how it draws itself on the screen and for deciding when to ship itself. When a fish will ship is based on two factors: how many fish are in the tank and if there is a specific type of fish (say a shark) in the tank. When a fish ships itself, it will notify the list that it has left, which will allow all of the remaining fish to re-figure their own probabilities of leaving. Each fish should run its drawing in its own thread with the appropriate yield call so that it will not hog processor time on the Solaris platform (until the implementation of the Virtual Machine changes). Our fish act in an objective sense similarly to the queens of the 8-Queens problem. Each fish will know about its neighbor and will forward the appropriate messages to them. There is a NullFish that acts as a sentinel at the end of the list. Now, no fish has to check to see if it is the last fish in the list, and the NullFish can act appropriately when it receives messages (it will return 0 for the number of fish including it to its left, etc.) There is also be a FirstFish that will be the first fish in the list. Since the list is doubly linked, it will serve the same purpose as the NullFish; however, it will also add any new fish to the list right below it. The NullFish and the First fish are always in the tank and can never leave. There are also a fish list head for both the tank module and each individual fish, so when a fish leaves the list, that fish can tell the rest of the fish that it has left and code can be executed accordingly. Responsibilities
|
|
|
3.4.1 NullFish
|
|
Responsibilities
|
|
|
3.4.2 First Fish
|
|
Responsibilities
|
|
|
3.5 FishList
|
|
|
This module is responsible for holding the top of a list of fish. The list is doubly linked and all fish are passed to the FirstFish to be added to the list. All fish added to the list will have a pointer to the FishList so that they can tell the list when they are leaving or to check if a certain fish is in the tank. The list lets the fish manage themselves. Responsibilities
|
|
|
3.6 FishShipper
|
|
|
This module is responsible for receiving a fish and sending it to its own tank, as well as, receiving a message to send a fish to a specific tank. This message will be forwarded to the tank as well. The fish will be received using RMI. A fish will call a FishShipper's method to ship itself to the new tank to be reinstanciated. The tank must compare the fish's tank location bag with its own to get any new tank locations as well as having the fish add its location to its own bag. The fish will then be passed to the tank to be put in it to swim. Responsibilities
|
|
|
3.7 Calculation
|
|
|
This module does the calculation on the fish. It is specific to the problem being solved and what needs to be swapped out when a different problem is being tackled. It will run in its own thread and let the fish know when it is done. If it is possible, it should be able to swap these module on the fly.
Responsibilities
|
|
|
3.8 Inheritance
|
|
All other objects are descendant from the base class of Object.
|
|
|
3.9 Dependencies
|
|
Reasons for the circular dependencies: In almost all designs circular dependencies are extremely bad. It makes it very hard to test a class using regression testing (though in Java, not impossible) and to use a specific class you need to grab all of the objects in the circular dependency. However, the choice to have a circular dependency was a conscious one with its advantages far outweighing its disadvantages. First, the important classes are not in the circular dependence. By important we mean: the classes that we envision of being some real use to someone else and the classes that would have to be swapped out on a regular basis if the system is up and running for a long time. A TankLocationBag would be a useful thing to someone else, while a fish without the associated tank and fish list, etc. would not be. The calculation class and the fish server have a very simple and well defined dependency. It will be easy to rewrite those based on the problem to solve, test them in isolation and then plug them into the system. Also, the dependency of Fish on the Fish Shipper allows the fish to act more naturally like a fish. It can decide when to ship itself, and to a certain extent, where to ship itself. A design that would not have a circular dependency would have the fish shipper shipping the fish based on either when the fish wants to leave or when the shipper thinks it should leave. While either of these models makes logical sense, the implementation would require a fish shipper to periodically check to see if a fish wants to leave. This is a slow design, as it would have a lot of unnecessary method invocations. Also even with this design, one can not remove the dependency of the shipping object to the receiving object, which is inherent in RMI.
|
|
|
4.0 Scenarios
|
|
|
Here we provide a walk-through tour of the major object and module-level interactions.
|
|
|
4.1 Adding a new Fish Tank to the Network
|
|
1. User starts up Networked Fish Tank Java Application
|
|
|
4.2 Shipping a Fish from One Tank to Another
|
|
1. Fish tells Fish Shipper: "I want to leave."
|
|
|
4.3 Creating a Fish
|
|
1. Fish is created. The user picks the fish type and the fish's message.
|
|
|
4.4 Calculation
|
|
1. Fish contacts Fish Server and requests a calculation. The Fish Server returns a calculation.
|
|
|
4.5 Shipping A Fish
|
|
Left Column
Right Column
|
|