All Downloads are FREE. Search and download functionalities are using the official Maven repository.

bboss.org.jgroups.protocols.Discovery Maven / Gradle / Ivy

The newest version!
package bboss.org.jgroups.protocols;

import java.io.InterruptedIOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Vector;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;

import bboss.org.jgroups.Address;
import bboss.org.jgroups.Event;
import bboss.org.jgroups.Message;
import bboss.org.jgroups.PhysicalAddress;
import bboss.org.jgroups.View;
import bboss.org.jgroups.ViewId;
import bboss.org.jgroups.annotations.MBean;
import bboss.org.jgroups.annotations.ManagedAttribute;
import bboss.org.jgroups.annotations.ManagedOperation;
import bboss.org.jgroups.annotations.Property;
import bboss.org.jgroups.protocols.pbcast.JoinRsp;
import bboss.org.jgroups.stack.Protocol;
import bboss.org.jgroups.util.Promise;
import bboss.org.jgroups.util.TimeScheduler;
import bboss.org.jgroups.util.Tuple;
import bboss.org.jgroups.util.UUID;
import bboss.org.jgroups.util.Util;


/**
 * The Discovery protocol layer retrieves the initial membership (used by the
 * GMS when started by sending event FIND_INITIAL_MBRS down the stack). We do
 * this by specific subclasses, e.g. by mcasting PING requests to an IP MCAST
 * address or, if gossiping is enabled, by contacting the GossipRouter. The
 * responses should allow us to determine the coordinator whom we have to
 * contact, e.g. in case we want to join the group. When we are a server (after
 * having received the BECOME_SERVER event), we'll respond to PING requests with
 * a PING response.
 * 

* The FIND_INITIAL_MBRS event will eventually be answered with a * FIND_INITIAL_MBRS_OK event up the stack. The following properties are * available *

    *
  • timeout - the timeout (ms) to wait for the initial members, default is * 3000=3 secs *
  • num_initial_members - the minimum number of initial members for a * FIND_INITAL_MBRS, default is 2 *
  • num_ping_requests - the number of GET_MBRS_REQ messages to be sent * (min=1), distributed over timeout ms *
* * @author Bela Ban * @version $Id: Discovery.java,v 1.73 2010/04/08 09:36:24 belaban Exp $ */ @MBean public abstract class Discovery extends Protocol { /* ----------------------------------------- Properties -------------------------------------------------- */ @Property(description="Timeout to wait for the initial members. Default is 3000 msec") long timeout=3000; @Property(description="Minimum number of initial members to get a response from. Default is 2") int num_initial_members=2; @Property(description="Minimum number of server responses (PingData.isServer()=true). If this value is " + "greater than 0, we'll ignore num_initial_members") int num_initial_srv_members=0; @Property(description="Return from the discovery phase as soon as we have 1 coordinator response") boolean break_on_coord_rsp=true; @Property(description="Number of discovery requests to be sent distributed over timeout. Default is 2") int num_ping_requests=2; @Property(description="Whether or not to return the entire logical-physical address cache mappings on a " + "discovery request, or not. Default is false, except for TCPPING") boolean return_entire_cache=false; /* --------------------------------------------- JMX ------------------------------------------------------ */ @ManagedAttribute(description="Total number of discovery requests sent ") int num_discovery_requests=0; /** The largest cluster size foudn so far (gets reset on stop()) */ @ManagedAttribute private volatile int max_found_members=0; /* --------------------------------------------- Fields ------------------------------------------------------ */ private volatile boolean is_server=false; protected TimeScheduler timer=null; protected View view; protected final Vector
members=new Vector
(11); protected Address local_addr=null; protected String group_addr=null; protected final Set ping_responses=new HashSet(); private final PingSenderTask sender=new PingSenderTask(); public void init() throws Exception { timer=getTransport().getTimer(); if(timer == null) throw new Exception("timer cannot be retrieved from protocol stack"); } public abstract void sendGetMembersRequest(String cluster_name, Promise promise, boolean return_views_only) throws Exception; public void handleDisconnect() { } public void handleConnect() { } public long getTimeout() { return timeout; } public void setTimeout(long timeout) { this.timeout=timeout; } public int getNumInitialMembers() { return num_initial_members; } public void setNumInitialMembers(int num_initial_members) { this.num_initial_members=num_initial_members; } public int getNumPingRequests() { return num_ping_requests; } public void setNumPingRequests(int num_ping_requests) { this.num_ping_requests=num_ping_requests; } public int getNumberOfDiscoveryRequestsSent() { return num_discovery_requests; } @ManagedAttribute public String getView() {return view != null? view.getViewId().toString() : "null";} public Vector providedUpServices() { Vector ret=new Vector(1); ret.addElement(new Integer(Event.FIND_INITIAL_MBRS)); return ret; } public void resetStats() { super.resetStats(); num_discovery_requests=0; } public void start() throws Exception { super.start(); } public void stop() { is_server=false; max_found_members=0; } /** * Finds the views: sends a GET_MBRS_REQ to all members, waits 'timeout' ms or * until 'num_initial_members' have been retrieved * @return List */ public List findAllViews(Promise promise) { List rsps=findAllMembers(promise); List retval=new ArrayList(); if(rsps != null) { for(PingData data: rsps) { if(data.getView() != null) retval.add(data.getView()); } } return retval; } /** * Finds initial members * @param promise * @return */ public List findInitialMembers(Promise promise) { return findInitialMembers(promise, num_initial_members, break_on_coord_rsp, false); } public List findAllMembers(Promise promise) { int num_expected_mbrs=Math.max(max_found_members, Math.max(num_initial_members, view.size())); max_found_members=Math.max(max_found_members, num_expected_mbrs); return findInitialMembers(promise, num_expected_mbrs, false, true); } protected List findInitialMembers(Promise promise, int num_expected_rsps, boolean break_on_coord, boolean return_views_only) { num_discovery_requests++; final Responses rsps=new Responses(num_expected_rsps, num_initial_srv_members, break_on_coord, promise); synchronized(ping_responses) { ping_responses.add(rsps); } sender.start(group_addr, promise, return_views_only); try { return rsps.get(timeout); } catch(Exception e) { return new LinkedList(); } finally { sender.stop(); synchronized(ping_responses) { ping_responses.remove(rsps); } } } @ManagedOperation(description="Runs the discovery protocol to find initial members") public String findInitialMembersAsString() { List results=findInitialMembers(null); if(results == null || results.isEmpty()) return ""; StringBuilder sb=new StringBuilder(); for(PingData rsp: results) { sb.append(rsp).append("\n"); } return sb.toString(); } @ManagedOperation(description="Runs the discovery protocol to find all views") public String findAllViewsAsString() { List results=findAllViews(null); if(results == null || results.isEmpty()) return ""; StringBuilder sb=new StringBuilder(); for(View view: results) { sb.append(view).append("\n"); } return sb.toString(); } /** * An event was received from the layer below. Usually the current layer will want to examine * the event type and - depending on its type - perform some computation * (e.g. removing headers from a MSG event type, or updating the internal membership list * when receiving a VIEW_CHANGE event). * Finally the event is either a) discarded, or b) an event is sent down * the stack using PassDown or c) the event (or another event) is sent up * the stack using PassUp. *

* For the PING protocol, the Up operation does the following things. * 1. If the event is a Event.MSG then PING will inspect the message header. * If the header is null, PING simply passes up the event * If the header is PingHeader.GET_MBRS_REQ then the PING protocol * will PassDown a PingRequest message * If the header is PingHeader.GET_MBRS_RSP we will add the message to the initial members * vector and wake up any waiting threads. * 2. If the event is Event.SET_LOCAL_ADDR we will simple set the local address of this protocol * 3. For all other messages we simple pass it up to the protocol above * * @param evt - the event that has been sent from the layer below */ @SuppressWarnings("unchecked") public Object up(Event evt) { switch(evt.getType()) { case Event.MSG: Message msg=(Message)evt.getArg(); PingHeader hdr=(PingHeader)msg.getHeader(this.id); if(hdr == null) return up_prot.up(evt); PingData data=hdr.arg; Address logical_addr=data != null? data.getAddress() : null; switch(hdr.type) { case PingHeader.GET_MBRS_REQ: // return Rsp(local_addr, coord) if(group_addr == null || hdr.cluster_name == null) { if(log.isWarnEnabled()) log.warn("group_addr (" + group_addr + ") or cluster_name of header (" + hdr.cluster_name + ") is null; passing up discovery request from " + msg.getSrc() + ", but this should not" + " be the case"); } else { if(!group_addr.equals(hdr.cluster_name)) { if(log.isWarnEnabled()) log.warn("discarding discovery request for cluster '" + hdr.cluster_name + "' from " + msg.getSrc() + "; our cluster name is '" + group_addr + "'. " + "Please separate your clusters cleanly."); return null; } } // add physical address (if available) to transport's cache if(data != null) { if(logical_addr == null) logical_addr=msg.getSrc(); Collection physical_addrs=data.getPhysicalAddrs(); PhysicalAddress physical_addr=physical_addrs != null && !physical_addrs.isEmpty()? physical_addrs.iterator().next() : null; if(logical_addr != null && physical_addr != null) down(new Event(Event.SET_PHYSICAL_ADDRESS, new Tuple(logical_addr, physical_addr))); if(logical_addr != null && data.getLogicalName() != null) UUID.add((UUID)logical_addr, data.getLogicalName()); } if(return_entire_cache && !hdr.return_view_only) { Map cache=(Map)down(new Event(Event.GET_LOGICAL_PHYSICAL_MAPPINGS)); if(cache != null) { for(Map.Entry entry: cache.entrySet()) { Address addr=entry.getKey(); PhysicalAddress physical_addr=entry.getValue(); sendDiscoveryResponse(addr, Arrays.asList(physical_addr), is_server, UUID.get(addr), msg.getSrc()); } } } else { List physical_addrs=hdr.return_view_only? null : Arrays.asList((PhysicalAddress)down(new Event(Event.GET_PHYSICAL_ADDRESS, local_addr))); sendDiscoveryResponse(local_addr, physical_addrs, is_server, UUID.get(local_addr), msg.getSrc()); } return null; case PingHeader.GET_MBRS_RSP: // add response to vector and notify waiting thread // add physical address (if available) to transport's cache if(data != null) { Address response_sender=msg.getSrc(); if(logical_addr == null) logical_addr=msg.getSrc(); Collection physical_addrs=data.getPhysicalAddrs(); PhysicalAddress physical_addr=physical_addrs != null && !physical_addrs.isEmpty()? physical_addrs.iterator().next() : null; if(logical_addr != null && physical_addr != null) down(new Event(Event.SET_PHYSICAL_ADDRESS, new Tuple(logical_addr, physical_addr))); if(logical_addr != null && data.getLogicalName() != null) UUID.add((UUID)logical_addr, data.getLogicalName()); if(log.isTraceEnabled()) log.trace("received GET_MBRS_RSP from " + response_sender + ": " + data); boolean overwrite=logical_addr != null && logical_addr.equals(response_sender); synchronized(ping_responses) { for(Responses response: ping_responses) { response.addResponse(data, overwrite); } } } return null; default: if(log.isWarnEnabled()) log.warn("got PING header with unknown type (" + hdr.type + ')'); return null; } case Event.GET_PHYSICAL_ADDRESS: try { sendGetMembersRequest(group_addr, null, false); } catch(InterruptedIOException ie) { if(log.isWarnEnabled()){ log.warn("Discovery request for cluster " + group_addr + " interrupted"); } Thread.currentThread().interrupt(); } catch(Exception ex) { if(log.isErrorEnabled()) log.error("failed sending discovery request", ex); } return null; } return up_prot.up(evt); } /** * An event is to be sent down the stack. The layer may want to examine its type and perform * some action on it, depending on the event's type. If the event is a message MSG, then * the layer may need to add a header to it (or do nothing at all) before sending it down * the stack using PassDown. In case of a GET_ADDRESS event (which tries to * retrieve the stack's address from one of the bottom layers), the layer may need to send * a new response event back up the stack using up_prot.up(). * The PING protocol is interested in several different down events, * Event.FIND_INITIAL_MBRS - sent by the GMS layer and expecting a GET_MBRS_OK * Event.TMP_VIEW and Event.VIEW_CHANGE - a view change event * Event.BECOME_SERVER - called after client has joined and is fully working group member * Event.CONNECT, Event.DISCONNECT. */ @SuppressWarnings("unchecked") public Object down(Event evt) { switch(evt.getType()) { case Event.FIND_INITIAL_MBRS: // sent by GMS layer case Event.FIND_ALL_MBRS: // sends the GET_MBRS_REQ to all members, waits 'timeout' ms or until 'num_initial_members' have been retrieved long start=System.currentTimeMillis(); boolean find_all_views=evt.getType() == Event.FIND_ALL_MBRS; Promise promise=(Promise)evt.getArg(); List rsps=find_all_views? findAllMembers(promise) : findInitialMembers(promise); long diff=System.currentTimeMillis() - start; if(log.isTraceEnabled()) log.trace("discovery took "+ diff + " ms: responses: " + Util.printPingData(rsps)); return rsps; case Event.TMP_VIEW: case Event.VIEW_CHANGE: Vector

tmp; view=(View)evt.getArg(); if((tmp=view.getMembers()) != null) { synchronized(members) { members.clear(); members.addAll(tmp); } } return down_prot.down(evt); case Event.BECOME_SERVER: // called after client has joined and is fully working group member down_prot.down(evt); is_server=true; return null; case Event.SET_LOCAL_ADDRESS: local_addr=(Address)evt.getArg(); return down_prot.down(evt); case Event.CONNECT: case Event.CONNECT_WITH_STATE_TRANSFER: case Event.CONNECT_USE_FLUSH: case Event.CONNECT_WITH_STATE_TRANSFER_USE_FLUSH: group_addr=(String)evt.getArg(); Object ret=down_prot.down(evt); handleConnect(); return ret; case Event.DISCONNECT: handleDisconnect(); return down_prot.down(evt); default: return down_prot.down(evt); // Pass on to the layer below us } } /* -------------------------- Private methods ---------------------------- */ @SuppressWarnings("unchecked") protected final View makeView(Vector
mbrs) { return new View(new ViewId(local_addr), mbrs); } private void sendDiscoveryResponse(Address logical_addr, List physical_addrs, boolean is_server, String logical_name, Address sender) { PingData ping_rsp=new PingData(logical_addr, view, is_server, logical_name, physical_addrs); Message rsp_msg=new Message(sender, null, null); rsp_msg.setFlag(Message.OOB); PingHeader rsp_hdr=new PingHeader(PingHeader.GET_MBRS_RSP, ping_rsp); rsp_msg.putHeader(this.id, rsp_hdr); if(log.isTraceEnabled()) log.trace("received GET_MBRS_REQ from " + sender + ", sending response " + rsp_hdr); down_prot.down(new Event(Event.MSG, rsp_msg)); } class PingSenderTask { private Future senderFuture; public PingSenderTask() {} public synchronized void start(final String cluster_name, final Promise promise, final boolean return_views_only) { long delay = (long)(timeout / (double)num_ping_requests); if(senderFuture == null || senderFuture.isDone()) { senderFuture=timer.scheduleWithFixedDelay(new Runnable() { public void run() { try { sendGetMembersRequest(cluster_name, promise, return_views_only); } catch(InterruptedIOException ie) { ; } catch(InterruptedException ex) { ; } catch(Throwable ex) { if(log.isErrorEnabled()) log.error("failed sending discovery request", ex); } } }, 0, delay, TimeUnit.MILLISECONDS); } } public synchronized void stop() { if(senderFuture != null) { senderFuture.cancel(true); senderFuture=null; } } } protected static class Responses { final Promise promise; final List ping_rsps=new ArrayList(); final int num_expected_rsps; final int num_expected_srv_rsps; final boolean break_on_coord_rsp; public Responses(int num_expected_rsps, int num_expected_srv_rsps, boolean break_on_coord_rsp, Promise promise) { this.num_expected_rsps=num_expected_rsps; this.num_expected_srv_rsps=num_expected_srv_rsps; this.break_on_coord_rsp=break_on_coord_rsp; this.promise=promise != null? promise : new Promise(); } public void addResponse(PingData rsp) { addResponse(rsp, false); } public void addResponse(PingData rsp, boolean overwrite) { if(rsp == null) return; promise.getLock().lock(); try { if(overwrite) ping_rsps.remove(rsp); // https://jira.jboss.org/jira/browse/JGRP-1179 int index=ping_rsps.indexOf(rsp); if(index == -1) { ping_rsps.add(rsp); promise.getCond().signalAll(); } else if(rsp.isCoord()) { PingData pr=ping_rsps.get(index); // Check if the already existing element is not server if(!pr.isCoord()) { ping_rsps.set(index, rsp); promise.getCond().signalAll(); } } } finally { promise.getLock().unlock(); } } public List get(long timeout) throws InterruptedException{ long start_time=System.currentTimeMillis(), time_to_wait=timeout; promise.getLock().lock(); try { while(time_to_wait > 0 && !promise.hasResult()) { // if num_expected_srv_rsps > 0, then it overrides num_expected_rsps if(num_expected_srv_rsps > 0) { int received_srv_rsps=getNumServerResponses(ping_rsps); if(received_srv_rsps >= num_expected_srv_rsps) return new LinkedList(ping_rsps); } else if(ping_rsps.size() >= num_expected_rsps) { return new LinkedList(ping_rsps); } if(break_on_coord_rsp && containsCoordinatorResponse(ping_rsps)) return new LinkedList(ping_rsps); promise.getCond().await(time_to_wait, TimeUnit.MILLISECONDS); time_to_wait=timeout - (System.currentTimeMillis() - start_time); } return new LinkedList(ping_rsps); } finally { promise.getLock().unlock(); } } private static int getNumServerResponses(Collection rsps) { int cnt=0; for(PingData rsp: rsps) { if(rsp.isServer()) cnt++; } return cnt; } private static boolean containsCoordinatorResponse(Collection rsps) { if(rsps == null || rsps.isEmpty()) return false; for(PingData rsp: rsps) { if(rsp.isCoord()) return true; } return false; } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy