Signed-off-by: Eric Zhao <sczyh16@gmail.com>master
@@ -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. | ||||
@@ -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; | ||||
@@ -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); | |||||
} | } | ||||
} | } |
@@ -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); | |||||
} | } | ||||
}); | }); | ||||
} | } | ||||
@@ -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); | |||||
} | } | ||||
}); | }); | ||||
} | } | ||||