Migration from multiplexer to shared transport

Introduction

 

The purpose of both the multiplexer and the shared transport is to make it possible for different services that need to use JGroups to share some of the resources JGroups uses. For example, in AS 4 and earlier, each clustered service needed to open its own JGroups channel; no resources were shared. This has become a bigger and bigger issue as the number of clustered services has grown.

 

The resources most desirable for sharing are:

 

1) Network sockets. Sharing these between services simplifies configuration and administration and saves memory (i.e. network buffers).
2) Threads. A JGroups channel creates a thread pool for passing incoming messages up to the clustered service. A pool that is shared across services can more effectively manage the number of threads in use.

 

The JGroups multiplexer was the original way JGroups sought to provide sharable resources. Basically, an entire underlying JChannel was shared, with an adapter (Multiplexer+MuxChannel) on top that multiplexed/demultiplexed messages and passed them to the appropriate service.

 

The shared transport was added in JGroups 2.6.2. Here the shared object is not an entire JChannel, but rather the transport protocol (UDP or TCP) that makes up the bottommost element in its protocol stack. The network sockets and the thread pool are all managed by the transport protocol, so just sharing this protocol lets us achieve the most desirable sharing.

 

With both approaches, a key goal is that an application using JGroups does not need to know if it is using shared resources or not. The application codes to the abstract org.jgroups.Channel class' API; whether that API is implemented using a JChannel+Multiplexer+MuxChannel or by a shared transport JChannel or just by a plain JChannel should be transparent to the application.

 

Advantages of Shared Transport over Multiplexer

 

The shared transport has a number of major advantages over the multiplexer:

 

1) No "impedence mismatch". A number of JChannel behaviors, particularly around view management, need to be massaged/hidden from the application if the Multiplexer+MuxChannel is used. E.g. the underlying JChannel sees the group membership as {A, B, C}. But, if Service1 connects a MuxChannel on nodes A and B but not C, Service1 should get a view of just {A, B}. The Multiplexer+MuxChannel needs to have some pretty complex (and fragile) logic to resolve the impedence mismatch between what the JChannel says is the view and what the application needs to see as the view.

 

2) Testability. A transport protocol has a much more limited set of behaviors than a full JChannel. It also has a known and limited set of configuration options, whereas a JChannel is infinitely configurable. As a result, rigorous testing of sharing behavior is much more manageable with a transport protocol.

 

3) Greater configuration independence. Different services that wish to share a transport protocol need only agree on the configuration of that transport protocol. The rest of their protocol stack can be completely different. It was clear that getting agreement on a multiplexed channel's protocol stack for all AS 5 services was either not going to happen or would force a kind of lowest common denominator config.

 

4) FLUSH. The FLUSH protocol stops all activity on a channel for a period. Mostly happens around view changes and state transfer. This is somewhat disruptive to the service using the channel. With the multiplexer, a FLUSH initiated by Service1 also effects Service2, Service3 and Service4, with no benefit to those other services. With a shared transport, a FLUSH on Service1's channel is transparent to the channels used by Service2, Service3 and Service4.

 

Advantages of Multiplexer over Shared Transport

 

The one advantage of the multiplexer is improved startup times. Until a few members have joined a JChannel's group, the discovery sequence takes a few seconds. With the multiplexer there is only one JChannel so the discovery sequence only occurs once per VM. With shared transport, it occurs once per service. However, even this issue has been addressed by introduction of two new parameters in PING discovery protocol: num_initial_srv_members and break_on_coord_rsp.

 

Migration

 

First of all if you are not using multiplexer all you have to do to create a shared channel is to instantiate JChannel with a regular configuration file that has one additional parameter in transport protocol: singleton_name. For example, all channels created with the same configuration file that has an additional parameter singleton_name="udp" will be sharing one single instance of that particular transport protocol per JVM. Note that the particular value assiged to singleton_name is not really important unless you plan to create additional channels in the same JVM that need a different set of protocols than the above mentioned singleton_name="udp".

 

If you are using JChannelFactory to create multiplexed channel (i.e depracated JChannelFactory#createMultiplexerChannel(String stack_name, String id)) it is recommended that you provide your own implementation of ChannelFactory that creates shared transport channels instead of multiplexed channels. Finally, in your code where channels are created simply replace the default JChannelFactory with your own. You can use JChannelFactory and deprecated stacks.xml as a basis for a new configuration file where each defined protocol stack has an additional singleton_name parameter in transport protocol.

 

Due to API compatibility reasons we have to keep current solution in place until the end of 2.x release series. In JGroups 3.0 we will provide a set of simpler channel creation APIs that build on experience gained from previous releases and that, at the same time, circumvent these current issues.