org.jgroups.protocols.relay.RELAY3 Maven / Gradle / Ivy
Go to download
This artifact provides a single jar that contains all classes required to use remote Jakarta Enterprise Beans and Jakarta Messaging, including
all dependencies. It is intended for use by those not using maven, maven users should just import the Jakarta Enterprise Beans and
Jakarta Messaging 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).
The newest version!
package org.jgroups.protocols.relay;
import org.jgroups.*;
import org.jgroups.Message.Flag;
import org.jgroups.annotations.MBean;
import org.jgroups.annotations.ManagedAttribute;
import org.jgroups.annotations.ManagedOperation;
import org.jgroups.annotations.Property;
import org.jgroups.protocols.UNICAST3;
import org.jgroups.protocols.relay.SiteAddress.Type;
import org.jgroups.protocols.relay.SiteStatus.Status;
import org.jgroups.stack.AddressGenerator;
import org.jgroups.stack.ProtocolStack;
import org.jgroups.util.UUID;
import org.jgroups.util.*;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.function.Predicate;
import java.util.function.Supplier;
import static org.jgroups.protocols.relay.RelayHeader.*;
// todo: check if copy is needed in route(), passUp() and deliver(); possibly pass a boolean as parameter (copy or not)
// todo: use CompletableFutures in routeThen(); this could parallelize routing and delivery/passsing up
// todo: check if a message can bypass RELAY2 completely when NO_RELAY is set (in up(),down())
/**
* Provides relaying of messages between autonomous sites.
* Design: ./doc/design/RELAY2.txt and at https://github.com/belaban/JGroups/blob/master/doc/design/RELAY2.txt.
* JIRA: https://issues.redhat.com/browse/JGRP-1433
* @author Bela Ban
* @since 3.2
*/
@SuppressWarnings("ArraysAsListWithZeroOrOneArgument")
@MBean(description="RELAY3 protocol")
public class RELAY3 extends RELAY {
// to prevent duplicate sitesUp()/sitesDown() notifications; this is needed in every member: routes are only
// maintained by site masters (relayer != null)
// todo: replace with topo once JGRP-2706 is in place
@ManagedAttribute(description="A cache maintaining a list of sites that are up")
protected final SiteStatus site_status=new SiteStatus();
@Property(description="Number of millis by which SITE_UNREACHABLE events are delayed; see " +
"RelayTest.testFailover() for details")
protected long delay_site_unreachable_events=3000;
protected Delayer site_unreachable_delayer;
protected UNICAST3 unicast;
public SiteStatus siteStatus() {return site_status;}
public long delaySiteUnreachableEvents() {return delay_site_unreachable_events;}
public RELAY3 delaySiteUnreachableEvents(long t) {
delay_site_unreachable_events=t;
if(site_unreachable_delayer != null)
site_unreachable_delayer.timeout(t);
return this;}
@Override public void configure() throws Exception {
super.configure();
JChannel ch=getProtocolStack().getChannel();
ch.addAddressGenerator(new AddressGenerator() {
@Override public Address generateAddress() {return generateAddress(null);}
@Override public Address generateAddress(String name) {
SiteUUID uuid=new SiteUUID(UUID.randomUUID(), name, site);
if(can_become_site_master)
uuid.setFlag(can_become_site_master_flag);
return uuid;
}
});
}
@Override
public void init() throws Exception {
super.init();
site_unreachable_delayer=new Delayer<>(delay_site_unreachable_events);
unicast=ProtocolStack.findProtocol(up_prot, false, UNICAST3.class);
}
@Override
public void destroy() {
super.destroy();
site_unreachable_delayer.clear();
}
@ManagedOperation(description="Prints the topology (site masters and local members) of this site")
public String printTopology(boolean all_sites) {
if(!all_sites)
return printLocalTopology();
return topo.print();
}
@ManagedOperation(description="Prints the topology (site masters and local members) of this site")
public String printLocalTopology() {
return topo.print(this.site);
}
public Object down(Message msg) {
//if(msg.isFlagSet(Flag.NO_RELAY))
// return down_prot.down(msg);
if(msg.src() == null)
msg.src(local_addr);
return process(true, msg);
}
public Object up(Message msg) {
// if(msg.isFlagSet(Flag.NO_RELAY))
// return up_prot.up(msg);
Message copy=msg;
RelayHeader hdr=msg.getHeader(id);
if(hdr != null) {
switch(hdr.type) {
case SITE_UNREACHABLE:
Set tmp_sites=hdr.getSites();
if(tmp_sites != null) {
for(String s: tmp_sites)
triggerSiteUnreachableEvent(new SiteMaster(s));
}
return null;
case MBR_UNREACHABLE:
Address unreachable_mbr=msg.getObject();
triggerMemberUnreachableEvent(unreachable_mbr);
return null;
}
//todo: check if copy is needed!
copy=copy(msg).dest(hdr.final_dest).src(hdr.original_sender).putHeader(id, hdr);
}
return process(false, copy);
}
public void up(MessageBatch batch) {
Set unreachable_sites=null;
for(Iterator it=batch.iterator(); it.hasNext();) {
Message msg=it.next(), copy=msg;
// if(msg.isFlagSet(Flag.NO_RELAY))
// continue;
RelayHeader hdr=msg.getHeader(id);
it.remove();
if(hdr != null) {
switch(hdr.type) {
case SITE_UNREACHABLE:
Set tmp_sites=hdr.getSites();
if(tmp_sites != null) {
if(unreachable_sites == null)
unreachable_sites=new HashSet<>();
unreachable_sites.addAll(tmp_sites);
}
continue;
case MBR_UNREACHABLE:
Address unreachable_mbr=msg.getObject();
triggerMemberUnreachableEvent(unreachable_mbr);
continue;
}
copy=copy(msg).dest(hdr.final_dest).src(hdr.original_sender).putHeader(id, hdr);
}
process(false, copy);
}
if(unreachable_sites != null) {
for(String s: unreachable_sites)
triggerSiteUnreachableEvent(new SiteMaster(s)); // https://issues.redhat.com/browse/JGRP-2586
}
if(!batch.isEmpty())
up_prot.up(batch);
}
/**
* Returns information about all members of all sites, or only the local members of a given site
* @param all_sites When true, return information about all sites (1 msg/site), otherwise only about this site
* @param dest The address to which to send the response
*/
protected void sendResponseTo(Address dest, boolean all_sites) {
if(all_sites) {
sendResponsesForAllSites(dest);
return;
}
if(log.isDebugEnabled())
log.debug("%s: sending topo response to %s: %s", local_addr, dest, view);
RelayHeader hdr=new RelayHeader(TOPO_RSP, dest, local_addr).addToSites(this.site);
Message rsp=new ObjectMessage(dest, this.view).putHeader(this.id, hdr)
.setFlag(Message.TransientFlag.DONT_LOOPBACK);
down(rsp);
}
protected void sendResponsesForAllSites(Address dest) {
for(Map.Entry e: topo.cache().entrySet()) {
String site_name=e.getKey();
View v=e.getValue();
if(log.isDebugEnabled())
log.debug("%s: sending topo response to %s: %s", local_addr, dest, view);
RelayHeader hdr=new RelayHeader(TOPO_RSP, dest, local_addr).addToSites(site_name);
Message rsp=new ObjectMessage(dest, v).putHeader(this.id, hdr)
.setFlag(Message.TransientFlag.DONT_LOOPBACK);
down(rsp);
}
}
public void handleView(View view) {
View old_view=this.view;
this.view=view;
members=view.getMembers(); // First, save the members for routing received messages to local members
int max_num_site_masters=max_site_masters;
if(site_masters_ratio > 0)
max_num_site_masters=(int)Math.max(max_site_masters, site_masters_ratio * view.size());
List old_site_masters=site_masters;
List new_site_masters=determineSiteMasters(view, max_num_site_masters);
boolean become_site_master=new_site_masters.contains(local_addr)
&& (old_site_masters == null || !old_site_masters.contains(local_addr));
boolean cease_site_master=old_site_masters != null
&& old_site_masters.contains(local_addr) && !new_site_masters.contains(local_addr);
site_masters=new_site_masters;
if(!site_masters.isEmpty() && site_masters.get(0).equals(local_addr))
broadcast_route_notifications=true;
if(become_site_master) {
is_site_master=true;
final String bridge_name="_" + NameCache.get(local_addr);
if(relayer != null)
relayer.stop();
relayer=new Relayer3(this, log);
final Relayer3 tmp=(Relayer3)relayer;
final long start=System.currentTimeMillis();
if(async_relay_creation)
timer.execute(() -> startRelayer(tmp, bridge_name)
.handleAsync((r,t) -> handleRelayerStarted(relayer, start, r, t)));
else
startRelayer(tmp, bridge_name)
.handleAsync((r,t) -> handleRelayerStarted(relayer, start, r, t));
notifySiteMasterListener(true);
}
else {
if(cease_site_master) { // ceased being the site master: stop the relayer
is_site_master=false;
notifySiteMasterListener(false);
log.trace(local_addr + ": ceased to be site master; closing bridges");
if(relayer != null)
relayer.stop();
}
}
suppress_log_no_route.removeExpired(suppress_time_no_route_errors);
topo.put(site, view);
if(!topo.globalViews())
return;
if(is_site_master) {
if(!become_site_master) {
// send my own information as a TOPO_RSP to all site masters, who then forward it locally with NO_RELAY
sendResponseTo(new SiteMaster(null), false);
}
}
else { // not site master
// on the first view and as non site master -> fetch the cache from the site master
if(old_view == null && !cease_site_master) {
topo.refresh(this.site, true);
// CompletableFuture.runAsync(() -> topo.refresh(this.site, true));
}
}
}
protected Object handleRelayerStarted(Relayer r, long start, T ignored, Throwable t) {
if(t != null)
log.error(local_addr + ": failed starting relayer", t);
else {
log.info("%s: relayer was started in %d ms: %s", local_addr, System.currentTimeMillis() - start, r);
if(topo.globalViews()) {
// get the caches from all site masters
topo.refresh(null);
// send my own information as a TOPO_RSP to all site masters, who then forward it locally with NO_RELAY
sendResponseTo(new SiteMaster(null), true);
}
}
return null;
}
/** Called to handle a message received from a different site (via a bridge channel) */
protected void handleRelayMessage(Message msg) {
RelayHeader hdr=msg.getHeader(id);
if(hdr == null) {
log.warn("%s: received a message without a relay header; discarding it", local_addr);
return;
}
try {
Header[] original_hdrs=hdr.originalHeaders();
Message copy=copy(msg).dest(hdr.final_dest).src(hdr.original_sender);
copy.clearHeaders();
if(original_hdrs != null && Headers.size(original_hdrs) > 0)
((BaseMessage)copy).headers(original_hdrs); // removes/overwrites all headers added by the bridge cluster
copy.putHeader(id, hdr);
if(msg.dest() != null)
copy.setFlag(hdr.originalFlags(), false);
// todo: check if copy is needed!
process(true, copy);
}
catch(Exception ex) {
log.error("%s: failed handling message relayed from %s: %s", local_addr, msg.src(), ex);
}
}
/** Handles SITES_UP/SITES_DOWN/TOPO_REQ/TOPO_RSP messages */
protected boolean handleAdminMessage(RelayHeader hdr, Message msg) {
switch(hdr.type) {
case SITES_UP:
case SITES_DOWN:
Set tmp_sites=new HashSet<>();
if(hdr.hasSites())
tmp_sites.addAll(hdr.getSites());
tmp_sites.remove(this.site);
if(tmp_sites != null && !tmp_sites.isEmpty()) {
Status status=hdr.type == SITES_UP? Status.up : Status.down;
Set tmp=site_status.add(tmp_sites, status);
if(status == Status.down)
topo.removeAll(tmp_sites);
if(route_status_listener != null && !tmp.isEmpty()) {
String[] t=tmp.toArray(new String[]{});
if(hdr.type == SITES_UP)
route_status_listener.sitesUp(t);
else
route_status_listener.sitesDown(t);
}
}
return true;
case TOPO_REQ:
if(!Objects.equals(local_addr, msg.src()))
sendResponseTo(msg.src(), hdr.returnEntireCache());
return true;
case TOPO_RSP:
View v=msg.getObject();
String site_name=Objects.requireNonNull(hdr.getSite());
if(v != null) {
topo.put(site_name, v);
if(!topo.globalViews())
return true;
Address dest=msg.getDest();
if(dest != null && ((SiteAddress)dest).type() == Type.SM_ALL) {
Message local_mcast=new ObjectMessage(null, v).putHeader(id, hdr);
deliver(null, local_mcast, true, true, true);
}
}
return true;
}
return false;
}
@Override
protected void triggerSiteUnreachableEvent(SiteAddress s) {
if(!delay_sites_down) {
_triggerSiteUnreachableEvent(s);
return;
}
String unreachable_site=s.getSite();
Predicate pred=k -> relayer != null && relayer.hasRouteTo(k);
site_unreachable_delayer.add(unreachable_site, pred, success -> {
if(!success) // only trigger if the route was *not* found
_triggerSiteUnreachableEvent(s);
});
}
protected void _triggerSiteUnreachableEvent(SiteAddress s) {
super.triggerSiteUnreachableEvent(s);
if(unicast != null) {
Predicate pred=addr -> addr.isSiteAddress() && ((SiteAddress)addr).getSite().equals(s.getSite());
unicast.removeSendConnection(pred);
}
}
@Override
protected void triggerMemberUnreachableEvent(Address mbr) {
super.triggerMemberUnreachableEvent(mbr);
if(unicast != null)
unicast.removeSendConnection(mbr);
}
// todo: use CompletableFutures and possibly thenRunAsync() to parallelize (e.g.) routing and local delivery
protected Object routeThen(Message msg, List sites, Supplier
© 2015 - 2025 Weber Informatics LLC | Privacy Policy