Skip to content
Andrew Trimble edited this page Oct 9, 2015 · 18 revisions

Core Concepts

The Main Interface

All interaction with BigIO is done via the io.bigio.BigIO object. This class allows components to register for events, send events and query the status of the system.

Basic Usage

In order to obtain an instance of io.bigio.BigIO, use the bootstrap method. For example:

import io.bigio.BigIO;

public static void main(String[] args) {
    BigIO bigio = BigIO.bootstrap();
    bigio.addListener(...);
    bigio.send(...);
}

In order to use this facility, the BigIO agent must be started on the process wishing to utilize BigIO. This is done by passing in the following parameter to the JVM:

-javaagent:bigio-agent-<version>.jar

Messages

Communication in BigIO is accomplished by sending and receiving messages. A message is a Java object annotated with the io.bigio.Message annotation. This annotation alerts the framework that encoding and decoding methods should be constructed for handling the serialization of the object. Messages are not serialized for notifications within a VM; they are sent as-is. However, for communication with other BigIO members, the message must be serialized before being sent over the wire.

A common issue is a ClassCastException when trying to serialize a message. This is caused when the @Message annotation is missing, or the Java Agent has not been attached to the runtime. In the latter case, ensure that the -javaagent:<path to bigio-agent jar> parameter is supplied when starting your program. See the run.sh for an example of how to properly use this parameter.

Sending Messages

Messages are sent across topics and optionally, partitions. They can be sent right away, or with an optional time offset. bigio.sendMessage("ATopic", new Message()) will send the message right away, while bigio.sendMessage("ATopic", new Message(), 1000) will send a message in 1 second. In order to ensure accurate delivery, all messages are sent immediately and it is left to the receiving BigIO instance to handle the time offset.

For a given topic, messages can be sent in one of 3 ways: Broadcast to all listeners, Round Robin to a single listener in a round robin fashion or Random to a single, random listener. bigio.setDeliveryType("ATopic", DeliveryType.RANDOM) will set the delivery type for a given topic.

Receiving Messages

Objects interested in receiving messages over a given topic need only register with the Speaker to receive such objects. The receiver neither knows nor cares if the message has been generated inside the same VM or in an external process.

private BigIO bigio; // Initialize this with injection or bootstrapping.

// As an anonymous inner class
bigio.addListener("ATopic", new MessageListener<MyMessage>() {
    @Override
    public void receive(MyMessage message) {
        // process message
    }
});

// As a Lambda expression
bigio.addListener("ATopic", (MyMessage message) -> {
    // process message
});

Message Interceptors

MessageInterceptors are used for intercepting a message before it is processed by the listeners. This is useful logging purposes, injecting message latency or even altering messages before they are received. The main difference between Interceptors and Listeners is that Interceptors work on the Envelope object. Envelopes contain the decoded message payload as well as meta-data e.g., sender, topic, partition etc. The following example adds an interceptor that logs the sender of the message.

private BigIO bigio; // Initialize this with injection or bootstrapping.

// As an anonymous inner class
bigio.addInterceptor("ATopic", new Interceptor() {
    @Override
    public Envelope intercept(Envelope envelope) {
        System.out.println(envelope.getSenderKey());
        return envelope;
    }
});

// As a Lambda expression
bigio.addInterceptor("ATopic", (Envelope envelope) -> {
    System.out.println(envelope.getSenderKey());
    return envelope;
});

Topics and Partitions

Messages are sent across topics. Topics are defined as strings. Messages must be sent across a specific topic and listeners must be added to a specific topic. When registering a listener, a regular expression may be used to select the topic across which to listen.

Partitions are a further filter mechanism. If provided either by the producer or the consumer, the messages will only go to members matching both topic name and partition name. If no partition name is provided, the producer or consumer will register for a topic across all partitions.

It should be noted that topics are strongly typed. An error will occur if the incorrect type of message is sent across a topic.

Putting it All Together

@Message
public class MyMessage {
    private String message;

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }
}

@Component
public class Consumer {
    
    @Inject
    private BigIO bigio;

    public Consumer () {

    }

    @Initialize
    public void initialize() {
        bigio.addListener("MyTopic", new MessageListener<MyMessage>() {
            @Override
            public void receive(MyMessage message) {
                System.out.println(message.getMessage());
            }
        });
    }
}

@Component
public class Producer {

    @Inject
    private BigIO bigio;

    public Producer() {

    }

    @Initialize
    public void init() {
        new Thread() {
            @Override
            public void run() {
                try {
                    while(true) {
                        MyMessage message = new MyMessage();
                        message.setMessage("Hello World!");
                        speaker.send("MyTopic", message);

                        Thread.sleep(1000l);
                    }
                } catch(Exception ex) {

                }
            }
        }.start();
    }
}

In the previous example, three components are defined: a message, a message producer and a message consumer. The message producer will send messages across the topic "MyTopic" once per second. The consumer will simply print the string within the message. Even if the producer and consumer are in separate instances of BigIO separated by a network, the messages will still be delivered.

Deployment

Messages and components can be in any number of jars. The Runtime project constructs a fully functional package of BigIO. It is a Maven project that produces a .zip file and a .tar.gz file that, when extracted, provides a complete installation of the software. In order to deploy a set of jars to BigIO, simply place the jars in the "components" directory of the extracted installation and start the system via the "run.bat" or "run.sh" script. BigIO will automatically detect the components and instantiate them.

Reference Injection

BigIO is constructed around the Inversion of Control (IoC) concept (see this Wikipedia article for more information). In such a system, object instances are constructed by the framework and injected at runtime. This allows specific implementations to be selected at runtime rather than at compile time. For system objects (such as the Speaker), this ensures that there is a single, common instance and removes the need to "new-up" framework objects.

In BigIO's IoC framework, components are defined by the @Component annotation. This alerts the framework that it should handle instantiation of these objects. References to Components are injected via the @Inject annotation. The following example should clarify the concept:

import io.bigio.Component;
import io.bigio.Inject;
import io.bigio.Initialize;

@Component
public class UtilityComponent {
    // BigIO requires a no-argument constructor for components
    public UtilityComponent () {
        ...
    }

    public void doSomething() {
        ...
    }
}

@Component
public class Worker {

    // BigIO will provide an actual object for this field at runtime
    @Inject
    private UtilityComponent util;

    public Worker() {

    }

    // Methods with @Initialize will be called once all references have been injected
    @Initialize
    public void init() {
        util.doSomething();
    }
}

While BigIO provides a light-weight container capability, it is possible and often desirable to use other technologies such as Spring or Guice. To facilitate this, BigIO provides a static bootstrap method that will instantiate BigIO and return a fully functional Speaker object. In order to bootstrap the system, simply call Starter.bootstrap()

BigIO Shell

When running with the standard BigIO deployment, a shell is provided that implements several command line interface options. In addition, it is possible to create your own shell command by implementing the io.bigio.CommandLine interface. The provided commands are as follows:

  • help - displays a list of installed commands
  • log - sets the log level of the console appender
  • tag - displays and sets tags associated with BigIO cluster members
  • members - displays all known BigIO cluster members
  • components - displays a list of of all installed components in the current BigIO instance
  • listeners - displays all listeners across a BigIO cluster
  • net - displays information about all network interfaces on the current machine
  • mem - displays current memory usage
  • threads - displays all JVM threads
  • whoami - displays the IP address and used ports of the current BigIO process
  • gc - perform a garbage collection
  • exit - exits out of BigIO
  • quit - exits out of BigIO