org.jgroups.protocols.relay.RELAY Maven / Gradle / Ivy
Go to download
This artifact provides a single jar that contains all classes required to use remote EJB and JMS, including
all dependencies. It is intended for use by those not using maven, maven users should just import the EJB and
JMS BOM's instead (shaded JAR's cause lots of problems with maven, as it is very easy to inadvertently end up
with different versions on classes on the class path).
package org.jgroups.protocols.relay;
import org.jgroups.*;
import org.jgroups.annotations.*;
import org.jgroups.conf.ConfiguratorFactory;
import org.jgroups.conf.XmlNode;
import org.jgroups.protocols.relay.config.RelayConfig;
import org.jgroups.protocols.relay.config.RelayConfig.SiteConfig;
import org.jgroups.stack.Protocol;
import org.jgroups.util.ExtendedUUID;
import org.jgroups.util.SuppressLog;
import org.jgroups.util.TimeScheduler;
import org.jgroups.util.Util;
import java.io.InputStream;
import java.util.*;
import java.util.concurrent.atomic.LongAdder;
import java.util.function.Consumer;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static java.util.concurrent.TimeUnit.NANOSECONDS;
import static org.jgroups.conf.AttributeType.TIME;
import static org.jgroups.protocols.relay.RelayHeader.SITES_DOWN;
import static org.jgroups.protocols.relay.RelayHeader.SITES_UP;
/**
* Common superclass for {@link RELAY2} and {@link RELAY3}
* @author Bela Ban
* @since 5.2.17
*/
@XmlInclude(schema="relay.xsd",type=XmlInclude.Type.IMPORT,namespace="urn:jgroups:relay:1.0",alias="relay")
@XmlElement(name="RelayConfiguration",type="relay:RelayConfigurationType")
public abstract class RELAY extends Protocol {
// reserved flags
public static final short can_become_site_master_flag = 1 << 1;
@Property(description="Name of the site; must be defined in the configuration",writable=false)
protected String site;
@Property(description="Name of the relay configuration",writable=false)
protected String config;
@Property(description="Whether or not this node can become the site master. If false, " +
"and we become the coordinator, we won't start the bridge(s)",writable=false)
protected boolean can_become_site_master=true;
@Property(description="Maximum number of site masters. Setting this to a value greater than 1 means that we can " +
"have multiple site masters. If the value is greater than the number of cluster nodes, everyone in the site " +
"will be a site master (and thus join the global cluster",writable=false)
protected int max_site_masters=1;
@Property(description="Ratio of members that are site masters, out of range [0..1] (0 disables this). The number " +
"of site masters is computes as Math.min(max_site_masters, view.size() * site_masters_ratio). " +
"See https://issues.redhat.com/browse/JGRP-2581 for details")
protected double site_masters_ratio;
@Property(description="Fully qualified name of a class implementing SiteMasterPicker")
protected String site_master_picker_impl;
@Property(description="If true, the creation of the relay channel (and the connect()) are done in the background. " +
"Async relay creation is recommended, so the view callback won't be blocked")
protected boolean async_relay_creation=true;
@Property(description="Time during which identical errors about no route to host will be suppressed. " +
"0 disables this (every error will be logged).",type= TIME)
protected long suppress_time_no_route_errors=60000;
@ManagedAttribute(description="Whether this member is a site master")
protected volatile boolean is_site_master;
@ManagedAttribute(description="A list of site masters in this (local) site")
protected volatile List site_masters;
@ManagedAttribute(description="The first of all site masters broadcasts route-up/down messages to all members of " +
"the local cluster")
protected volatile boolean broadcast_route_notifications;
protected volatile Relayer relayer;
@Component(description="Maintains a cache of sites and members",name="topo")
protected final Topology topo=new Topology(this);
/** Number of messages forwarded to the local SiteMaster */
protected final LongAdder forward_to_site_master=new LongAdder();
protected final LongAdder forward_sm_time=new LongAdder();
/** Number of messages relayed by the local SiteMaster to a remote SiteMaster */
protected final LongAdder relayed=new LongAdder();
/** Total time spent relaying messages from the local SiteMaster to remote SiteMasters (in ns) */
protected final LongAdder relayed_time=new LongAdder();
/** Number of messages (received from a remote Sitemaster and) delivered by the local SiteMaster to a local node */
protected final LongAdder forward_to_local_mbr=new LongAdder();
protected final LongAdder forward_to_local_mbr_time=new LongAdder();
protected short[] prots_above; // protocol IDs above RELAY
protected TimeScheduler timer;
protected SiteMasterPicker site_master_picker;
protected volatile List members=new ArrayList<>(11);
protected volatile RouteStatusListener route_status_listener;
/** Listens for notifications about becoming site master (arg: true), or ceasing to be site master (arg: false) */
protected Consumer site_master_listener;
/** A map containing site names (e.g. "LON") as keys and SiteConfigs as values */
protected final Map sites=new HashMap<>();
protected SiteConfig site_config;
/** Log to suppress identical errors for messages to non-existing sites ('no route to site X') */
protected SuppressLog suppress_log_no_route;
@ManagedAttribute(description="Number of messages forwarded to the local SiteMaster")
public long getNumForwardedToSiteMaster() {return forward_to_site_master.sum();}
@ManagedAttribute(description="The total time (in ms) spent forwarding messages to the local SiteMaster",type=TIME)
public long getTimeForwardingToSM() {return MILLISECONDS.convert(forward_sm_time.sum(), NANOSECONDS);}
@ManagedAttribute(description="The average number of messages / s for forwarding messages to the local SiteMaster")
public long getAvgMsgsForwardingToSM() {return getTimeForwardingToSM() > 0?
(long)(getNumForwardedToSiteMaster() / (getTimeForwardingToSM()/1000.0)) : 0;}
@ManagedAttribute(description="Number of messages sent by this SiteMaster to a remote SiteMaster")
public long getNumRelayed() {return relayed.sum();}
@ManagedAttribute(description="The total time (ms) spent relaying messages from this SiteMaster to remote SiteMasters"
,type=TIME)
public long getTimeRelaying() {return MILLISECONDS.convert(relayed_time.sum(), NANOSECONDS);}
@ManagedAttribute(description="The average number of messages / s for relaying messages from this SiteMaster to remote SiteMasters")
public long getAvgMsgsRelaying() {return getTimeRelaying() > 0? (long)(getNumRelayed() / (getTimeRelaying()/1000.0)) : 0;}
@ManagedAttribute(description="Number of messages (received from a remote Sitemaster and) delivered " +
"by this SiteMaster to a local node")
public long getNumForwardedToLocalMbr() {return forward_to_local_mbr.sum();}
@ManagedAttribute(description="The total time (in ms) spent forwarding messages to a member in the same site",
type=TIME)
public long getTimeForwardingToLocalMbr() {return MILLISECONDS.convert(forward_to_local_mbr_time.sum(), NANOSECONDS);}
@ManagedAttribute(description="The average number of messages / s for forwarding messages to a member in the same site")
public long getAvgMsgsForwardingToLocalMbr() {return getTimeForwardingToLocalMbr() > 0?
(long)(getNumForwardedToLocalMbr() / (getTimeForwardingToLocalMbr()/1000.0)) : 0;}
@ManagedAttribute(description="Number of 'no route to site X' errors")
public int getNumberOfNoRouteErrors() {return suppress_log_no_route.getCache().size();}
@ManagedOperation(description="Clears the 'no route to site X' cache")
public T clearNoRouteCache() {suppress_log_no_route.getCache().clear(); return (T)this;}
public String getSite() {return site;}
public String site() {return site;}
public T setSite(String s) {this.site=s; return (T)this;}
public T site(String s) {site=s; return (T)this;}
public Topology topo() {return topo;}
public List members() {return members;}
public String config() {return config;}
public T config(String cfg) {config=cfg; return (T)this;}
public String getConfig() {return config;}
public T setConfig(String c) {this.config=c; return (T)this;}
public TimeScheduler getTimer() {return timer;}
public void incrementRelayed() {relayed.increment();}
public void addToRelayedTime(long d) {relayed_time.add(d);}
public List siteMasters() {return site_masters;}
public boolean canBecomeSiteMaster() {return can_become_site_master;}
public T canBecomeSiteMaster(boolean f) {can_become_site_master=f; return (T)this;}
public int getMaxSiteMasters() {return max_site_masters;}
public T setMaxSiteMasters(int m) {this.max_site_masters=m; return (T)this;}
public double getSiteMastersRatio() {return site_masters_ratio;}
public T setSiteMastersRatio(double r) {site_masters_ratio=r; return (T)this;}
public String getSiteMasterPickerImpl() {return site_master_picker_impl;}
public T setSiteMasterPickerImpl(String s) {this.site_master_picker_impl=s; return (T)this;}
public T siteMasterPicker(SiteMasterPicker s) {if(s != null) this.site_master_picker=s; return (T)this;}
public boolean asyncRelayCreation() {return async_relay_creation;}
public T asyncRelayCreation(boolean flag) {async_relay_creation=flag; return (T)this;}
public boolean broadcastRouteNotifications() {return broadcast_route_notifications;}
public T broadcastRouteNotifications(boolean b) {this.broadcast_route_notifications=b; return (T)this;}
public RouteStatusListener getRouteStatusListener() {return route_status_listener;}
public void setRouteStatusListener(RouteStatusListener l) {this.route_status_listener=l;}
public T setSiteMasterListener(Consumer l) {site_master_listener=l; return (T)this;}
public T addSite(String n, SiteConfig cfg) {sites.put(n,cfg); return (T)this;}
public List siteNames() {return getSites();}
@ManagedAttribute(description="Whether or not this instance is a site master")
public boolean isSiteMaster() {return relayer != null;}
public List getSites() {
return sites.isEmpty()? Collections.emptyList() : new ArrayList<>(sites.keySet());
}
@ManagedOperation(description="Prints the routes that are currently up. " +
"Only available if we're the current coordinator (site master)")
public String printSites() {
return relayer != null? Util.print(relayer.getSiteNames()) : "n/a (not site master)";
}
@ManagedOperation(description="Prints the contents of the routing table. " +
"Only available if we're the current coordinator (site master)")
public String printRoutes() {
return relayer != null? relayer.printRoutes() : "n/a (not site master)";
}
/**
* Returns the route to a given site
* @param site_name The site name, e.g. "SFO"
* @return The route to the given site, or null if no route was found or we're not the coordinator
*/
public Route getRoute(String site_name) {
Relayer tmp=relayer;
return tmp != null? tmp.getRoute(site_name): null;
}
/**
* @return A {@link List} of sites name that are currently up or {@code null} if this node is not a Site Master (i.e.
* {@link #isSiteMaster()} returns false).
*/
public List getCurrentSites() { // must return a List, as Infinispan expects a List (not a Collection)
Relayer rel=relayer;
return rel == null ? null : new ArrayList<>(rel.getSiteNames());
}
/**
* Returns the bridge channel to a given site
* @param site_name The site name, e.g. "SFO"
* @return The JChannel to the given site, or null if no route was found or we're not the coordinator
*/
public JChannel getBridge(String site_name) {
Relayer tmp=relayer;
Route route=tmp != null? tmp.getRoute(site_name): null;
return route != null? route.bridge() : null;
}
public View getBridgeView(String cluster_name) {
Relayer tmp=relayer;
return tmp != null? tmp.getBridgeView(cluster_name) : null;
}
public void resetStats() {
super.resetStats();
forward_to_site_master.reset();
forward_sm_time.reset();
relayed.reset();
relayed_time.reset();
forward_to_local_mbr.reset();
forward_to_local_mbr_time.reset();
clearNoRouteCache();
}
public void init() throws Exception {
super.init();
configure();
if(site_master_picker == null) {
site_master_picker=new SiteMasterPicker() {
public Address pickSiteMaster(List site_masters, Address original_sender) {
return Util.pickRandomElement(site_masters);
}
public Route pickRoute(String site, List routes, Address original_sender) {
return Util.pickRandomElement(routes);
}
};
}
if(suppress_time_no_route_errors <= 0)
throw new IllegalArgumentException("suppress_time_no_route_errors has to be > 0");
suppress_log_no_route=new SuppressLog<>(log, "RelayNoRouteToSite", "SuppressMsgRelay");
}
public void stop() {
super.stop();
is_site_master=false;
log.trace("%s: ceased to be site master; closing bridges", local_addr);
if(relayer != null)
relayer.stop();
}
public void configure() throws Exception {
timer=getTransport().getTimer();
if(site == null)
throw new IllegalArgumentException("site cannot be null");
if(max_site_masters < 1) {
log.warn("max_size_masters was " + max_site_masters + ", changed to 1");
max_site_masters=1;
}
if(site_masters_ratio < 0) {
log.warn("%s: changing incorrect site_masters_ratio of %.2f to 0", local_addr, site_masters_ratio);
site_masters_ratio=0.0;
}
else if(site_masters_ratio > 1) {
log.warn("%s: changing incorrect site_masters_ratio of %.2f to 1", local_addr, site_masters_ratio);
site_masters_ratio=1.0;
}
if(site_master_picker_impl != null) {
Class clazz=(Class)Util.loadClass(site_master_picker_impl, (Class>)null);
this.site_master_picker=clazz.getDeclaredConstructor().newInstance();
}
if(config != null)
parseSiteConfiguration(sites);
site_config=sites.get(site);
if(site_config == null)
throw new Exception("site configuration for \"" + site + "\" not found in " + config);
log.trace("site configuration:\n" + site_config);
prots_above=getIdsAbove();
}
@Override
public void parse(XmlNode node) throws Exception {
RelayConfig.parse(node, sites);
}
public Object down(Event evt) {
if(evt.getType() == Event.VIEW_CHANGE)
handleView(evt.getArg());
return down_prot.down(evt);
}
public Object up(Event evt) {
if(evt.getType() == Event.VIEW_CHANGE)
handleView(evt.getArg());
return up_prot.up(evt);
}
public abstract void handleView(View v);
protected abstract void handleRelayMessage(Message msg);
public String toString() {
return String.format("%s%s", getClass().getSimpleName(), local_addr != null? String.format(" (%s)", local_addr) : "");
}
/** Parses the configuration by reading the config file */
protected void parseSiteConfiguration(final Map map) throws Exception {
try(InputStream input=ConfiguratorFactory.getConfigStream(config)) {
RelayConfig.parse(input, map);
}
}
/** Copies the message, but only the headers above the current protocol (RELAY) (or RpcDispatcher related headers) */
protected Message copy(Message msg) {
return Util.copy(msg, true, Global.BLOCKS_START_ID, this.prots_above);
}
protected void sitesChange(boolean down, Set sites) {
if(!broadcast_route_notifications || sites == null || sites.isEmpty())
return;
RelayHeader hdr=new RelayHeader(down? SITES_DOWN : SITES_UP, null, null)
.addToSites(sites);
down_prot.down(new EmptyMessage(null).putHeader(id, hdr));
}
protected void notifySiteMasterListener(boolean flag) {
if(site_master_listener != null)
site_master_listener.accept(flag);
}
protected PhysicalAddress getPhysicalAddress(Address mbr) {
return mbr != null? (PhysicalAddress)down(new Event(Event.GET_PHYSICAL_ADDRESS, mbr)) : null;
}
/**
* Iterates over the list of members and adds every member if the member's rank is below max_site_masters. Skips
* members which cannot become site masters (can_become_site_master == false). If no site master can be found,
* the first member of the view will be returned (even if it has can_become_site_master == false)
*/
protected static List determineSiteMasters(View view, int max_num_site_masters) {
List retval=new ArrayList<>(view.size());
int selected=0;
for(Address member: view) {
if(member instanceof ExtendedUUID && !((ExtendedUUID)member).isFlagSet(can_become_site_master_flag))
continue;
if(selected++ < max_num_site_masters)
retval.add(member);
}
if(retval.isEmpty()) {
Address coord=view.getCoord();
if(coord != null)
retval.add(coord);
}
return retval;
}
/** Returns a site master from site_masters */
protected Address pickSiteMaster(Address sender) {
List masters=site_masters;
if(masters.size() == 1)
return masters.get(0);
return site_master_picker.pickSiteMaster(masters, sender);
}
protected void triggerSiteUnreachableEvent(SiteAddress remoteSite) {
up_prot.up(new Event(Event.SITE_UNREACHABLE, remoteSite));
if(route_status_listener != null)
route_status_listener.sitesUnreachable(remoteSite.getSite());
}
}