组通信之jgroups篇----JChannel

 

In order to join a group and send messages, a process has to create a channel. A channel is like a socket. When a client connects to a channel, it gives the the name of the group it would like to join. Thus, a channel is (in its connected state) always associated with a particular group. The protocol stack takes care that channels with the same group name find each other: whenever a client connects to a channel given group name G, then it tries to find existing channels with the same name, and joins them, resulting in a new view being installed (which contains the new member). If no members exist, a new group will be created.

A state transition diagram for the major states a channel can assume are shown in Figure 3.2.

When a channel is first created, it is in the unconnected state. An attempt to perform certain operations which are only valid in the connected state (e.g. send/receive messages) will result in an exception. After a successful connection by a client, it moves to the connected state. Now channels will receive messages, views and suspicions from other members and may send messages to other members or to the group. Getting the local address of a channel is guaranteed to be a valid operation in this state (see below). When the channel is disconnected, it moves back to the unconnected state. Both a connected and unconnected channel may be closed, which makes the channel unusable for further operations. Any attempt to do so will result in an exception. When a channel is closed directly from a connected state, it will first be disconnected, and then closed.

The methods available for creating and manipulating channels are discussed now.

 

进程必须创建一个channel,才能加入组和发送消息.channel就象socket一样.当某个客户端连接到一个channel,它给出了它想加入的组的名字.于是,一个channel代表了一个具体的组.协议栈能保证具有相同组名的channel能互相找到对方.任何时刻当有客户端以某个组名G连接到channel时,它会尝试找到具有相同名字的所有已存在channel,和他们共同形成一个新的view.如果没有其他成员,则会有该新组创建.
一个channel的状态转换如图3.2.(有未连接,连接和关闭三种).
当一个channel第一次创建时,它是未连接状态.当想尝试进行相关操作时只有在连接状态下才行(如发送/接收消息).当channel被客户端成功连接后,它转换到已连接状态.chennel可以接收消息,视图,怀疑消息等,也可以向组内其他成员发送消息.在这种状态下,得到本地地址是有效操作.当断开连接时,它又转换到未连接状态.不管是已连接 channel还是未连接channel,都可以被关闭,这将导致channel将不再可用.否则会出现异常.当一个channel直接从已连接状态关闭,它先断开连接,然后关闭.

创建和操作channel的可用方法现在进一步讨论.

 

3.6.1. Creating a channel

A channel can be created in two ways: an instance of a subclass of Channel is created directly using its public constructor (e.g. new JChannel() ), or a channel factory is created, which -- upon request -- creates instances of channels. We will only look at the first method of creating channel: by direct instantiation.

The public constructor of JChannel looks as follows:
        public JChannel(String props) throws ChannelException {}

It creates an instance of JChannel . The props argument points to an XML file containing the configuration of the protocol stack to be used. This can be a String, but there are also other constructors which take for example a DOM element or a URL (more on this later).

If the props argument is null, the default properties will be used. An exception will be thrown if the channel cannot be created. Possible causes include protocols that were specified in the property argument, but were not found, or wrong parameters to protocols.

A stack is wrapped by <config> and </config> elements and lists all protocols from bottom (UDP) to top (STATE_TRANSFER). Each element defines one protocol.

Each protocol is implemented as a Java class. When a protocol stack is created based on the above XML configuration,the first element ("UDP") becomes the bottom-most layer, the second one will be placed on the first, etc: the stack is created from the bottom to the top.

Each element has to be the name of a Java class that resides in the org.jgroups.stack.protocols package. Note that only the base name has to be given, not the fully specified class name ( UDP instead of org.jgroups.stack.protocols.UDP ). If the protocol class is not found, JGroups assumes that the name given is a fully qualified classname and will therefore try to instantiate that class. If this does not work an exception is thrown. This allows for protocol classes to reside in different packages altogether, e.g. a valid protocol name could be com.sun.eng.protocols.reliable.UCAST .

Each layer may have zero or more arguments, which are specified as a list of name/value pairs in parentheses directly after the protocol name. In the example above, UDP is configured with some options, one of them being the
IP multicast address (mcast_addr) which is set to 228.10.10.10, or to the value of the system property
jgroups.udp.mcast_addr, if set.
Note that all members in a group have to have the same protocol stack.

 

创建channel有两种方法:通过channel子类构造函数的实例直接实现,或者先创建一个channel工厂,然后创建channel实例.我们介绍第一种直接创建channel的方法.JChannel的构造函数如下.

它创建了一个JChannel实例.参数是一个XML文件,用于配置协议栈.可以是一个字符串,也可以是一个DOM元素或URL

如果参数为空,将使用默认属性.当创建channel失败将抛出异常.比如,协议在参数中被定义,但未发现实现,或协议参数有错误.

参见http://www.jgroups.org/udp.xml, 一个协议栈是由<config> and </config>元素组组成的,而且列出了所有协议,从最底层UDP协议到最上层STATE_TRANSFER协议.每个元素定义了一个协议.

每个协议均由一个Java类实现.当一个协议栈是由上面的XML文件配置时,第一个元素"UDP"是最底层协议,然后第二个元素位于第一个元素协议上.整个协议栈如此从下到上创建.

每个元素都是java一个类,其位于org.jgroups.stack.protocols包内.只是基本名而不是全名称类名(UDP而不是org.jgroups.stack.protocols.UDP).如果协议类没有发现,JGroups认为这是一个全类名去实例化,如果仍然错误,则有异常抛出.所以,com.sun.eng.protocols.reliable.UCAST协议也是有效的.

每个协议层可以有0个或多个参数,其定义在协议名后,以键值对列表示.在上例中,UDP配置有一些选项,一个是组播地址,设置为228.10.10.10.

注意:同一组中的所有成员必须使用相同的协议栈.

 

3.6.2. Setting options
A number of options can be set in a channel. To do so, the following method is used:
        public void setOpt(int option, Object value);      

Arguments are the options number and a value. The following options are currently recognized:

Channel.BLOCK
The argument is a boolean object. If true, block messages will be received.

Channel.LOCAL
Local delivery. The argument is a boolean value. If set to true, a member will receive all messages it sent to itself. Otherwise, all messages sent by itself will be discarded. This option allows to send messages to the group, without receiving a copy. Default is true (members will receive their own copy of messages multicast to the group).

Channel.AUTO_RECONNECT
When set to true, a shunned channel will leave the group and then try to automatically re-join. Default is false. Note that in 2.8, shunning has been removed, therefore this option has been deprecated.

Channel.AUTO_GETSTATE
When set to true a shunned channel, after reconnection, will attempt to fetch the state from the coordinator. This requires AUTO_RECONNECT to be true as well. Default is false. Note that in 2.8, shunning has been removed, therefore this option has been deprecated.

The equivalent method to get options is getOpt() :
        public Object getOpt(int option);   

Given an option, the current value of the option is returned.

 

channel可以设置一些选项.用下面函数可以做到.

hannel.BLOCK     这是一个Boolean型的属性,缺省是false.如果设置成true,那么会接收到block message.
Channel.LOCAL   这是一个Boolean型的属性,缺省设置成true.如果是true,那么当集群中的实例向集群发送消息时,这个实例本身也会收到这个消息.
Channel.AUTO_RECONNECT   这是一个Boolean型的属性,缺省是false.如果设置成true,那么shunned channel 在离开集群后,会自动重新加入到集群中.在2.8版本中,shunning已经被移除,所以这个选项也不用了.
Channel.AUTO_GETSTATE       这是一个Boolean型的属性,缺省是false.如果设置成true,那么shunned channel在自动重新加入到集群后,会自动尝试向集群的coordinator 获得集群的状态(需要AUTO_RECONNECT也设置成true).同样,在2.8版本中,该选项也不用了.
同样有getOpt函数,用以得到现在设置的属性值.

 

3.6.3. Giving the channel a logical name
A channel can be given a logical name which is then used instead of the channel's address. A logical name might show the function of a channel, e.g. "HostA-HTTP-Cluster", which is more legible than a UUID 3c7e52ea-4087-1859-e0a9-77a0d2f69f29.

For example, when we have 3 channels, using logical names we might see a view "{A,B,C}", which is nicer than "{56f3f99e-2fc0-8282-9eb0-866f542ae437, ee0be4af-0b45-8ed6-3f6e-92548bfa5cde, 9241a071-10ce-a931-f675-ff2e3240e1ad} !"

If no logical name is set, JGroups generates one, using the hostname and a random number, e.g. linux-3442. If this is not desired and the UUIDs should be shown, use system property -Djgroups.print_uuids=true.

The logical name can be set using:

        public void setName(String logical_name);

This should be done before connecting a channel. Note that the logical name stays with a channel until the channel is destroyed, whereas a UUID is created on each connection.

 

channel可以得到一个逻辑名称,用以替代channel Address.逻辑名可以表示channel功能,如HostA-HTTP-Cluster", 这比一个UUID值3c7e52ea-4087-1859-e0a9-77a0d2f69f29更易读.

比如,我们有3个channel,使用逻辑名可以看到视图"{A,B,C}",这比用"{56f3f99e-2fc0-8282-9eb0-866f542ae437, ee0be4af-0b45-8ed6-3f6e-92548bfa5cde, 9241a071-10ce-a931-f675-ff2e3240e1ad}"要好多了.

如果没有设置逻辑名,JGroups将产生一个.使用主机名加一个随机数,如linux-3442.如果我们并不想如此,而只想显示UUID,则可以设置系统属性Djgroups.print_uuids为真.

使用setName函数设置逻辑名.这必须在连接channel前设置.逻辑名将随channel一直存在直到channel被销毁.而UUID则在每次连接时都会创建(也即加入一个新的组都会有一个新的UUID).

 

3.6.4. Connecting to a channel
When a client wants to join a group, it connects to a channel giving the name of the group to be joined:
        public void connect(String clustername) throws ChannelClosed;

The cluster name is a string, naming the cluster to be joined. All channels that are connected to the same name form a cluster. Messages multicast on any channel in the cluster will be received by all members (including the one who sent it).

The method returns as soon as the group has been joined successfully. If the channel is in the closed state (see Figure 3.2, “Channel states” ), an exception will be thrown. If there are no other members, i.e. no other member has connected to a group with this name, then a new group is created and the member joined. The first member of a group becomes its coordinator . A coordinator is in charge of multicasting new views whenever the membership changes.

 

当一个客户端想加入一个组,它将使用组名连接channel加入.

组名是一个字符串,表示要加入哪个组.所有连接到同一个组名的channel组成一个组.组内任何一个channel组播的消息将被组内所有成员接收到.

此方法将很快返回如果加组成功.当channel处于关闭状态时,则将有异常被抛出.如果没有其他成员,即没有成员以此名连接到组,则将创建一个新的组并且此成员加入该组.组内第一个成员成为主.当组成员发生变化时,会有组播的视图信息发送出来,主的产生在此被管理.

 

 3.6.5. Connecting to a channel and getting the state in one operation
Clients can also join a cluster group and fetch cluster state in one operation. The best way to conceptualize connect and fetch state connect method is to think of it as an invocation of regular connect and getstate methods executed in succession. However, there are several advantages of using connect and fetch state connect method over regular connect. First of all, underlying message exchange is heavily optimized, especially if the flush protocol is used in the stack. But more importantly, from clients perspective, connect and join operations become one atomic operation.

       public void connect(string cluster_name, address target, string state_id, long timeout) throws channelexception;
Just as in regular connect method cluster name represents a cluster to be joined. Address parameter indicates a cluster member to fetch state from. Null address parameter indicates that state should be fetched from the cluster coordinator. If state should be fetched from a particular member other than coordinator clients can provide an address of that member. State id used for partial state transfer while timeout bounds entire join and fetch operation.

 

客户可以在同一个操作中完成加入组并获取组状态信息.完成连接并获取状态的连接方法可以理解为封装了连接函数和获取状态函数并执行成功.但是,使用它比使用普通的连接函数有一些优点.首先,底层的消息交换被优化了,尤其当使用了flush协议.更重要的是,从用户角度看,连接和加入组成为了一个原子操作.

参数组名代表了要加入的组,参数Address代表了将从哪个组成员获取状态信息.空地址表示将从主获取状态.如果想从某个其他成员获取状态信息则要传该成员参数进去.State id是用于部分组状态信息传输.timeout用于加入和获取状态操作超时.

 

3.6.6. Getting the local address and the group name
Method getLocalAddress() returns the local address of the channel. In the case of JChannel , the local address is generated by the bottom-most layer of the protocol stack when the stack is connected to. That means that -- depending on the channel implementation -- the local address may or may not be available when a channel is in the unconnected state.
        public Address getLocalAddress();  

Method getClusterName() returns the name of the cluster in which the channel is a member:
        public String getClusterName();     

Again, the result is undefined if the channel is in the unconnected or closed state.

 

getLocalAddress方法返回channel的地址.在JChannel中,本地地址在连接协议栈时在最底层协议生成.这表明,本地地址实现依赖于channel的实现.当channel处于未连接状态时,本地地址或许不可用.

getClusterName方法返回组名,如果channel是组成员.另外,当channel处于未连接状态和关闭状态,返回结果是未定义的.

 

3.6.7. Getting the current view
The following method can be used to get the current view of a channel:
        public View getView();        

This method does not retrieve a new view (message) from the channel, but only returns the current view of the channel. The current view is updated every time a view message is received: when method receive() is called, and the return value is a view, before the view is returned, it will be installed in the channel, i.e. it will become the current view.

Calling this method on an unconnected or closed channel is implementation defined. A channel may return null, or it may return the last view it knew of.

 

getView方法用于获取channel当前的view.

此方法不是让channel去获取新的view,仅仅返回当前channel中存在的view.当前view会在接收到view消息时被及时更新.在此消息返回前会在channel层设置view值,即当前view

当channel未连接状态或关闭状态,此函数将返回空或它知道的上次view.

 

3.6.8. Sending a message
Once the channel is connected, messages can be sent using the send() methods:
        public void send(Message msg) throws ChannelNotConnected, ChannelClosed;
        public void send(Address dst, Address src, Object obj) throws ChannelNotConnected, ChannelClosed;    
The first send() method has only one argument, which is the message to be sent. The message's destination should either be the address of the receiver (unicast) or null (multicast). When it is null, the message will be sent to all members of the group (including itself). The source address may be null; if it is, it will be set to the channel's address (so that recipients may generate a response and send it back to the sender).
The second send() method is a helper method and uses the former method internally. It requires the address of receiver and sender and an object (which has to be serializable), constructs a Message and sends it.
If the channel is not connected, or was closed, an exception will be thrown upon attempting to send a message.
Here's an example of sending a (multicast) message to all members of a group:
                Hashtable data; // any serializable data
                try {
                    channel.send(null, null, data);
                }
                catch(Exception ex) {
                    // handle errors
                }        
Here's an example of sending a (unicast) message to the first member (coordinator) of a group:
                Address receiver;
                Message msg;
                Hashtable data;
                try {
                    receiver=channel.getView().getMembers().first();
                    channel.send(receiver, null, data);
                }
                catch(Exception ex) {
                    // handle errors
                }         

 

当channel已连接时,可以通过send函数发送消息.

第一个send方法只有一个参数,即将要发送的消息.消息的目的地可以是接收端的地址(单播)或空(组播).当为空时,消息将发送到组内所有成员(包括自己).源端地址可以为空,将被设置为channel地址(所以,接收端可以发送回应消息给发送端).

第二个send方法是一个辅助方法,其内部实现用了第一个方法.它需要参数接收端,发送端和一个对象(须序列化),然后组装消息并发送之.

如果channel是未连接或关闭的,则有异常被抛出.

下面是一个组播消息到组内所有成员的例子.

这里是单播发送消息到组内第一个成员的例子.

 

3.6.9. Receiving a message
Method receive() is used to receive messages, views, suspicions and blocks:
        public Object receive(long timeout) throws ChannelNotConnected, ChannelClosed, Timeout;  

A channel receives messages asynchronously from the network and stores them in a queue. When receive() is called, the next available message from the top of that queue is removed and returned. When there are no messages on the queue, the method will block. If timeout is greater than 0, it will wait the specified number of milliseconds for a message to be received, and throw a TimeoutException exception if none was received during that time. If the timeout is 0 or negative, the method will wait indefinitely for the next available message.
Depending on the channel options (see Section 3.6.2, “Setting options” ), the following types of objects may be received:
Message
A regular message. To send a response to the sender, a new message can be created. Its destination address would be the received message's source address. Method Message.makeReply() is a helper method to create a response.
View
A view change, signalling that a member has joined, left or crashed. The application may or may not perform some action upon receiving a view change (e.g. updating a GUI object of the membership, or redistributing a load-balanced collaborative task to all members). Note that a longer action, or any action that blocks should be performed in a separate thread. A MergeView will be received when 2 or more subgroups merged into one (see Section 3.5.2, “MergeView” for details). Here, a possible state merge by the application needs to be done in a separate thread.
SuspectEvent
Notification of a member that is suspected. Method SuspectEvent.getMember() retrieves the address of the suspected member. Usually this message will be followed by a view change.
BlockEvent
The application has to stop sending messages. When the application has stopped sending messages, it needs to acknowledge this message with a Channel.blockOk() method.
The BlockEvent reception can be used to complete pending tasks, e.g. send pending messages, but once Channel.blockOk() has been called, all threads that send messages (calling Channel.send() or Channel.down()) will be blocked until FLUSH unblocks them.
UnblockEvent
The application can resume sending messages. Any previously messages blocked by FLUSH will be unblocked; when the UnblockEvent is received the channel has already been unblocked.
GetStateEvent
Received when the application's current state should be saved (for a later state transfer. A copy of the current state should be made (possibly wrapped in a synchronized statement and returned calling method Channel.returnState() . If state transfer events are not enabled on the channel (default), then this event will never be received. This message will only be received with the Virtual Synchrony suite of protocols (see the Programmer's Guide).
StreamingGetStateEvent
Received when the application's current state should be provided to a state requesting group member. If state transfer events are not enabled on the channel (default), or if channel is not configured with pbcast.STREAMING_STATE_TRANSFER then this event will never be received.
SetStateEvent
Received as response to a getState(s) method call. The argument contains the state of a single member ( byte[] ) or of all members ( Vector ). Since the state of a single member could also be a vector, the interpretation of the argument is left to the application.
StreamingSetStateEvent
Received at state requesting member when the state InputStream becomes ready for reading. If state transfer events are not enabled on the channel (default), or if channel is not configured with pbcast.STREAMING_STATE_TRANSFER then this event will never be received.
The caller has to check the type of the object returned. This can be done using the instanceof operator, as follows:
                Object obj;
                Message msg;
                View v;
                obj=channel.receive(0); // wait forever
                if(obj instanceof Message)
                    msg=(Message)obj;
                else if(obj instanceof View)
                    v=(View)obj;
                else
                    ; // don't handle suspicions or blocks          
If for example views, suspicions and blocks are disabled, then the caller is guaranteed to only receive return values of type Message . In this case, the return value can be cast to a Message directly, without using the instanceof operator.
If the channel is not connected, or was closed, a corresponding exception will be thrown.
The example below shows how to retrieve the "Hello world" string from a message:
                Message msg; // received above
                String s;
                try {
                    s=(String)msg.getObject(); // error if object not Serializable
                    // alternative: s=new String(msg.getBuffer());
                }
                catch(Exception ex) {
                    // handle errors, e.g. casting error above)
                }         
The Message.getObject() method retrieves the message's byte buffer, converts it into a (serializable) object and returns the object.

 

 receive方法用于接收消息,视图,怀疑信息和阻塞信息.

channel以异步方式从网络上接收消息并放入队列中.当receive函数被响应时,队列中最上面的可用消息将被移除并返回.当队列中无消息时,该方法将被阻塞.如果超时大于0,接收消息将会等待该值的毫秒数,或超时抛出异常.如果超时为0或负值,该方法将无限等待知道接收到下一个可用消息.

可被接收的对象有下面几种:

Message 普通消息,Message.makeReply()可以同于创建消息的应答,即以当前消息的源地址作为应答消息的目标地址。
View 当集群的成员发生变化的时候,集群的每个成员都会收到view message.当接收到该消息时,应用程序可能做也可能不做任何动作.注意,一个较长时间执行的动作或一个阻塞式动作需要另开线程处理.当两个或者多个子集群(subgroups)合并成一个的时候,集群中的成员会收到MergeView message.当应用程序需要合并一些状态时,需要用单独线程处理.

SuspectEvent 当集群中的某个成员被怀疑崩溃时,集群中的其它成员会收到SuspectEvent message.通过调用SuspectEvent.getMember()可以得到可疑成员的地址.在收到这个消息后,通常还会收到view message.
BlockEvent 当收到BlockEvent message后,实例应该停止发送消息,然后应该调用Channel.blockOk()方法确认已经停止发送消息.接收到BlockEvent可用于完成为成功的任务,如发送未决的消息.当Channel.blockOk()方法调用完毕之后,所有发送消息的线程都会被阻塞直到FLUSH协议解除阻塞.

UnblockEvent 当收到UnblockEvent message后,应用程序可以继续发送消息.以前被FLUSH协议阻塞的消息也将被解除阻塞.
GetStateEvent 当收到GetStateEvent message后,应该保存当前的状态以用于以后的状态传输.当前状态将被拷贝(需要互斥操作)作为Channel.returnState()方法的返回值.为了接收GetStateEvent message,需要在channel中使其可用(默认不可用). 该消息只在相关协议中被接收到(可参考pbcast.STATE_TRANSFER).

StreamingGetStateEvent 如果在channel中没有设置使其可用(默认情况即如此)或者没有配pbcast.STREAMING_STATE_TRANSFER协议,则此消息也将不会被接收到.

SetStateEvent 此方法的接收是对getState方法的回应.其参数可以为1个成员或多个成员组成的向量.

StreamingSetStateEvent 当状态输入流可读时,请求状态的成员将接收到该消息.如果在channel中没有设置使其可用(默认情况即如此)或者没有配pbcast.STREAMING_STATE_TRANSFER协议,则此消息也将不会被接收到.

 

3.6.10. Using a Receiver to receive messages
Instead of pulling messages from a channel in an application thread, a Receiver can be registered with a channel; all received messages, view changes and state transfer requests will invoke callbacks on the registered Receiver:

                JChannel ch=new JChannel();

                ch.setReceiver(new ExtendedReceiverAdapter() {
                    public void receive(Message msg) {
                        System.out.println("received message " + msg);
                    }
                    public void viewAccepted(View new_view) {
                        System.out.println("received view " + new_view);
                    }
                });
                ch.connect("bla");
The ExtendedReceiverAdapter class implements all callbacks of ExtendedReceiver with no-ops, in the example above we override receive() and viewAccepted().

The advantage of using a Receiver is that the application doesn't have to waste 1 thread for pulling messages out of a channel. In addition, the channel doesn't have to maintain an (unbounded) queue of messages/views, which can quickly get large if the receiver cannot process messages fast enough, and the sender keeps sending messages.

Note
Note that the Channel.receive() method has been deprecated, and will be removed in 3.0. Use the Receiver interface instead and register as a Receiver with Channel.setReceiver(Receiver r).

不同于在一个线程中以pull模式取消息,Receiver可以注册到一个channel,这样,消息,视图或状态信息接收都可以触发相应的函数.

ExtendedReceiverAdapter 类实现了ExtendedReceiver类的所有方法,如上面的receive函数和viewAccepted函数

使用Receiver的好处是应用程序不需要浪费一个线程去取消息.而且,channel也不需要维护一个队列放消息等,该队列很容易变的很大如果接收端处理消息不及时,而发送端却在继续发送消息.

注意:3.0版本中将不再使用Channel.receive() 方法,而统一注册Receiver.

 

3.6.11. Peeking at a message
Instead of removing the next available message from the channel, peek() just returns a reference to the next message, but does not remove it. This is useful when one has to check the type of the next message, e.g. whether it is a regular message, or a view change. The signature of this method is not shown here, it is the same as for receive() .

Note
The peek() method has also been deprecated, and will be removed in 3.0.

 

和从channel中移除下一个可用消息不同,peek仅仅获得下一个消息的引用而不移除.这对于检查下一个消息的类型等是非常有用的.

注意:3.0版本中将不再使用peek方法

 

3.6.12. Getting the group's state
A newly joined member may wish to retrieve the state of the group before starting work. This is done with getState(). This method returns the state of one member (in most cases, of the oldest member, the coordinator). It returns true or false, depending on whether a valid state could be retrieved. For example, if a member is a singleton, then calling this method would always return false

The actual state is returned as the return value of one of the subsequent receive() calls, in the form of a SetStateEvent object. If getState() returned true, then a valid state (non-null) will be returned, otherwise a null state will be returned. Alternatively if an application uses MembershipListener (see Section 3.2.3, “MembershipListener” ) instead of pulling messages from a channel, the getState() method will be invoked and a copy of the current state should be returned. By the same token, setting a state would be accomplished by JGroups calling the setState() method of the state fetcher.

The reason for not directly returning the state as a result of getState() is that the state has to be returned in the correct position relative to other messages. Returning it directly would violate the FIFO properties of a channel, and state transfer would not be correct.

The following code fragment shows how a group member participates in state transfers:

                channel=new JChannel();
                channel.connect("TestChannel");
                boolean rc=channel.getState(null, 5000);

                ...

                Object state, copy;
                Object ret=channel.receive(0);
                if(ret instanceof Message)
                    ;
                else if(ret instanceof GetStateEvent) {
                    copy=copyState(state); // make a copy so that other msgs don't change the state
                    channel.returnState(Util.objectToByteBuffer(copy));
                }
                else if(ret instanceof SetStateEvent) {
                    SetStateEvent e=(SetStateEvent)ret;
                    state=e.getArg();
                }
A JChannel has to be created whose stack includes the STATE_TRANSFER or pbcast.STATE_TRANSFER protocols (see Chapter 5, Advanced Concepts ). Method getState() subsequently asks the channel to return the current state. If there is a current state (there may not be any other members in the group !), then true is returned. In this case, one of the subsequent receive() method invocations on the channel will return a SetStateEvent object which contains the current state. In this case, the caller sets its state to the one received from the channel.

Method receive() might return a GetStateEvent object, requesting the state of the member to be returned. In this case, a copy of the current state should be made and returned using JChannel.returnState() . It is important to a) synchronize access to the state when returning it since other accesses may modify it while it is being returned and b) make a copy of the state since other accesses after returning the state may still be able to modify it ! This is possible because the state is not immediately returned, but travels down the stack (in the same address space), and a reference to it could still alter it.

 

一个新加入的成员或许想在工作前得到组的一些状态,这是通过getState(channel层调用)函数实现的.这个函数作用是得到某个成员(一般是最老的成员即主)的状态.不过该函数本身返回值为真或假,依赖于是否有有效的状态可以获得,比如如果该成员是唯一成员,则返回为false.

真实的状态是通过后来的receive响应得到的,接收到的是一个SetStateEvent对象.如果刚才的getState函数返回真,则最终将有一个有效的状态返回,否则返回空值.如果应用程序实现了MessageListener接口,则响应端的getState()接口将被调用并返回当前的应用状态(给获取端).同样,获取端的setState()接口将被调用,至此即得到了组内相关状态信息.

至于为什么不直接返回状态,是因为我们需要保证相关消息的顺序性.

下面的状态说明了某个组成员如何参与状态传输.

首先,JChannel被创建,而且其协议栈需包含STATE_TRANSFER协议或pbcast.STATE_TRANSFER协议.channel层的getState函数用于获取当前状态.如果有当前状态可以获得,此函数返回为真.这种情况下,receive函数将得到SetStateEvent对象,其包含了当前状态信息.这样,调用者就可以设置状态值了.

receive函数还可能得到一个GetStateEvent对象,它将返回当前成员的状态.这种情况下,当前状态将被拷贝,然后通过JChannel.returnState()方法获得.这点很重要. a)可以避免别的地方会修改该状态值,从而影响该返回值.b)拷贝这段时间,状态信息仍有可能被改变.

 

3.6.13. Getting the state with a Receiver
As an alternative to handling the GetStateEvent and SetStateEvent events, and calling Channel.returnState(), a Receiver could be used. The example above would look like this:

                class MyReceiver extends ReceiverAdapter {
                    final Map m=new HashMap();
                    public byte[] getState() {
                        synchronized(m) { // so nobody else can modify the map while we serialize it
                            byte[] state=Util.objectToByteBuffer(m);
                            return state;
                        }
                    }

                    public void setState(byte[] state) {
                        synchronized(m) {
                            Map new_m=(Map)Util.objectFromByteBuffer(state);
                            m.clear();
                            m.addAll(new_m);
                        }
                    }
                }

                channel=new JChannel(); // use default properties (has to include pbcast.STATE_TRANSFER protocol)
                channel.setReceiver(new MyReceiver());
                channel.connect("TestChannel");
                boolean rc=channel.getState(null, 5000);      
In a group consisting of A,B and C, with D joining the group and calling Channel.getState(), the following sequence of callbacks happens:

D calls Channel.getState(). The state will be retrieved from the oldest member, A
A MyReceiver.getState() is called. A returns a copy of its hashmap
D getState() returns true
D MyReceiver.setState() is called with the serialized state. D unserializes the state and sets it

 

我们可以通过上面说的GetStateEvent方法,SetStateEvent和Channel.returnState()方法获取状态信息外,还可以通过下面的例子实现.

某个组已有成员A,B,C.这是D新加入,调用Channel.getState()方法,发生事情如下:

D调用Channel.getState(),状态信息将从最老的成员A获得.

A的MyReceiver子类的getState方法被调用,它返回状态的一份拷贝.

D刚才调用的getState方法先返回真

D的MyReceiver子类的setState方法被调用,该函数参数信息为序列化后的状态.D反序列化为状态后并设置状态值.

 

3.6.14. Partial state transfer
Partial state transfer means that instead of transferring the entire state, we may want to transfer only a substate. For example, with HTTP session replication, a new node in a cluster may want to transfer only the state of a specific session, not all HTTP sessions. This can be done with either the pull or push model. The method to call would be Channel.getState(), including the ID of the substate (a string). In the pull model, GetStateEvent and SetStateEvent have an additional member, state_id, and in the push model, there are 2 additional getState() and setState() callbacks. The example below shows partial state transfer for the push model:

                class MyReceiver extends ExtendedReceiverAdapter {
                    final Map m=new HashMap();

                    public byte[] getState() {
                        return getState(null);
                    }

                    public byte[] getState(String substate_id) {
                        synchronized(m) { // so nobody else can modify the map while we serialize it
                            byte[] state=null;
                            if(substate_id == null) {
                                state=Util.objectToByteBuffer(m);
                            }
                            else {
                                Object value=m.get(substate_id);
                                if(value != null) {
                                    return Util.objectToByteBuffer(value);
                                }
                            }
                            return state;
                        }
                    }

                    public void setState(byte[] state) {
                        setState(null, state);
                    }

                    public void setState(String substate_id, byte[] state) {
                        synchronized(m) {
                            if(substate_id != null) {
                                Object value=Util.objectFromByteBuffer(state);
                                m.put(substate_id, value);
                            }
                            else {
                                Map new_m=(Map)Util.objectFromByteBuffer(state);
                                m.clear();
                                m.addAll(new_m);
                            }
                        }
                    }
                }

                channel=new JChannel(); // use default properties (has to include pbcast.STATE_TRANSFER protocol)
                channel.setReceiver(new MyReceiver());
                channel.connect("TestChannel");
                boolean rc=channel.getState(null, "MyID", 5000);
The example shows that the Channel.getState() method specifies the ID of the substate, in this case "MyID". The getState(String substate_id) method checks whether the substate ID is not null, and returns the substate pertaining to the ID, or the entire state if the substate_id is null. The same goes for setting the substate: if setState(String substate_id, byte[] state) has a non-null substate_id, only that part of the current state will be overwritten, otherwise (if null) the entire state will be overwritten.

 

部分状态信息传输指的是我们不需要传递所有状态信息,而只需要传递一部分子集的状态信息.比方说,http会话,组内某个节点可能只想传输具体某个http会话的状态信息而不是所有http会话的状态信息.它同样可以通过pull模式或push模式实现.仍然是由Channel.getState()函数实现,包含子状态的一个ID信息.在pull模式下,GetStateEvent和SetStateEvent对象有一个另外的成员,状态ID号.在push模式下,有两个另外的函数,getState()和setState()回调函数,push模式的部分状态信息传输实现如下.

由例子可以看出,Channel.getState() 方法有个子状态的ID编号,即"MyID". getState函数先检查子状态ID号是否为空,然后返回与该ID号相关的子状态,或者返回所有状态如果ID为空.同理,如果ID为空,setState函数将设置所有状态信息,否则只设置与该ID相关的子状态信息.

 

3.6.15. Streaming state transfer
Streaming state transfer allows transfer of application (partial) state without having to load entire state into memory prior to sending it to a joining member. Streaming state transfer is especially useful if the state is very large (>1Gb), and use of regular state transfer would likely result in OutOfMemoryException. Streaming state transfer was introduced in JGroups 2.4. JGroups channel has to be configured with either regular or streaming state transfer. The JChannel API that invokes state transfer (i.e. JChannel.getState(long timeout, Address member)) remains the same.

Streaming state transfer, just as regular byte based state transfer, can be used in both pull and push mode. Similarly to the current getState and setState methods of org.jgroups.MessageListener, the application interested in streaming state transfer in a push mode would implement streaming getState method(s) by sending/writing state through a provided OutputStream reference and setState method(s) by receiving/reading state through a provided InputStream reference. In order to use streaming state transfer in a push mode, existing ExtendedMessageListener has been expanded to include additional four methods:

public interface ExtendedMessageListener {

      /*non-streaming callback methods ommitted for clarity*/
       void getState(OutputStream ostream);
       void getState(String state_id, OutputStream ostream);
       void setState(InputStream istream);
       void setState(String state_id, InputStream istream);

}           
For a pull mode (when application uses channel.receive() to fetch events) two new event classes will be introduced:

StreamingGetStateEvent

StreamingSetStateEvent

These two events/classes are very similar to existing GetStateEvent and SetStateEvent but introduce a new field; StreamingGetStateEvent has an OutputStream and StreamingSetStateEvent has an InputStream.

The following code snippet demonstrates how to pull events from a channel, processing StreamingGetStateEvent and sending hypothetical state through a provided OutputStream reference. Handling of StreamingSetStateEvent is analogous to this example:

   ...
   Object obj=channel.receive(0);
   if(obj instanceof StreamingGetStateEvent) {
     StreamingGetStateEvent evt=(StreamingGetStateEvent)obj;
     OutputStream oos = null;
     try {
         oos = new ObjectOutputStream(evt.getArg());
         oos.writeObject(state);
         oos.flush();
     }
     catch (Exception e) {}
     finally {
         try {
             oos.close();
         }
         catch (IOException e) {
             System.err.println(e);
         }
    }
  }               
  ...
JGroups has a great flexibility with state transfer methodology by allowing application developers to implement both byte based and streaming based state transfers. Application can, for example, implement streaming and byte based state transfer callbacks and then interchange state transfer protocol in channel configuration to use either streaming or byte based state transfer. However, one cannot configure a channel with both state transfers at the same time and then in runtime choose which particular state transfer type to use.

 

流状态信息传输允许应用程序可以不用将状态信息加载到内存而直接发送给组内新加入的成员.在状态信息非常大的时候,流传输将非常有用,因为此时我们要用普通状态传输方法会出现内存溢出错误.流状态传输在2.4版本中加入.JChannel要么配置一般状态传输要么配置流状态传输.JChannel层的调用仍然一样.即JChannel.getState.

流状态传输同样可以有pull和push模式实现.在push模式下,发送写状态是通过OutputStream来实现getState方法的,接收读状态是通过InputStream来实现setState方法的.为了实现流状态传输(还有部分状态流传输)的push模式,必须实现ExtendedMessageListener,它包含有新的四个另外的方法.

对pull模式,我们需要介绍StreamingGetStateEvent和StreamingSetStateEvent对象.这两个对象类似于GetStateEvent和SetStateEvent对象.不过,StreamingGetStateEvent基于OutputStream,StreamingSetStateEvent基于InputStream.

下面的代码展示了怎样从channel去pull一个事件,处理StreamingGetStateEvent对象,通过OutputStream发送状态信息.同样,处理StreamingSetStateEvent对象也类似.

JGroups提供了灵活的状态传输方法供开发者使用,可以基于字节传输也可以基于流传输.所以,我们可以用其中任何一种方法配置channel,但却不能同时配置,然后指望在程序运行时再决定使用什么传输方法.

 

3.6.16. Disconnecting from a channel
Disconnecting from a channel is done using the following method:
        public void disconnect(); 

It will have no effect if the channel is already in the disconnected or closed state. If connected, it will remove itself from the group membership. This is done (transparently for a channel user) by sending a leave request to the current coordinator. The latter will subsequently remove the channel's address from its local view and send the new view to all remaining members.

After a successful disconnect, the channel will be in the unconnected state, and may subsequently be re-connected to.

 

用下面的方法可以断开channel的连接.

如果该channel已处于断开连接状态或关闭状态,不做任何事.否则,它会将自己从组成员中删除出去.其具体实现是,它将向组发送离开消息,主会将其从本地视图删除,然后发送新的视图到组内所有成员.

成功断开连接后,channel处于未连接状态,其可以被再次连接.

 

3.6.17. Closing a channel
To destroy a channel instance (destroy the associated protocol stack, and release all resources), method close() is used:
        public void close();

It moves the channel to the closed state, in which no further operations are allowed (most throw an exception when invoked on a closed channel). In this state, a channel instance is not considered used any longer by an application and -- when the reference to the instance is reset -- the channel essentially only lingers around until it is garbage collected by the Java runtime system.

 

可以通过close函数销毁一个channel.

它将channel状态改变为关闭状态.对一个已关闭channel再次关闭会抛出异常.关闭一个channel意味着应用程序以后将不会再使用它.

 

 

 

 

 

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
探索全栈前端技术的魅力:HTML+CSS+JS+JQ+Bootstrap网站源码深度解析 在这个数字化时代,构建一个既美观又功能强大的网站成为了许多开发者和企业追逐的目标。本份资源精心汇集了一套完整网站源码,融合了HTML的骨架搭建、CSS的视觉美化、JavaScript的交互逻辑、jQuery的高效操作以及Bootstrap的响应式设计,全方位揭秘了现代网页开发的精髓。 HTML,作为网页的基础,它构建了信息的框架;CSS则赋予网页生动的外观,让设计创意跃然屏上;JavaScript的加入,使网站拥有了灵动的交互体验;jQuery,作为JavaScript的强力辅助,简化了DOM操作与事件处理,让编码更为高效;而Bootstrap的融入,则确保了网站在不同设备上的完美呈现,响应式设计让访问无界限。 通过这份源码,你将: 学习如何高效织HTML结构,提升页面加载速度与SEO友好度; 掌握CSS高级技巧,如Flexbox与Grid布局,打造适应各种屏幕的视觉盛宴; 理解JavaScript核心概念,动手实现动画、表单验证等动态效果; 利用jQuery插件快速增强用户体验,实现滑动效果、Ajax请求等; 深入Bootstrap框架,掌握移动优先的开发策略,响应式设计信手拈来。 无论是前端开发新手渴望系统学习,还是资深开发者寻求灵感与实用技巧,这份资源都是不可多得的宝藏。立即深入了解,开启你的全栈前端探索之旅,让每一个网页都成为技术与艺术的完美融合!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值