/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.ignite.spi.loadbalancing.adaptive;
import org.apache.ignite.cluster.ClusterMetrics;
import org.apache.ignite.cluster.ClusterNode;
import org.apache.ignite.internal.util.typedef.internal.A;
import org.apache.ignite.internal.util.typedef.internal.S;
/**
* Implementation of node load probing based on CPU load.
* <p>
* Based on {@link #setUseAverage(boolean)}
* parameter, this implementation will either use average CPU load
* values or current (default is to use averages).
* <p>
* Based on {@link #setUseProcessors(boolean)} parameter, this implementation
* will either take number of processors on the node into account or not.
* Since CPU load on multi-processor boxes shows medium load of multiple CPU's it
* usually means that the remaining capacity is proportional to the number of
* CPU's (or cores) on the node. This configuration parameter indicates
* whether to divide each node's CPU load by the number of processors on that node
* (default is {@code true}).
* <p>
* Also note that in some environments every processor may not be adding 100% of
* processing power. For example, if you are using multi-core CPU's, then addition of
* every core would probably result in about 75% of extra CPU power. To account
* for that, you should set {@link #setProcessorCoefficient(double)} parameter to
* {@code 0.75} .
* <p>
* Below is an example of how CPU load probe would be configured in Ignite
* Spring configuration file:
* <pre name="code" class="xml">
* <property name="loadBalancingSpi">
* <bean class="org.apache.ignite.spi.loadBalancing.adaptive.GridAdaptiveLoadBalancingSpi">
* <property name="loadProbe">
* <bean class="org.apache.ignite.spi.loadBalancing.adaptive.GridAdaptiveCpuLoadProbe">
* <property name="useAverage" value="true"/>
* <property name="useProcessors" value="true"/>
* <property name="processorCoefficient" value="0.9"/>
* </bean>
* </property>
* </bean>
* </property>
* </pre>
* <p>
* This implementation is used by default by {@link AdaptiveLoadBalancingSpi} SPI.
*/
public class AdaptiveCpuLoadProbe implements AdaptiveLoadProbe {
/** Flag indicating whether to use average CPU load vs. current. */
private boolean useAvg = true;
/**
* Flag indicating whether to divide each node's CPU load
* by the number of processors on that node.
*/
private boolean useProcs = true;
/**
* Coefficient of every CPU processor. By default it is {@code 1}, but
* in some environments every processor may not be adding 100% of processing
* power. For example, if you are using multi-core CPU's, then addition of
* every core would probably result in about 75% of extra CPU power, and hence
* you would set this coefficient to {@code 0.75} .
*/
private double procCoefficient = 1;
/**
* Initializes CPU load probe to use CPU load average by default.
*/
public AdaptiveCpuLoadProbe() {
// No-op.
}
/**
* Specifies whether to use average CPU load vs. current and whether or
* not to take number of processors into account.
* <p>
* Since CPU load on multi-processor boxes shows medium load of multiple CPU's it
* usually means that the remaining capacity is proportional to the number of
* CPU's (or cores) on the node.
*
* @param useAvg Flag indicating whether to use average CPU load vs. current
* (default is {@code true}).
* @param useProcs Flag indicating whether to divide each node's CPU load
* by the number of processors on that node (default is {@code true}).
*/
public AdaptiveCpuLoadProbe(boolean useAvg, boolean useProcs) {
this.useAvg = useAvg;
this.useProcs = useProcs;
}
/**
* Specifies whether to use average CPU load vs. current and whether or
* not to take number of processors into account. It also allows to
* specify the coefficient of addition power every CPU adds.
* <p>
* Since CPU load on multi-processor boxes shows medium load of multiple CPU's it
* usually means that the remaining capacity is proportional to the number of
* CPU's (or cores) on the node.
* <p>
* Also, in some environments every processor may not be adding 100% of processing
* power. For example, if you are using multi-core CPU's, then addition of
* every core would probably result in about 75% of extra CPU power, and hence
* you would set this coefficient to {@code 0.75} .
*
* @param useAvg Flag indicating whether to use average CPU load vs. current
* (default is {@code true}).
* @param useProcs Flag indicating whether to divide each node's CPU load
* by the number of processors on that node (default is {@code true}).
* @param procCoefficient Coefficient of every CPU processor (default value is {@code 1}).
*/
public AdaptiveCpuLoadProbe(boolean useAvg, boolean useProcs, double procCoefficient) {
this.useAvg = useAvg;
this.useProcs = useProcs;
this.procCoefficient = procCoefficient;
}
/**
* Gets flag indicating whether to use average CPU load vs. current.
*
* @return Flag indicating whether to use average CPU load vs. current.
*/
public boolean isUseAverage() {
return useAvg;
}
/**
* Sets flag indicating whether to use average CPU load vs. current.
* If not explicitly set, then default value is {@code true}.
*
* @param useAvg Flag indicating whether to use average CPU load vs. current.
*/
public void setUseAverage(boolean useAvg) {
this.useAvg = useAvg;
}
/**
* Gets flag indicating whether to use average CPU load vs. current
* (default is {@code true}).
* <p>
* Since CPU load on multi-processor boxes shows medium load of multiple CPU's it
* usually means that the remaining capacity is proportional to the number of
* CPU's (or cores) on the node.
*
* @return Flag indicating whether to divide each node's CPU load
* by the number of processors on that node (default is {@code true}).
*/
public boolean isUseProcessors() {
return useProcs;
}
/**
* Sets flag indicating whether to use average CPU load vs. current
* (default is {@code true}).
* <p>
* Since CPU load on multi-processor boxes shows medium load of multiple CPU's it
* usually means that the remaining capacity is proportional to the number of
* CPU's (or cores) on the node.
* <p>
* If not explicitly set, then default value is {@code true}.
*
* @param useProcs Flag indicating whether to divide each node's CPU load
* by the number of processors on that node (default is {@code true}).
*/
public void setUseProcessors(boolean useProcs) {
this.useProcs = useProcs;
}
/**
* Gets coefficient of every CPU processor. By default it is {@code 1}, but
* in some environments every processor may not be adding 100% of processing
* power. For example, if you are using multi-core CPU's, then addition of
* every core would probably result in about 75% of extra CPU power, and hence
* you would set this coefficient to {@code 0.75} .
* <p>
* This value is ignored if {@link #isUseProcessors()} is set to {@code false}.
*
* @return Coefficient of every CPU processor.
*/
public double getProcessorCoefficient() {
return procCoefficient;
}
/**
* Sets coefficient of every CPU processor. By default it is {@code 1}, but
* in some environments every processor may not be adding 100% of processing
* power. For example, if you are using multi-core CPU's, then addition of
* every core would probably result in about 75% of extra CPU power, and hence
* you would set this coefficient to {@code 0.75} .
* <p>
* This value is ignored if {@link #isUseProcessors()} is set to {@code false}.
*
* @param procCoefficient Coefficient of every CPU processor.
*/
public void setProcessorCoefficient(double procCoefficient) {
A.ensure(procCoefficient > 0, "procCoefficient > 0");
this.procCoefficient = procCoefficient;
}
/** {@inheritDoc} */
@Override public double getLoad(ClusterNode node, int jobsSentSinceLastUpdate) {
ClusterMetrics metrics = node.metrics();
double k = 1.0d;
if (useProcs) {
int procs = metrics.getTotalCpus();
if (procs > 1)
k = procs * procCoefficient;
}
double load = (useAvg ? metrics.getAverageCpuLoad() : metrics.getCurrentCpuLoad()) / k;
return load < 0 ? 0 : load;
}
/** {@inheritDoc} */
@Override public String toString() {
return S.toString(AdaptiveCpuLoadProbe.class, this);
}
}