org.jgroups.protocols.RATE_LIMITER Maven / Gradle / Ivy
Go to download
This artifact provides a single jar that contains all classes required to use remote EJB and JMS, including
all dependencies. It is intended for use by those not using maven, maven users should just import the EJB and
JMS BOM's instead (shaded JAR's cause lots of problems with maven, as it is very easy to inadvertently end up
with different versions on classes on the class path).
package org.jgroups.protocols;
import org.jgroups.Event;
import org.jgroups.Message;
import org.jgroups.annotations.*;
import org.jgroups.conf.AttributeType;
import org.jgroups.stack.Protocol;
import org.jgroups.util.Util;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.LockSupport;
import java.util.concurrent.locks.ReentrantLock;
/**
* Protocol which sends at most max_bytes in time_period milliseconds. Can be used instead of a flow control protocol,
* e.g. UFC or MFC (same position in the stack)
* @author Bela Ban
*/
@Experimental
@MBean(description="Limits the sending rate to max_bytes per time_period")
public class RATE_LIMITER extends Protocol {
@Property(description="Max number of bytes to be sent in time_period ms. Blocks the sender if exceeded until a new " +
"time period has started",type=AttributeType.BYTES)
protected long max_bytes=300_000;
@Property(description="Number of milliseconds during which max_bytes bytes can be sent",type=AttributeType.TIME)
protected long time_period=10L;
protected long time_period_ns;
/** Keeps track of the number of bytes sent in the current time period */
@GuardedBy("lock")
@ManagedAttribute(description="Number of bytes sent in the current time period. Reset after every time period.",
type=AttributeType.BYTES)
protected long num_bytes_sent_in_period=0L;
@GuardedBy("lock")
protected long current_period_start; // time (ns) at which the current period was started
protected final Lock lock=new ReentrantLock();
@ManagedAttribute
protected int num_blockings=0;
protected long total_block_time=0L; // ns
protected int frag_size=0;
protected volatile boolean running=true;
public long getMaxBytes() {
return max_bytes;
}
public void setMaxBytes(long max_bytes) {
this.max_bytes=max_bytes;
}
public long getTimePeriod() {
return time_period;
}
public void setTimePeriod(long time_period) {
this.time_period=time_period;
this.time_period_ns=TimeUnit.NANOSECONDS.convert(time_period, TimeUnit.MILLISECONDS);
}
@ManagedAttribute(description="Total block time in milliseconds",type=AttributeType.TIME)
public long getTotalBlockTime() {
return TimeUnit.MILLISECONDS.convert(total_block_time,TimeUnit.NANOSECONDS);
}
@ManagedAttribute(description="Average block time in ms (total block time / number of blockings)")
public double getAverageBlockTime() {
long block_time_ms=getTotalBlockTime();
return num_blockings == 0? 0.0 : block_time_ms / (double)num_blockings;
}
public void resetStats() {
super.resetStats();
num_blockings=0; total_block_time=0;
}
public void init() throws Exception {
super.init();
if(time_period <= 0)
throw new IllegalArgumentException("time_period needs to be positive");
time_period_ns=TimeUnit.NANOSECONDS.convert(time_period, TimeUnit.MILLISECONDS);
}
public void start() throws Exception {
super.start();
if(max_bytes < frag_size)
throw new IllegalStateException("max_bytes (" + max_bytes + ") need to be bigger than frag_size (" + frag_size + ")");
running=true;
}
public void stop() {
running=false;
super.stop();
}
public Object down(Event evt) {
if(evt.getType() == Event.CONFIG) {
Map map=evt.getArg();
Integer tmp=map != null? (Integer)map.get("frag_size") : null;
if(tmp != null)
frag_size=tmp;
if(frag_size > 0 && max_bytes % frag_size != 0)
log.warn("For optimal performance, max_bytes (%d) should be a multiple of frag_size (%d)", max_bytes, frag_size);
}
return down_prot.down(evt);
}
public Object down(Message msg) {
int len=msg.getLength();
if(len == 0 || msg.isFlagSet(Message.Flag.NO_FC))
return down_prot.down(msg);
lock.lock();
try {
if(len > max_bytes) {
log.error(Util.getMessage("MessageLength") + len + " bytes) exceeded max_bytes (" + max_bytes + "); " +
"adjusting max_bytes to " + len);
max_bytes=len;
}
if(num_bytes_sent_in_period + len > max_bytes) { // size exceeded
long current_time=System.nanoTime();
long block_time=time_period_ns - (current_time - current_period_start);
if(block_time > 0) {
LockSupport.parkNanos(block_time);
num_blockings++;
total_block_time+=block_time;
}
current_period_start=block_time > 0? current_time + block_time : System.nanoTime();
num_bytes_sent_in_period=0;
}
}
finally {
num_bytes_sent_in_period+=len;
lock.unlock();
}
return down_prot.down(msg);
}
}