[jgroups-dev] Advice on best practices for implementing a new protocol
Mike Jensen <[email protected]>
| Newsgroups | gmane.comp.java.javagroups.devel |
|---|---|
| Message-ID | <[email protected]> |
I started work today to implement my mesh (tentatively calling it
"TREEMESH" but open to suggestions, I am so uncreative with names >.<).
So far a couple (simple) questions have come up.
The first question is, how much information is too much for a header
placed on a message? Currently I am storing the hop record for each
message in the header. Which contains the JGroups address of each hop,
and 3 shorts for each hop. Would having a potentially largish header be
an issue (biggest natural concern of mine would be fragmentation)?
How should properties that should be defined as final be used? Meaning,
I have a couple properties that should be final, because they are needed
at init() time and after that should not be adjusted. But I would still
like to allow them to be configurable on some level before run time.
Should I define those as static finals, and expect code changes for them
to change (a user probably wont want to change them anyways)? Or is
there a better way to set them so they can be more flexible? An example
of these two values are:
* ProcessPeriod, period of wait time for regular run processes. I
schedule a thread which regularly will look if it needs to establish new
connections, timeout dead peers, and remove duplicate connections (which
may likely occur with leaf connections due to two leaves both deciding
at the same time they want to connect to each other, this is likely
since they probably have the same internal models and thus both decide
they want a connection at the same time). This can't be adjusted
because the value will be used at init time to schedule a reoccurring task.
* sampleSize, this value determines how quickly we want to make
decisions vs how sure we want to be of our selves before any decision or
recommendations are made....with some changes this could be adjusted at
runtime, but I think that would be a mistake
Initial discovery....I was hoping I could reuse one of the existing
discovery protocols like TCPPING. But I am struggling to understand
how, and wondering if this is possible now. Basically I was hoping the
initial join process could look something like this
* Discovery finds a node that we can connect to
* We connect to this node and now discovery goes hands off for further
work (meaning once we establish a connection to the first node, I don't
want this protocol layer to try and start connecting to more nodes).
* The TreeMESH protocol then exchanges information with this node, it
may remain connected, but most likely we will just be informed of a
different node we should connect to instead.
* Once established in the mesh, if we are a leaf, the TreeMESH protocol
will identify other leafs that would be good choices and connect to them too
The problem is, I don't see how other protocols become aware of
discovery events (without the use of GMS which sends view updates, but I
don't think will work for my needs). Am I correct in saying that
Event.CONNECT only represents this process joining the channel (not
establishing a connection to another node, or another node establishing
to us...if that is the case, how do we even know when another node has
connected to us?)? As far as I can tell, I will need to create my own
discovery in order to accomplish what I described above?
(p.s. while investigating this, I think I found a small logging bug. I
think line 499 (CVS head) in JChannel should be this.cluster_name...i
was thinking this might return the wrong name if your trying to connect
to a different channel than your already connected to)
I am not sure how to tell the transport layer to establish a connection
directly to another node. I assume this would be an event, but I can't
tell what kind of event would do such a thing.
If you have time, I would appreciate a quick look at the start of my
main TREEMESH.java protocol (attached). Right now it is just a template
with lots of TODO statements. I am sure there is much more definition
that needs to be added in here still. But I was hoping you guys could
take a quick look through it and let me know if I am on the right track,
or what recommendations you have. My biggest concern right now is how
to integrate in the protocol stack properly. I am not sure what events
I can/will get, or what events I should send.
Feel free to ask any questions you might have about the directions I
have been taking this. Thanks for spending some time to help me get
some momentum to this.
Cheers
------------------------------------------------------------------------------
Sell apps to millions through the Intel(R) Atom(Tm) Developer Program
Be part of this innovative community and reach millions of netbook users
worldwide. Take advantage of special opportunities to increase revenue and
speed time-to-market. Join now, and jumpstart your future.
http://p.sf.net/sfu/intel-atom-d2d
_______________________________________________
Javagroups-development mailing list
TREEMESH.java
(text/x-java, 6.7 KB)
package org.jgroups.protocols.jentMESH;
import org.jgroups.*;
import org.jgroups.annotations.*;
import org.jgroups.protocols.RELAY.RelayHeader;
import org.jgroups.stack.Protocol;
import org.jgroups.util.TimeScheduler;
import org.jgroups.util.Util;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.Serializable;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.TimeUnit;
/**
* Protocol to establish groups where all nodes are not connected to all other nodes.
* See design document located at: http://archive.jentfoo.com/coding_projects/jentMesh_design.pdf
*
* @author Mike Jensen
*/
@Experimental @Unsupported
@MBean(description="TREEMESH protocol")
public class TREEMESH extends Protocol {
/* ------------------------------------------ Properties ---------------------------------------------- */
@Property(description="How frequently in milliseconds should the node broadcast it's heartbeat")
protected int broadcastIntravel = 1000;
@Property(description="How long till we timeout a silent node, should be above the heartbeat")
protected int nodeTimeout = broadcastIntravel * 2;
@Property(description="How many connections should each peer try to maintain")
protected short peerConMax = 3;
@Property(description="Number representing how sure we want to be before we make decisions")
protected final short sampleSize = 5;
@Property(description="Time delay between regular process calls in milliseconds")
protected final long processPeriod = 500;
/* --------------------------------------------- Fields ------------------------------------------------ */
protected Address local_addr;
@ManagedAttribute
protected volatile boolean is_coord = false; // TODO - is_coord = Util.isCoordinator(view, local_addr);
protected TimeScheduler timer;
private MeshModel meshModel;
public void init() throws Exception {
meshModel = new MeshModel(sampleSize);
timer = getTransport().getTimer();
// schedule a thread to do regular tasks
timer.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
maybeMakePeerConnections();
maybeMakeLeafConnections();
removeDuplicateConnections();
timeoutDeadNodes();
}
}, 500, processPeriod, TimeUnit.MILLISECONDS);
// schedule a thread to send the heartbeat
timer.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
// TODO - send heartbeat
}
}, 500, broadcastIntravel, TimeUnit.MILLISECONDS);
}
protected void maybeMakePeerConnections() {
/* TODO - if no peer connections exist,
* connect to a peer recommended from the MeshModel */
}
protected void maybeMakeLeafConnections() {
/* TODO - if one peer connection exists, and we have less than peerConMax - 1 leaf connections,
* establish a connection to the most distant leaf */
}
protected void removeDuplicateConnections() {
/* TODO - cycle through all connections,
* if there is a duplicate connection and the hash of our address is lower than the remote address,
* remove the connection
* (if it is higher, we expect the remote peer will destroy the connection,
* this way both connections are not destroyed at the same time) */
}
protected void timeoutDeadNodes() {
/* TODO - Cycle through all known nodes,
* if we have not heard any heartbeats or messages that are from or passed through that node,
* assume they are dead and take appropriate action */
}
public void stop() {
}
public Object down(Event event) {
switch (event.getType()) {
case Event.MSG:
// add our header on the message before passing on
Message msg = (Message)event.getArg();
msg.putHeaderIfAbsent(getId(), new TreeMeshHeader(local_addr,
meshModel.getAvgHops(),
meshModel.peerConnectionCount(),
meshModel.leafConnectionCount()));
break;
case Event.DISCONNECT:
break;
case Event.SET_LOCAL_ADDRESS:
local_addr = (Address) event.getArg();
break;
default:
log.warn("Got unhandled down event: " + event.toString()); // TODO - remove temp logging
break;
}
return down_prot.down(event);
}
public Object up(Event event) {
switch (event.getType()) {
case Event.MSG:
Message msg = (Message)event.getArg();
Address dest = msg.getDest();
TreeMeshHeader hdr = (TreeMeshHeader)msg.getHeader(getId());
// analyze the message for more informatio
meshModel.analyzeMessageHopRecord(hdr.hopRecord);
// TODO - forward the msg to other connected nodes as needed
// TODO - forward to application if needed
break;
default:
log.warn("Got unhandled up event: " + event.toString()); // TODO - remove temp logging
break;
}
return up_prot.up(event);
}
public static class TreeMeshHeader extends Header {
private List<MsgHop> hopRecord;
private TreeMeshHeader() {
hopRecord = new LinkedList<MsgHop>();
}
public TreeMeshHeader(Address local_addr, short avgHops, short peerConnections, short leafConnections) {
this();
addHop(local_addr, avgHops, peerConnections, leafConnections);
}
public int size() {
// hop entries * size of entry
return hopRecord.size() * (((Short.SIZE / 8) * 3) + 1); // TODO - this is not very accurate
}
public void writeTo(DataOutputStream out) throws IOException {
}
public void readFrom(DataInputStream in) throws IOException, IllegalAccessException,
InstantiationException {
}
public void addHop(Address local_addr, short avgHops, short peerConnections, short leafConnections) {
if (local_addr != null) {
hopRecord.add(new MsgHop(local_addr, avgHops, peerConnections, leafConnections));
} else {
// TODO - log something scary
}
}
public class MsgHop implements Serializable {
public final Address host;
public final short avgHops;
public final short peerConnections;
public final short leafConnections;
public MsgHop(Address host, short avgHops, short peerConnections, short leafConnections) {
this.host = host;
this.avgHops = avgHops;
this.peerConnections = peerConnections;
this.leafConnections = leafConnections;
}
}
}
}