O O D B Design And Implementation - O D Manager and O D M S -

Joe

Thành viên VIP
21/1/13
2,969
1,311
113
Hi

(cont. of ODBWorker/ODMS)

Coding work is just a toiling work, but not a creative work. And we know that a creative work won't make you sweating. It makes you sleepless and that's the reason why the academics are usually slim, very slim and look like a sleepless zombies.


Img.1

The above figure shows you the "biggest" headache if you want to design and to implement a workable Distributed Object-Oriented Database. As you see in the configuration every client can access any server within the cluster. And his data could be stowed in one or more ODB servers. The wiring is never a problem. The virtual wiring between them is the real challenge:
  • One-to-all and all-to-one or only one-to-one as in a ring?
  • Broadcasting or point-to-point communication?
  • Static or dynamic communication?
  • Authentication and problem handling?
  • Data Consistency and multiple users?
  • Crucial (e.g. banking) or lazy (e.g. web paging) response time?
  • and more...
The mentioned points are in some cases mutually-inclusive, or worst: mutually exclusive. A ring of Servers is, for example, causes more delay (latency) from server to server and that physically worsens the quick response time, while in the Many-to-One and One-to-Many configuration each server can reach the others directly and that reduces the latency between the interconnection. Or in other words: fast response time.

The data access is within the server is also never a big problem. But the data access on "foreign" servers is a big headache. It requires not only the authentication (that takes naturally time), but also the "pre-knowledge" about the data on every one of them. Further, the data must be somehow synchronized and protected from intermingling (due to multiple users). A true implementing challenge.

As I show you roughly the communication between ODBConnect (client) and ODBWorker (server) and it is not difficult to see that the communication is a point-to-point communication. But: the data could be anywhere in the cluster. It requires also a Many-to-One communication. It's a contradiction in itself, isn't it? Well, NO. The figure in previous section also gives you a view that ODBWorker interacts with a boss named ODManager (Object Data Manager).

ODManager controls the real accesses to the real data (stored locally on any physical storage) and the accesses are done by ODMS (Data Management System). ODMS is the so-called "driver" which interacts with the local drivers of the local storage medium. The local storage-drivers (proxied by OS) deliver the raw data, and ODMS prepares them into some meaningful structures (e.g. keys, data caching, etc.) so that ODManager can present the requested data as an object to ODBWorker. In the next section I'll talk about this ODMS.

In case that the local ODMS cannot find the requested data ODManager must somehow find the way to ask all the remote ODMS for the needed data. To do that ODManager initiates some Agents which act as if they were some clients to the active servers in the cluster. The agents deliver the requested (distributed) data and their boss, ODManager, hands them to ODBWorker so that it can present them to its client ODBConnect.The agent is called ODBCluster -a similar construction as ODBConnect.


Img.2

The description sounds complex and complicated. But it isn't. Image 2 gives you the idea how this complex communication maze works. As simple as Kalashnikov principle, isn't it? The real DEVELOPMENT work is the programming logic. It's the work of the fluent communication process and the correct flow of interchanges of all participants.

The heaviest task of ODManager is to synchronize the requests of multiple clients (or ODBWorker and ODBCluster). The JAVA Concurrency Package is efficient enough to give you some reliefs. However, you should be careful with your logic. JAVA Concurrency does not mean that your multiple-users logic can be as lazy as it shouldn't be. Why? You are working with the time (communication) and the time won't tolerate any overlapping. Otherwise something could get lost and the data consistency would be damaged. And that's the reason.

The most difficult approach is the networking. Because there are always some servers (or nodes) offline or going down in a cluster, or some are coming up it's the challenge how to make the active nodes knowing about the events IN TIME.

  1. We can give a list of nodes in the cluster for each node and the node which is online (active) needs periodically "to ping" the nodes in the list and the positive or negative response gives the state of the pinged node. This Peer-to-Peer (P2P) way is not always instantly and the latency is high.
  2. Or we run the Publisher-Subscriber principle (PubSub). Every node is itself a Publisher for the others and at the same time a Subscriber to the others. When a node is up it broadcasts a message telling the others that it's now available (online), or unavailable (going down). This way happens almost instantly and the latency is low.

The Peer-To-Peer (P2P) principle is reliable, but quite inflexible. Every new added node requires a big update and the problem to inform the "unknown" clients that this or that node is active or inactive. For an implementation of Distributed ODB P2P is not very practicable.

The PubSub way is flexible, but quite unreliable. An opposite of P2P. Unreliable because the broadcasting message might get lost (UDP/IP transmission) or "unheard". However, unknown clients can participate the PubSub and so that they could know what nodes are available or what nodes are down. PubSub is quite simple and very practicable for both Clients and Servers. If you have some interests on this issue then read this blog "Instant Messaging System".

The PubSub implementation requires 3 parts:

  • An Event. Here: ODBEvent as a JAVA interface
  • A Publisher. Here: ODBBroadcaster
  • A Subcriber. Here: ODBEventListener on the Server site, ODBClientListener on the Client site.
PHP:
package joodb;
// Joe T. Nartca
public interface ODBEvent {
  public void odbEvent(String msg);
}
ODBBroadcater
PHP:
public class ODBBroadcaster extends Thread {
  public ODBBroadcaster(String host_port) {
    this.host_port = host_port;
    this.start();
  }
  /**
  @param msg String, message to be sent
  */
  public void broadcast(String msg) {
    msg = msg.trim();
    if (msg.length() > 0 && !msgLst.contains(msg)) msgLst.add(msg);
  }
  // stopped by ODBWorker
  public void halt() {
    loop = false;
  }
  public void run( ) {
    int port = host_port.indexOf(":");
    String host = host_port.substring(0, port);
    port = ODBTools.toInt(host_port.substring(port+1));
    try (MulticastSocket mcs = new MulticastSocket(port)){
      InetAddress group = InetAddress.getByName(host);
      while (loop) {
        while (msgLst.size() > 0) {
          String msg = msgLst.remove(0); // reset msg
          mcs.send(new DatagramPacket(msg.getBytes(), msg.length(), group, port));
        }
        java.util.concurrent.TimeUnit.MICROSECONDS.sleep(100);
      }
    } catch (Exception ex) { }
  }
  private String host_port;
  private volatile boolean loop = true;
  private volatile ArrayList<String> msgLst = new ArrayList<String>();
}
ODBEventListener
PHP:
public class ODBClientListener extends Thread {
  public ODBClientListener(String host_port) {
    this.host_port = host_port;
    this.start();
  }
  /**
  @param odbe ODBEvent, implemented object
  */
  public void addListener(ODBEvent odbe) {
    if (!set.contains(odbe)) set.add(odbe);
  }
  /**
  @param odbe ODBEvent, implemented object
  */
  public void removeListener(ODBEvent odbe) {
    set.remove(odbe);
  }
  public void exitListening() {
    listened = false;
  }
  public void run() {
    new Thread() {
      public void run() {
        while (listened) {
          while (msgLst.size() > 0) {
            String msg = msgLst.remove(0);
            for (ODBEvent odbe : set) odbe.odbEvent(msg);
          }
          try {
            TimeUnit.MILLISECONDS.sleep(1);
          } catch (Exception ex) { }
        }
      }
    }.start();
    int port = host_port.indexOf(":");
    String host = host_port.substring(0, port);
    port = ODBTools.toInt(host_port.substring(port+1));
    try (MulticastSocket dbListener = new MulticastSocket(port)){
       dbListener.joinGroup(InetAddress.getByName(host));
       while (listened) {
         DatagramPacket packet = new DatagramPacket(new byte[256], 256);
         dbListener.receive(packet); // wait for the incoming msg
         byte[] buf = packet.getData();
         String msg = new String(buf).trim();
         if (msg.endsWith("OFFLINE") || msg.endsWith("READY")) {
           if (!msgLst.contains(msg)) msgLst.add(msg);
         }
       }
    } catch (Exception ex) { }
  }
  private String host_port;
  private volatile boolean listened = true;
  private Set<ODBEvent> set = new CopyOnWriteArraySet<ODBEvent>( );
  private List<String> msgLst = Collections.synchronizedList(new ArrayList<String>());
}
An example (in JavaFX)
PHP:
// an implementation of ODBEvent
public class eDict extends Application implements ODBEvent {
  public void start(Stage stage) {
    Application.Parameters params = getParameters();
    java.util.List<String> pl = params.getRaw();
    cluster = new ArrayList<String>();
    if (pl.size() != 3) {
      System.out.println("userID Password port");
      System.exit(0);
    }
    try {
      int port = ODBTools.toInt(pl.get(2));
      // ODBConnect
      dict = new eOpenDict(pl.get(0),pl.get(1),"localhost", port);
      dict.getKeys();
    } catch (Exception ex) {
      ex.printStackTrace();
      System.exit(0);
    }
    // Register this app to ODBClientListener
    dict.register(this);
    ...
  }
  // implement the ODBEvent-----------------------------------------------------
  public void odbEvent(String msg) {
    String node = msg.substring(0, msg.indexOf(" "));
    // some noe is offline - refresh the key list
    if (msg.indexOf("OFF") > 0) {
      loadKeys( );
      txtArea.setText("Node:"+node+" is down. KeyList AutoRefresh.\n");
      cluster.remove(node);
      return;
    }
    // if new node -> refresh the key list
    if (!cluster.contains(node)) {
      loadKeys( );
      txtArea.setText("Node:"+node+" is active. KeyList AutoRefresh.\n");
      cluster.add(node);
    }
  }
//----------------------------------------------------------------------------------------------------------------
  private void loadKeys( ) {
    dict.getKeys();
    if (dict.words == null) {
      txtArea.setText("ODBServer is probably down...\n");
      dic.clear();
      return;
    }
    dic.setAll(dict.words);
  }
  ...
(Next: OODB Design and Implementation: ODMS)
 
Sửa lần cuối: