From a5d6773cbcc05a1769aac17c8c05dc8c200bc04d Mon Sep 17 00:00:00 2001 From: Eric Zhao Date: Mon, 23 Dec 2019 17:34:25 +0800 Subject: [PATCH] dashboard: Improve error handling in FlowController and update sample WebConfig Signed-off-by: Eric Zhao --- .../dashboard/client/SentinelApiClient.java | 39 ++++++++++++-- .../sentinel/dashboard/config/WebConfig.java | 11 +++- .../controller/FlowControllerV1.java | 53 +++++++++++-------- .../app/scripts/controllers/flow_v1.js | 10 ++-- .../app/scripts/controllers/identity.js | 14 ++--- 5 files changed, 88 insertions(+), 39 deletions(-) diff --git a/sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/client/SentinelApiClient.java b/sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/client/SentinelApiClient.java index 79e44818..008e67c1 100755 --- a/sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/client/SentinelApiClient.java +++ b/sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/client/SentinelApiClient.java @@ -365,17 +365,44 @@ public class SentinelApiClient { params.put("type", type); params.put("data", data); 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; - } 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; } catch (Exception e) { - logger.warn("setRules failed", e); + logger.error("setRules API failed, type={}", type, e); return false; } } + private CompletableFuture setRulesAsync(String app, String ip, int port, String type, List 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 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 fetchResourceOfMachine(String ip, int port, String type) { 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); } + public CompletableFuture setFlowRuleOfMachineAsync(String app, String ip, int port, List rules) { + return setRulesAsync(app, ip, port, FLOW_RULE_TYPE, rules); + } + /** * set rules of the machine. rules == null will return immediately; * rules.isEmpty() means setting the rules to empty. diff --git a/sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/config/WebConfig.java b/sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/config/WebConfig.java index f0183430..92e51e54 100755 --- a/sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/config/WebConfig.java +++ b/sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/config/WebConfig.java @@ -15,6 +15,10 @@ */ 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.callback.WebCallbackManager; import com.alibaba.csp.sentinel.dashboard.auth.AuthorizationInterceptor; @@ -75,6 +79,9 @@ public class WebConfig implements WebMvcConfigurer { registration.addUrlPatterns("/*"); registration.setName("sentinelFilter"); 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"); @@ -83,12 +90,14 @@ public class WebConfig implements WebMvcConfigurer { @PostConstruct public void doInit() { + Set suffixSet = new HashSet<>(Arrays.asList(".js", ".css", ".html", ".ico", ".txt", + ".woff", ".woff2")); // Example: register a UrlCleaner to exclude URLs of common static resources. WebCallbackManager.setUrlCleaner(url -> { if (StringUtil.isEmpty(url)) { return url; } - if (url.endsWith(".js") || url.endsWith(".css") || url.endsWith("html")) { + if (suffixSet.stream().anyMatch(url::endsWith)) { return null; } return url; diff --git a/sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/controller/FlowControllerV1.java b/sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/controller/FlowControllerV1.java index 3c3a7318..50c4e32f 100755 --- a/sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/controller/FlowControllerV1.java +++ b/sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/controller/FlowControllerV1.java @@ -17,6 +17,9 @@ package com.alibaba.csp.sentinel.dashboard.controller; import java.util.Date; 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.AuthService.PrivilegeType; @@ -145,19 +148,19 @@ public class FlowControllerV1 { entity.setResource(entity.getResource().trim()); try { 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") @AuthAction(PrivilegeType.WRITE_RULE) - public Result updateIfNotNull(Long id, String app, + public Result apiUpdateFlowRule(Long id, String app, String limitApp, String resource, Integer grade, Double count, Integer strategy, String refResource, Integer controlBehavior, Integer warmUpPeriodSec, @@ -222,21 +225,22 @@ public class FlowControllerV1 { try { entity = repository.save(entity); 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") @AuthAction(PrivilegeType.WRITE_RULE) - public Result delete(Long id) { + public Result apiDeleteFlowRule(Long id) { if (id == null) { return Result.ofFail(-1, "id can't be null"); @@ -251,14 +255,19 @@ public class FlowControllerV1 { } catch (Exception e) { 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 publishRules(String app, String ip, Integer port) { List rules = repository.findAllByMachine(MachineInfo.of(app, ip, port)); - return sentinelApiClient.setFlowRuleOfMachine(app, ip, port, rules); + return sentinelApiClient.setFlowRuleOfMachineAsync(app, ip, port, rules); } } diff --git a/sentinel-dashboard/src/main/webapp/resources/app/scripts/controllers/flow_v1.js b/sentinel-dashboard/src/main/webapp/resources/app/scripts/controllers/flow_v1.js index 8e4be2ab..3c644937 100755 --- a/sentinel-dashboard/src/main/webapp/resources/app/scripts/controllers/flow_v1.js +++ b/sentinel-dashboard/src/main/webapp/resources/app/scripts/controllers/flow_v1.js @@ -148,18 +148,18 @@ app.controller('FlowControllerV1', ['$scope', '$stateParams', 'FlowServiceV1', ' getMachineRules(); confirmDialog.close(); } else { - alert('失败!'); + alert('失败:' + data.msg); } }); }; function addNewRule(rule) { FlowService.newRule(rule).success(function (data) { - if (data.code == 0) { + if (data.code === 0) { getMachineRules(); flowRuleDialog.close(); } else { - alert('失败!'); + alert('失败:' + data.msg); } }); }; @@ -173,7 +173,7 @@ app.controller('FlowControllerV1', ['$scope', '$stateParams', 'FlowServiceV1', ' function saveRule(rule, edit) { FlowService.saveRule(rule).success(function (data) { - if (data.code == 0) { + if (data.code === 0) { getMachineRules(); if (edit) { flowRuleDialog.close(); @@ -181,7 +181,7 @@ app.controller('FlowControllerV1', ['$scope', '$stateParams', 'FlowServiceV1', ' confirmDialog.close(); } } else { - alert('失败!'); + alert('失败:' + data.msg); } }); } diff --git a/sentinel-dashboard/src/main/webapp/resources/app/scripts/controllers/identity.js b/sentinel-dashboard/src/main/webapp/resources/app/scripts/controllers/identity.js index 1deecc83..f8116be7 100755 --- a/sentinel-dashboard/src/main/webapp/resources/app/scripts/controllers/identity.js +++ b/sentinel-dashboard/src/main/webapp/resources/app/scripts/controllers/identity.js @@ -98,7 +98,7 @@ app.controller('IdentityCtl', ['$scope', '$stateParams', 'IdentityService', let url = '/dashboard/flow/' + $scope.app; $location.path(url); } else { - alert('失败!'); + alert('失败:' + data.msg); } }).error((data, header, config, status) => { alert('未知错误'); @@ -110,10 +110,10 @@ app.controller('IdentityCtl', ['$scope', '$stateParams', 'IdentityService', return; } FlowService.newRule(flowRuleDialogScope.currentRule).success(function (data) { - if (data.code == 0) { + if (data.code === 0) { flowRuleDialog.close(); } else { - alert('失败!'); + alert('失败:' + data.msg); } }); } @@ -159,12 +159,12 @@ app.controller('IdentityCtl', ['$scope', '$stateParams', 'IdentityService', return; } DegradeService.newRule(degradeRuleDialogScope.currentRule).success(function (data) { - if (data.code == 0) { + if (data.code === 0) { degradeRuleDialog.close(); var url = '/dashboard/degrade/' + $scope.app; $location.path(url); } else { - alert('失败!'); + alert('失败:' + data.msg); } }); } @@ -174,10 +174,10 @@ app.controller('IdentityCtl', ['$scope', '$stateParams', 'IdentityService', return; } DegradeService.newRule(degradeRuleDialogScope.currentRule).success(function (data) { - if (data.code == 0) { + if (data.code === 0) { degradeRuleDialog.close(); } else { - alert('失败!'); + alert('失败:' + data.msg); } }); }