Browse Source

dashboard: Improve error handling in FlowController and update sample WebConfig

Signed-off-by: Eric Zhao <sczyh16@gmail.com>
master
Eric Zhao 5 years ago
parent
commit
a5d6773cbc
5 changed files with 88 additions and 39 deletions
  1. +35
    -4
      sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/client/SentinelApiClient.java
  2. +10
    -1
      sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/config/WebConfig.java
  3. +31
    -22
      sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/controller/FlowControllerV1.java
  4. +5
    -5
      sentinel-dashboard/src/main/webapp/resources/app/scripts/controllers/flow_v1.js
  5. +7
    -7
      sentinel-dashboard/src/main/webapp/resources/app/scripts/controllers/identity.js

+ 35
- 4
sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/client/SentinelApiClient.java View File

@@ -365,17 +365,44 @@ public class SentinelApiClient {
params.put("type", type); params.put("type", type);
params.put("data", data); params.put("data", data);
String result = executeCommand(app, ip, port, SET_RULES_PATH, params, true).get(); String result = executeCommand(app, ip, port, SET_RULES_PATH, params, true).get();
logger.info("setRules: {}", result);
logger.info("setRules result: {}, type={}", result, type);
return true; return true;
} catch (InterruptedException | ExecutionException e) {
logger.warn("setRules api failed: {}", type, e);
} catch (InterruptedException e) {
logger.warn("setRules API failed: {}", type, e);
return false;
} catch (ExecutionException e) {
logger.warn("setRules API failed: {}", type, e.getCause());
return false; return false;
} catch (Exception e) { } catch (Exception e) {
logger.warn("setRules failed", e);
logger.error("setRules API failed, type={}", type, e);
return false; return false;
} }
} }


private CompletableFuture<Void> setRulesAsync(String app, String ip, int port, String type, List<? extends RuleEntity> entities) {
try {
AssertUtil.notNull(entities, "rules cannot be null");
AssertUtil.notEmpty(app, "Bad app name");
AssertUtil.notEmpty(ip, "Bad machine IP");
AssertUtil.isTrue(port > 0, "Bad machine port");
String data = JSON.toJSONString(
entities.stream().map(r -> r.toRule()).collect(Collectors.toList()));
Map<String, String> params = new HashMap<>(2);
params.put("type", type);
params.put("data", data);
return executeCommand(app, ip, port, SET_RULES_PATH, params, true)
.thenCompose(r -> {
if ("success".equalsIgnoreCase(r.trim())) {
return CompletableFuture.completedFuture(null);
}
return AsyncUtils.newFailedFuture(new CommandFailedException(r));
});
} catch (Exception e) {
logger.error("setRulesAsync API failed, type={}", type, e);
return AsyncUtils.newFailedFuture(e);
}
}

public List<NodeVo> fetchResourceOfMachine(String ip, int port, String type) { public List<NodeVo> fetchResourceOfMachine(String ip, int port, String type) {
return fetchItems(ip, port, RESOURCE_URL_PATH, type, NodeVo.class); return fetchItems(ip, port, RESOURCE_URL_PATH, type, NodeVo.class);
} }
@@ -487,6 +514,10 @@ public class SentinelApiClient {
return setRules(app, ip, port, FLOW_RULE_TYPE, rules); return setRules(app, ip, port, FLOW_RULE_TYPE, rules);
} }


public CompletableFuture<Void> setFlowRuleOfMachineAsync(String app, String ip, int port, List<FlowRuleEntity> rules) {
return setRulesAsync(app, ip, port, FLOW_RULE_TYPE, rules);
}

/** /**
* set rules of the machine. rules == null will return immediately; * set rules of the machine. rules == null will return immediately;
* rules.isEmpty() means setting the rules to empty. * rules.isEmpty() means setting the rules to empty.


+ 10
- 1
sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/config/WebConfig.java View File

@@ -15,6 +15,10 @@
*/ */
package com.alibaba.csp.sentinel.dashboard.config; package com.alibaba.csp.sentinel.dashboard.config;


import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;

import com.alibaba.csp.sentinel.adapter.servlet.CommonFilter; import com.alibaba.csp.sentinel.adapter.servlet.CommonFilter;
import com.alibaba.csp.sentinel.adapter.servlet.callback.WebCallbackManager; import com.alibaba.csp.sentinel.adapter.servlet.callback.WebCallbackManager;
import com.alibaba.csp.sentinel.dashboard.auth.AuthorizationInterceptor; import com.alibaba.csp.sentinel.dashboard.auth.AuthorizationInterceptor;
@@ -75,6 +79,9 @@ public class WebConfig implements WebMvcConfigurer {
registration.addUrlPatterns("/*"); registration.addUrlPatterns("/*");
registration.setName("sentinelFilter"); registration.setName("sentinelFilter");
registration.setOrder(1); registration.setOrder(1);
// If this is enabled, the entrance of all Web URL resources will be unified as a single context name.
// In most scenarios that's enough, and it could reduce the memory footprint.
registration.addInitParameter(CommonFilter.WEB_CONTEXT_UNIFY, "true");


logger.info("Sentinel servlet CommonFilter registered"); logger.info("Sentinel servlet CommonFilter registered");


@@ -83,12 +90,14 @@ public class WebConfig implements WebMvcConfigurer {


@PostConstruct @PostConstruct
public void doInit() { public void doInit() {
Set<String> suffixSet = new HashSet<>(Arrays.asList(".js", ".css", ".html", ".ico", ".txt",
".woff", ".woff2"));
// Example: register a UrlCleaner to exclude URLs of common static resources. // Example: register a UrlCleaner to exclude URLs of common static resources.
WebCallbackManager.setUrlCleaner(url -> { WebCallbackManager.setUrlCleaner(url -> {
if (StringUtil.isEmpty(url)) { if (StringUtil.isEmpty(url)) {
return url; return url;
} }
if (url.endsWith(".js") || url.endsWith(".css") || url.endsWith("html")) {
if (suffixSet.stream().anyMatch(url::endsWith)) {
return null; return null;
} }
return url; return url;


+ 31
- 22
sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/controller/FlowControllerV1.java View File

@@ -17,6 +17,9 @@ package com.alibaba.csp.sentinel.dashboard.controller;


import java.util.Date; import java.util.Date;
import java.util.List; import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;


import com.alibaba.csp.sentinel.dashboard.auth.AuthAction; import com.alibaba.csp.sentinel.dashboard.auth.AuthAction;
import com.alibaba.csp.sentinel.dashboard.auth.AuthService.PrivilegeType; import com.alibaba.csp.sentinel.dashboard.auth.AuthService.PrivilegeType;
@@ -145,19 +148,19 @@ public class FlowControllerV1 {
entity.setResource(entity.getResource().trim()); entity.setResource(entity.getResource().trim());
try { try {
entity = repository.save(entity); entity = repository.save(entity);
} catch (Throwable throwable) {
logger.error("Failed to add flow rule", throwable);
return Result.ofThrowable(-1, throwable);
}
if (!publishRules(entity.getApp(), entity.getIp(), entity.getPort())) {
logger.error("Publish flow rules failed after rule add");

publishRules(entity.getApp(), entity.getIp(), entity.getPort()).get(5000, TimeUnit.MILLISECONDS);
return Result.ofSuccess(entity);
} catch (Throwable t) {
Throwable e = t instanceof ExecutionException ? t.getCause() : t;
logger.error("Failed to add new flow rule, app={}, ip={}", entity.getApp(), entity.getIp(), e);
return Result.ofFail(-1, e.getMessage());
} }
return Result.ofSuccess(entity);
} }


@PutMapping("/save.json") @PutMapping("/save.json")
@AuthAction(PrivilegeType.WRITE_RULE) @AuthAction(PrivilegeType.WRITE_RULE)
public Result<FlowRuleEntity> updateIfNotNull(Long id, String app,
public Result<FlowRuleEntity> apiUpdateFlowRule(Long id, String app,
String limitApp, String resource, Integer grade, String limitApp, String resource, Integer grade,
Double count, Integer strategy, String refResource, Double count, Integer strategy, String refResource,
Integer controlBehavior, Integer warmUpPeriodSec, Integer controlBehavior, Integer warmUpPeriodSec,
@@ -222,21 +225,22 @@ public class FlowControllerV1 {
try { try {
entity = repository.save(entity); entity = repository.save(entity);
if (entity == null) { if (entity == null) {
return Result.ofFail(-1, "save entity fail");
return Result.ofFail(-1, "save entity fail: null");
} }
} catch (Throwable throwable) {
logger.error("save error:", throwable);
return Result.ofThrowable(-1, throwable);
}
if (!publishRules(entity.getApp(), entity.getIp(), entity.getPort())) {
logger.info("publish flow rules fail after rule update");

publishRules(entity.getApp(), entity.getIp(), entity.getPort()).get(5000, TimeUnit.MILLISECONDS);
return Result.ofSuccess(entity);
} catch (Throwable t) {
Throwable e = t instanceof ExecutionException ? t.getCause() : t;
logger.error("Error when updating flow rules, app={}, ip={}, ruleId={}", entity.getApp(),
entity.getIp(), id, e);
return Result.ofFail(-1, e.getMessage());
} }
return Result.ofSuccess(entity);
} }


@DeleteMapping("/delete.json") @DeleteMapping("/delete.json")
@AuthAction(PrivilegeType.WRITE_RULE) @AuthAction(PrivilegeType.WRITE_RULE)
public Result<Long> delete(Long id) {
public Result<Long> apiDeleteFlowRule(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");
@@ -251,14 +255,19 @@ public class FlowControllerV1 {
} catch (Exception e) { } catch (Exception e) {
return Result.ofFail(-1, e.getMessage()); return Result.ofFail(-1, e.getMessage());
} }
if (!publishRules(oldEntity.getApp(), oldEntity.getIp(), oldEntity.getPort())) {
logger.info("publish flow rules fail after rule delete");
try {
publishRules(oldEntity.getApp(), oldEntity.getIp(), oldEntity.getPort()).get(5000, TimeUnit.MILLISECONDS);
return Result.ofSuccess(id);
} catch (Throwable t) {
Throwable e = t instanceof ExecutionException ? t.getCause() : t;
logger.error("Error when deleting flow rules, app={}, ip={}, id={}", oldEntity.getApp(),
oldEntity.getIp(), id, e);
return Result.ofFail(-1, e.getMessage());
} }
return Result.ofSuccess(id);
} }


private boolean publishRules(String app, String ip, Integer port) {
private CompletableFuture<Void> publishRules(String app, String ip, Integer port) {
List<FlowRuleEntity> rules = repository.findAllByMachine(MachineInfo.of(app, ip, port)); List<FlowRuleEntity> rules = repository.findAllByMachine(MachineInfo.of(app, ip, port));
return sentinelApiClient.setFlowRuleOfMachine(app, ip, port, rules);
return sentinelApiClient.setFlowRuleOfMachineAsync(app, ip, port, rules);
} }
} }

+ 5
- 5
sentinel-dashboard/src/main/webapp/resources/app/scripts/controllers/flow_v1.js View File

@@ -148,18 +148,18 @@ app.controller('FlowControllerV1', ['$scope', '$stateParams', 'FlowServiceV1', '
getMachineRules(); getMachineRules();
confirmDialog.close(); confirmDialog.close();
} else { } else {
alert('失败!');
alert('失败:' + data.msg);
} }
}); });
}; };


function addNewRule(rule) { function addNewRule(rule) {
FlowService.newRule(rule).success(function (data) { FlowService.newRule(rule).success(function (data) {
if (data.code == 0) {
if (data.code === 0) {
getMachineRules(); getMachineRules();
flowRuleDialog.close(); flowRuleDialog.close();
} else { } else {
alert('失败!');
alert('失败:' + data.msg);
} }
}); });
}; };
@@ -173,7 +173,7 @@ app.controller('FlowControllerV1', ['$scope', '$stateParams', 'FlowServiceV1', '


function saveRule(rule, edit) { function saveRule(rule, edit) {
FlowService.saveRule(rule).success(function (data) { FlowService.saveRule(rule).success(function (data) {
if (data.code == 0) {
if (data.code === 0) {
getMachineRules(); getMachineRules();
if (edit) { if (edit) {
flowRuleDialog.close(); flowRuleDialog.close();
@@ -181,7 +181,7 @@ app.controller('FlowControllerV1', ['$scope', '$stateParams', 'FlowServiceV1', '
confirmDialog.close(); confirmDialog.close();
} }
} else { } else {
alert('失败!');
alert('失败:' + data.msg);
} }
}); });
} }


+ 7
- 7
sentinel-dashboard/src/main/webapp/resources/app/scripts/controllers/identity.js View File

@@ -98,7 +98,7 @@ app.controller('IdentityCtl', ['$scope', '$stateParams', 'IdentityService',
let url = '/dashboard/flow/' + $scope.app; let url = '/dashboard/flow/' + $scope.app;
$location.path(url); $location.path(url);
} else { } else {
alert('失败!');
alert('失败:' + data.msg);
} }
}).error((data, header, config, status) => { }).error((data, header, config, status) => {
alert('未知错误'); alert('未知错误');
@@ -110,10 +110,10 @@ app.controller('IdentityCtl', ['$scope', '$stateParams', 'IdentityService',
return; return;
} }
FlowService.newRule(flowRuleDialogScope.currentRule).success(function (data) { FlowService.newRule(flowRuleDialogScope.currentRule).success(function (data) {
if (data.code == 0) {
if (data.code === 0) {
flowRuleDialog.close(); flowRuleDialog.close();
} else { } else {
alert('失败!');
alert('失败:' + data.msg);
} }
}); });
} }
@@ -159,12 +159,12 @@ app.controller('IdentityCtl', ['$scope', '$stateParams', 'IdentityService',
return; return;
} }
DegradeService.newRule(degradeRuleDialogScope.currentRule).success(function (data) { DegradeService.newRule(degradeRuleDialogScope.currentRule).success(function (data) {
if (data.code == 0) {
if (data.code === 0) {
degradeRuleDialog.close(); degradeRuleDialog.close();
var url = '/dashboard/degrade/' + $scope.app; var url = '/dashboard/degrade/' + $scope.app;
$location.path(url); $location.path(url);
} else { } else {
alert('失败!');
alert('失败:' + data.msg);
} }
}); });
} }
@@ -174,10 +174,10 @@ app.controller('IdentityCtl', ['$scope', '$stateParams', 'IdentityService',
return; return;
} }
DegradeService.newRule(degradeRuleDialogScope.currentRule).success(function (data) { DegradeService.newRule(degradeRuleDialogScope.currentRule).success(function (data) {
if (data.code == 0) {
if (data.code === 0) {
degradeRuleDialog.close(); degradeRuleDialog.close();
} else { } else {
alert('失败!');
alert('失败:' + data.msg);
} }
}); });
} }


Loading…
Cancel
Save