`
kavy
  • 浏览: 884126 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

JGroups tutorial

 
阅读更多

Chapter 2. Writing a simple application

The goal of this chapter is to write a simple text-based chat application (SimpleChat), with the following features:

  • All instances of SimpleChat find each other and form a cluster.
  • There is no need to run a central chat server to which instances have to connect. Therefore, there is no single point of failure.
  • A message is sent to all instances of the cluster.
  • An instance gets a notification callback when another instance leaves (or crashes) and when other instances join.
  • (Optional) We maintain a common cluster-wide shared state, e.g. the chat history. New instances acquire that history from existing instances.

 

2.1. JGroups overview

JGroups uses a JChannel as the main API to connect to a cluster, send and receive messages, and to register listeners that are called when things (such as member joins) happen.

What is sent around are Messages, which contain a byte buffer (the payload), plus the sender's and receiver's address. Addresses are subclasses of org.jgroups.Address, and usually contain an IP address plus a port.

The list of instances in a cluster is called a view (org.jgroups.View), and every instance contains exactly the same View. The list of the addresses of all instances can get retrieved by calling View.getMembers().

Instances can only send or receive messages when they've joined a cluster.

When an instance wants to leave the cluster, methods JChannel.disconnect() or JChannel.close() can be called. The latter actually calls disconnect() if the channel is still connected before closing the channel.

2.2. Creating a channel and joining a cluster

To join a cluster, we'll use a JChannel. An instance of JChannel is created with a configuration (e.g. an XML file) which defines the properties of the channel. To actually connect to the cluster, the connect(String name) method is used. All channel instances which call connect() with the same argument will join the same cluster. So, let's actually create a JChannel and connect to a cluster called "ChatCluster":

import org.jgroups.JChannel;

public class SimpleChat {
    JChannel channel;
    String user_name=System.getProperty("user.name", "n/a");

    private void start() throws Exception {
        channel=new JChannel();
        channel.connect("ChatCluster");
    }

    public static void main(String[] args) throws Exception {
        new SimpleChat().start();
    }
}
        

First we create a channel using the empty contructor. This configures the channel with the default properties. Alternatively, we could pass an XML file to configure the channel, e.g. new JChannel("/home/bela/udp.xml").

The connect() method joins cluster "ChatCluster". Note that we don't need to explicitly create a cluster beforehand; connect() creates the cluster if it is the first instance. All instances which join the same cluster will be in the same cluster (of course!), for example if we have

  • ch1 joining "cluster-one"
  • ch2 joining "cluster-two"
  • ch3 joining "cluster-two"
  • ch4 joining "cluster-one"
  • ch5 joining "cluster-three"

, then we will have 3 clusters: "cluster-one" with instances ch1 and ch4, "cluster-two" with ch2 and ch3, and "cluster-three" with only ch5.

2.3. The main event loop and sending chat messages

We now run an event loop, which reads input from stdin ('a message') and sends it to all instances currently in the cluster. When "exit" or "quit" quit are entered, we fall out of the loop and close the channel.

    private void start() throws Exception {
        channel=new JChannel();
        channel.connect("ChatCluster");
        eventLoop();
        channel.close();
    }

    private void eventLoop() {
        BufferedReader in=new BufferedReader(new InputStreamReader(System.in));
        while(true) {
            try {
                System.out.print("> "); System.out.flush();
                String line=in.readLine().toLowerCase();
                if(line.startsWith("quit") || line.startsWith("exit")) {
                    break;
                }
                line="[" + user_name + "] " + line;
                Message msg=new Message(null, null, line);
                channel.send(msg);
            }
            catch(Exception e) {
            }
        }
    }
        

We added the call to eventLoop() and the closing of the channel to the start() method, and we provided an implementation of eventLoop.

The event loop blocks until a new line is ready (from standard input), then sends a message to the cluster. This is done by creating a new Message and calling Channel.send() with it as argument.

The first argument of the Message constructor is the destination address. A null destination address means send the message to everyone in the cluster (a non-null address of an instance would send a message from us to only 1 instance).

The second argument is our own address. This is null as well, as the stack will insert the correct address anyway.

The third argument is the line that we read from stdin, this uses Java serialization to create a byte[] buffer and set the message's payload to it. Note that we could also serialize the object ourselves (which is actually the recommended way !) and use the Message contructor which takes a byte[] buffer as third argument.

The application is now fully functional, except that we don't yet receive messages or view notifications. This is done in the next section below.

2.4. Receiving messages and view change notifications

Let's now register as a Receiver to receive message and view changes. To this end, we could implement org.jgroups.Receiver (with 6 methods), however, I chose to extend ReceiverAdapter which has default implementations, and only override callbacks (receive() and viewChange()) we're interested in. We now need to extend ReceiverAdapter:

public class SimpleChat extends ReceiverAdapter {
            

 

, set the receiver in start():

    private void start() throws Exception {
        channel=new JChannel();
        channel.setReceiver(this);
        channel.connect("ChatCluster");
        eventLoop();
        channel.close();
    }
            

 

, and implement receive() and viewAccepted():

   public void viewAccepted(View new_view) {
        System.out.println("** view: " + new_view);
    }

    public void receive(Message msg) {
        System.out.println(msg.getSrc() + ": " + msg.getObject());
    }
            

 

The viewAccepted() callback is called whenever a new instance joins the cluster, or an existing instance leaves (crashes included). Its toString() method prints out the view ID (an increasing ID) and a list of the current instances in the cluster

In receive(), we get a Message as argument. We simply get its buffer as an object (again using Java serialization) and print it to stdout. We also print the sender's address (Message.getSrc()).

Note that we could also get the byte[] buffer (the payload) by calling Message.getBuffer() and then de-serializing it ourselves, e.g. String line=new String(msg.getBuffer()).

2.5. Trying out the SimpleChat application

Now that the demo chat application is fully functional, let's try it out. Start an instances of SimpleChat:

                [mac] /Users/bela$ java SimpleChat
                -------------------------------------------------------
                GMS: address is 192.168.0.6:49963
                -------------------------------------------------------
                ** view: [192.168.0.6:49963|0] [192.168.0.6:49963]
                >
            

 

The address of this instance is 192.168.0.6:49963 (IP address:port). It is the only instance so far. So let's start the second instance and type something:

 

                [mac] /Users/bela$ java SimpleChat
                -------------------------------------------------------
                GMS: address is 192.168.0.6:49964
                -------------------------------------------------------
                ** view: [192.168.0.6:49963|1] [192.168.0.6:49963, 192.168.0.6:49964]
                >
            

 

The cluster list is now [192.168.0.6:49963, 192.168.0.6:49964], showing the first and second instance that joined the cluster. Note that the first instance (192.168.0.6:49963) also received the same view, so both instances have the exact same view with the same ordering of its instances in the list. The instances are listed in order of joining the cluster, with the oldest instance as first element.

Sending messages is now as simple as typing a message after the prompt and pressing return. The message will be sent to the cluster and therefore it will be received by both instances, including the sender.

If the word "exit" or "quit" is entered, then the instance will leave the cluster gracefully. This means, a new view will be installed immediately.

To simulate a crash, simply kill an instance (e.g. via CTRL-C, or from the process manager). The other surviving instance will receive a new view, with only 1 instance (itself) and excluding the crashed instance.

2.6. Extra credits: maintaining shared cluster state

One of the uses of JGroups is for maintaining state that is replicated across a cluster. For example, state could be all the HTTP sessions in a web server. If those sessions are replicated across a cluster, then clients can access any server in the cluster after a server which hosted the client's session crashed, and the user sessions will still be available.

Any update to a session is replicated across the cluster, e.g. by serializing the attribute that was modified and sending the modification to every server in the cluster via JChannel.send(). This is needed so that all servers have the same state.

However, what happens when a new server is started ? That server has to somehow get the existing state (e.g. all HTTP sessions) from an existing server in the cluster. This is called state transfer.

State transfer in JGroups is done by implementing 2 (getState() and setState()) callbacks and calling the JChannel.getState() method. method. Note that, in order to be able to use state transfer in an application, the protocol stack has to have a state transfer protocol (the default stack used by the demo app does).

The start() method is now modified to include the call to JChannel.getState():

    private void start() throws Exception {
        channel=new JChannel();
        channel.setReceiver(this);
        channel.connect("ChatCluster");
        channel.getState(null, 10000);
        eventLoop();
        channel.close();
    }
            

 

The getState() method actually returns a boolean, which is false for the first instance in a cluster, and should be true for subsequent instances.

The Receiver interface defines a callback getState() which is called on an existing instance to fetch the cluster state. In our demo application, we define the state to be the chat conversation. This is a simple list, to the tail of which we add every message we receive. (Note that this is probably not the best example for state, as this state always grows. As a workaround, we could have a bounded list, which is not done here though).

The list is defined as an instance variable:

    final List<String> state=new LinkedList<String>();
            

 

The getState() callback implementation is

    public byte[] getState() {
        synchronized(state) {
            try {
                return Util.objectToByteBuffer(state);
            }
            catch(Exception e) {
                e.printStackTrace();
                return null;
            }
        }
    }
            

 

The getState() method is called in the state provider, ie. an existing instance, to return the shared cluster state.

Since access to state may be concurrent, we synchronize it. Then we call Util.objectToByteBuffer() which is a JGroups utility method using simple serialization to generate a byte buffer from an object.

The setState() method is called on the state requester, ie. the instance which called JChannel.getState(). Its task is to deserialize the byte buffer and set its state accordingly:

    public void setState(byte[] new_state) {
        try {
            List<String> list=(List<String>)Util.objectFromByteBuffer(new_state);
            synchronized(state) {
                state.clear();
                state.addAll(list);
            }
            System.out.println("received state (" + list.size() + " messages in chat history):");
            for(String str: list) {
                System.out.println(str);
            }
        }
        catch(Exception e) {
            e.printStackTrace();
        }
    }
            

 

We again call a JGroups utility method (Util.objectFromByteBuffer()) to create an object from a byte buffer (using Java serialization).

Then we synchronize on state, and set its contents from the received state.

We also print the number of messages in the received chat history to stdout. Note that this is not feasible with a large chat history, but - again - we could have a bounded chat history list.

2.7. Conclusion

In this tutorial, we showed how to create a channel, join and leave a cluster, send and receive messages, get notified of view changes and implement state transfer. This is the core functionality provided by JGroups through the JChannel and Receiver APIs.

JGroups has two more areas that weren't covered: building blocks and the protocol stack.

Building blocks are classes residing on top of a JChannel and provide a higher abstraction level, e.g. request-response correlators, cluster-wide method calls, replicated hashmaps and so forth.

The protocol stack allows for complete customization of JGroups: protocols can be configured, removed, replaced, enhanced, or new protocols can be written and added to the stack.

We'll cover the protocol stack and available protocols in a later article.

The code for SimpleChat can be found here.

Here are some links for further information about JGroups:

 

分享到:
评论

相关推荐

    JavaEE源代码 jgroups-2.2.8

    JavaEE源代码 jgroups-2.2.8JavaEE源代码 jgroups-2.2.8JavaEE源代码 jgroups-2.2.8JavaEE源代码 jgroups-2.2.8JavaEE源代码 jgroups-2.2.8JavaEE源代码 jgroups-2.2.8JavaEE源代码 jgroups-2.2.8JavaEE源代码 ...

    Ehcache通过Jgroups做集群

    在Ehcache通过Jgroups进行集群配置时,首先需要理解Jgroups的配置文件——`jgroups.xml`。这个文件定义了集群中节点如何相互发现、通信以及故障检测的规则。配置文件中的关键元素包括: 1. **Transport**: 定义了...

    Jgroups 教程

    ### JGroups教程:深入理解与应用 #### 一、安装JGroups JGroups是一款高性能、高可用性的集群通信中间件,适用于构建分布式系统。本文档将详细介绍如何安装配置JGroups,并编写一个简单的应用来演示其主要功能。 ...

    jgroups-2.2.7.jar

    jgroups-2.2.7.jar jgroups-2.2.7.jar

    JGroups的Raft实现jgroups-raft.zip

    jgroups-raft 项目是 JGroups 框架对 Raft 的实现。Maven:&lt;groupId&gt;org.jgroups &lt;artifactId&gt;jgroups-raft &lt;version&gt;0.2&lt;/version&gt;Raft 是一个容易理解的共识算法。在容错和性能方面它相当于 Paxos(Google 的一致...

    jgroups.part1

    jgroups.part1

    基于JGroups的共享电子白板系统的研究与实现

    ### 基于JGroups的共享电子白板系统的研究与实现 #### 一、引言与背景 在当今数字化时代,远程教育与协同工作已成为推动社会进步的重要力量。电子白板作为计算机支持的协同工作(Computer Supported Cooperative ...

    jgroups源代码

    《深入解析JGroups开源框架:基于belaban-JGroups-19d7183源代码》 JGroups是一个用于构建高可用性集群的Java框架,它提供了可靠的消息传递、组成员管理和故障检测等功能,广泛应用于分布式系统中。本文将基于bela...

    jgroups官方帮助文档html格式打包2.X版本

    此文档主要针对JGroups 2.X版本的官方帮助文档进行详细解读,旨在帮助开发者深入理解并有效地利用JGroups。 一、JGroups简介 JGroups的核心目标是确保在分布式环境中数据的一致性。它提供了一套完整的工具,用于...

    JGroups-jdk.zip_jgroups

    《JGroups:构建高效可靠的组通信系统》 JGroups是一个用Java编程语言编写的开源库,专注于实现基于IP组播的高效、可配置的组通信协议栈。它为分布式系统提供了一种健壮且灵活的方式来实现节点间的通信,是构建大...

    jgroups-3.0.2

    JGroups是一个开源的纯java编写的可靠的群组通讯工具。其是一个可靠的组播通讯工具集(需要说明的是,这并不是说必须要使用IP Multicast,JGroups也可以使用TCP来实现)。其工作模式基于IP多播,但可以在可靠性和群组...

    Jgroups中的UNICAST3协议中文翻译

    Jgroups 中的 UNICAST3 协议详解 Jgroups 是一种基于 IP 多播的可靠的组播中间件,UNICAST3 协议是 Jgroups 中的一种单播协议,旨在保持单播和 UNICAST2 的正面特征,而修正负面特征。 UNICAST3 协议的主要特点是...

    jgroups

    ### 关于JGroups 2.5教程:安装与开发简易应用程序 #### 安装与配置JGroups **JGroups**是一款高性能、可扩展且高度可靠的群集通信库,旨在为分布式系统提供消息传递功能。本教程将深入探讨如何安装配置JGroups,...

    jgroups-3.2

    JGroups是一个开源的纯java编写的可靠的群组通讯工具。其是一个可靠的组播通讯工具集(需要说明的是,这并不是说必须要使用IP Multicast,JGroups也可以使用TCP来实现)。其工作模式基于IP多播,但可以在可靠性和群组...

    jgroups.part3

    jgroups.part3

    Android代码-jgroups-android

    JGroups - A Framework for Group Communication in Java ======================================================== March 3, 1998 Bela Ban 4114 Upson Hall Cornell University Ithaca, NY 14853 bba@...

    Java多播通讯框架 JGroups

    Java多播通讯框架JGroups是Java开发者用于构建高可用、高性能和可伸缩的集群通信系统的重要工具。它提供了一套全面的协议栈,能够处理网络中的节点发现、消息传递、故障检测和恢复等问题,从而使得开发分布式应用变...

    jgroups-2.6.8.GA.jar

    jgroups-2.6.8.GA.jar jgroups-2.6.8.GA.jar

Global site tag (gtag.js) - Google Analytics