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.client.SentinelApiClient; | ||||
import com.alibaba.csp.sentinel.dashboard.discovery.MachineInfo; | import com.alibaba.csp.sentinel.dashboard.discovery.MachineInfo; | ||||
import com.alibaba.csp.sentinel.dashboard.auth.AuthService.PrivilegeType; | 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.RuleConstant; | ||||
import com.alibaba.csp.sentinel.slots.block.degrade.circuitbreaker.CircuitBreakerStrategy; | |||||
import com.alibaba.csp.sentinel.util.StringUtil; | import com.alibaba.csp.sentinel.util.StringUtil; | ||||
import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.DegradeRuleEntity; | import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.DegradeRuleEntity; | ||||
import com.alibaba.csp.sentinel.dashboard.domain.Result; | import com.alibaba.csp.sentinel.dashboard.domain.Result; | ||||
import com.alibaba.csp.sentinel.dashboard.repository.rule.InMemDegradeRuleStore; | |||||
import org.slf4j.Logger; | import org.slf4j.Logger; | ||||
import org.slf4j.LoggerFactory; | import org.slf4j.LoggerFactory; | ||||
import org.springframework.beans.factory.annotation.Autowired; | 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.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 { | public class DegradeController { | ||||
private final Logger logger = LoggerFactory.getLogger(DegradeController.class); | private final Logger logger = LoggerFactory.getLogger(DegradeController.class); | ||||
@Autowired | @Autowired | ||||
private InMemDegradeRuleStore repository; | |||||
private RuleRepository<DegradeRuleEntity, Long> repository; | |||||
@Autowired | @Autowired | ||||
private SentinelApiClient sentinelApiClient; | private SentinelApiClient sentinelApiClient; | ||||
@ResponseBody | |||||
@RequestMapping("/rules.json") | |||||
@GetMapping("/rules.json") | |||||
@AuthAction(PrivilegeType.READ_RULE) | @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)) { | if (StringUtil.isEmpty(app)) { | ||||
return Result.ofFail(-1, "app can't be null or empty"); | 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) | @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(); | Date date = new Date(); | ||||
entity.setGmtCreate(date); | entity.setGmtCreate(date); | ||||
entity.setGmtModified(date); | entity.setGmtModified(date); | ||||
try { | try { | ||||
entity = repository.save(entity); | 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); | return Result.ofSuccess(entity); | ||||
} | } | ||||
@ResponseBody | |||||
@RequestMapping("/save.json") | |||||
@PutMapping("/rule/{id}") | |||||
@AuthAction(PrivilegeType.WRITE_RULE) | @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 { | try { | ||||
entity = repository.save(entity); | 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())) { | 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); | return Result.ofSuccess(entity); | ||||
} | } | ||||
@ResponseBody | |||||
@RequestMapping("/delete.json") | |||||
@DeleteMapping("/rule/{id}") | |||||
@AuthAction(PrivilegeType.DELETE_RULE) | @AuthAction(PrivilegeType.DELETE_RULE) | ||||
public Result<Long> delete(Long id) { | |||||
public Result<Long> delete(@PathVariable("id") Long id) { | |||||
if (id == null) { | if (id == null) { | ||||
return Result.ofFail(-1, "id can't be null"); | return Result.ofFail(-1, "id can't be null"); | ||||
} | } | ||||
@@ -198,11 +152,11 @@ public class DegradeController { | |||||
try { | try { | ||||
repository.delete(id); | repository.delete(id); | ||||
} catch (Throwable throwable) { | } catch (Throwable throwable) { | ||||
logger.error("delete error:", throwable); | |||||
logger.error("Failed to delete degrade rule, id={}", id, throwable); | |||||
return Result.ofThrowable(-1, throwable); | return Result.ofThrowable(-1, throwable); | ||||
} | } | ||||
if (!publishRules(oldEntity.getApp(), oldEntity.getIp(), oldEntity.getPort())) { | 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); | return Result.ofSuccess(id); | ||||
} | } | ||||
@@ -211,4 +165,57 @@ public class DegradeController { | |||||
List<DegradeRuleEntity> rules = repository.findAllByMachine(MachineInfo.of(app, ip, port)); | List<DegradeRuleEntity> rules = repository.findAllByMachine(MachineInfo.of(app, ip, port)); | ||||
return sentinelApiClient.setDegradeRuleOfMachine(app, ip, port, rules); | 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 | * @author leyou | ||||
*/ | */ | ||||
public class DegradeRuleEntity implements RuleEntity { | public class DegradeRuleEntity implements RuleEntity { | ||||
private Long id; | private Long id; | ||||
private String app; | private String app; | ||||
private String ip; | private String ip; | ||||
private Integer port; | private Integer port; | ||||
private String resource; | private String resource; | ||||
private String limitApp; | private String limitApp; | ||||
private Double count; | private Double count; | ||||
private Integer timeWindow; | private Integer timeWindow; | ||||
/** | |||||
* 0 rt 限流; 1为异常; | |||||
*/ | |||||
private Integer grade; | private Integer grade; | ||||
private Integer minRequestAmount; | |||||
private Double slowRatioThreshold; | |||||
private Integer statIntervalMs; | |||||
private Date gmtCreate; | private Date gmtCreate; | ||||
private Date gmtModified; | private Date gmtModified; | ||||
@@ -48,6 +52,9 @@ public class DegradeRuleEntity implements RuleEntity { | |||||
entity.setCount(rule.getCount()); | entity.setCount(rule.getCount()); | ||||
entity.setTimeWindow(rule.getTimeWindow()); | entity.setTimeWindow(rule.getTimeWindow()); | ||||
entity.setGrade(rule.getGrade()); | entity.setGrade(rule.getGrade()); | ||||
entity.setMinRequestAmount(rule.getMinRequestAmount()); | |||||
entity.setSlowRatioThreshold(rule.getSlowRatioThreshold()); | |||||
entity.setStatIntervalMs(rule.getStatIntervalMs()); | |||||
return entity; | return entity; | ||||
} | } | ||||
@@ -128,6 +135,33 @@ public class DegradeRuleEntity implements RuleEntity { | |||||
this.grade = grade; | 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 | @Override | ||||
public Date getGmtCreate() { | public Date getGmtCreate() { | ||||
return gmtCreate; | return gmtCreate; | ||||
@@ -153,6 +187,16 @@ public class DegradeRuleEntity implements RuleEntity { | |||||
rule.setCount(count); | rule.setCount(count); | ||||
rule.setTimeWindow(timeWindow); | rule.setTimeWindow(timeWindow); | ||||
rule.setGrade(grade); | rule.setGrade(grade); | ||||
if (minRequestAmount != null) { | |||||
rule.setMinRequestAmount(minRequestAmount); | |||||
} | |||||
if (slowRatioThreshold != null) { | |||||
rule.setSlowRatioThreshold(slowRatioThreshold); | |||||
} | |||||
if (statIntervalMs != null) { | |||||
rule.setStatIntervalMs(statIntervalMs); | |||||
} | |||||
return rule; | return rule; | ||||
} | } | ||||
} | } |
@@ -66,7 +66,9 @@ app.controller('DegradeCtl', ['$scope', '$stateParams', 'DegradeService', 'ngDia | |||||
app: $scope.app, | app: $scope.app, | ||||
ip: mac[0], | ip: mac[0], | ||||
port: mac[1], | port: mac[1], | ||||
limitApp: 'default' | |||||
limitApp: 'default', | |||||
minRequestAmount: 5, | |||||
statIntervalMs: 1000, | |||||
}; | }; | ||||
$scope.degradeRuleDialog = { | $scope.degradeRuleDialog = { | ||||
title: '新增降级规则', | title: '新增降级规则', | ||||
@@ -95,7 +97,7 @@ app.controller('DegradeCtl', ['$scope', '$stateParams', 'DegradeService', 'ngDia | |||||
function parseDegradeMode(grade) { | function parseDegradeMode(grade) { | ||||
switch (grade) { | switch (grade) { | ||||
case 0: | case 0: | ||||
return 'RT'; | |||||
return '慢调用比例'; | |||||
case 1: | case 1: | ||||
return '异常比例'; | return '异常比例'; | ||||
case 2: | case 2: | ||||
@@ -137,7 +139,7 @@ app.controller('DegradeCtl', ['$scope', '$stateParams', 'DegradeService', 'ngDia | |||||
getMachineRules(); | getMachineRules(); | ||||
confirmDialog.close(); | confirmDialog.close(); | ||||
} else { | } else { | ||||
alert('失败!'); | |||||
alert('失败:' + data.msg); | |||||
} | } | ||||
}); | }); | ||||
}; | }; | ||||
@@ -148,7 +150,7 @@ app.controller('DegradeCtl', ['$scope', '$stateParams', 'DegradeService', 'ngDia | |||||
getMachineRules(); | getMachineRules(); | ||||
degradeRuleDialog.close(); | degradeRuleDialog.close(); | ||||
} else { | } else { | ||||
alert('失败!'); | |||||
alert('失败:' + data.msg); | |||||
} | } | ||||
}); | }); | ||||
}; | }; | ||||
@@ -163,7 +165,7 @@ app.controller('DegradeCtl', ['$scope', '$stateParams', 'DegradeService', 'ngDia | |||||
confirmDialog.close(); | confirmDialog.close(); | ||||
} | } | ||||
} else { | } else { | ||||
alert('失败!'); | |||||
alert('失败:' + data.msg); | |||||
} | } | ||||
}); | }); | ||||
} | } | ||||
@@ -132,6 +132,8 @@ app.controller('IdentityCtl', ['$scope', '$stateParams', 'IdentityService', | |||||
strategy: 0, | strategy: 0, | ||||
resource: resource, | resource: resource, | ||||
limitApp: 'default', | limitApp: 'default', | ||||
minRequestAmount: 5, | |||||
statIntervalMs: 1000, | |||||
app: $scope.app, | app: $scope.app, | ||||
ip: mac[0], | ip: mac[0], | ||||
port: mac[1] | port: mac[1] | ||||
@@ -15,21 +15,10 @@ app.service('DegradeService', ['$http', function ($http) { | |||||
}; | }; | ||||
this.newRule = function (rule) { | 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({ | 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, | grade: rule.grade, | ||||
count: rule.count, | count: rule.count, | ||||
timeWindow: rule.timeWindow, | timeWindow: rule.timeWindow, | ||||
statIntervalMs: rule.statIntervalMs, | |||||
minRequestAmount: rule.minRequestAmount, | |||||
slowRatioThreshold: rule.slowRatioThreshold, | |||||
}; | }; | ||||
return $http({ | return $http({ | ||||
url: '/degrade/save.json', | |||||
params: param, | |||||
method: 'GET' | |||||
url: '/degrade/rule/' + rule.id, | |||||
data: param, | |||||
method: 'PUT' | |||||
}); | }); | ||||
}; | }; | ||||
this.deleteRule = function (rule) { | 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) { | this.checkRuleValid = function (rule) { | ||||
@@ -74,8 +61,20 @@ app.service('DegradeService', ['$http', function ($http) { | |||||
alert('降级阈值不能为空或小于 0'); | alert('降级阈值不能为空或小于 0'); | ||||
return false; | 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; | return false; | ||||
} | } | ||||
// 异常比率类型. | // 异常比率类型. | ||||
@@ -83,6 +82,16 @@ app.service('DegradeService', ['$http', function ($http) { | |||||
alert('异常比率超出范围:[0.0 - 1.0]'); | alert('异常比率超出范围:[0.0 - 1.0]'); | ||||
return false; | 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; | return true; | ||||
}; | }; | ||||
}]); | }]); |
@@ -33,21 +33,15 @@ | |||||
<td style="width: 40%"> | <td style="width: 40%"> | ||||
资源名 | 资源名 | ||||
</td> | </td> | ||||
<!--<td style="width: 10%;">--> | |||||
<!--降级应用--> | |||||
<!--</td>--> | |||||
<td style="width: 10%;"> | <td style="width: 10%;"> | ||||
降级模式 | |||||
降级策略 | |||||
</td> | </td> | ||||
<td style="width: 10%;"> | <td style="width: 10%;"> | ||||
阈值 | 阈值 | ||||
</td> | </td> | ||||
<td style="width: 10%;"> | <td style="width: 10%;"> | ||||
时间窗口(s) | |||||
熔断时长(s) | |||||
</td> | </td> | ||||
<!--<td style="width: 8%;">--> | |||||
<!--状态--> | |||||
<!--</td>--> | |||||
<td style="width: 12%;"> | <td style="width: 12%;"> | ||||
操作 | 操作 | ||||
</td> | </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.resource}}</td> | ||||
<!--<td style="word-wrap:break-word;word-break:break-all;">{{rule.limitApp }}</td>--> | <!--<td style="word-wrap:break-word;word-break:break-all;">{{rule.limitApp }}</td>--> | ||||
<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> | ||||
<td style="word-wrap:break-word;word-break:break-all;"> | <td style="word-wrap:break-word;word-break:break-all;"> | ||||
{{rule.count}} | {{rule.count}} | ||||
@@ -22,30 +22,53 @@ | |||||
<!--</div>--> | <!--</div>--> | ||||
<div class="form-group"> | <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="col-sm-9"> | ||||
<div class="form-control highlight-border" align="center"> | <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> | ||||
</div> | </div> | ||||
<div class="form-group"> | <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> | <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="异常数"/> | <input type='number' class="form-control highlight-border" ng-model='currentRule.count' ng-if="currentRule.grade == 2" placeholder="异常数"/> | ||||
</div> | </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"> | <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> | ||||
</div> | </div> | ||||
</form> | </form> | ||||
</div> | </div> | ||||
<div class="separator"></div> | <div class="separator"></div> | ||||
@@ -48,7 +48,7 @@ const JS_APP = [ | |||||
'app/scripts/services/appservice.js', | 'app/scripts/services/appservice.js', | ||||
'app/scripts/services/flow_service_v1.js', | 'app/scripts/services/flow_service_v1.js', | ||||
'app/scripts/services/flow_service_v2.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/systemservice.js', | ||||
'app/scripts/services/machineservice.js', | 'app/scripts/services/machineservice.js', | ||||
'app/scripts/services/identityservice.js', | 'app/scripts/services/identityservice.js', | ||||