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

org.jdiameter.client.impl.router.WeightedRoundRobinRouter Maven / Gradle / Ivy

The newest version!
/*
 * TeleStax, Open Source Cloud Communications
 * Copyright 2011-2016, Telestax Inc and individual contributors
 * by the @authors tag.
 *
 * This program is free software: you can redistribute it and/or modify
 * under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation; either version 3 of
 * the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see 
 */

package org.jdiameter.client.impl.router;

import org.jdiameter.api.Configuration;
import org.jdiameter.api.MetaData;
import org.jdiameter.api.PeerState;
import org.jdiameter.client.api.IContainer;
import org.jdiameter.client.api.controller.IPeer;
import org.jdiameter.client.api.controller.IRealmTable;
import org.jdiameter.common.api.concurrent.IConcurrentFactory;

import java.util.List;

/**
 * Weighted round-robin router implementation
 *
 * @see http://kb.linuxvirtualserver.org/wiki/Weighted_Round-Robin_Scheduling
 * @author Nils Sowen
 */
public class WeightedRoundRobinRouter extends RouterImpl {

  private int lastSelectedPeer = -1;
  private int currentWeight = 0;

  protected WeightedRoundRobinRouter(IRealmTable table, Configuration config) {
    super(null, null, table, config, null);
  }

  public WeightedRoundRobinRouter(IContainer container, IConcurrentFactory concurrentFactory,
                                  IRealmTable realmTable, Configuration config, MetaData aMetaData) {
    super(container, concurrentFactory, realmTable, config, aMetaData);
  }

  /**
   * Select peer by weighted round-robin scheduling
   * As documented in http://kb.linuxvirtualserver.org/wiki/Weighted_Round-Robin_Scheduling
   *
   * 

* The weighted round-robin scheduling is designed to better handle servers * with different processing capacities. Each server can be assigned a weight, * an integer value that indicates the processing capacity. Servers with higher * weights receive new connections first than those with less weights, and servers * with higher weights get more connections than those with less weights and servers * with equal weights get equal connections. The pseudo code of weighted round-robin * scheduling is as follows: *

* Supposing that there is a server set S = {S0, S1, …, Sn-1}; * W(Si) indicates the weight of Si; * i indicates the server selected last time, and i is initialized with -1; * cw is the current weight in scheduling, and cw is initialized with zero; * max(S) is the maximum weight of all the servers in S; * gcd(S) is the greatest common divisor of all server weights in S; *

*

   * {@code
   *   while (true) {
   *   i = (i + 1) mod n;
   *   if (i == 0) {
   *     cw = cw - gcd(S);
   *     if (cw <= 0) {
   *       cw = max(S);
   *       if (cw == 0)
   *       return NULL;
   *     }
   *   }
   *   if (W(Si) >= cw)
   *     return Si;
   *   }
   * }
   * 
*

* For example, the real servers, A, B and C, have the weights, 4, 3, 2 respectively, * a scheduling sequence will be AABABCABC in a scheduling period (mod sum(Wi)). *

* In an optimized implementation of the weighted round-robin scheduling, a scheduling sequence * will be generated according to the server weights after the rules of IPVS are modified. * The network connections are directed to the different real servers based on the scheduling * sequence in a round-robin manner. *

* The weighted round-robin scheduling is better than the round-robin scheduling, when the * processing capacity of real servers are different. However, it may lead to dynamic load * imbalance among the real servers if the load of the requests vary highly. In short, there * is the possibility that a majority of requests requiring large responses may be directed * to the same real server. *

* Actually, the round-robin scheduling is a special instance of the weighted round-robin * scheduling, in which all the weights are equal. *

* This method is internally synchronized due to concurrent modifications to lastSelectedPeer and currentWeight. * Please consider this when relying on heavy throughput. * * Please note: if the list of availablePeers changes between calls (e.g. if a peer becomes active or inactive), * the balancing algorithm is disturbed and might be distributed uneven. * This is likely to happen if peers are flapping. * * @param availablePeers list of peers that are in {@link PeerState#OKAY OKAY} state * @return the selected peer according to algorithm * @see http://kb.linuxvirtualserver.org/wiki/Weighted_Round-Robin_Scheduling */ @Override public IPeer selectPeer(List availablePeers) { int peerSize = availablePeers != null ? availablePeers.size() : 0; // Return none if empty, or first if only one member found if (peerSize <= 0) { return null; } if (peerSize == 1) { return availablePeers.iterator().next(); } // Find maximum weight and greatest common divisor of weight across all peers int maxWeight = 0; Integer gcd = null; for (IPeer peer : availablePeers) { maxWeight = Math.max(maxWeight, peer.getRating()); gcd = (gcd == null) ? peer.getRating() : gcd(gcd, peer.getRating()); } // Find best matching candidate. Synchronized here due to consistent changes on member variables synchronized (this) { for ( ;; ) { lastSelectedPeer = (lastSelectedPeer + 1) % peerSize; if (lastSelectedPeer == 0) { currentWeight = currentWeight - gcd; if (currentWeight <= 0) { currentWeight = maxWeight; } } if (peerSize <= lastSelectedPeer) { lastSelectedPeer = -1; // safety first, restart if peer size has accidentally changed. continue; } IPeer candidate = availablePeers.get(lastSelectedPeer); if (candidate.getRating() >= currentWeight) { return availablePeers.get(lastSelectedPeer); } } } } /** * Return greatest common divisor for two integers * https://en.wikipedia.org/wiki/Greatest_common_divisor#Using_Euclid.27s_algorithm * * @param a * @param b * @return greatest common divisor */ protected int gcd(int a, int b) { return (b == 0) ? a : gcd(b, a % b); } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy