Sling Discovery API
As Adobe moved to cloud-native solution for AEM by introducing AEM as Cloud Service which can be scalable on both Author & Publish instances, there are scenarios for certain use cases which needs to be targeted to be run on one instance rather than all the current running instances. This article talks about how this problem can be solved by using Sling Discovery API.
In
AEM as Cloud Service, Sling-based deployment consist of several author &
publish instances which are scaled depending on the traffic & load. This
number of instances would form a cluster that shares a common
content repository.
The
discovery-api bundle is introduced as an abstraction for such
scenarios called topology. It provides access to the current
topology, allows to be informed of any changes in the topology (Joining &
Leaving Instances Nodes).
Discovery
Entities
The
following entities are defined by Discovery API.
Instance
& Instance Description
Every
AEM Instance node in Cloud Service is represented in the Discovery API by an InstanceDescription.
·
It represents one Sling instance.
·
It has a unique Sling ID
·
It has a flag that marks if it is leader in a cluster.
·
It has properties which are provided via PropertyProvider.
Cluster
& ClusterView
Multiple
Instances that are connected to the same underlying repository are called as a Cluster.
In Discovery API cluster concept is represented via a ClusterView.
Its features are as below.
·
Each cluster has a stable leader unless the leader crashes.
·
It has an ordered & stable list of instances which are
currently live. The order of the list is stable & it only moves up a
position if an instance listed crashes & replacing newly started instance
will always be added at the end of the list.
·
It has unique id that is persistent across restarts.
Topology
& TopologyView
The
Topology or TopologyView represents a snapshot or
view of several loosely coupled Sling instances (InstanceDescription)
and Clusters (ClusterView) of a particular deployment. A cluster
can consist of one or more instances. Each instance is always part of a cluster
(Even if the cluster consists of only one cluster).
Cluster
Leader
Discovery
API introduces support for Cluster Leader.
1. Within each cluster, the
API guarantees that one & only one instance is leader at any time & it
is guaranteed to be stable.
2. As long it stays alive
& is visible by other instances of the same cluster, it will stay as
leader.
3. As soon as it leaves the
cluster another instance in the cluster is elected as leader.
4. *The leader can be used to
deal with work that must be guaranteed to only execute on one instance in the
cluster.
Topology
Changes
1. The DiscoveryService
provides access to the current valid TopologyView.
2. Applications can register
a TopologyEventListener & any change to the topology are informed.
Whenever the discovery service detects that an instance is no longer responding
or has newly joined, or a new leader has been elected it sends a
TOPOLOGY_CHANGED event, starts settling the change within the topology.
3. When only properties are
changed a PROPERTIES_CHANGED event is sent.
4. Following is an example of
using listener with real time scenario.
Say, If a Service/Servlet/Scheduler
implementations needs to be called only on Cluster Leader Instance in
either author or publish tiers to execute any functionality/task that should
not be executed by multiple running instances at the same time in AEM as Cloud
Service, such use cases require using DiscoveryService by implementing TopologyEventListener.
Cluster
Leader Use Case
1. CREATE CLUSTER INTERFACE
package com.training.sling.service;
import org.apache.sling.discovery.DiscoveryService;
import org.apache.sling.discovery.InstanceDescription;
/**
* Marker interface.
*
*/
public interface ClusterLeader {
}
2. CREATE CLUSTER EVENT
LISTENER
package
com.training.sling.service.impl;
import java.util.Dictionary;
import java.util.Hashtable;
import
org.apache.felix.scr.annotations.Activate;
import
org.apache.felix.scr.annotations.Component;
import
org.apache.felix.scr.annotations.Deactivate;
import
org.apache.felix.scr.annotations.Reference;
import
org.apache.felix.scr.annotations.Service;
import
org.apache.sling.discovery.DiscoveryService;
import
org.apache.sling.discovery.TopologyEvent;
import
org.apache.sling.discovery.TopologyEventListener;
import
org.apache.sling.discovery.TopologyView;
import
org.osgi.framework.BundleContext;
import
org.osgi.framework.ServiceRegistration;
import
com.training.sling.service.ClusterLeader;
/**
* The listener interface for receiving
clusterLeaderEvent events.
*
*/
@Component
@Service(
value = { TopologyEventListener.class
})
public class
ClusterLeaderEventListener implements TopologyEventListener {
/** The discovery service. */
@Reference
private DiscoveryService discoveryService;
/** The bundle context. */
private BundleContext bundleContext;
/** The cluster leader service
registration. */
ServiceRegistration
clusterLeaderServiceRegistration = null;
/**
* Handle topology event.
*
* @param event
* the event
*/
@Override
public void
handleTopologyEvent(TopologyEvent event) {
final TopologyView newView =
event.getNewView();
if (newView != null) {
if
(newView.getLocalInstance().isLeader()) {
registerClusterLeader();
} else {
unregisterClusterLeader();
}
}
}
@Activate
public void activate(BundleContext
bundleContext) {
this.bundleContext = bundleContext;
if
(discoveryService.getTopology().getLocalInstance().isLeader()) {
registerClusterLeader();
}
}
@Deactivate
public void deactivate() {
unregisterClusterLeader();
}
/**
* Register cluster leader.
*/
private synchronized void
registerClusterLeader() {
final Dictionary<String, Object>
props = new Hashtable<>();
if (clusterLeaderServiceRegistration ==
null) {
clusterLeaderServiceRegistration =
bundleContext.registerService(ClusterLeader.class.getName(), new
ClusterLeader() {
}, props);
}
}
/**
* Unregister cluster leader.
*/
private synchronized void
unregisterClusterLeader() {
if (clusterLeaderServiceRegistration !=
null) {
clusterLeaderServiceRegistration.unregister();
clusterLeaderServiceRegistration =
null;
}
}
}
3. INJECT CLUSTER LEADER
/** The cluster leader. */
@Reference
private
transient ClusterLeader clusterLeader;
Properties
The
discovery API provides mechanism for announcing properties for each instance to
the topology via PropertyProvider service interface.
Simple
use case is the announcements of endpoint URLs or ports such that applications
can communicate to other instances in the topology.
Example:
-
import
org.apache.felix.scr.annotations.Component;
import
org.apache.felix.scr.annotations.Service;
import
org.apache.sling.discovery.PropertyProvider;
@Component
@Service(value
= { PropertyProvider.class })
@Property(name
= PropertyProvider.PROPERTY_PROPERTIES,
value = {"sample.value1",
"sample.value2" })
public class
SamplePropertyProvider implements PropertyProvider {
public String getProperty(final String
name) {
if
("sample.value1".equals(name)) {
return "foo";
} else if
("sample.value2".equals(name)) {
return "bar";
} else {
return null;
}
}
}
Comments
Post a Comment