RAGE : Reckoner AGEnts
An easy toolkit for distributed computing
Introduction
Rage is an easy to use framework for distributed computing. It
allows to develop, distribute and run calculus on heterogeneous
framework with no need of background in distributed computing or
agents technologies. The user can only direct its efforts towards
his very calculus.
Rage was buit to show that multi-agent paradigms: agent animacy,
organization and roles, are key notions to support the analysis,
design and implementation of open large-scale distributed systems.
It is particulary significant that those application models are in
adequation with multi-agent paradigms.
User point of view on Rage
Rage wants to be a framework that offers an easy development of
distributed calculus, that allows multi-applications computing in
parallel and that can scale with the available power (clustering).
Simplicity was a preliminary condition for the framework.
The main idea was to provide an easy framework for non computer
scientists. To achieve this goal, we had to define a small number of
concepts that the user has to understand in order to feed the system
with its calculus. It is important to note that the end user has no
need to know anything about agent or multi-agent systems, even if he
has to understand at least some object-oriented notions (since he must
inherits some predefined patterns).
The user has mainly to know two concepts :
- what is a task ?
- what is a a result ?
A task can be seen as the algorithm that is distributed, while
a result represents the data produced by the task and
which must be stored.
The user of the framework has to define what should be distributed,
and what is the global scheduling of its main algorithm.
The task defines the chunk of algorithm that will be
distributed among agents. The simplest way for the user to create a
task is to subclass the AbstractTask class and to
define the only two methods:
abstract public void compute();
abstract public boolean finished();
The computation has to follow this scheme, so the agent that has to
compute the task can do :
while(!finished() && !notInterrupted) { compute(); }
The complexity for the user is then not bigger than writing a Java
Applet.
Therefore, the end user has a rather OO view of the framework: he
extends one class to tailor it to his distributed application and uses
the underlying framework without necessary explicitly knowing what
happens.
Developer point of view on Rage
Rage has been developed with the Magique
toolkit. The framework is composed by several kind of agents :
- Boss is responsible of all the interactions with the user part
- Task Dispatcher has to dispatch tasks and deal with fault tolerancy
- Platform Manager manages the agents that will compute tasks, and must
ensure that there are always available tasks for these agents.
- Reckoner Agent is the worker of the framework, it computes tasks, and
send back the results of this computation.
- Repositories Manager manages the storage of results
- Result Repository is a mirror of the database of results (not yet implemented)
In fact agents in Magique, agents are all the same : they're empty shells. Then the
skills that we provide them, tailor them to their task. Thus, it isn't agents
that are described above, but more precisely roles, which are concretized
as skills. Besides agent's abilities, an organization of those agents is necessary
to provide a default communication path between them. The hierarchy for Rage is the
following :
Except the TaskManager which is on the "client side", all others agents
belongs to the framework. When a task is sent by the TaskManager
to the Boss, it is forwarded to the TaskDispatcher, which send
them back on request to PlaformManagers. Once tasks are available
to PlatformManagers, they are dispatched to ReckonerAgents that
compute them. When computations are over, results are stored thanks to the
ResultRepository. Then, clients can query the Boss to retrieve
results.
Tutorial example
This example illustrate how a simple computation can be distributed with Rage.
The aim is to compute an approximation of PI, using a Monte-Carlo method.
Here is the implementation of the task :
public class PiTask extends AbstractTask {
public Double pi;
private int currentIter;
private int total;
private int inner;
private int niter;
public PiTask(String factoryId, String taskId, int niter)
{
super(factoryId, taskId);
total = 0;
inner = 0;
this.niter = niter;
}
public void compute() {
double x, y, tmp;
int n = 0;
for(currentIter = 0; currentIter
< niter; currentIter ++) {
x = Math.random();
y = Math.random();
if (java.lang.Math.sqrt(x*x
+ y*y) <= 1.0)
inner ++;
}
tmp = (double)inner / (double)niter;
tmp *= 4;
pi = new Double(tmp);
}
public boolean finished() {
return currentIter == niter;
}
}
This simple example illustrate how a task should be designed :
- extending AbstractTask is the easiest way to create a
task (it is also possible to implement directly the Task interface,
- public members are automatically saved and returned as result (again
this can be customized, but the easiest way to return results is to use the
public modifier,
- implementing the finished and compute methods.
That is all that has to be done concerning tasks and results.
Now, the following source code illustrates how to retrieve results :
public void getPiResult() {
Set theResults = (Set) askNow("getAll");
int nres = theResults.size();
double pi = 0;
if (nres == 0) {
gui.setInfo("No
result found in repository");
return;
}
gui.setInfo(nres + " results found");
Iterator it = theResults.iterator();
while(it.hasNext()) {
Result
res = (Result) it.next();
Double
d = (Double) res.get(Double.class, "pi");
pi = pi
+ d.doubleValue();
}
pi = pi / nres;
gui.setInfo("Result PI = " + pi);
}
In this example, all results are retrieved, using the getAll
method, but it is possible to fetch results individually.
More examples can be found in the distribution : enjoy :)