2010-01-11 28 views
10

Tengo una lista de hosts en una matriz que representa los servidores disponibles para realizar un trabajo en particular. Actualmente simplemente itero a través de la lista buscando y establezco comunicaciones con un host para verificar que no esté ocupado. Si no, le enviaré un trabajo. Este enfoque tiende a significar que el primer host en la lista tiende a calentarse constantemente con la carga no equilibrada correctamente con el resto de los hosts disponibles.round robin programación de iteradores de Java

en pseudocódigo ..

for (Host h : hosts) { 

    //checkstatus 
    if status == job accepted break; 

} 

me gustaría equilibrar esta carga correctamente entre los anfitriones, es decir, por primera vez anfitrión de un segundo de tiempo se utiliza el método se utiliza anfitrión 2. Sólo me preguntaba que la solución más elegante a esto es ??

Gracias W

+0

Creo que probablemente im después de que se . Probablemente implementando un iterador personalizado para mi lista de hosts. Simplemente no estoy seguro de cómo hacer esto. Tengo la sensación de que necesito extender la clase ArrayList e implementar la clase ListIterator. ¿Suena razonable? – wmitchell

Respuesta

11

Se puede crear un nuevo tipo de Iterable que proporciona round-robin de iteración:

public class RoundRobin<T> implements Iterable<T> { 
     private List<T> coll; 

     public RoundRobin(List<T> coll) { this.coll = coll; } 

     public Iterator<T> iterator() { 
     return new Iterator<T>() { 
      private int index = 0; 

      @Override 
      public boolean hasNext() { 
       return true; 
      } 

      @Override 
      public T next() { 
       T res = coll.get(index); 
       index = (index + 1) % coll.size(); 
       return res; 
      } 

      @Override 
      public void remove() { 
       throw new UnsupportedOperationException(); 
      } 

     }; 
    } 
} 

Es necesario definir sus anfitriones como RoundRobin<Host>.

[fijo basado en el comentario de Mirko]

+2

Buena solución. Un pequeño problema: realizar coll.get (index) es caro para algunas listas. Yo diría que mantener un iterador y hacer lo siguiente en él, y obtener un iterador nuevo cuando se quede sin elementos es mejor. – Buhb

+0

Este es el tipo de cosa después de las gracias Itay y Buhb – wmitchell

+0

Su implementación tiene errores, porque la primera llamada de next() entrega un nulo. Ver: http://stackoverflow.com/a/12931655/1268954 – Mirko

4

Si la lista es mutable y el costo de la edición es insignificante en comparación con I/O con los anfitriones, que sólo puede girar:

List<String> list = Arrays.asList("one", "two", "three"); 
Collections.rotate(list, -1); 
System.out.println(list); 
0

Si está creando un iterador, es mejor crear primero una copia defensiva y hacer que el iterador trabaje en eso.

return new MyIterator(ImmutableList.<T>copyOf(list)); 
+1

'of' debe ser' copyOf'; de lo contrario, tiene una lista que contiene como único elemento una referencia a la lista original. –

+0

Sí. Lo siento, más bien es un mal tipeo – gpampara

4

mi humilde opinión la API estándar de Java ya proporciona una manera fácil de lograr esto, sin recurrir a bibliotecas externas o incluso la necesidad de implementar un iterador personalizado. Simplemente use un Deque en el que tiraría el primer servidor, úselo o deséchelo, y luego vuelva a agregarlo al final del Deque. He aquí algunos ejemplos de código:

// Initialize the Deque. This might be at your class constructor. 
Deque<Host> dq = new ArrayDeque<Host>(); 
dq.addAll(Arrays.asList(hosts)); 

void sendJob(Job myJob) { 
    boolean jobInProcess = false; 
    do { 
     Host host = dq.removeFirst(); // Remove the host from the top 
     if(!host.isBusy()) { 
      host.sendJob(myJob); 
      jobInProcess = true; 
     } 
     dq.addLast(host); // Put the host back at the end 
    } 
    while(!jobInProcess); // Might add another condition to prevent an infinite loop...  
} 

Esto es sólo una muestra en la que siempre anfitriones de ping con el fin de round robin en un bucle que sólo termina cuando uno de ellos es disponibles y que tenga el trabajo. Podrías jugar fácilmente para ir solo por la cola una vez (usa un contador con un máximo establecido en el tamaño de la cola) o varias veces antes de lanzar una excepción, o dormir entre rondas para evitar golpear a los hosts cuando todos estén ocupados .

1

Mi aplicación RoundRobin, en base a la aplicación de https://stackoverflow.com/a/2041772/1268954

/** 
* 
* @author Mirko Schulze 
* 
* @param <T> 
*/ 
public class RoundRobin<T> implements Iterable<T> { 

    private final List<T> coll; 

    public RoundRobin(final List<T> coll) { 
     this.coll = NullCheck.throwExceptionIfNull(coll, "collection is null"); 
    } 

    @Override 
    public Iterator<T> iterator() { 
     return new Iterator<T>() { 

      private int index; 

      @Override 
      public boolean hasNext() { 
       return true; 
      } 

      @Override 
      public T next() { 
       this.index = this.index % RoundRobin.this.coll.size(); 
       final T t = RoundRobin.this.coll.get(this.index); 
       this.index++; 
       return t; 
      } 

      @Override 
      public void remove() { 
       throw new IllegalArgumentException("remove not allowd"); 
      } 
     }; 
    } 
} 

Y el Junit TestCase

/** 
* 
* @author Mirko Schulze 
* 
*/ 
@RunWith(JUnit4.class) 
public class RoundRobinTest extends TestCase { 

    private List<Integer> getCollection() { 
     final List<Integer> retval = new Vector<Integer>(); 
     retval.add(Integer.valueOf(1)); 
     retval.add(Integer.valueOf(2)); 
     retval.add(Integer.valueOf(3)); 
     retval.add(Integer.valueOf(4)); 
     retval.add(Integer.valueOf(5)); 
     return retval; 
    } 

    @Test 
    public void testIteration() { 
     final List<Integer> l = this.getCollection(); 
     final Integer frst = l.get(0); 
     final Integer scnd = l.get(1); 
     final Integer thrd = l.get(2); 
     final Integer frth = l.get(3); 
     final Integer last = l.get(4); 
     Assert.assertEquals("die Collection hat für diesen Test nicht die passende Größe!", 5, l.size()); 
     final RoundRobin<Integer> rr = new RoundRobin<Integer>(l); 
     final Iterator<Integer> i = rr.iterator(); 
     for (int collectionIterations = 0; collectionIterations < 4; collectionIterations++) { 
      final Integer i1 = i.next(); 
      Assert.assertEquals("nicht das erste Element", frst, i1); 
      final Integer i2 = i.next(); 
      Assert.assertEquals("nicht das zweite Element", scnd, i2); 
      final Integer i3 = i.next(); 
      Assert.assertEquals("nicht das dritte Element", thrd, i3); 
      final Integer i4 = i.next(); 
      Assert.assertEquals("nicht das vierte Element", frth, i4); 
      final Integer i5 = i.next(); 
      Assert.assertEquals("nicht das letzte Element", last, i5); 
     } 
    } 
} 
0
public class RoundRobinIterator<T> implements Serializable { 

     private static final long serialVersionUID = -2472203060894189676L; 
     // 
     private List<T> list; 
     private Iterator<T> it; 
     private AtomicInteger index = new AtomicInteger(0); 

     public RoundRobinIterator(List<T> list) throws NullPointerException { 
      super(); 
      if (list==null) { 
       throw new NullPointerException("List is null"); 
      } 
      this.list=Collections.unmodifiableList(list); 
     } 
     public RoundRobinIterator(Collection<T> values) { 
      this(new ArrayList<T>(values)); 
     } 
     public RoundRobinIterator(Iterator<T> values) { 
      this(copyIterator(values)); 
     } 
     public RoundRobinIterator(Enumeration<T> values) { 
      this(Collections.list(values)); 
     } 



     private final List<T> getList() { 
      return list; 
     } 
     private final Iterator<T> getIt() { 
      return it; 
     } 
     public final int size() { 
      return list.size(); 
     } 
     public final synchronized T getNext(Filter<T> filter) { 
      int start = index.get(); 
      T t = getNext(); 
      T result = null; 
      while ((result==null) && (start!=getIndex())) { 
       if (filter.accept(t)) { 
        result=t; 
       } else { 
        t = getNext(); 
       } 
      } 
      return result; 
     } 

     public final synchronized T getNext() { 
      if (getIt()==null) { 
       if (getList().size()==0) { 
        index.set(0); 
        return null; 
       } else { 
        it = getList().iterator(); 
        index.set(0); 
        return it.next(); 
       } 
      } else if (it.hasNext()) { 
       index.incrementAndGet(); 
       return it.next(); 
      } else { 
       if (list.size()==0) { 
        index.set(0); 
        return null; 
       } else { 
        index.set(0); 
        it = list.iterator();    
        return it.next(); 
       } 
      } 
     } 

     public final synchronized int getIndex() { 
      return index.get(); 
     } 


     private static <T> List<T> copyIterator(Iterator<T> iter) { 
      List<T> copy = new ArrayList<T>(); 
      while (iter.hasNext()) { 
       copy.add(iter.next()); 
      } 
      return copy; 
     } 
    } 

Dónde filtro es

public interface Filter<T> { 

     public boolean accept(T t); 

    }