- Add two attributes in DegradeRule: rtSlowRequestAmount and minRequestAmountmaster
@@ -36,6 +36,10 @@ public final class RuleConstant { | |||||
*/ | */ | ||||
public static final int DEGRADE_GRADE_EXCEPTION_COUNT = 2; | public static final int DEGRADE_GRADE_EXCEPTION_COUNT = 2; | ||||
public static final int DEGRADE_GRADE_RT_MAX_EXCEED_N = 5; | |||||
public static final int DEGRADE_GRADE_MIN_REQUEST_EXCEED_N = 5; | |||||
public static final int AUTHORITY_WHITE = 0; | public static final int AUTHORITY_WHITE = 0; | ||||
public static final int AUTHORITY_BLACK = 1; | public static final int AUTHORITY_BLACK = 1; | ||||
@@ -15,12 +15,6 @@ | |||||
*/ | */ | ||||
package com.alibaba.csp.sentinel.slots.block.degrade; | package com.alibaba.csp.sentinel.slots.block.degrade; | ||||
import java.util.concurrent.Executors; | |||||
import java.util.concurrent.ScheduledExecutorService; | |||||
import java.util.concurrent.TimeUnit; | |||||
import java.util.concurrent.atomic.AtomicBoolean; | |||||
import java.util.concurrent.atomic.AtomicLong; | |||||
import com.alibaba.csp.sentinel.concurrent.NamedThreadFactory; | import com.alibaba.csp.sentinel.concurrent.NamedThreadFactory; | ||||
import com.alibaba.csp.sentinel.context.Context; | import com.alibaba.csp.sentinel.context.Context; | ||||
import com.alibaba.csp.sentinel.node.ClusterNode; | import com.alibaba.csp.sentinel.node.ClusterNode; | ||||
@@ -29,6 +23,12 @@ import com.alibaba.csp.sentinel.slots.block.AbstractRule; | |||||
import com.alibaba.csp.sentinel.slots.block.RuleConstant; | import com.alibaba.csp.sentinel.slots.block.RuleConstant; | ||||
import com.alibaba.csp.sentinel.slots.clusterbuilder.ClusterBuilderSlot; | import com.alibaba.csp.sentinel.slots.clusterbuilder.ClusterBuilderSlot; | ||||
import java.util.concurrent.Executors; | |||||
import java.util.concurrent.ScheduledExecutorService; | |||||
import java.util.concurrent.TimeUnit; | |||||
import java.util.concurrent.atomic.AtomicBoolean; | |||||
import java.util.concurrent.atomic.AtomicLong; | |||||
/** | /** | ||||
* <p> | * <p> | ||||
* Degrade is used when the resources are in an unstable state, these resources | * Degrade is used when the resources are in an unstable state, these resources | ||||
@@ -55,7 +55,15 @@ import com.alibaba.csp.sentinel.slots.clusterbuilder.ClusterBuilderSlot; | |||||
*/ | */ | ||||
public class DegradeRule extends AbstractRule { | public class DegradeRule extends AbstractRule { | ||||
private static final int RT_MAX_EXCEED_N = 5; | |||||
/** | |||||
* minimum number of consecutive slow requests that can trigger RT circuit breaking | |||||
*/ | |||||
private int rtSlowRequestAmount = RuleConstant.DEGRADE_GRADE_RT_MAX_EXCEED_N; | |||||
/** | |||||
* minimum number of requests (in an active statistic time span) that can trigger circuit breaking | |||||
*/ | |||||
private int minRequestAmount = RuleConstant.DEGRADE_GRADE_MIN_REQUEST_EXCEED_N; | |||||
@SuppressWarnings("PMD.ThreadPoolCreationRule") | @SuppressWarnings("PMD.ThreadPoolCreationRule") | ||||
private static ScheduledExecutorService pool = Executors.newScheduledThreadPool( | private static ScheduledExecutorService pool = Executors.newScheduledThreadPool( | ||||
@@ -137,7 +145,7 @@ public class DegradeRule extends AbstractRule { | |||||
return false; | return false; | ||||
} | } | ||||
DegradeRule that = (DegradeRule)o; | |||||
DegradeRule that = (DegradeRule) o; | |||||
if (count != that.count) { | if (count != that.count) { | ||||
return false; | return false; | ||||
@@ -148,6 +156,13 @@ public class DegradeRule extends AbstractRule { | |||||
if (grade != that.grade) { | if (grade != that.grade) { | ||||
return false; | return false; | ||||
} | } | ||||
if (rtSlowRequestAmount != that.rtSlowRequestAmount) { | |||||
return false; | |||||
} | |||||
if (minRequestAmount != that.minRequestAmount) { | |||||
return false; | |||||
} | |||||
return true; | return true; | ||||
} | } | ||||
@@ -157,6 +172,8 @@ public class DegradeRule extends AbstractRule { | |||||
result = 31 * result + new Double(count).hashCode(); | result = 31 * result + new Double(count).hashCode(); | ||||
result = 31 * result + timeWindow; | result = 31 * result + timeWindow; | ||||
result = 31 * result + grade; | result = 31 * result + grade; | ||||
result = 31 * result + rtSlowRequestAmount; | |||||
result = 31 * result + minRequestAmount; | |||||
return result; | return result; | ||||
} | } | ||||
@@ -179,20 +196,21 @@ public class DegradeRule extends AbstractRule { | |||||
} | } | ||||
// Sentinel will degrade the service only if count exceeds. | // Sentinel will degrade the service only if count exceeds. | ||||
if (passCount.incrementAndGet() < RT_MAX_EXCEED_N) { | |||||
if (passCount.incrementAndGet() < rtSlowRequestAmount) { | |||||
return true; | return true; | ||||
} | } | ||||
} else if (grade == RuleConstant.DEGRADE_GRADE_EXCEPTION_RATIO) { | } else if (grade == RuleConstant.DEGRADE_GRADE_EXCEPTION_RATIO) { | ||||
double exception = clusterNode.exceptionQps(); | double exception = clusterNode.exceptionQps(); | ||||
double success = clusterNode.successQps(); | double success = clusterNode.successQps(); | ||||
double total = clusterNode.totalQps(); | double total = clusterNode.totalQps(); | ||||
// if total qps less than RT_MAX_EXCEED_N, pass. | |||||
if (total < RT_MAX_EXCEED_N) { | |||||
// if total qps less than minRequestAmount, pass. | |||||
if (total < minRequestAmount) { | |||||
return true; | return true; | ||||
} | } | ||||
//in the same aligned statistic time window, success (aka. completed count) = exception count + non-exception count (realSuccess) | |||||
double realSuccess = success - exception; | double realSuccess = success - exception; | ||||
if (realSuccess <= 0 && exception < RT_MAX_EXCEED_N) { | |||||
if (realSuccess <= 0 && exception < minRequestAmount) { | |||||
return true; | return true; | ||||
} | } | ||||
@@ -214,17 +232,37 @@ public class DegradeRule extends AbstractRule { | |||||
return false; | return false; | ||||
} | } | ||||
@Override | @Override | ||||
public String toString() { | public String toString() { | ||||
return "DegradeRule{" + | return "DegradeRule{" + | ||||
"resource=" + getResource() + | |||||
", grade=" + grade + | |||||
", count=" + count + | |||||
", limitApp=" + getLimitApp() + | |||||
", timeWindow=" + timeWindow + | |||||
"}"; | |||||
"resource=" + getResource() + | |||||
", grade=" + grade + | |||||
", count=" + count + | |||||
", limitApp=" + getLimitApp() + | |||||
", timeWindow=" + timeWindow + | |||||
", rtSlowRequestAmount=" + rtSlowRequestAmount + | |||||
", minRequestAmount=" + minRequestAmount + | |||||
"}"; | |||||
} | |||||
public int getRtSlowRequestAmount() { | |||||
return rtSlowRequestAmount; | |||||
} | |||||
public void setRtSlowRequestAmount(int rtSlowRequestAmount) { | |||||
this.rtSlowRequestAmount = rtSlowRequestAmount; | |||||
} | |||||
public int getMinRequestAmount() { | |||||
return minRequestAmount; | |||||
} | |||||
public void setMinRequestAmount(int minRequestAmount) { | |||||
this.minRequestAmount = minRequestAmount; | |||||
} | } | ||||
private static final class ResetTask implements Runnable { | private static final class ResetTask implements Runnable { | ||||
private DegradeRule rule; | private DegradeRule rule; | ||||
@@ -15,14 +15,6 @@ | |||||
*/ | */ | ||||
package com.alibaba.csp.sentinel.slots.block.degrade; | package com.alibaba.csp.sentinel.slots.block.degrade; | ||||
import static org.junit.Assert.*; | |||||
import static org.mockito.Mockito.mock; | |||||
import static org.mockito.Mockito.when; | |||||
import java.util.concurrent.TimeUnit; | |||||
import org.junit.Test; | |||||
import com.alibaba.csp.sentinel.EntryType; | import com.alibaba.csp.sentinel.EntryType; | ||||
import com.alibaba.csp.sentinel.context.Context; | import com.alibaba.csp.sentinel.context.Context; | ||||
import com.alibaba.csp.sentinel.node.ClusterNode; | import com.alibaba.csp.sentinel.node.ClusterNode; | ||||
@@ -30,6 +22,14 @@ import com.alibaba.csp.sentinel.node.DefaultNode; | |||||
import com.alibaba.csp.sentinel.slotchain.StringResourceWrapper; | import com.alibaba.csp.sentinel.slotchain.StringResourceWrapper; | ||||
import com.alibaba.csp.sentinel.slots.block.RuleConstant; | import com.alibaba.csp.sentinel.slots.block.RuleConstant; | ||||
import com.alibaba.csp.sentinel.slots.clusterbuilder.ClusterBuilderSlot; | import com.alibaba.csp.sentinel.slots.clusterbuilder.ClusterBuilderSlot; | ||||
import org.junit.Test; | |||||
import java.util.concurrent.TimeUnit; | |||||
import static org.junit.Assert.assertFalse; | |||||
import static org.junit.Assert.assertTrue; | |||||
import static org.mockito.Mockito.mock; | |||||
import static org.mockito.Mockito.when; | |||||
/** | /** | ||||
* @author jialiang.linjl | * @author jialiang.linjl | ||||
@@ -47,12 +47,16 @@ public class DegradeTest { | |||||
when(node.getClusterNode()).thenReturn(cn); | when(node.getClusterNode()).thenReturn(cn); | ||||
when(cn.avgRt()).thenReturn(2d); | when(cn.avgRt()).thenReturn(2d); | ||||
int rtSlowRequestAmount = 10; | |||||
DegradeRule rule = new DegradeRule(); | DegradeRule rule = new DegradeRule(); | ||||
rule.setCount(1); | rule.setCount(1); | ||||
rule.setResource(key); | rule.setResource(key); | ||||
rule.setTimeWindow(2); | rule.setTimeWindow(2); | ||||
rule.setGrade(RuleConstant.DEGRADE_GRADE_RT); | |||||
rule.setRtSlowRequestAmount(rtSlowRequestAmount); | |||||
for (int i = 0; i < 4; i++) { | |||||
//Will true | |||||
for (int i = 0; i < rtSlowRequestAmount - 1; i++) { | |||||
assertTrue(rule.passCheck(context, node, 1)); | assertTrue(rule.passCheck(context, node, 1)); | ||||
} | } | ||||
@@ -69,9 +73,6 @@ public class DegradeTest { | |||||
public void testExceptionRatioModeDegrade() throws Throwable { | public void testExceptionRatioModeDegrade() throws Throwable { | ||||
String key = "test_degrade_exception_ratio"; | String key = "test_degrade_exception_ratio"; | ||||
ClusterNode cn = mock(ClusterNode.class); | ClusterNode cn = mock(ClusterNode.class); | ||||
when(cn.exceptionQps()).thenReturn(2d); | |||||
// Indicates that there are QPS more than min threshold. | |||||
when(cn.totalQps()).thenReturn(12d); | |||||
ClusterBuilderSlot.getClusterNodeMap().put(new StringResourceWrapper(key, EntryType.IN), cn); | ClusterBuilderSlot.getClusterNodeMap().put(new StringResourceWrapper(key, EntryType.IN), cn); | ||||
Context context = mock(Context.class); | Context context = mock(Context.class); | ||||
@@ -83,17 +84,39 @@ public class DegradeTest { | |||||
rule.setResource(key); | rule.setResource(key); | ||||
rule.setTimeWindow(2); | rule.setTimeWindow(2); | ||||
rule.setGrade(RuleConstant.DEGRADE_GRADE_EXCEPTION_RATIO); | rule.setGrade(RuleConstant.DEGRADE_GRADE_EXCEPTION_RATIO); | ||||
rule.setMinRequestAmount(20); | |||||
when(cn.successQps()).thenReturn(8d); | |||||
// Will fail. | |||||
// Will true. While totalQps < minRequestAmount | |||||
when(cn.totalQps()).thenReturn(8d); | |||||
assertTrue(rule.passCheck(context, node, 1)); | |||||
// Will true. | |||||
when(cn.totalQps()).thenReturn(21d); | |||||
when(cn.successQps()).thenReturn(9d); | |||||
when(cn.exceptionQps()).thenReturn(9d); | |||||
assertTrue(rule.passCheck(context, node, 1)); | |||||
// Will true. While totalQps > minRequestAmount and exceptionRation < count | |||||
when(cn.totalQps()).thenReturn(100d); | |||||
when(cn.successQps()).thenReturn(90d); | |||||
when(cn.exceptionQps()).thenReturn(10d); | |||||
assertTrue(rule.passCheck(context, node, 1)); | |||||
// Will fail. While totalQps > minRequestAmount and exceptionRation > count | |||||
rule.setMinRequestAmount(5); | |||||
when(cn.totalQps()).thenReturn(12d); | |||||
when(cn.successQps()).thenReturn(8d); | |||||
when(cn.exceptionQps()).thenReturn(6d); | |||||
assertFalse(rule.passCheck(context, node, 1)); | assertFalse(rule.passCheck(context, node, 1)); | ||||
// Restore from the degrade timeout. | // Restore from the degrade timeout. | ||||
TimeUnit.MILLISECONDS.sleep(2200); | TimeUnit.MILLISECONDS.sleep(2200); | ||||
when(cn.successQps()).thenReturn(20d); | |||||
// Will pass. | // Will pass. | ||||
when(cn.totalQps()).thenReturn(106d); | |||||
when(cn.successQps()).thenReturn(100d); | |||||
assertTrue(rule.passCheck(context, node, 1)); | assertTrue(rule.passCheck(context, node, 1)); | ||||
} | } | ||||
@@ -127,4 +150,33 @@ public class DegradeTest { | |||||
assertTrue(rule.passCheck(context, node, 1)); | assertTrue(rule.passCheck(context, node, 1)); | ||||
} | } | ||||
@Test | |||||
public void testEquals() { | |||||
DegradeRule degradeRule1 = new DegradeRule(); | |||||
DegradeRule degradeRule2 = new DegradeRule(); | |||||
assertTrue(degradeRule1.equals(degradeRule2)); | |||||
int rtSlowRequestAmount = 10; | |||||
int minRequestAmount = 20; | |||||
double count = 1.0; | |||||
int timeWindow = 2; | |||||
degradeRule1.setRtSlowRequestAmount(rtSlowRequestAmount); | |||||
degradeRule1.setMinRequestAmount(minRequestAmount); | |||||
degradeRule1.setCount(count); | |||||
degradeRule1.setTimeWindow(timeWindow); | |||||
degradeRule1.setGrade(RuleConstant.DEGRADE_GRADE_RT); | |||||
degradeRule2.setRtSlowRequestAmount(rtSlowRequestAmount); | |||||
degradeRule2.setMinRequestAmount(minRequestAmount); | |||||
degradeRule2.setCount(count); | |||||
degradeRule2.setGrade(RuleConstant.DEGRADE_GRADE_RT); | |||||
degradeRule2.setTimeWindow(timeWindow); | |||||
assertTrue(degradeRule1.equals(degradeRule2)); | |||||
degradeRule2.setMinRequestAmount(100); | |||||
assertFalse(degradeRule1.equals(degradeRule2)); | |||||
} | |||||
} | } |
@@ -15,11 +15,6 @@ | |||||
*/ | */ | ||||
package com.alibaba.csp.sentinel.demo.degrade; | package com.alibaba.csp.sentinel.demo.degrade; | ||||
import java.util.ArrayList; | |||||
import java.util.List; | |||||
import java.util.concurrent.TimeUnit; | |||||
import java.util.concurrent.atomic.AtomicInteger; | |||||
import com.alibaba.csp.sentinel.Entry; | import com.alibaba.csp.sentinel.Entry; | ||||
import com.alibaba.csp.sentinel.SphU; | import com.alibaba.csp.sentinel.SphU; | ||||
import com.alibaba.csp.sentinel.Tracer; | import com.alibaba.csp.sentinel.Tracer; | ||||
@@ -29,6 +24,11 @@ import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRule; | |||||
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRuleManager; | import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRuleManager; | ||||
import com.alibaba.csp.sentinel.util.TimeUtil; | import com.alibaba.csp.sentinel.util.TimeUtil; | ||||
import java.util.ArrayList; | |||||
import java.util.List; | |||||
import java.util.concurrent.TimeUnit; | |||||
import java.util.concurrent.atomic.AtomicInteger; | |||||
/** | /** | ||||
* <p> | * <p> | ||||
* Degrade is used when the resources are in an unstable state, these resources | * Degrade is used when the resources are in an unstable state, these resources | ||||
@@ -115,6 +115,7 @@ public class ExceptionRatioDegradeDemo { | |||||
rule.setCount(0.1); | rule.setCount(0.1); | ||||
rule.setGrade(RuleConstant.DEGRADE_GRADE_EXCEPTION_RATIO); | rule.setGrade(RuleConstant.DEGRADE_GRADE_EXCEPTION_RATIO); | ||||
rule.setTimeWindow(10); | rule.setTimeWindow(10); | ||||
rule.setMinRequestAmount(20); | |||||
rules.add(rule); | rules.add(rule); | ||||
DegradeRuleManager.loadRules(rules); | DegradeRuleManager.loadRules(rules); | ||||
} | } | ||||