Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor the underlying model of SystemRuleManager #3158

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,11 @@ public BlockException(String ruleLimitApp, String message, AbstractRule rule) {
this.rule = rule;
}

public BlockException(AbstractRule rule) {
super();
this.rule = rule;
}

@Override
public Throwable fillInStackTrace() {
return this;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@

/**
* @author jialiang.linjl
* @author guozhong.huang
*/
public class SystemBlockException extends BlockException {

Expand All @@ -34,6 +35,12 @@ public SystemBlockException(String resourceName, String limitType) {
this.resourceName = resourceName;
}


public SystemBlockException(String resourceName, SystemRule systemRule) {
super(systemRule);
this.resourceName = resourceName;
}

public String getResourceName() {
return resourceName;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package com.alibaba.csp.sentinel.slots.system;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you please prepend the license header here?


/**
* Metric type of system rule.
*
* @author guozhong.huang
*/
public enum SystemMetricType {
/**
* Load represents system load1 in Linux/Unix.
*/
LOAD(0),
/**
* AvgRT represents the average response time of all inbound requests.
*/
AVG_RT(1),
/**
* Concurrency represents the concurrency of all inbound requests.
*/
CONCURRENCY(2),

/**
* InboundQPS represents the QPS of all inbound requests.
*/
INBOUND_QPS(3),

/**
* CpuUsage represents the CPU usage percentage of the system.
*/
CPU_USAGE(4);

private int type;

SystemMetricType(int type) {
this.type = type;
}

public int getType() {
return type;
}
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A formatting mistake here.

Original file line number Diff line number Diff line change
Expand Up @@ -24,170 +24,87 @@
* provides a measurement of system's load, but only available on Linux.
* </p>
* <p>
* We recommend to coordinate {@link #highestSystemLoad}, {@link #qps}, {@link #avgRt}
* and {@link #maxThread} to make sure your system run in safety level.
* We recommend to coordinate {@link SystemMetricType#LOAD}, {@link SystemMetricType#INBOUND_QPS}, {@link SystemMetricType#AVG_RT}
* and {@link SystemMetricType#CONCURRENCY} to make sure your system run in safety level.
* </p>
* <p>
* To set the threshold appropriately, performance test may be needed.
* </p>
*
* @author jialiang.linjl
* @author Carpenter Lee
* @author guozhong.huang
* @see SystemRuleManager
*/
public class SystemRule extends AbstractRule {

/**
* negative value means no threshold checking.
*/
private double highestSystemLoad = -1;
/**
* cpu usage, between [0, 1]
*/
private double highestCpuUsage = -1;
private double qps = -1;
private long avgRt = -1;
private long maxThread = -1;

public double getQps() {
return qps;
}

/**
* Set max total QPS. In a high concurrency condition, real passed QPS may be greater than max QPS set.
* The real passed QPS will nearly satisfy the following formula:<br/>
*
* <pre>real passed QPS = QPS set + concurrent thread number</pre>
*
* @param qps max total QOS, values <= 0 are special for clearing the threshold.
* MetricType indicates the type of the trigger metric.
*/
public void setQps(double qps) {
this.qps = qps;
}

public long getMaxThread() {
return maxThread;
}
private SystemMetricType systemMetricType;

/**
* Set max PARALLEL working thread. When concurrent thread number is greater than {@code maxThread} only
* maxThread will run in parallel.
*
* @param maxThread max parallel thread number, values <= 0 are special for clearing the threshold.
* TriggerCount represents the lower bound trigger of the adaptive strategy.
* Adaptive strategies will not be activated until target metric has reached the trigger count.
*/
public void setMaxThread(long maxThread) {
this.maxThread = maxThread;
}
private double triggerCount;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just use count to name it like any other rule.


public long getAvgRt() {
return avgRt;
public SystemMetricType getSystemMetricType() {
return systemMetricType;
}

/**
* Set max average RT(response time) of all passed requests.
*
* @param avgRt max average response time, values <= 0 are special for clearing the threshold.
*/
public void setAvgRt(long avgRt) {
this.avgRt = avgRt;
public SystemRule setSystemMetricType(SystemMetricType systemMetricType) {
this.systemMetricType = systemMetricType;
return this;
}

public double getHighestSystemLoad() {
return highestSystemLoad;
}

/**
* <p>
* Set highest load. The load is not same as Linux system load, which is not sensitive enough.
* To calculate the load, both Linux system load, current global response time and global QPS will be considered,
* which means that we need to coordinate with {@link #setAvgRt(long)} and {@link #setQps(double)}
* </p>
* <p>
* Note that this parameter is only available on Unix like system.
* </p>
*
* @param highestSystemLoad highest system load, values <= 0 are special for clearing the threshold.
* @see SystemRuleManager
*/
public void setHighestSystemLoad(double highestSystemLoad) {
this.highestSystemLoad = highestSystemLoad;
public double getTriggerCount() {
return triggerCount;
}

/**
* Get highest cpu usage. Cpu usage is between [0, 1]
*
* @return highest cpu usage
*/
public double getHighestCpuUsage() {
return highestCpuUsage;
public SystemRule setTriggerCount(double triggerCount) {
this.triggerCount = triggerCount;
return this;
}

/**
* set highest cpu usage. Cpu usage is between [0, 1]
*
* @param highestCpuUsage the value to set.
*/
public void setHighestCpuUsage(double highestCpuUsage) {
this.highestCpuUsage = highestCpuUsage;
@Override
public String toString() {
return "SystemRule{" +
"systemMetricType=" + systemMetricType +
", triggerCount=" + triggerCount +
'}';
}

@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (!(o instanceof SystemRule)) {
if (o == null || getClass() != o.getClass()) {
return false;
}
if (!super.equals(o)) {
return false;
}

SystemRule that = (SystemRule)o;

if (Double.compare(that.highestSystemLoad, highestSystemLoad) != 0) {
return false;
}
if (Double.compare(that.highestCpuUsage, highestCpuUsage) != 0) {
return false;
}

if (Double.compare(that.qps, qps) != 0) {
return false;
}
SystemRule that = (SystemRule) o;

if (avgRt != that.avgRt) {
if (Double.compare(that.triggerCount, triggerCount) != 0) {
return false;
}
return maxThread == that.maxThread;
return systemMetricType == that.systemMetricType;
}

@Override
public int hashCode() {
int result = super.hashCode();
long temp;
temp = Double.doubleToLongBits(highestSystemLoad);
result = 31 * result + (int)(temp ^ (temp >>> 32));

temp = Double.doubleToLongBits(highestCpuUsage);
result = 31 * result + (int)(temp ^ (temp >>> 32));

temp = Double.doubleToLongBits(qps);
result = 31 * result + (int)(temp ^ (temp >>> 32));

result = 31 * result + (int)(avgRt ^ (avgRt >>> 32));
result = 31 * result + (int)(maxThread ^ (maxThread >>> 32));
result = 31 * result + systemMetricType.hashCode();
temp = Double.doubleToLongBits(triggerCount);
result = 31 * result + (int) (temp ^ (temp >>> 32));
return result;
}

@Override
public String toString() {
return "SystemRule{" +
"highestSystemLoad=" + highestSystemLoad +
", highestCpuUsage=" + highestCpuUsage +
", qps=" + qps +
", avgRt=" + avgRt +
", maxThread=" + maxThread +
"}";
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
package com.alibaba.csp.sentinel.slots.system;
Copy link
Collaborator

@LearningGp LearningGp Dec 24, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ditto.


import com.alibaba.csp.sentinel.Constants;
import com.alibaba.csp.sentinel.EntryType;
import com.alibaba.csp.sentinel.concurrent.NamedThreadFactory;
import com.alibaba.csp.sentinel.slotchain.ResourceWrapper;

import java.util.Collection;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;



/**
* <p>
* Sentinel System Rule makes the inbound traffic and capacity meet. It takes
* average rt, qps, thread count of incoming requests into account. And it also
* provides a measurement of system's load, but only available on Linux.
* </p>
* <p>
* rt, qps, thread count is easy to understand. If the incoming requests'
* rt,qps, thread count exceeds its threshold, the requests will be
* rejected.however, we use a different method to calculate the load.
* </p>
* <p>
* Consider the system as a pipeline,transitions between constraints result in
* three different regions (traffic-limited, capacity-limited and danger area)
* with qualitatively different behavior. When there isn’t enough request in
* flight to fill the pipe, RTprop determines behavior; otherwise, the system
* capacity dominates. Constraint lines intersect at inflight = Capacity ×
* RTprop. Since the pipe is full past this point, the inflight –capacity excess
* creates a queue, which results in the linear dependence of RTT on inflight
* traffic and an increase in system load.In danger area, system will stop
* responding.<br/>
* Referring to BBR algorithm to learn more.
* </p>
* <p>
* Note that {@link SystemRule} only effect on inbound requests, outbound traffic
* will not limit by {@link SystemRule}
* </p>
*
* @author jialiang.linjl
* @author leyou
* @author guozhong.huang
*/
public class SystemRuleChecker {

private static SystemStatusListener statusListener = null;
private final static ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1,
new NamedThreadFactory("sentinel-system-status-record-task", true));

static {
statusListener = new SystemStatusListener();
scheduler.scheduleAtFixedRate(statusListener, 0, 1, TimeUnit.SECONDS);
}

public static void checkSystem(Collection<SystemRule> rules, ResourceWrapper resourceWrapper, int count) throws SystemBlockException {
if (resourceWrapper.getEntryType() != EntryType.IN) {
return;
}
if (rules != null) {
for (SystemRule rule : rules) {
if (!canPassCheck(rule, count)) {
throw new SystemBlockException(resourceWrapper.getName(), rule);
}
}
}
}


public static boolean canPassCheck(SystemRule rule, int acquireCount) {
SystemMetricType systemMetricType = rule.getSystemMetricType();
double threshold = rule.getTriggerCount();
switch (systemMetricType) {
case LOAD:
double currentSystemAvgLoad = getCurrentSystemAvgLoad();
if (currentSystemAvgLoad > threshold) {
return checkBbr();
}
return true;
case AVG_RT:
double rt = Constants.ENTRY_NODE.avgRt();
return rt <= threshold ;
case CONCURRENCY:
int currentThread = Constants.ENTRY_NODE.curThreadNum();
return currentThread <= threshold;
case INBOUND_QPS:
double currentQps = Constants.ENTRY_NODE.passQps();
return currentQps + acquireCount <= threshold;
case CPU_USAGE:
double currentCpuUsage = getCurrentCpuUsage();
return currentCpuUsage <= threshold;
default:
return true;
}
}


private static boolean checkBbr() {
int currentThread = Constants.ENTRY_NODE.curThreadNum();
if (currentThread > 1 &&
currentThread > Constants.ENTRY_NODE.maxSuccessQps() * Constants.ENTRY_NODE.minRt() / 1000) {
return false;
}
return true;
}

public static double getCurrentSystemAvgLoad() {
return statusListener.getSystemAverageLoad();
}

public static double getCurrentCpuUsage() {
return statusListener.getCpuUsage();
}
}
Loading