wangjx 3 лет назад
Родитель
Сommit
785f945cf7
5 измененных файлов: 449 добавлений и 3 удалений
  1. +228
    -0
      sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/controller/v2/DegradeControllerV2.java
  2. +0
    -2
      sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/controller/v2/FlowControllerV2.java
  3. +16
    -0
      sentinel-dashboard/src/main/webapp/resources/app/scripts/app.js
  4. +204
    -0
      sentinel-dashboard/src/main/webapp/resources/app/scripts/controllers/degrade_v2.js
  5. +1
    -1
      sentinel-dashboard/src/main/webapp/resources/app/scripts/directives/sidebar/sidebar.html

+ 228
- 0
sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/controller/v2/DegradeControllerV2.java Просмотреть файл

@@ -0,0 +1,228 @@
/*
* Copyright 1999-2018 Alibaba Group Holding Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.csp.sentinel.dashboard.controller.v2;

import com.alibaba.csp.sentinel.dashboard.auth.AuthAction;
import com.alibaba.csp.sentinel.dashboard.auth.AuthService.PrivilegeType;
import com.alibaba.csp.sentinel.dashboard.client.SentinelApiClient;
import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.DegradeRuleEntity;
import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.FlowRuleEntity;
import com.alibaba.csp.sentinel.dashboard.discovery.MachineInfo;
import com.alibaba.csp.sentinel.dashboard.domain.Result;
import com.alibaba.csp.sentinel.dashboard.repository.rule.RuleRepository;
import com.alibaba.csp.sentinel.dashboard.rule.DynamicRuleProvider;
import com.alibaba.csp.sentinel.dashboard.rule.DynamicRulePublisher;
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 org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.web.bind.annotation.*;

import java.util.Date;
import java.util.List;

/**
* Controller regarding APIs of degrade rules. Refactored since 1.8.0.
*
* @author Carpenter Lee
* @author Eric Zhao
*/
@RestController
@RequestMapping("/v2/degrade")
public class DegradeControllerV2 {

private final Logger logger = LoggerFactory.getLogger(DegradeControllerV2.class);

@Autowired
private RuleRepository<DegradeRuleEntity, Long> repository;
@Autowired
// @Qualifier("flowRuleDefaultProvider")
@Qualifier("degradeRuleNacosProvider")
private DynamicRuleProvider<List<DegradeRuleEntity>> ruleProvider;
@Autowired
// @Qualifier("flowRuleDefaultPublisher")
@Qualifier("degradeRuleNacosPublisher")
private DynamicRulePublisher<List<DegradeRuleEntity>> rulePublisher;

@Autowired
private SentinelApiClient sentinelApiClient;

@GetMapping("/rules.json")
@AuthAction(PrivilegeType.READ_RULE)
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");
}
if (StringUtil.isEmpty(ip)) {
return Result.ofFail(-1, "ip can't be null or empty");
}
if (port == null) {
return Result.ofFail(-1, "port can't be null");
}
try {
// List<DegradeRuleEntity> rules = sentinelApiClient.fetchDegradeRuleOfMachine(app, ip, port);
List<DegradeRuleEntity> rules = ruleProvider.getRules(app);
rules = repository.saveAll(rules);
return Result.ofSuccess(rules);
} catch (Throwable throwable) {
logger.error("queryApps error:", throwable);
return Result.ofThrowable(-1, throwable);
}
}

@PostMapping("/rule")
@AuthAction(PrivilegeType.WRITE_RULE)
public Result<DegradeRuleEntity> apiAddRule(@RequestBody DegradeRuleEntity entity) throws Exception {
Result<DegradeRuleEntity> checkResult = checkEntityInternal(entity);
if (checkResult != null) {
return checkResult;
}
Date date = new Date();
entity.setGmtCreate(date);
entity.setGmtModified(date);
try {
entity = repository.save(entity);
} 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(entity.getApp(), entity.getIp(), entity.getPort())) {
logger.warn("Publish degrade rules failed, app={}", entity.getApp());
}
return Result.ofSuccess(entity);
}

@PutMapping("/rule/{id}")
@AuthAction(PrivilegeType.WRITE_RULE)
public Result<DegradeRuleEntity> apiUpdateRule(@PathVariable("id") Long id,
@RequestBody DegradeRuleEntity entity) throws Exception {
if (id == null || id <= 0) {
return Result.ofFail(-1, "id can't be null or negative");
}
DegradeRuleEntity oldEntity = repository.findById(id);
if (oldEntity == null) {
return Result.ofFail(-1, "Degrade rule does not exist, id=" + id);
}
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;
}

entity.setGmtCreate(oldEntity.getGmtCreate());
entity.setGmtModified(new Date());
try {
entity = repository.save(entity);
} 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.warn("Publish degrade rules failed, app={}", entity.getApp());
}
return Result.ofSuccess(entity);
}

@DeleteMapping("/rule/{id}")
@AuthAction(PrivilegeType.DELETE_RULE)
public Result<Long> delete(@PathVariable("id") Long id) throws Exception {
if (id == null) {
return Result.ofFail(-1, "id can't be null");
}

DegradeRuleEntity oldEntity = repository.findById(id);
if (oldEntity == null) {
return Result.ofSuccess(null);
}

try {
repository.delete(id);
} catch (Throwable 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.warn("Publish degrade rules failed, app={}", oldEntity.getApp());
}
return Result.ofSuccess(id);
}

private boolean publishRules(String app, String ip, Integer port) throws Exception {
List<DegradeRuleEntity> rules = repository.findAllByMachine(MachineInfo.of(app, ip, port));

rulePublisher.publish(app, 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;
}
}

+ 0
- 2
sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/controller/v2/FlowControllerV2.java Просмотреть файл

@@ -29,8 +29,6 @@ import com.alibaba.csp.sentinel.dashboard.rule.DynamicRuleProvider;
import com.alibaba.csp.sentinel.dashboard.rule.DynamicRulePublisher;
import com.alibaba.csp.sentinel.dashboard.domain.Result;

import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;


+ 16
- 0
sentinel-dashboard/src/main/webapp/resources/app/scripts/app.js Просмотреть файл

@@ -254,6 +254,22 @@ angular
}
})

.state('dashboard.degradeV2', {
templateUrl: 'app/views/degrade.html',
url: '/degrade/:app',
controller: 'DegradeControllerV2',
resolve: {
loadMyFiles: ['$ocLazyLoad', function ($ocLazyLoad) {
return $ocLazyLoad.load({
name: 'sentinelDashboardApp',
files: [
'app/scripts/controllers/degrade.js',
]
});
}]
}
})

.state('dashboard.system', {
templateUrl: 'app/views/system.html',
url: '/system/:app',


+ 204
- 0
sentinel-dashboard/src/main/webapp/resources/app/scripts/controllers/degrade_v2.js Просмотреть файл

@@ -0,0 +1,204 @@
var app = angular.module('sentinelDashboardApp');

app.controller('DegradeControllerV2', ['$scope', '$stateParams', 'DegradeService', 'ngDialog', 'MachineService',
function ($scope, $stateParams, DegradeService, ngDialog, MachineService) {
//初始化
$scope.app = $stateParams.app;
$scope.rulesPageConfig = {
pageSize: 10,
currentPageIndex: 1,
totalPage: 1,
totalCount: 0,
};
$scope.macsInputConfig = {
searchField: ['text', 'value'],
persist: true,
create: false,
maxItems: 1,
render: {
item: function (data, escape) {
return '<div>' + escape(data.text) + '</div>';
}
},
onChange: function (value, oldValue) {
$scope.macInputModel = value;
}
};
getMachineRules();
function getMachineRules() {
if (!$scope.macInputModel) {
return;
}
var mac = $scope.macInputModel.split(':');
DegradeService.queryMachineRules($scope.app, mac[0], mac[1]).success(
function (data) {
if (data.code == 0 && data.data) {
$scope.rules = data.data;
$scope.rulesPageConfig.totalCount = $scope.rules.length;
} else {
$scope.rules = [];
$scope.rulesPageConfig.totalCount = 0;
}
});
};
$scope.getMachineRules = getMachineRules;

var degradeRuleDialog;
$scope.editRule = function (rule) {
$scope.currentRule = angular.copy(rule);
$scope.degradeRuleDialog = {
title: '编辑降级规则',
type: 'edit',
confirmBtnText: '保存'
};
degradeRuleDialog = ngDialog.open({
template: '/app/views/dialog/degrade-rule-dialog.html',
width: 680,
overlay: true,
scope: $scope
});
};

$scope.addNewRule = function () {
var mac = $scope.macInputModel.split(':');
$scope.currentRule = {
grade: 0,
app: $scope.app,
ip: mac[0],
port: mac[1],
limitApp: 'default',
minRequestAmount: 5,
statIntervalMs: 1000,
};
$scope.degradeRuleDialog = {
title: '新增降级规则',
type: 'add',
confirmBtnText: '新增'
};
degradeRuleDialog = ngDialog.open({
template: '/app/views/dialog/degrade-rule-dialog.html',
width: 680,
overlay: true,
scope: $scope
});
};

$scope.saveRule = function () {
if (!DegradeService.checkRuleValid($scope.currentRule)) {
return;
}
if ($scope.degradeRuleDialog.type === 'add') {
addNewRule($scope.currentRule);
} else if ($scope.degradeRuleDialog.type === 'edit') {
saveRule($scope.currentRule, true);
}
};

function parseDegradeMode(grade) {
switch (grade) {
case 0:
return '慢调用比例';
case 1:
return '异常比例';
case 2:
return '异常数';
default:
return '未知';
}
}

var confirmDialog;
$scope.deleteRule = function (rule) {
$scope.currentRule = rule;
$scope.confirmDialog = {
title: '删除降级规则',
type: 'delete_rule',
attentionTitle: '请确认是否删除如下降级规则',
attention: '资源名: ' + rule.resource +
', 降级模式: ' + parseDegradeMode(rule.grade) + ', 阈值: ' + rule.count,
confirmBtnText: '删除',
};
confirmDialog = ngDialog.open({
template: '/app/views/dialog/confirm-dialog.html',
scope: $scope,
overlay: true
});
};

$scope.confirm = function () {
if ($scope.confirmDialog.type == 'delete_rule') {
deleteRule($scope.currentRule);
} else {
console.error('error');
}
};

function deleteRule(rule) {
DegradeService.deleteRule(rule).success(function (data) {
if (data.code == 0) {
getMachineRules();
confirmDialog.close();
} else {
alert('失败:' + data.msg);
}
});
};

function addNewRule(rule) {
DegradeService.newRule(rule).success(function (data) {
if (data.code == 0) {
getMachineRules();
degradeRuleDialog.close();
} else {
alert('失败:' + data.msg);
}
});
};

function saveRule(rule, edit) {
DegradeService.saveRule(rule).success(function (data) {
if (data.code == 0) {
getMachineRules();
if (edit) {
degradeRuleDialog.close();
} else {
confirmDialog.close();
}
} else {
alert('失败:' + data.msg);
}
});
}
queryAppMachines();
function queryAppMachines() {
MachineService.getAppMachines($scope.app).success(
function (data) {
if (data.code == 0) {
// $scope.machines = data.data;
if (data.data) {
$scope.machines = [];
$scope.macsInputOptions = [];
data.data.forEach(function (item) {
if (item.healthy) {
$scope.macsInputOptions.push({
text: item.ip + ':' + item.port,
value: item.ip + ':' + item.port
});
}
});
}
if ($scope.macsInputOptions.length > 0) {
$scope.macInputModel = $scope.macsInputOptions[0].value;
}
} else {
$scope.macsInputOptions = [];
}
}
);
};
$scope.$watch('macInputModel', function () {
if ($scope.macInputModel) {
getMachineRules();
}
});
}]);

+ 1
- 1
sentinel-dashboard/src/main/webapp/resources/app/scripts/directives/sidebar/sidebar.html Просмотреть файл

@@ -67,7 +67,7 @@
</li>

<li ui-sref-active="active">
<a ui-sref="dashboard.degrade({app: entry.app})">
<a ui-sref="dashboard.degradeV2({app: entry.app})">
<i class="glyphicon glyphicon-flash"></i>&nbsp;&nbsp;
降级规则</a>
</li>


Загрузка…
Отмена
Сохранить