Signed-off-by: Eric Zhao <sczyh16@gmail.com>master
@@ -22,40 +22,46 @@ import com.alibaba.csp.sentinel.dashboard.auth.AuthAction; | |||
import com.alibaba.csp.sentinel.dashboard.client.SentinelApiClient; | |||
import com.alibaba.csp.sentinel.dashboard.discovery.MachineInfo; | |||
import com.alibaba.csp.sentinel.dashboard.auth.AuthService.PrivilegeType; | |||
import com.alibaba.csp.sentinel.dashboard.repository.rule.RuleRepository; | |||
import com.alibaba.csp.sentinel.slots.block.RuleConstant; | |||
import com.alibaba.csp.sentinel.slots.block.degrade.circuitbreaker.CircuitBreakerStrategy; | |||
import com.alibaba.csp.sentinel.util.StringUtil; | |||
import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.DegradeRuleEntity; | |||
import com.alibaba.csp.sentinel.dashboard.domain.Result; | |||
import com.alibaba.csp.sentinel.dashboard.repository.rule.InMemDegradeRuleStore; | |||
import org.slf4j.Logger; | |||
import org.slf4j.LoggerFactory; | |||
import org.springframework.beans.factory.annotation.Autowired; | |||
import org.springframework.http.MediaType; | |||
import org.springframework.stereotype.Controller; | |||
import org.springframework.web.bind.annotation.DeleteMapping; | |||
import org.springframework.web.bind.annotation.GetMapping; | |||
import org.springframework.web.bind.annotation.PathVariable; | |||
import org.springframework.web.bind.annotation.PostMapping; | |||
import org.springframework.web.bind.annotation.PutMapping; | |||
import org.springframework.web.bind.annotation.RequestBody; | |||
import org.springframework.web.bind.annotation.RequestMapping; | |||
import org.springframework.web.bind.annotation.ResponseBody; | |||
import org.springframework.web.bind.annotation.RestController; | |||
/** | |||
* @author leyou | |||
* Controller regarding APIs of degrade rules. Refactored since 1.8.0. | |||
* | |||
* @author Carpenter Lee | |||
* @author Eric Zhao | |||
*/ | |||
@Controller | |||
@RequestMapping(value = "/degrade", produces = MediaType.APPLICATION_JSON_VALUE) | |||
@RestController | |||
@RequestMapping("/degrade") | |||
public class DegradeController { | |||
private final Logger logger = LoggerFactory.getLogger(DegradeController.class); | |||
@Autowired | |||
private InMemDegradeRuleStore repository; | |||
private RuleRepository<DegradeRuleEntity, Long> repository; | |||
@Autowired | |||
private SentinelApiClient sentinelApiClient; | |||
@ResponseBody | |||
@RequestMapping("/rules.json") | |||
@GetMapping("/rules.json") | |||
@AuthAction(PrivilegeType.READ_RULE) | |||
public Result<List<DegradeRuleEntity>> queryMachineRules(String app, String ip, Integer port) { | |||
public Result<List<DegradeRuleEntity>> apiQueryMachineRules(String app, String ip, Integer port) { | |||
if (StringUtil.isEmpty(app)) { | |||
return Result.ofFail(-1, "app can't be null or empty"); | |||
} | |||
@@ -75,117 +81,65 @@ public class DegradeController { | |||
} | |||
} | |||
@ResponseBody | |||
@RequestMapping("/new.json") | |||
@PostMapping("/rule") | |||
@AuthAction(PrivilegeType.WRITE_RULE) | |||
public Result<DegradeRuleEntity> add(String app, String ip, Integer port, String limitApp, String resource, | |||
Double count, Integer timeWindow, Integer grade) { | |||
if (StringUtil.isBlank(app)) { | |||
return Result.ofFail(-1, "app can't be null or empty"); | |||
} | |||
if (StringUtil.isBlank(ip)) { | |||
return Result.ofFail(-1, "ip can't be null or empty"); | |||
} | |||
if (port == null) { | |||
return Result.ofFail(-1, "port can't be null"); | |||
} | |||
if (StringUtil.isBlank(limitApp)) { | |||
return Result.ofFail(-1, "limitApp can't be null or empty"); | |||
} | |||
if (StringUtil.isBlank(resource)) { | |||
return Result.ofFail(-1, "resource can't be null or empty"); | |||
} | |||
if (count == null) { | |||
return Result.ofFail(-1, "count can't be null"); | |||
} | |||
if (timeWindow == null) { | |||
return Result.ofFail(-1, "timeWindow can't be null"); | |||
public Result<DegradeRuleEntity> apiAddRule(@RequestBody DegradeRuleEntity entity) { | |||
Result<DegradeRuleEntity> checkResult = checkEntityInternal(entity); | |||
if (checkResult != null) { | |||
return checkResult; | |||
} | |||
if (grade == null) { | |||
return Result.ofFail(-1, "grade can't be null"); | |||
} | |||
if (grade < RuleConstant.DEGRADE_GRADE_RT || grade > RuleConstant.DEGRADE_GRADE_EXCEPTION_COUNT) { | |||
return Result.ofFail(-1, "Invalid grade: " + grade); | |||
} | |||
DegradeRuleEntity entity = new DegradeRuleEntity(); | |||
entity.setApp(app.trim()); | |||
entity.setIp(ip.trim()); | |||
entity.setPort(port); | |||
entity.setLimitApp(limitApp.trim()); | |||
entity.setResource(resource.trim()); | |||
entity.setCount(count); | |||
entity.setTimeWindow(timeWindow); | |||
entity.setGrade(grade); | |||
Date date = new Date(); | |||
entity.setGmtCreate(date); | |||
entity.setGmtModified(date); | |||
try { | |||
entity = repository.save(entity); | |||
} catch (Throwable throwable) { | |||
logger.error("add error:", throwable); | |||
return Result.ofThrowable(-1, throwable); | |||
} catch (Throwable t) { | |||
logger.error("Failed to add new degrade rule, app={}, ip={}", entity.getApp(), entity.getIp(), t); | |||
return Result.ofThrowable(-1, t); | |||
} | |||
if (!publishRules(app, ip, port)) { | |||
logger.info("publish degrade rules fail after rule add"); | |||
if (!publishRules(entity.getApp(), entity.getIp(), entity.getPort())) { | |||
logger.warn("Publish degrade rules failed, app={}", entity.getApp()); | |||
} | |||
return Result.ofSuccess(entity); | |||
} | |||
@ResponseBody | |||
@RequestMapping("/save.json") | |||
@PutMapping("/rule/{id}") | |||
@AuthAction(PrivilegeType.WRITE_RULE) | |||
public Result<DegradeRuleEntity> updateIfNotNull(Long id, String app, String limitApp, String resource, | |||
Double count, Integer timeWindow, Integer grade) { | |||
if (id == null) { | |||
return Result.ofFail(-1, "id can't be null"); | |||
public Result<DegradeRuleEntity> apiUpdateRule(@PathVariable("id") Long id, | |||
@RequestBody DegradeRuleEntity entity) { | |||
if (id == null || id <= 0) { | |||
return Result.ofFail(-1, "id can't be null or negative"); | |||
} | |||
if (grade != null) { | |||
if (grade < RuleConstant.DEGRADE_GRADE_RT || grade > RuleConstant.DEGRADE_GRADE_EXCEPTION_COUNT) { | |||
return Result.ofFail(-1, "Invalid grade: " + grade); | |||
} | |||
} | |||
DegradeRuleEntity entity = repository.findById(id); | |||
if (entity == null) { | |||
return Result.ofFail(-1, "id " + id + " dose not exist"); | |||
DegradeRuleEntity oldEntity = repository.findById(id); | |||
if (oldEntity == null) { | |||
return Result.ofFail(-1, "Degrade rule does not exist, id=" + id); | |||
} | |||
if (StringUtil.isNotBlank(app)) { | |||
entity.setApp(app.trim()); | |||
entity.setApp(oldEntity.getApp()); | |||
entity.setIp(oldEntity.getIp()); | |||
entity.setPort(oldEntity.getPort()); | |||
entity.setId(oldEntity.getId()); | |||
Result<DegradeRuleEntity> checkResult = checkEntityInternal(entity); | |||
if (checkResult != null) { | |||
return checkResult; | |||
} | |||
if (StringUtil.isNotBlank(limitApp)) { | |||
entity.setLimitApp(limitApp.trim()); | |||
} | |||
if (StringUtil.isNotBlank(resource)) { | |||
entity.setResource(resource.trim()); | |||
} | |||
if (count != null) { | |||
entity.setCount(count); | |||
} | |||
if (timeWindow != null) { | |||
entity.setTimeWindow(timeWindow); | |||
} | |||
if (grade != null) { | |||
entity.setGrade(grade); | |||
} | |||
Date date = new Date(); | |||
entity.setGmtModified(date); | |||
entity.setGmtCreate(oldEntity.getGmtCreate()); | |||
entity.setGmtModified(new Date()); | |||
try { | |||
entity = repository.save(entity); | |||
} catch (Throwable throwable) { | |||
logger.error("save error:", throwable); | |||
return Result.ofThrowable(-1, throwable); | |||
} catch (Throwable t) { | |||
logger.error("Failed to save degrade rule, id={}, rule={}", id, entity, t); | |||
return Result.ofThrowable(-1, t); | |||
} | |||
if (!publishRules(entity.getApp(), entity.getIp(), entity.getPort())) { | |||
logger.info("publish degrade rules fail after rule update"); | |||
logger.warn("Publish degrade rules failed, app={}", entity.getApp()); | |||
} | |||
return Result.ofSuccess(entity); | |||
} | |||
@ResponseBody | |||
@RequestMapping("/delete.json") | |||
@DeleteMapping("/rule/{id}") | |||
@AuthAction(PrivilegeType.DELETE_RULE) | |||
public Result<Long> delete(Long id) { | |||
public Result<Long> delete(@PathVariable("id") Long id) { | |||
if (id == null) { | |||
return Result.ofFail(-1, "id can't be null"); | |||
} | |||
@@ -198,11 +152,11 @@ public class DegradeController { | |||
try { | |||
repository.delete(id); | |||
} catch (Throwable throwable) { | |||
logger.error("delete error:", throwable); | |||
logger.error("Failed to delete degrade rule, id={}", id, throwable); | |||
return Result.ofThrowable(-1, throwable); | |||
} | |||
if (!publishRules(oldEntity.getApp(), oldEntity.getIp(), oldEntity.getPort())) { | |||
logger.info("publish degrade rules fail after rule delete"); | |||
logger.warn("Publish degrade rules failed, app={}", oldEntity.getApp()); | |||
} | |||
return Result.ofSuccess(id); | |||
} | |||
@@ -211,4 +165,57 @@ public class DegradeController { | |||
List<DegradeRuleEntity> rules = repository.findAllByMachine(MachineInfo.of(app, ip, port)); | |||
return sentinelApiClient.setDegradeRuleOfMachine(app, ip, port, rules); | |||
} | |||
private <R> Result<R> checkEntityInternal(DegradeRuleEntity entity) { | |||
if (StringUtil.isBlank(entity.getApp())) { | |||
return Result.ofFail(-1, "app can't be blank"); | |||
} | |||
if (StringUtil.isBlank(entity.getIp())) { | |||
return Result.ofFail(-1, "ip can't be null or empty"); | |||
} | |||
if (entity.getPort() == null || entity.getPort() <= 0) { | |||
return Result.ofFail(-1, "invalid port: " + entity.getPort()); | |||
} | |||
if (StringUtil.isBlank(entity.getLimitApp())) { | |||
return Result.ofFail(-1, "limitApp can't be null or empty"); | |||
} | |||
if (StringUtil.isBlank(entity.getResource())) { | |||
return Result.ofFail(-1, "resource can't be null or empty"); | |||
} | |||
Double threshold = entity.getCount(); | |||
if (threshold == null || threshold < 0) { | |||
return Result.ofFail(-1, "invalid threshold: " + threshold); | |||
} | |||
Integer recoveryTimeoutSec = entity.getTimeWindow(); | |||
if (recoveryTimeoutSec == null || recoveryTimeoutSec <= 0) { | |||
return Result.ofFail(-1, "recoveryTimeout should be positive"); | |||
} | |||
Integer strategy = entity.getGrade(); | |||
if (strategy == null) { | |||
return Result.ofFail(-1, "circuit breaker strategy cannot be null"); | |||
} | |||
if (strategy < CircuitBreakerStrategy.SLOW_REQUEST_RATIO.getType() | |||
|| strategy > RuleConstant.DEGRADE_GRADE_EXCEPTION_COUNT) { | |||
return Result.ofFail(-1, "Invalid circuit breaker strategy: " + strategy); | |||
} | |||
if (entity.getMinRequestAmount() == null || entity.getMinRequestAmount() <= 0) { | |||
return Result.ofFail(-1, "Invalid minRequestAmount"); | |||
} | |||
if (entity.getStatIntervalMs() == null || entity.getStatIntervalMs() <= 0) { | |||
return Result.ofFail(-1, "Invalid statInterval"); | |||
} | |||
if (strategy == RuleConstant.DEGRADE_GRADE_RT) { | |||
Double slowRatio = entity.getSlowRatioThreshold(); | |||
if (slowRatio == null) { | |||
return Result.ofFail(-1, "SlowRatioThreshold is required for slow request ratio strategy"); | |||
} else if (slowRatio < 0 || slowRatio > 1) { | |||
return Result.ofFail(-1, "SlowRatioThreshold should be in range: [0.0, 1.0]"); | |||
} | |||
} else if (strategy == RuleConstant.DEGRADE_GRADE_EXCEPTION_RATIO) { | |||
if (threshold > 1) { | |||
return Result.ofFail(-1, "Ratio threshold should be in range: [0.0, 1.0]"); | |||
} | |||
} | |||
return null; | |||
} | |||
} |
@@ -23,18 +23,22 @@ import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRule; | |||
* @author leyou | |||
*/ | |||
public class DegradeRuleEntity implements RuleEntity { | |||
private Long id; | |||
private String app; | |||
private String ip; | |||
private Integer port; | |||
private String resource; | |||
private String limitApp; | |||
private Double count; | |||
private Integer timeWindow; | |||
/** | |||
* 0 rt 限流; 1为异常; | |||
*/ | |||
private Integer grade; | |||
private Integer minRequestAmount; | |||
private Double slowRatioThreshold; | |||
private Integer statIntervalMs; | |||
private Date gmtCreate; | |||
private Date gmtModified; | |||
@@ -48,6 +52,9 @@ public class DegradeRuleEntity implements RuleEntity { | |||
entity.setCount(rule.getCount()); | |||
entity.setTimeWindow(rule.getTimeWindow()); | |||
entity.setGrade(rule.getGrade()); | |||
entity.setMinRequestAmount(rule.getMinRequestAmount()); | |||
entity.setSlowRatioThreshold(rule.getSlowRatioThreshold()); | |||
entity.setStatIntervalMs(rule.getStatIntervalMs()); | |||
return entity; | |||
} | |||
@@ -128,6 +135,33 @@ public class DegradeRuleEntity implements RuleEntity { | |||
this.grade = grade; | |||
} | |||
public Integer getMinRequestAmount() { | |||
return minRequestAmount; | |||
} | |||
public DegradeRuleEntity setMinRequestAmount(Integer minRequestAmount) { | |||
this.minRequestAmount = minRequestAmount; | |||
return this; | |||
} | |||
public Double getSlowRatioThreshold() { | |||
return slowRatioThreshold; | |||
} | |||
public DegradeRuleEntity setSlowRatioThreshold(Double slowRatioThreshold) { | |||
this.slowRatioThreshold = slowRatioThreshold; | |||
return this; | |||
} | |||
public Integer getStatIntervalMs() { | |||
return statIntervalMs; | |||
} | |||
public DegradeRuleEntity setStatIntervalMs(Integer statIntervalMs) { | |||
this.statIntervalMs = statIntervalMs; | |||
return this; | |||
} | |||
@Override | |||
public Date getGmtCreate() { | |||
return gmtCreate; | |||
@@ -153,6 +187,16 @@ public class DegradeRuleEntity implements RuleEntity { | |||
rule.setCount(count); | |||
rule.setTimeWindow(timeWindow); | |||
rule.setGrade(grade); | |||
if (minRequestAmount != null) { | |||
rule.setMinRequestAmount(minRequestAmount); | |||
} | |||
if (slowRatioThreshold != null) { | |||
rule.setSlowRatioThreshold(slowRatioThreshold); | |||
} | |||
if (statIntervalMs != null) { | |||
rule.setStatIntervalMs(statIntervalMs); | |||
} | |||
return rule; | |||
} | |||
} |
@@ -66,7 +66,9 @@ app.controller('DegradeCtl', ['$scope', '$stateParams', 'DegradeService', 'ngDia | |||
app: $scope.app, | |||
ip: mac[0], | |||
port: mac[1], | |||
limitApp: 'default' | |||
limitApp: 'default', | |||
minRequestAmount: 5, | |||
statIntervalMs: 1000, | |||
}; | |||
$scope.degradeRuleDialog = { | |||
title: '新增降级规则', | |||
@@ -95,7 +97,7 @@ app.controller('DegradeCtl', ['$scope', '$stateParams', 'DegradeService', 'ngDia | |||
function parseDegradeMode(grade) { | |||
switch (grade) { | |||
case 0: | |||
return 'RT'; | |||
return '慢调用比例'; | |||
case 1: | |||
return '异常比例'; | |||
case 2: | |||
@@ -137,7 +139,7 @@ app.controller('DegradeCtl', ['$scope', '$stateParams', 'DegradeService', 'ngDia | |||
getMachineRules(); | |||
confirmDialog.close(); | |||
} else { | |||
alert('失败!'); | |||
alert('失败:' + data.msg); | |||
} | |||
}); | |||
}; | |||
@@ -148,7 +150,7 @@ app.controller('DegradeCtl', ['$scope', '$stateParams', 'DegradeService', 'ngDia | |||
getMachineRules(); | |||
degradeRuleDialog.close(); | |||
} else { | |||
alert('失败!'); | |||
alert('失败:' + data.msg); | |||
} | |||
}); | |||
}; | |||
@@ -163,7 +165,7 @@ app.controller('DegradeCtl', ['$scope', '$stateParams', 'DegradeService', 'ngDia | |||
confirmDialog.close(); | |||
} | |||
} else { | |||
alert('失败!'); | |||
alert('失败:' + data.msg); | |||
} | |||
}); | |||
} | |||
@@ -132,6 +132,8 @@ app.controller('IdentityCtl', ['$scope', '$stateParams', 'IdentityService', | |||
strategy: 0, | |||
resource: resource, | |||
limitApp: 'default', | |||
minRequestAmount: 5, | |||
statIntervalMs: 1000, | |||
app: $scope.app, | |||
ip: mac[0], | |||
port: mac[1] | |||
@@ -15,21 +15,10 @@ app.service('DegradeService', ['$http', function ($http) { | |||
}; | |||
this.newRule = function (rule) { | |||
var param = { | |||
id: rule.id, | |||
resource: rule.resource, | |||
limitApp: rule.limitApp, | |||
count: rule.count, | |||
timeWindow: rule.timeWindow, | |||
grade: rule.grade, | |||
app: rule.app, | |||
ip: rule.ip, | |||
port: rule.port | |||
}; | |||
return $http({ | |||
url: '/degrade/new.json', | |||
params: param, | |||
method: 'GET' | |||
url: '/degrade/rule', | |||
data: rule, | |||
method: 'POST' | |||
}); | |||
}; | |||
@@ -41,24 +30,22 @@ app.service('DegradeService', ['$http', function ($http) { | |||
grade: rule.grade, | |||
count: rule.count, | |||
timeWindow: rule.timeWindow, | |||
statIntervalMs: rule.statIntervalMs, | |||
minRequestAmount: rule.minRequestAmount, | |||
slowRatioThreshold: rule.slowRatioThreshold, | |||
}; | |||
return $http({ | |||
url: '/degrade/save.json', | |||
params: param, | |||
method: 'GET' | |||
url: '/degrade/rule/' + rule.id, | |||
data: param, | |||
method: 'PUT' | |||
}); | |||
}; | |||
this.deleteRule = function (rule) { | |||
var param = { | |||
id: rule.id, | |||
app: rule.app | |||
}; | |||
return $http({ | |||
url: '/degrade/delete.json', | |||
params: param, | |||
method: 'GET' | |||
}); | |||
return $http({ | |||
url: '/degrade/rule/' + rule.id, | |||
method: 'DELETE' | |||
}); | |||
}; | |||
this.checkRuleValid = function (rule) { | |||
@@ -74,8 +61,20 @@ app.service('DegradeService', ['$http', function ($http) { | |||
alert('降级阈值不能为空或小于 0'); | |||
return false; | |||
} | |||
if (rule.timeWindow === undefined || rule.timeWindow === '' || rule.timeWindow <= 0) { | |||
alert('降级时间窗口必须大于 0'); | |||
if (rule.timeWindow == undefined || rule.timeWindow === '' || rule.timeWindow <= 0) { | |||
alert('熔断时长必须大于 0s'); | |||
return false; | |||
} | |||
if (rule.minRequestAmount == undefined || rule.minRequestAmount <= 0) { | |||
alert('最小请求数目需大于 0'); | |||
return false; | |||
} | |||
if (rule.statIntervalMs == undefined || rule.statIntervalMs <= 0) { | |||
alert('统计窗口时长需大于 0s'); | |||
return false; | |||
} | |||
if (rule.statIntervalMs !== undefined && rule.statIntervalMs > 60 * 1000 * 2) { | |||
alert('统计窗口时长不能超过 120 分钟'); | |||
return false; | |||
} | |||
// 异常比率类型. | |||
@@ -83,6 +82,16 @@ app.service('DegradeService', ['$http', function ($http) { | |||
alert('异常比率超出范围:[0.0 - 1.0]'); | |||
return false; | |||
} | |||
if (rule.grade == 0) { | |||
if (rule.slowRatioThreshold == undefined) { | |||
alert('慢调用比率不能为空'); | |||
return false; | |||
} | |||
if (rule.slowRatioThreshold < 0 || rule.slowRatioThreshold > 1) { | |||
alert('慢调用比率超出范围:[0.0 - 1.0]'); | |||
return false; | |||
} | |||
} | |||
return true; | |||
}; | |||
}]); |
@@ -33,21 +33,15 @@ | |||
<td style="width: 40%"> | |||
资源名 | |||
</td> | |||
<!--<td style="width: 10%;">--> | |||
<!--降级应用--> | |||
<!--</td>--> | |||
<td style="width: 10%;"> | |||
降级模式 | |||
降级策略 | |||
</td> | |||
<td style="width: 10%;"> | |||
阈值 | |||
</td> | |||
<td style="width: 10%;"> | |||
时间窗口(s) | |||
熔断时长(s) | |||
</td> | |||
<!--<td style="width: 8%;">--> | |||
<!--状态--> | |||
<!--</td>--> | |||
<td style="width: 12%;"> | |||
操作 | |||
</td> | |||
@@ -59,9 +53,9 @@ | |||
<td style="word-wrap:break-word;word-break:break-all;">{{rule.resource}}</td> | |||
<!--<td style="word-wrap:break-word;word-break:break-all;">{{rule.limitApp }}</td>--> | |||
<td> | |||
<span ng-if="rule.grade == 0">RT</span> | |||
<span ng-if="rule.grade == 1" title="秒级异常比例">异常比例</span> | |||
<span ng-if="rule.grade == 2" title="分钟级异常数">异常数</span> | |||
<span ng-if="rule.grade == 0">慢调用比例</span> | |||
<span ng-if="rule.grade == 1" title="异常比例">异常比例</span> | |||
<span ng-if="rule.grade == 2" title="异常数">异常数</span> | |||
</td> | |||
<td style="word-wrap:break-word;word-break:break-all;"> | |||
{{rule.count}} | |||
@@ -22,30 +22,53 @@ | |||
<!--</div>--> | |||
<div class="form-group"> | |||
<label class="col-sm-2 control-label">降级策略</label> | |||
<label class="col-sm-2 control-label">熔断策略</label> | |||
<div class="col-sm-9"> | |||
<div class="form-control highlight-border" align="center"> | |||
<input type="radio" name="grade" value="0" checked ng-model='currentRule.grade' title="秒级平均响应时间" /> RT | |||
<input type="radio" name="grade" value="1" ng-model='currentRule.grade' title="秒级异常比例" /> 异常比例 | |||
<input type="radio" name="grade" value="2" ng-model='currentRule.grade' title="分钟级异常数,仅 1.3.0 及以上版本生效" /> 异常数 | |||
<input type="radio" name="grade" value="0" checked ng-model='currentRule.grade' title="慢调用比例(1.8.0+ 版本生效)" /> 慢调用比例 | |||
<input type="radio" name="grade" value="1" ng-model='currentRule.grade' title="异常比例" /> 异常比例 | |||
<input type="radio" name="grade" value="2" ng-model='currentRule.grade' title="异常数" /> 异常数 | |||
</div> | |||
</div> | |||
</div> | |||
<div class="form-group"> | |||
<label ng-if="currentRule.grade == 0" class="col-sm-2 control-label">RT</label> | |||
<label ng-if="currentRule.grade == 1" class="col-sm-2 control-label">异常比例</label> | |||
<label ng-if="currentRule.grade == 0" class="col-sm-2 control-label" title="最大 RT,超过该值则计为慢调用">最大 RT</label> | |||
<label ng-if="currentRule.grade == 1" class="col-sm-2 control-label">比例阈值</label> | |||
<label ng-if="currentRule.grade == 2" class="col-sm-2 control-label">异常数</label> | |||
<div class="col-sm-3"> | |||
<input type='number' class="form-control highlight-border" ng-model='currentRule.count' ng-if="currentRule.grade == 0" placeholder="毫秒"/> | |||
<input type='number' class="form-control highlight-border" ng-model='currentRule.count' ng-if="currentRule.grade == 1" placeholder="0.0~1.0"/> | |||
<div class="col-sm-4"> | |||
<input type='number' class="form-control highlight-border" ng-model='currentRule.count' ng-if="currentRule.grade == 0" placeholder="RT (毫秒)"/> | |||
<input type='number' class="form-control highlight-border" ng-model='currentRule.count' ng-if="currentRule.grade == 1" placeholder="取值范围 [0.0,1.0]"/> | |||
<input type='number' class="form-control highlight-border" ng-model='currentRule.count' ng-if="currentRule.grade == 2" placeholder="异常数"/> | |||
</div> | |||
<label class="col-sm-2 control-label">时间窗口</label> | |||
<div ng-if="currentRule.grade == 0"> | |||
<label class="col-sm-2 control-label">比例阈值</label> | |||
<div class="col-sm-3"> | |||
<input type='number' min="0" class="form-control highlight-border" ng-model='currentRule.slowRatioThreshold' | |||
placeholder="取值 [0.0, 1.0]" /> | |||
</div> | |||
</div> | |||
</div> | |||
<div class="form-group"> | |||
<label class="col-sm-2 control-label">熔断时长</label> | |||
<div class="col-sm-4"> | |||
<input type='number' min="0" class="form-control highlight-border" ng-model='currentRule.timeWindow' placeholder="降级时间间隔, 单位秒" /> | |||
<div class="input-group"> | |||
<input type='number' min="0" class="form-control highlight-border" ng-model='currentRule.timeWindow' | |||
placeholder="熔断时长(s)" /> | |||
<span class="input-group-addon">s</span> | |||
</div> | |||
</div> | |||
<label class="col-sm-2 control-label" style="text-align: center; padding-right: 5px;" | |||
title="触发熔断的最小请求数目,若当前统计窗口内的请求数小于此值,即使达到熔断条件规则也不会触发">最小请求数</label> | |||
<div class="col-sm-3"> | |||
<input type='number' min="1" class="form-control highlight-border" ng-model='currentRule.minRequestAmount' | |||
placeholder="最小请求数目" /> | |||
</div> | |||
</div> | |||
</form> | |||
</div> | |||
<div class="separator"></div> | |||
@@ -48,7 +48,7 @@ const JS_APP = [ | |||
'app/scripts/services/appservice.js', | |||
'app/scripts/services/flow_service_v1.js', | |||
'app/scripts/services/flow_service_v2.js', | |||
'app/scripts/services/degradeservice.js', | |||
'app/scripts/services/degrade_service.js', | |||
'app/scripts/services/systemservice.js', | |||
'app/scripts/services/machineservice.js', | |||
'app/scripts/services/identityservice.js', | |||