Signed-off-by: Eric Zhao <sczyh16@gmail.com>master
@@ -66,22 +66,38 @@ angular | |||
} | |||
}) | |||
.state('dashboard.flow', { | |||
templateUrl: 'app/views/flow.html', | |||
url: '/flow/:app', | |||
controller: 'FlowCtl', | |||
.state('dashboard.flowV1', { | |||
templateUrl: 'app/views/flow_old.html', | |||
url: '/v1/flow/:app', | |||
controller: 'FlowControllerV1', | |||
resolve: { | |||
loadMyFiles: ['$ocLazyLoad', function ($ocLazyLoad) { | |||
return $ocLazyLoad.load({ | |||
name: 'sentinelDashboardApp', | |||
files: [ | |||
'app/scripts/controllers/flow.js', | |||
'app/scripts/controllers/flow_old.js', | |||
] | |||
}); | |||
}] | |||
} | |||
}) | |||
.state('dashboard.flow', { | |||
templateUrl: 'app/views/flow.html', | |||
url: '/flow/:app', | |||
controller: 'FlowController', | |||
resolve: { | |||
loadMyFiles: ['$ocLazyLoad', function ($ocLazyLoad) { | |||
return $ocLazyLoad.load({ | |||
name: 'sentinelDashboardApp', | |||
files: [ | |||
'app/scripts/controllers/flow.js', | |||
] | |||
}); | |||
}] | |||
} | |||
}) | |||
.state('dashboard.paramFlow', { | |||
templateUrl: 'app/views/param_flow.html', | |||
url: '/paramFlow/:app', | |||
@@ -98,6 +114,22 @@ angular | |||
} | |||
}) | |||
.state('dashboard.clusterAll', { | |||
templateUrl: 'app/views/cluster.html', | |||
url: '/cluster/:app', | |||
controller: 'SentinelClusterController', | |||
resolve: { | |||
loadMyFiles: ['$ocLazyLoad', function ($ocLazyLoad) { | |||
return $ocLazyLoad.load({ | |||
name: 'sentinelDashboardApp', | |||
files: [ | |||
'app/scripts/controllers/cluster.js', | |||
] | |||
}); | |||
}] | |||
} | |||
}) | |||
.state('dashboard.authority', { | |||
templateUrl: 'app/views/authority.html', | |||
url: '/authority/:app', | |||
@@ -0,0 +1,237 @@ | |||
var app = angular.module('sentinelDashboardApp'); | |||
app.controller('SentinelClusterController', ['$scope', '$stateParams', 'ngDialog', | |||
'MachineService', 'ClusterStateService', | |||
function ($scope, $stateParams, ngDialog, MachineService, ClusterStateService) { | |||
$scope.app = $stateParams.app; | |||
const UNSUPPORTED_CODE = 4041; | |||
const CLUSTER_MODE_CLIENT = 0; | |||
const CLUSTER_MODE_SERVER = 1; | |||
$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; | |||
} | |||
}; | |||
function convertSetToString(set) { | |||
if (set === undefined) { | |||
return ''; | |||
} | |||
let s = ''; | |||
for (let i = 0; i < set.length; i++) { | |||
s = s + set[i]; | |||
if (i < set.length - 1) { | |||
s = s + ','; | |||
} | |||
} | |||
return s; | |||
} | |||
function convertStrToNamespaceSet(str) { | |||
if (str === undefined || str == '') { | |||
return []; | |||
} | |||
let arr = []; | |||
let spliced = str.split(','); | |||
spliced.forEach((v) => { | |||
arr.push(v.trim()); | |||
}); | |||
return arr; | |||
} | |||
function fetchMachineClusterState() { | |||
if (!$scope.macInputModel) { | |||
return; | |||
} | |||
let mac = $scope.macInputModel.split(':'); | |||
ClusterStateService.fetchClusterUniversalState($scope.app, mac[0], mac[1]).success(function (data) { | |||
if (data.code == 0 && data.data) { | |||
$scope.loadError = undefined; | |||
$scope.stateVO = data.data; | |||
$scope.stateVO.currentMode = $scope.stateVO.stateInfo.mode; | |||
if ($scope.stateVO.server && $scope.stateVO.server.namespaceSet) { | |||
$scope.stateVO.server.namespaceSetStr = convertSetToString($scope.stateVO.server.namespaceSet); | |||
} | |||
} else { | |||
$scope.stateVO = {}; | |||
if (data.code === UNSUPPORTED_CODE) { | |||
$scope.loadError = {message: '机器 ' + mac[0] + ':' + mac[1] + ' 的 Sentinel 客户端版本不支持集群限流,请升级至 1.4.0 以上版本并引入相关依赖。'} | |||
} else { | |||
$scope.loadError = {message: data.msg}; | |||
} | |||
} | |||
}).error((data, header, config, status) => { | |||
$scope.loadError = {message: '未知错误'}; | |||
}); | |||
} | |||
fetchMachineClusterState(); | |||
function checkValidClientConfig(stateVO) { | |||
if (!stateVO.client || !stateVO.client.clientConfig) { | |||
alert('不合法的配置'); | |||
return false; | |||
} | |||
let config = stateVO.client.clientConfig; | |||
if (!config.serverHost || config.serverHost.trim() == '') { | |||
alert('请输入有效的 Token Server IP'); | |||
return false; | |||
} | |||
if (config.serverPort === undefined || config.serverPort <= 0 || config.serverPort > 65535) { | |||
alert('请输入有效的 Token Server 端口'); | |||
return false; | |||
} | |||
if (config.requestTimeout === undefined || config.requestTimeout <= 0) { | |||
alert('请输入有效的请求超时时长'); | |||
return false; | |||
} | |||
return true; | |||
} | |||
function sendClusterClientRequest(stateVO) { | |||
if (!checkValidClientConfig(stateVO)) { | |||
return; | |||
} | |||
if (!$scope.macInputModel) { | |||
return; | |||
} | |||
let mac = $scope.macInputModel.split(':'); | |||
let request = { | |||
app: $scope.app, | |||
ip: mac[0], | |||
port: mac[1], | |||
}; | |||
request.mode = CLUSTER_MODE_CLIENT; | |||
request.clientConfig = stateVO.client.clientConfig; | |||
ClusterStateService.modifyClusterConfig(request).success(function (data) { | |||
if (data.code == 0 && data.data) { | |||
alert('修改集群限流客户端配置成功'); | |||
window.location.reload(); | |||
} else { | |||
if (data.code === UNSUPPORTED_CODE) { | |||
alert('机器 ' + mac[0] + ':' + mac[1] + ' 的 Sentinel 客户端版本不支持集群限流客户端,请升级至 1.4.0 以上版本并引入相关依赖。'); | |||
} else { | |||
alert('修改失败:' + data.msg); | |||
} | |||
} | |||
}).error((data, header, config, status) => { | |||
alert('未知错误'); | |||
}); | |||
} | |||
function checkValidServerConfig(stateVO) { | |||
if (!stateVO.server || !stateVO.server.transport) { | |||
alert('不合法的配置'); | |||
return false; | |||
} | |||
if (stateVO.server.namespaceSetStr === undefined || stateVO.server.namespaceSetStr == '') { | |||
alert('请输入有效的命名空间集合(多个 namespace 以 , 分隔)'); | |||
return false; | |||
} | |||
let transportConfig = stateVO.server.transport; | |||
if (transportConfig.port === undefined || transportConfig.port <= 0 || transportConfig.port > 65535) { | |||
alert('请输入有效的 Token Server 端口'); | |||
return false; | |||
} | |||
// if (transportConfig.idleSeconds === undefined || transportConfig.idleSeconds <= 0) { | |||
// alert('请输入有效的连接清理时长 (idleSeconds)'); | |||
// return false; | |||
// } | |||
return true; | |||
} | |||
function sendClusterServerRequest(stateVO) { | |||
if (!checkValidServerConfig(stateVO)) { | |||
return; | |||
} | |||
if (!$scope.macInputModel) { | |||
return; | |||
} | |||
let mac = $scope.macInputModel.split(':'); | |||
let request = { | |||
app: $scope.app, | |||
ip: mac[0], | |||
port: mac[1], | |||
}; | |||
request.mode = CLUSTER_MODE_SERVER; | |||
request.flowConfig = stateVO.server.flow; | |||
request.transportConfig = stateVO.server.transport; | |||
request.namespaceSet = convertStrToNamespaceSet(stateVO.server.namespaceSetStr); | |||
ClusterStateService.modifyClusterConfig(request).success(function (data) { | |||
if (data.code == 0 && data.data) { | |||
alert('修改集群限流服务端配置成功'); | |||
window.location.reload(); | |||
} else { | |||
if (data.code === UNSUPPORTED_CODE) { | |||
alert('机器 ' + mac[0] + ':' + mac[1] + ' 的 Sentinel 客户端版本不支持集群限流服务端,请升级至 1.4.0 以上版本并引入相关依赖。'); | |||
} else { | |||
alert('修改失败:' + data.msg); | |||
} | |||
} | |||
}).error((data, header, config, status) => { | |||
alert('未知错误'); | |||
}); | |||
} | |||
$scope.saveConfig = () => { | |||
let ok = confirm('是否确定修改集群限流配置?'); | |||
if (!ok) { | |||
return; | |||
} | |||
let mode = $scope.stateVO.stateInfo.mode; | |||
if (mode != 1 && mode != 0) { | |||
alert('未知的集群限流模式'); | |||
return; | |||
} | |||
if (mode == 0) { | |||
sendClusterClientRequest($scope.stateVO); | |||
} else { | |||
sendClusterServerRequest($scope.stateVO); | |||
} | |||
}; | |||
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.health) { | |||
$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) { | |||
fetchMachineClusterState(); | |||
} | |||
}); | |||
queryAppMachines(); | |||
}]); |
@@ -1,6 +1,6 @@ | |||
var app = angular.module('sentinelDashboardApp'); | |||
app.controller('FlowCtl', ['$scope', '$stateParams', 'FlowService', 'ngDialog', | |||
app.controller('FlowController', ['$scope', '$stateParams', 'FlowServiceV2', 'ngDialog', | |||
'MachineService', | |||
function ($scope, $stateParams, FlowService, ngDialog, | |||
MachineService) { | |||
@@ -72,7 +72,11 @@ app.controller('FlowCtl', ['$scope', '$stateParams', 'FlowService', 'ngDialog', | |||
app: $scope.app, | |||
ip: mac[0], | |||
port: mac[1], | |||
limitApp: 'default' | |||
limitApp: 'default', | |||
clusterMode: false, | |||
clusterConfig: { | |||
thresholdType: 0 | |||
} | |||
}; | |||
$scope.flowRuleDialog = { | |||
title: '新增流控规则', | |||
@@ -0,0 +1,203 @@ | |||
var app = angular.module('sentinelDashboardApp'); | |||
app.controller('FlowControllerV1', ['$scope', '$stateParams', 'FlowServiceV1', 'ngDialog', | |||
'MachineService', | |||
function ($scope, $stateParams, FlowService, 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(':'); | |||
FlowService.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 flowRuleDialog; | |||
$scope.editRule = function (rule) { | |||
$scope.currentRule = rule; | |||
$scope.flowRuleDialog = { | |||
title: '编辑流控规则', | |||
type: 'edit', | |||
confirmBtnText: '保存', | |||
showAdvanceButton: rule.controlBehavior == 0 && rule.strategy == 0 | |||
}; | |||
flowRuleDialog = ngDialog.open({ | |||
template: '/app/views/dialog/flow-rule-dialog.html', | |||
width: 680, | |||
overlay: true, | |||
scope: $scope | |||
}); | |||
}; | |||
$scope.addNewRule = function () { | |||
var mac = $scope.macInputModel.split(':'); | |||
$scope.currentRule = { | |||
grade: 1, | |||
strategy: 0, | |||
controlBehavior: 0, | |||
app: $scope.app, | |||
ip: mac[0], | |||
port: mac[1], | |||
limitApp: 'default' | |||
}; | |||
$scope.flowRuleDialog = { | |||
title: '新增流控规则', | |||
type: 'add', | |||
confirmBtnText: '新增', | |||
showAdvanceButton: true, | |||
}; | |||
flowRuleDialog = ngDialog.open({ | |||
template: '/app/views/dialog/flow-rule-dialog.html', | |||
width: 680, | |||
overlay: true, | |||
scope: $scope | |||
}); | |||
}; | |||
$scope.saveRule = function () { | |||
if (!FlowService.checkRuleValid($scope.currentRule)) { | |||
return; | |||
} | |||
if ($scope.flowRuleDialog.type === 'add') { | |||
addNewRule($scope.currentRule); | |||
} else if ($scope.flowRuleDialog.type === 'edit') { | |||
saveRule($scope.currentRule, true); | |||
} | |||
}; | |||
var confirmDialog; | |||
$scope.deleteRule = function (rule) { | |||
$scope.currentRule = rule; | |||
$scope.confirmDialog = { | |||
title: '删除流控规则', | |||
type: 'delete_rule', | |||
attentionTitle: '请确认是否删除如下流控规则', | |||
attention: '资源名: ' + rule.resource + ', 流控应用: ' + rule.limitApp | |||
+ ', 阈值类型: ' + (rule.grade == 0 ? '线程数' : 'QPS') + ', 阈值: ' + 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) { | |||
FlowService.deleteRule(rule).success(function (data) { | |||
if (data.code == 0) { | |||
getMachineRules(); | |||
confirmDialog.close(); | |||
} else { | |||
alert('失败!'); | |||
} | |||
}); | |||
}; | |||
function addNewRule(rule) { | |||
FlowService.newRule(rule).success(function (data) { | |||
if (data.code == 0) { | |||
getMachineRules(); | |||
flowRuleDialog.close(); | |||
} else { | |||
alert('失败!'); | |||
} | |||
}); | |||
}; | |||
$scope.onOpenAdvanceClick = function () { | |||
$scope.flowRuleDialog.showAdvanceButton = false; | |||
}; | |||
$scope.onCloseAdvanceClick = function () { | |||
$scope.flowRuleDialog.showAdvanceButton = true; | |||
}; | |||
function saveRule(rule, edit) { | |||
FlowService.saveRule(rule).success(function (data) { | |||
if (data.code == 0) { | |||
getMachineRules(); | |||
if (edit) { | |||
flowRuleDialog.close(); | |||
} else { | |||
confirmDialog.close(); | |||
} | |||
} else { | |||
alert('失败!'); | |||
} | |||
}); | |||
} | |||
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.health) { | |||
$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,7 +1,7 @@ | |||
var app = angular.module('sentinelDashboardApp'); | |||
app.controller('IdentityCtl', ['$scope', '$stateParams', 'IdentityService', | |||
'ngDialog', 'FlowService', 'DegradeService', 'AuthorityRuleService', 'ParamFlowService', 'MachineService', | |||
'ngDialog', 'FlowServiceV2', 'DegradeService', 'AuthorityRuleService', 'ParamFlowService', 'MachineService', | |||
'$interval', '$location', '$timeout', | |||
function ($scope, $stateParams, IdentityService, ngDialog, | |||
FlowService, DegradeService, AuthorityRuleService, ParamFlowService, MachineService, $interval, $location, $timeout) { | |||
@@ -33,9 +33,13 @@ | |||
</li> | |||
<li ui-sref-active="active"> | |||
<a ui-sref="dashboard.flow({app: entry.app})"> | |||
<a ui-sref="dashboard.flowV1({app: entry.app})"> | |||
<i class="glyphicon glyphicon-filter"></i> 流控规则</a> | |||
</li> | |||
<!--<li ui-sref-active="active">--> | |||
<!--<a ui-sref="dashboard.flow({app: entry.app})">--> | |||
<!--<i class="glyphicon glyphicon-filter"></i> 流控规则 V1</a>--> | |||
<!--</li>--> | |||
<li ui-sref-active="active"> | |||
<a ui-sref="dashboard.degrade({app: entry.app})"> | |||
<i class="glyphicon glyphicon-flash"></i> 降级规则</a> | |||
@@ -52,6 +56,10 @@ | |||
<a ui-sref="dashboard.authority({app: entry.app})"> | |||
<i class="glyphicon glyphicon-check"></i> 授权规则</a> | |||
</li> | |||
<li ui-sref-active="active"> | |||
<a ui-sref="dashboard.clusterAll({app: entry.app})"> | |||
<i class="glyphicon glyphicon-cloud"></i> 集群限流</a> | |||
</li> | |||
<li ui-sref-active="active"> | |||
<a ui-sref="dashboard.machine({app: entry.app})"> | |||
<i class="glyphicon glyphicon-th-list"></i> 机器列表</a> | |||
@@ -0,0 +1,28 @@ | |||
/** | |||
* Parameter flow control service. | |||
* | |||
* @author Eric Zhao | |||
*/ | |||
angular.module('sentinelDashboardApp').service('ClusterStateService', ['$http', function ($http) { | |||
this.fetchClusterUniversalState = function(app, ip, port) { | |||
var param = { | |||
app: app, | |||
ip: ip, | |||
port: port | |||
}; | |||
return $http({ | |||
url: '/cluster/state', | |||
params: param, | |||
method: 'GET' | |||
}); | |||
}; | |||
this.modifyClusterConfig = function(config) { | |||
return $http({ | |||
url: '/cluster/config/modify', | |||
data: config, | |||
method: 'POST' | |||
}); | |||
}; | |||
}]); |
@@ -1,6 +1,6 @@ | |||
var app = angular.module('sentinelDashboardApp'); | |||
app.service('FlowService', ['$http', function ($http) { | |||
app.service('FlowServiceV1', ['$http', function ($http) { | |||
this.queryMachineRules = function (app, ip, port) { | |||
var param = { | |||
app: app, | |||
@@ -8,7 +8,7 @@ app.service('FlowService', ['$http', function ($http) { | |||
port: port | |||
}; | |||
return $http({ | |||
url: 'flow/rules.json', | |||
url: '/v1/flow/rules', | |||
params: param, | |||
method: 'GET' | |||
}); | |||
@@ -31,9 +31,9 @@ app.service('FlowService', ['$http', function ($http) { | |||
}; | |||
return $http({ | |||
url: '/flow/new.json', | |||
params: param, | |||
method: 'GET' | |||
url: '/v1/flow/rule', | |||
data: rule, | |||
method: 'POST' | |||
}); | |||
}; | |||
@@ -52,9 +52,9 @@ app.service('FlowService', ['$http', function ($http) { | |||
}; | |||
return $http({ | |||
url: '/flow/save.json', | |||
url: '/v1/flow/save.json', | |||
params: param, | |||
method: 'GET' | |||
method: 'PUT' | |||
}); | |||
}; | |||
@@ -65,9 +65,9 @@ app.service('FlowService', ['$http', function ($http) { | |||
}; | |||
return $http({ | |||
url: '/flow/delete.json', | |||
url: '/v1/flow/delete.json', | |||
params: param, | |||
method: 'GET' | |||
method: 'DELETE' | |||
}); | |||
}; | |||
@@ -0,0 +1,85 @@ | |||
var app = angular.module('sentinelDashboardApp'); | |||
app.service('FlowServiceV2', ['$http', function ($http) { | |||
this.queryMachineRules = function (app, ip, port) { | |||
var param = { | |||
app: app, | |||
ip: ip, | |||
port: port | |||
}; | |||
return $http({ | |||
url: '/v2/flow/rules', | |||
params: param, | |||
method: 'GET' | |||
}); | |||
}; | |||
this.newRule = function (rule) { | |||
return $http({ | |||
url: '/v2/flow/rule', | |||
data: rule, | |||
method: 'POST' | |||
}); | |||
}; | |||
this.saveRule = function (rule) { | |||
return $http({ | |||
url: '/v2/flow/rule/' + rule.id, | |||
data: rule, | |||
method: 'PUT' | |||
}); | |||
}; | |||
this.deleteRule = function (rule) { | |||
return $http({ | |||
url: '/v2/flow/rule/' + rule.id, | |||
method: 'DELETE' | |||
}); | |||
}; | |||
function notNumberAtLeastZero(num) { | |||
return num === undefined || num === '' || isNaN(num) || num < 0; | |||
} | |||
function notNumberGreaterThanZero(num) { | |||
return num === undefined || num === '' || isNaN(num) || num <= 0; | |||
} | |||
this.checkRuleValid = function (rule) { | |||
if (rule.resource === undefined || rule.resource === '') { | |||
alert('资源名称不能为空'); | |||
return false; | |||
} | |||
if (rule.count === undefined || rule.count < 0) { | |||
alert('限流阈值必须大于等于 0'); | |||
return false; | |||
} | |||
if (rule.strategy === undefined || rule.strategy < 0) { | |||
alert('无效的流控模式'); | |||
return false; | |||
} | |||
if (rule.strategy == 1 || rule.strategy == 2) { | |||
if (rule.refResource === undefined || rule.refResource == '') { | |||
alert('请填写关联资源或入口'); | |||
return false; | |||
} | |||
} | |||
if (rule.controlBehavior === undefined || rule.controlBehavior < 0) { | |||
alert('无效的流控整形方式'); | |||
return false; | |||
} | |||
if (rule.controlBehavior == 1 && notNumberGreaterThanZero(rule.warmUpPeriodSec)) { | |||
alert('预热时长必须大于 0'); | |||
return false; | |||
} | |||
if (rule.controlBehavior == 2 && notNumberGreaterThanZero(rule.maxQueueingTimeMs)) { | |||
alert('排队超时时间必须大于 0'); | |||
return false; | |||
} | |||
if (rule.clusterMode && (rule.clusterConfig === undefined || rule.clusterConfig.thresholdType === undefined)) { | |||
alert('集群限流配置不正确'); | |||
return false; | |||
} | |||
return true; | |||
}; | |||
}]); |
@@ -0,0 +1,96 @@ | |||
<div class="row" style="margin-left: 1px; margin-top:10px; height: 50px;"> | |||
<div class="col-md-6" style="margin-bottom: 10px;"> | |||
<span style="font-size: 30px;font-weight: bold;">{{app}}</span> | |||
</div> | |||
</div> | |||
<div class="separator"></div> | |||
<div class="container-fluid"> | |||
<div class="row" style="margin-top: 20px; margin-bottom: 20px;"> | |||
<div class="col-md-12"> | |||
<div class="card"> | |||
<div class="inputs-header"> | |||
<span class="brand" style="font-size: 13px;">集群限流</span> | |||
<button class="btn btn-primary" style="float: right; margin-right: 10px; height: 30px;font-size: 12px;" ng-click="getMachineRules()">刷新</button> | |||
<input class="form-control witdh-200" placeholder="关键字" ng-model="searchKey"> | |||
<div class="control-group" style="float:right;margin-right: 10px;margin-bottom: -10px;"> | |||
<selectize id="gsInput" class="selectize-input-200" config="macsInputConfig" options="macsInputOptions" ng-model="macInputModel" | |||
placeholder="机器"></selectize> | |||
</div> | |||
</div> | |||
<!-- error panel --> | |||
<div class="row clearfix" ng-if="loadError"> | |||
<div class="col-md-6 col-md-offset-3"> | |||
<div class="panel panel-default"> | |||
<div class="panel-body"> | |||
<center> | |||
<p>{{loadError.message}}</p> | |||
</center> | |||
</div> | |||
</div> | |||
</div> | |||
</div> | |||
<!--.tools-header --> | |||
<div class="card-body" style="padding: 0px 0px;"> | |||
<!--<span class="brand" style="font-weight:bold;">集群限流状态</span>--> | |||
<form role="form" class="form-horizontal"> | |||
<div class="form-group"> | |||
<label class="col-sm-2 control-label">当前模式</label> | |||
<p class="col-sm-6 control-label" style="text-align: left; font-weight: normal;" ng-if="stateVO.currentMode == 0">Client</p> | |||
<p class="col-sm-6 control-label" style="text-align: left; font-weight: normal;" ng-if="stateVO.currentMode == 1">Server</p> | |||
<p class="col-sm-6 control-label" style="text-align: left; font-weight: normal;" ng-if="stateVO.currentMode == -1">未开启</p> | |||
</div> | |||
<div class="form-group"> | |||
<label class="col-sm-2 control-label">集群限流模式变换</label> | |||
<div class="col-sm-4"> | |||
<div class="form-control highlight-border" align="center"> | |||
<input type="radio" name="mode" value="0" ng-model='stateVO.stateInfo.mode' ng-disabled="!stateVO.stateInfo.clientAvailable" /> Client | |||
<input type="radio" name="mode" value="1" ng-model='stateVO.stateInfo.mode' ng-disabled="!stateVO.stateInfo.serverAvailable" /> Server | |||
</div> | |||
</div> | |||
</div> | |||
</form> | |||
<!-- no-cluster-mode-available-panel.start --> | |||
<div ng-if="!stateVO.stateInfo.clientAvailable && !stateVO.stateInfo.serverAvailable"> | |||
<!-- error panel --> | |||
<div class="row clearfix"> | |||
<div class="col-md-6 col-md-offset-3"> | |||
<div class="panel panel-default"> | |||
<div class="panel-body"> | |||
<center> | |||
<p>该机器未引入 Sentinel 集群限流客户端或服务端的相关依赖,请引入相关依赖。</p> | |||
</center> | |||
</div> | |||
</div> | |||
</div> | |||
</div> | |||
</div> | |||
<!-- no-cluster-mode-available-panel.stop --> | |||
<div ng-if="stateVO.stateInfo.clientAvailable || stateVO.stateInfo.serverAvailable"> | |||
<div ng-if="stateVO.stateInfo.clientAvailable && stateVO.stateInfo.mode == 0"> | |||
<div ng-include="'app/views/cluster/client.html'"></div> | |||
</div> | |||
<div ng-if="stateVO.stateInfo.serverAvailable && stateVO.stateInfo.mode == 1"> | |||
<div ng-include="'app/views/cluster/server.html'"></div> | |||
</div> | |||
<div class="separator"></div> | |||
<div clss="row" style="margin-top: 20px;"> | |||
<button style="margin: 0 10px 10px 10px;" class="btn btn-outline-success" ng-click="saveConfig()">保存配置</button> | |||
</div> | |||
</div> | |||
</div> | |||
<!-- .card-body --> | |||
</div> | |||
<!-- .card --> | |||
</div> | |||
<!-- .col-md-12 --> | |||
</div> | |||
<!-- --> | |||
</div> | |||
<!-- .container-fluid --> |
@@ -0,0 +1,22 @@ | |||
<div class="row clearfix"> | |||
<form role="form" class="form-horizontal"> | |||
<div class="form-group"> | |||
<label class="col-sm-2 control-label">Token Server IP</label> | |||
<div class="col-sm-4"> | |||
<input type="text" class="form-control highlight-border" ng-model='stateVO.client.clientConfig.serverHost' placeholder='请指定 Token Server IP' /> | |||
</div> | |||
</div> | |||
<div class="form-group"> | |||
<label class="col-sm-2 control-label">Token Server 端口</label> | |||
<div class="col-sm-4"> | |||
<input type="number" min="0" max="65535" required class="form-control highlight-border" ng-model='stateVO.client.clientConfig.serverPort' placeholder='请指定 Token Server 端口' /> | |||
</div> | |||
</div> | |||
<div class="form-group"> | |||
<label class="col-sm-2 control-label">请求超时时间(ms)</label> | |||
<div class="col-sm-4"> | |||
<input type="number" min="0" required class="form-control highlight-border" ng-model='stateVO.client.clientConfig.requestTimeout' placeholder='请指定请求超时时间(ms)' /> | |||
</div> | |||
</div> | |||
</form> | |||
</div> |
@@ -0,0 +1,16 @@ | |||
<div class="row clearfix"> | |||
<form role="form" class="form-horizontal"> | |||
<div class="form-group"> | |||
<label class="col-sm-2 control-label">Token Server 端口</label> | |||
<div class="col-sm-4"> | |||
<input type="number" min="0" max="65535" required class="form-control highlight-border" ng-model='stateVO.server.transport.port' placeholder='请指定 Token Server 端口' /> | |||
</div> | |||
</div> | |||
<div class="form-group"> | |||
<label class="col-sm-2 control-label">命名空间集合</label> | |||
<div class="col-sm-4"> | |||
<input type="text" required class="form-control highlight-border" ng-model='stateVO.server.namespaceSetStr' placeholder='请指定服务端服务的命名空间集合(以,分隔)' /> | |||
</div> | |||
</div> | |||
</form> | |||
</div> |
@@ -30,9 +30,39 @@ | |||
<input type="radio" name="grade" value="0" ng-model='currentRule.grade' /> 线程数 | |||
</div> | |||
</div> | |||
<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.count' placeholder="单机阈值" /> | |||
<div ng-if="!currentRule.clusterMode"> | |||
<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.count' placeholder="单机阈值" /> | |||
</div> | |||
</div> | |||
<div ng-if="currentRule.clusterMode && currentRule.clusterConfig.thresholdType == 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.count' placeholder="单机均摊阈值" /> | |||
</div> | |||
</div> | |||
<div ng-if="currentRule.clusterMode && currentRule.clusterConfig.thresholdType == 1"> | |||
<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.count' placeholder="集群总体阈值" /> | |||
</div> | |||
</div> | |||
</div> | |||
<div class="form-group"> | |||
<label class="col-sm-2 control-label">是否集群</label> | |||
<div class="col-sm-2"> | |||
<input type="checkbox" name="clusterMode" ng-model="currentRule.clusterMode"> | |||
</div> | |||
<div ng-if="currentRule.clusterMode"> | |||
<label class="col-sm-3 control-label">集群阈值模式</label> | |||
<div class="col-sm-4"> | |||
<div class="form-control highlight-border" align="center"> | |||
<input type="radio" name="clusterThresholdType" value="0" ng-model='currentRule.clusterConfig.thresholdType' /> 单机均摊 | |||
<input type="radio" name="clusterThresholdType" value="1" ng-model='currentRule.clusterConfig.thresholdType' /> Global | |||
</div> | |||
</div> | |||
</div> | |||
</div> | |||
@@ -3,8 +3,11 @@ | |||
<span style="font-size: 30px;font-weight: bold;">{{app}}</span> | |||
</div> | |||
<div class="col-md-6"> | |||
<button class="btn btn-default-inverse" style="float: right; margin-right: 10px;" ng-disabled="!macInputModel" ng-click="addNewRule()"> | |||
<i class="fa fa-plus"></i> 新增流控规则</button> | |||
<button class="btn btn-default-inverse" style="float: right; margin-right: 10px;" ng-click="addNewRule()"> | |||
<i class="fa fa-plus"></i> 新增流控规则 | |||
</button> | |||
<a class="btn btn-default-inverse" style="float: right; margin-right: 10px;" ui-sref="dashboard.flowV1({app: app})"> | |||
回到旧版(单机)</a> | |||
</div> | |||
</div> | |||
@@ -16,13 +19,7 @@ | |||
<div class="card"> | |||
<div class="inputs-header"> | |||
<span class="brand" style="font-size: 13px;">流控规则</span> | |||
<!--<button class="btn btn-danger" style="float: right;margin-right: 10px;height: 30px;font-size: 12px;" ng-click="disableAll()">全部禁用</button>--> | |||
<button class="btn btn-primary" style="float: right; margin-right: 10px; height: 30px;font-size: 12px;" ng-click="getMachineRules()">刷新</button> | |||
<input class="form-control witdh-200" placeholder="关键字" ng-model="searchKey"> | |||
<div class="control-group" style="float:right;margin-right: 10px;margin-bottom: -10px;"> | |||
<selectize id="gsInput" class="selectize-input-200" config="macsInputConfig" options="macsInputOptions" ng-model="macInputModel" | |||
placeholder="机器"></selectize> | |||
</div> | |||
</div> | |||
<!--.tools-header --> | |||
@@ -36,21 +33,21 @@ | |||
<td style="width: 10%;"> | |||
流控应用 | |||
</td> | |||
<td style="width: 10%;"> | |||
<td style="width: 8%;"> | |||
流控模式 | |||
</td> | |||
<td style="width: 10%;"> | |||
<td style="width: 8%;"> | |||
阈值类型 | |||
</td> | |||
<td style="width: 10%;"> | |||
<td style="width: 8%;"> | |||
单机阈值 | |||
</td> | |||
<td style="width: 10%;"> | |||
<td style="width: 8%;"> | |||
是否集群 | |||
</td> | |||
<td style="width: 8%;"> | |||
流控效果 | |||
</td> | |||
<!--<td style="width: 8%;">--> | |||
<!--状态--> | |||
<!--</td>--> | |||
<td style="width: 12%;"> | |||
操作 | |||
</td> | |||
@@ -67,15 +64,20 @@ | |||
<span ng-if="rule.strategy == 2">链路</span> | |||
</td> | |||
<td> | |||
{{rule.grade==0 ? '线程数' : 'QPS'}} | |||
{{rule.grade == 0 ? '线程数' : 'QPS'}} | |||
</td> | |||
<td style="word-wrap:break-word;word-break:break-all;"> | |||
{{rule.count}} | |||
</td> | |||
<td> | |||
<span ng-if="rule.clusterMode">是</span> | |||
<span ng-if="!rule.clusterMode">否</span> | |||
</td> | |||
<td> | |||
<span ng-if="rule.controlBehavior == 0">快速失败</span> | |||
<span ng-if="rule.controlBehavior == 1">Warm Up</span> | |||
<span ng-if="rule.controlBehavior == 2">排队等待</span> | |||
<span ng-if="rule.controlBehavior == 3">预热排队</span> | |||
</td> | |||
<td> | |||
<button class="btn btn-xs btn-default" type="button" ng-click="editRule(rule)" style="font-size: 12px; height:25px;">编辑</button> | |||
@@ -0,0 +1,113 @@ | |||
<div class="row" style="margin-left: 1px; margin-top:10px; height: 50px;"> | |||
<div class="col-md-6" style="margin-bottom: 10px;"> | |||
<span style="font-size: 30px;font-weight: bold;">{{app}}</span> | |||
</div> | |||
<div class="col-md-6"> | |||
<button class="btn btn-default-inverse" style="float: right; margin-right: 10px;" ng-disabled="!macInputModel" ng-click="addNewRule()"> | |||
<i class="fa fa-plus"></i> 新增流控规则</button> | |||
<a class="btn btn-outline-success" style="float: right; margin-right: 10px;" ui-sref="dashboard.flow({app: app})"> | |||
回到新版</a> | |||
</div> | |||
</div> | |||
<div class="separator"></div> | |||
<div class="container-fluid"> | |||
<div class="row" style="margin-top: 20px; margin-bottom: 20px;"> | |||
<div class="col-md-12"> | |||
<div class="card"> | |||
<div class="inputs-header"> | |||
<span class="brand" style="font-size: 13px;">流控规则</span> | |||
<!--<button class="btn btn-danger" style="float: right;margin-right: 10px;height: 30px;font-size: 12px;" ng-click="disableAll()">全部禁用</button>--> | |||
<button class="btn btn-primary" style="float: right; margin-right: 10px; height: 30px;font-size: 12px;" ng-click="getMachineRules()">刷新</button> | |||
<input class="form-control witdh-200" placeholder="关键字" ng-model="searchKey"> | |||
<div class="control-group" style="float:right;margin-right: 10px;margin-bottom: -10px;"> | |||
<selectize id="gsInput" class="selectize-input-200" config="macsInputConfig" options="macsInputOptions" ng-model="macInputModel" | |||
placeholder="机器"></selectize> | |||
</div> | |||
</div> | |||
<!--.tools-header --> | |||
<div class="card-body" style="padding: 0px 0px;"> | |||
<table class="table" style="border-left: none; border-right:none;margin-top: 10px;"> | |||
<thead> | |||
<tr style="background: #F3F5F7;"> | |||
<td style="width: 40%"> | |||
资源名 | |||
</td> | |||
<td style="width: 10%;"> | |||
流控应用 | |||
</td> | |||
<td style="width: 10%;"> | |||
流控模式 | |||
</td> | |||
<td style="width: 10%;"> | |||
阈值类型 | |||
</td> | |||
<td style="width: 10%;"> | |||
单机阈值 | |||
</td> | |||
<td style="width: 10%;"> | |||
流控效果 | |||
</td> | |||
<!--<td style="width: 8%;">--> | |||
<!--状态--> | |||
<!--</td>--> | |||
<td style="width: 12%;"> | |||
操作 | |||
</td> | |||
</tr> | |||
</thead> | |||
<tbody> | |||
<tr dir-paginate="rule in rules | filter: searchKey | itemsPerPage: rulesPageConfig.pageSize " current-page="rulesPageConfig.currentPageIndex" | |||
pagination-id="entriesPagination"> | |||
<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.strategy == 0">直接</span> | |||
<span ng-if="rule.strategy == 1">关联</span> | |||
<span ng-if="rule.strategy == 2">链路</span> | |||
</td> | |||
<td> | |||
{{rule.grade==0 ? '线程数' : 'QPS'}} | |||
</td> | |||
<td style="word-wrap:break-word;word-break:break-all;"> | |||
{{rule.count}} | |||
</td> | |||
<td> | |||
<span ng-if="rule.controlBehavior == 0">快速失败</span> | |||
<span ng-if="rule.controlBehavior == 1">Warm Up</span> | |||
<span ng-if="rule.controlBehavior == 2">排队等待</span> | |||
</td> | |||
<td> | |||
<button class="btn btn-xs btn-default" type="button" ng-click="editRule(rule)" style="font-size: 12px; height:25px;">编辑</button> | |||
<button class="btn btn-xs btn-default" type="button" ng-click="deleteRule(rule)" style="font-size: 12px; height:25px;">删除</button> | |||
</td> | |||
</tr> | |||
</tbody> | |||
</table> | |||
</div> | |||
<!-- .card-body --> | |||
<div class="pagination-footer"> | |||
<dir-pagination-controls boundary-links="true" template-url="app/views/pagination.tpl.html" pagination-id="entriesPagination" | |||
on-page-change=""> | |||
</dir-pagination-controls> | |||
<div class="tools" style=""> | |||
<span>共 {{rulesPageConfig.totalCount}} 条记录, </span> | |||
<span> | |||
每页 | |||
<input class="form-control" ng-model="rulesPageConfig.pageSize"> 条记录 | |||
</span> | |||
<!--<span>第 {{rulesPageConfig.currentPageIndex}} / {{rulesPageConfig.totalPage}} 页</span>--> | |||
</div> | |||
<!-- .tools --> | |||
</div> | |||
<!-- pagination-footer --> | |||
</div> | |||
<!-- .card --> | |||
</div> | |||
<!-- .col-md-12 --> | |||
</div> | |||
<!-- --> | |||
</div> | |||
<!-- .container-fluid --> |
@@ -44,7 +44,8 @@ const JS_APP = [ | |||
'app/scripts/app.js', | |||
'app/scripts/filters/filters.js', | |||
'app/scripts/services/appservice.js', | |||
'app/scripts/services/flowservice.js', | |||
'app/scripts/services/flow_service_v1.js', | |||
'app/scripts/services/flow_service_v2.js', | |||
'app/scripts/services/degradeservice.js', | |||
'app/scripts/services/systemservice.js', | |||
'app/scripts/services/machineservice.js', | |||
@@ -52,6 +53,7 @@ const JS_APP = [ | |||
'app/scripts/services/metricservice.js', | |||
'app/scripts/services/param_flow_service.js', | |||
'app/scripts/services/authority_service.js', | |||
'app/scripts/services/cluster_state_service.js', | |||
]; | |||
gulp.task('lib', function () { | |||