Chapter 103. Master Component
Abstract
The Master component provides a way to ensure that only a single consumer in a cluster consumes from a given endpoint; with automatic failover if that JVM dies. This feature can be useful if you need to consume from a legacy back-end that doesn't support concurrent consumption or, due to commercial or stability reasons, you can have only a single connection to the back-end at any point in time.
Dependencies
The Master component can only be used in the context of a fabric-enabled Red Hat JBoss Fuse container. You must ensure that the
fabric-camel
feature is installed.
In the context of Fabric, you install a feature by adding it to the relevant profile. For example, if you are using a profile called
my-master-profile
, you would add the fabric-camel
feature by entering the following console command:
karaf@root> fabric:profile-edit --features fabric-camel my-master-profile
URI format
A Master endpoint can only be used as a consumer endpoint. It has the following URI format:
master:ClusterID:EndpointURI[?Options]
Where the URI,
EndpointURI
, is published in the fabric registry and associated with the ClusterId
cluster.
URI options
The Master component itself does not support any URI options. Any options on the URI are, therefore, applied to the specified consumer endpoint,
EndpointURI
.
How to use the Master component
The Master component is useful in cases where you need to poll messages from an endpoint, but you are only allowed to make one connection to that endpoint. In this case, you can use the Master component to define a failover cluster of consumer endpoints. Each Master endpoint in the cluster is capable of consuming messages from the given endpoint, but only one of the Master endpoints is active at any time (the master), while the other Master endpoints are waiting (the slaves).
For example, to set up a cluster of Master endpoints that can consume from the
seda:bar
endpoint, you would proceed as follows:
- Define the Master endpoints with the following URI (where each endpoint in the cluster uses exactly the same URI):
master:mysedalock:seda:bar
Each of the Master endpoints in the cluster tries to get themysedalock
lock (implemented as a key in the Zookeeper registry). The Master endpoint that succeeds in getting the lock becomes active (the master) and starts consuming messages from theseda:bar
endpoint. The other Master endpoints enter a waiting state and continue to try the lock (the slaves). - You must remember to include the
fabric-camel
feature in the profile that deploys a Master endpoint. - In Blueprint XML, you can define a Master endpoint at the start of a Camel route, as follows:
<?xml version="1.0" encoding="UTF-8"?> <blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> ... <camelContext id="camel" xmlns="http://camel.apache.org/schema/blueprint"> <route> <from uri="master:mysedalock:seda:bar"/> ... </route> </camelContext> ... </blueprint>
Example of a master-slave cluster polling a JMS ActiveMQ broker
For example, a typical way to use the Master component is to create a cluster of exclusive consumers for consuming messages from a JMS queue. Only one of the Master endpoints consumes from the queue at any time, and if that Master endpoint goes down, one of the other Master endpoints takes over (becomes the new master). In this example, we create a cluster of two Camel routes, where each route starts with a Master endpoint that is capable of consuming from the specified queue,
FABRIC.DEMO
.
Steps to create a cluster that polls messages from an ActiveMQ broker
To create a master-slave cluster that polls messages from an ActiveMQ broker, based on the Master component, perform the following steps:
- If you do not already have a fabric, enter the following console command to create one:
JBossFuse:karaf@root> fabric:create --new-user AdminUser --new-user-password AdminPass --zookeeper-password ZooPass --wait-for-provisioning
The--new-user
and--new-user-password
options specify the credentials for a new administrator user. The Zookeeper password is used to protect sensitive data in the Fabric registry service (all of the nodes under/fabric
).NoteIf you use a VPN (virtual private network) on your local machine, it is advisable to log off VPN before you create the fabric and to stay logged off while you are using the local container. A local Fabric Server is permanently associated with a fixed IP address or hostname. If VPN is enabled when you create the fabric, the underlying Java runtime is liable to detect and use the VPN hostname instead of your permanent local hostname. This can also be an issue with multi-homed machines. To be absolutely sure about the hostname, you could specify the IP address explicitly—see chapter "Creating a New Fabric" in "Fabric Guide". - For this example, you must have access to a running instance of an Apache ActiveMQ broker and you must know the TCP port of the broker's OpenWire connector. For example, you might get access to an ActiveMQ broker in one of the following ways:
- You just created the fabric on a clean installation of JBoss Fuse (after a cold restart). In this case, the
root
container ought to include thejboss-fuse-full
profile by default. You can check whether this is the case by entering thefabric:container-list
console command, as follows:JBossFuse:karaf@root> fabric:container-list [id] [version] [connected] [profiles] [provision status] root* 1.0 true fabric, fabric-ensemble-0000-1, jboss-fuse-full success
By default, thejboss-fuse-full
profile instantiates an ActiveMQ broker that listens on port61616
. You can use this broker for the current example. - If no broker is running in the root container (or any other container), you can quickly install a broker into a new fabric child container,
broker1
, by entering the following fabric command at the console prompt:JBossFuse:karaf@root> fabric:container-create-child --profile mq-default root broker1
In this case, you can use the browser-based Fuse Management Console to discover the TCP port of the OpenWire connector on the broker.
- Create the
master-example
profile, which will be used to deploy a simple Apache Camel route that uses the Master component. Enter the following console command to create the profile:JBossFuse:karaf@root> fabric:profile-create --parents default master-example
- Add the requisite Karaf features to the
master-example
profile. Enter the following console commands:fabric:profile-edit --features fabric-camel master-example fabric:profile-edit --features activemq-camel master-example
- Define the simple Camel route as a resource in the
master-example
profile. Invoke the built-in text editor to create a newcamel.xml
resource, as follows:fabric:profile-edit --resource camel.xml master-example
Copy and paste the following content into the built-in text editor:<?xml version="1.0" encoding="UTF-8"?> <blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <camelContext id="camel" xmlns="http://camel.apache.org/schema/blueprint"> <route id="fabric-server"> <from uri="master:lockhandle:activemq:queue:FABRIC.DEMO"/> <log message="Message received : ${body}"/> </route> </camelContext> <bean id="activemq" class="org.apache.activemq.camel.component.ActiveMQComponent"> <property name="brokerURL" value="tcp://localhost:OpenWirePort"/> <property name="userName" value="UserName"/> <property name="password" value="Password"/> </bean> </blueprint>
Remember to customize the route configuration by replacing OpenWirePort with the port number of the OpenWire connector on the broker, and by replacing UserName and Password by any valid JAAS credentials on the container (for example, you could substitute the AdminUser and AdminPass credentials created in Step 1 of these instructions).To save and exit from the text editor, type Ctrl-S, Ctrl-X. - Configure the
master-example
profile to deploy thecamel.xml
resource as an OSGi bundle. Enter the following console command to create a new entry in themaster-example
agent properties:fabric:profile-edit --bundles blueprint:profile:camel.xml master-example
NoteTheblueprint:
prefix tells Fabric to deploy the specified resource as a Blueprint XML file, and theprofile:
prefix tells Fabric where to find the resource (that is, in the current version of the current profile). - Create two new child containers, so that you can deploy the
master-example
profile as a cluster (one master and one slave). Enter the following console command:fabric:container-create-child root child 2
- Now deploy both the
master-example
profile and themq-client
profile to each of the child containers, as follows:fabric:container-change-profile child1 master-example mq-client fabric:container-change-profile child2 master-example mq-client
- If you now send some messages to the
FABRIC.DEMO
queue on the broker, the messages are consumed by one (and only one) of the deployed master endpoints. For example, you can easily create and send messages to the broker using the browser-based Fuse Management console. - If you stop the container that hosts the current master (initially, the
child1
container), the slave will be promoted to be the new master (in thechild2
container) and will start consuming messages from theFABRIC.DEMO
queue. For example, assuming thatchild2
contains the current master, you can stop it by entering the following console command:fabric:container-stop child2
OSGi bundle plug-in configuration
When defining an OSGi bundle that uses Master endpoints, the
Import-Package
bundle header must be configured to import the following Java packages:
io.fabric8.zookeeper
For example, assuming that you use Maven to build your application, Example 103.1, “Maven Bundle Plug-In Configuration” shows how you can configure the Maven bundle plug-in to import the required packages.
Example 103.1. Maven Bundle Plug-In Configuration
<project ... > ... <build> <defaultGoal>install</defaultGoal> <plugins> ... <plugin> <groupId>org.apache.felix</groupId> <artifactId>maven-bundle-plugin</artifactId> <extensions>true</extensions> <configuration> <instructions> <Bundle-SymbolicName>${project.groupId}.${project.artifactId}</Bundle-SymbolicName> <Import-Package> io.fabric8.zookeeper, * </Import-Package> </instructions> </configuration> </plugin> </plugins> </build> ... </project>