Package org.radargun.service

Source Code of org.radargun.service.JGroupsService

package org.radargun.service;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.ThreadLocalRandom;

import org.jgroups.Address;
import org.jgroups.JChannel;
import org.jgroups.Message;
import org.jgroups.ReceiverAdapter;
import org.jgroups.View;
import org.jgroups.blocks.MethodCall;
import org.jgroups.blocks.MethodLookup;
import org.jgroups.blocks.RequestOptions;
import org.jgroups.blocks.ResponseMode;
import org.jgroups.blocks.RpcDispatcher;
import org.jgroups.logging.Log;
import org.jgroups.logging.LogFactory;
import org.jgroups.util.RspList;
import org.jgroups.util.Util;
import org.radargun.Service;
import org.radargun.config.Property;
import org.radargun.traits.BasicOperations;
import org.radargun.traits.Clustered;
import org.radargun.traits.Lifecycle;
import org.radargun.traits.ProvidesTrait;


/**
* Plugin measuring the costs of remote gets and puts with JGroups, with regular arguments passed by RadarGun.
* However, a GET returns a <em>prefabricated</em> value (no cache handling) and a PUT simply invokes the remote call,
* but doesn't add anything to a hashmap.<p/>
* The point of this plugin is to measure the overhead of Infinispan's cache handling; it is a base line to the
* Infinispan plugin. The Infinispan plugin should be slower than the JGroups plugin, but the difference should always
* be constant, regardless of the cluster size.<p/>
* Properties, such as the size of the layload for gets, and the number of owners of a key, can be
* defined in jgroups.properties.
* @author Bela Ban
*/
@Service(doc = "JGroups faking cache operations")
public class JGroupsService extends ReceiverAdapter implements Lifecycle, Clustered, BasicOperations.Cache {
   protected static Log log = LogFactory.getLog(JGroupsService.class);

   private static final Method[] METHODS = new Method[6];
   protected static final short GET = 0;
   protected static final short CONTAINS_KEY = 1;
   protected static final short PUT = 2;
   protected static final short GET_AND_PUT = 3;
   protected static final short REMOVE = 4;
   protected static final short GET_AND_REMOVE = 5;

   protected JChannel ch;
   protected RpcDispatcher disp;

   protected volatile boolean started = false;
   protected volatile Address localAddr;
   protected volatile List<Address> members = Collections.emptyList();

   public enum SelfRequests {
      execute,
      exclude,
      noop
   }

   @Property(doc = "Number of nodes where the writes will be replicated.")
   private int numOwners = 2;
   @Property(doc = "What to do with requests directed on ourselves. Variants are 'execute', 'exclude' and 'noop'. Default is execute.")
   private SelfRequests selfRequests = SelfRequests.execute;
   @Property(doc = "Controls use of the DONT_BUNDLE flag. Default is false.")
   private boolean bundle;
   @Property(doc = "Controls use of the FC flag. Default is false.")
   private boolean flowControl;
   @Property(doc = "If true, reads are executed on all owners using ResponseMode GET_FIRST. Otherwise it just randomly picks one node for reading. Default is false.")
   private boolean getFirst;
   @Property(doc = "Controls use of the OOB flag. Default is false.")
   private boolean oob;
   @Property(doc = "Controls use of anycasting flag in RequestOptions. Default is false.")
   private boolean anycasting;

   @Property(name = "file", doc = "Configuration file for JGroups.", deprecatedName = "config")
   protected String configFile;

   private boolean excludeSelfRequests;
   private boolean noopSelfRequests;
   private volatile Object lastValue = null;

   static {
      try {
         METHODS[GET] = JGroupsService.class.getMethod("getFromRemote", Object.class);
         METHODS[CONTAINS_KEY] = JGroupsService.class.getMethod("containsKeyFromRemote", Object.class);
         METHODS[PUT] = JGroupsService.class.getMethod("putFromRemote", Object.class, Object.class);
         METHODS[GET_AND_PUT] = JGroupsService.class.getMethod("getAndPutFromRemote", Object.class, Object.class);
         METHODS[REMOVE] = JGroupsService.class.getMethod("removeFromRemote", Object.class);
         METHODS[GET_AND_REMOVE] = JGroupsService.class.getMethod("getAndRemoveFromRemote", Object.class);
      } catch (NoSuchMethodException e) {
         throw new RuntimeException(e);
      }
   }

   @ProvidesTrait
   public JGroupsService getSelf() {
      return this;
   }

   @ProvidesTrait
   public BasicOperations createOperations() {
      return new BasicOperations() {
         @Override
         public <K, V> Cache<K, V> getCache(String cacheName) {
            return JGroupsService.this;
         }
      };
   }

   @Override
   public void start() {
      excludeSelfRequests = selfRequests == SelfRequests.exclude;
      noopSelfRequests = selfRequests == SelfRequests.noop;

      log.debug("numOwners=" + numOwners + ", selfRequests=" + selfRequests + ", config=" + configFile);

      if (!started) {
         log.info("Loading JGroups form: " + org.jgroups.Version.class.getProtectionDomain().getCodeSource().getLocation());
         log.info("JGroups version: " + org.jgroups.Version.printDescription());

         try {
            ch = new JChannel(configFile);

            disp = new RpcDispatcher(ch, null, this, this);
            disp.setMethodLookup(new MethodLookup() {
               public Method findMethod(short id) {
                  return METHODS[id];
               }
            });

            ch.connect("x");
         } catch (Exception e) {
            throw new RuntimeException(e);
         }
         localAddr = ch.getAddress();

         started = true;
      }
   }

   @Override
   public void stop() {
      Util.close(ch);
      started = false;
   }

   @Override
   public boolean isRunning() {
      return ch.isConnected();
   }

   public Object getFromRemote(Object key) {
      return lastValue;
   }

   public boolean containsKeyFromRemote(Object key) {
      return true;
   }

   public void putFromRemote(Object key, Object value) {
      lastValue = value;
   }

   public Object getAndPutFromRemote(Object key, Object value) {
      Object last = lastValue;
      lastValue = value;
      return last;
   }

   public boolean removeFromRemote(Object key) {
      return true;
   }

   public Object getAndRemoveFromRemote(Object key) {
      return lastValue;
   }

   private Object read(MethodCall methodCall) {
      RequestOptions getOptions = new RequestOptions(getFirst ? ResponseMode.GET_FIRST : ResponseMode.GET_ALL, 20000, false, null);

      if (oob) {
         getOptions.setFlags(Message.Flag.OOB);
      }
      if (!bundle) {
         getOptions.setFlags(Message.Flag.DONT_BUNDLE);
      }
      if (!flowControl) {
         getOptions.setFlags(Message.Flag.NO_FC);
      }
      getOptions.setAnycasting(anycasting);

      try {
         if (getFirst) {
            List<Address> targets = pickReadTargets();
            if (targets == null) {
               return lastValue;
            }
            RspList<Object> responses = disp.callRemoteMethods(targets, methodCall, getOptions);
            return responses.getFirst();
         } else {
            Address target = pickReadTarget();

            // we're simulating picking ourselves, which returns the data directly from the local cache - no RPC involved
            if (target == null) {
               return lastValue;
            }

            return disp.callRemoteMethod(target, methodCall, getOptions);
         }
      } catch (Exception e) {
         throw new RuntimeException(e);
      }
   }

   public Object write(MethodCall methodCall) {
      RequestOptions putOptions = new RequestOptions(ResponseMode.GET_ALL, 20000, true, null); // uses anycasting

      if (oob) {
         putOptions.setFlags(Message.Flag.OOB);
      }
      if (!bundle) {
         putOptions.setFlags(Message.Flag.DONT_BUNDLE);
      }
      if (!flowControl) {
         putOptions.setFlags(Message.Flag.NO_FC);
      }

      Collection<Address> targets = pickWriteTargets();
      try {
         return disp.callRemoteMethods(targets, methodCall, putOptions).getFirst();
      } catch (Exception e) {
         throw new RuntimeException(e);
      }
   }

   @Override
   public Object get(Object key) {
      return read(new MethodCall(GET, new Object[]{key}));
   }

   @Override
   public boolean containsKey(Object key) {
      return (Boolean) read(new MethodCall(CONTAINS_KEY, new Object[]{key}));
   }

   @Override
   public void put(Object key, Object value) {
      write(new MethodCall(PUT, new Object[]{key, value}));
   }

   @Override
   public Object getAndPut(Object key, Object value) {
      return write(new MethodCall(GET_AND_PUT, new Object[]{key, value}));
   }

   @Override
   public boolean remove(Object key) {
      return (Boolean) write(new MethodCall(REMOVE, new Object[] { key }));
   }

   @Override
   public Object getAndRemove(Object key) {
      return write(new MethodCall(GET_AND_REMOVE, new Object[] { key }));
   }

   public void clear() {
      lastValue = null;
   }

   public void viewAccepted(View newView) {
      ArrayList<Address> members = new ArrayList<Address>(newView.getMembers());
      // put the local address at the end of the list
      Collections.rotate(members, members.size() - members.indexOf(ch.getAddress()));
      this.members = members;
   }

   @Override
   public boolean isCoordinator() {
      View view = ch.getView();
      if (view == null || view.getMembers() == null || view.getMembers().isEmpty()) return true;
      return ch.getAddress().equals(view.getMembers().get(0));
   }

   @Override
   public int getClusteredNodes() {
      View view = ch.getView();
      return view != null ? view.size() : 0;
   }

   private Address pickReadTarget() {
      List<Address> members = this.members; // grab reference
      int size = excludeSelfRequests ? members.size() - 1 : members.size();
      int index = ThreadLocalRandom.current().nextInt(size);

      // self also has the keys for the previous numOwners - 1 nodes
      if (noopSelfRequests && index >= members.size() - numOwners)
         return null;
     
      return members.get(index);
   }

   private List<Address> pickReadTargets() {
      List<Address> members = this.members;
      int size = excludeSelfRequests ? members.size() - 1 : members.size();
      int startIndex = ThreadLocalRandom.current().nextInt(size);

      // self also has the keys for the previous numOwners - 1 nodes
      if (noopSelfRequests && startIndex >= members.size() - numOwners)
         return null;

      int numTargets = Math.min(numOwners, excludeSelfRequests ? members.size() - 1 : members.size());
      List<Address> targets = new ArrayList<Address>(numTargets);
      for (int i = 0; i < numTargets; ++i) {
         targets.add(members.get((startIndex + i) % size));
      }
      return targets;
   }

   private Collection<Address> pickWriteTargets() {
      List<Address> members = this.members;
      int size = excludeSelfRequests ? members.size() - 1 : members.size();
      int startIndex = ThreadLocalRandom.current().nextInt(size);

      Collection<Address> targets = new ArrayList<Address>(numOwners);
      for (int i = 0; i < numOwners; i++) {
         int newIndex = (startIndex + i) % size;

         if (noopSelfRequests && newIndex == members.size() - 1)
            continue;

         targets.add(members.get(newIndex));
      }
      return targets;
   }

}
TOP

Related Classes of org.radargun.service.JGroupsService

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.