Add rule configuration support for parameter flow control in Sentinel Dashboard (#176)
- Update dashboard API client to support authority and parameter flow commands - Add REST API controller for parameter flow rules in dashboard - Add pages for configuring parameter flow rules - Update dependencies: upgrade Spring Boot to 2.x and remove unused dependencies - Other refinements Signed-off-by: Eric Zhao <sczyh16@gmail.com>
This commit is contained in:
parent
9ae079c152
commit
b06bb02b2c
|
|
@ -13,8 +13,9 @@
|
|||
<packaging>jar</packaging>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.target>1.8</maven.compiler.target>
|
||||
<maven.compiler.source>1.8</maven.compiler.source>
|
||||
<maven.compiler.target>1.8</maven.compiler.target>
|
||||
<spring.boot.version>2.0.5.RELEASE</spring.boot.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
|
|
@ -35,39 +36,30 @@
|
|||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.alibaba.csp</groupId>
|
||||
<artifactId>sentinel-transport-common</artifactId>
|
||||
<artifactId>sentinel-parameter-flow-control</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-thymeleaf</artifactId>
|
||||
<version>1.5.9.RELEASE</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
<version>1.5.9.RELEASE</version>
|
||||
<version>${spring.boot.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-logging</artifactId>
|
||||
<version>1.5.9.RELEASE</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-tomcat</artifactId>
|
||||
<version>1.5.9.RELEASE</version>
|
||||
<version>${spring.boot.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-devtools</artifactId>
|
||||
<version>1.5.9.RELEASE</version>
|
||||
<version>${spring.boot.version}</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<version>1.5.9.RELEASE</version>
|
||||
<version>${spring.boot.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
|
|
@ -117,7 +109,7 @@
|
|||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
<configuration>
|
||||
<fork>true</fork>
|
||||
<mainClass>com.taobao.csp.sentinel.dashboard.Application</mainClass>
|
||||
<mainClass>com.taobao.csp.sentinel.dashboard.DashboardApplication</mainClass>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
|
|
@ -132,8 +124,8 @@
|
|||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<configuration>
|
||||
<source>1.8</source>
|
||||
<target>1.8</target>
|
||||
<source>${maven.compiler.source}</source>
|
||||
<target>${maven.compiler.target}</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
|
|
|
|||
|
|
@ -17,19 +17,11 @@ package com.taobao.csp.sentinel.dashboard;
|
|||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.boot.builder.SpringApplicationBuilder;
|
||||
import org.springframework.boot.web.support.SpringBootServletInitializer;
|
||||
|
||||
@SpringBootApplication
|
||||
public class Application extends SpringBootServletInitializer {
|
||||
|
||||
@Override
|
||||
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
|
||||
return application.sources(Application.class);
|
||||
}
|
||||
public class DashboardApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(Application.class, args);
|
||||
SpringApplication.run(DashboardApplication.class, args);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
/*
|
||||
* 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.taobao.csp.sentinel.dashboard.client;
|
||||
|
||||
/**
|
||||
* @author Eric Zhao
|
||||
* @since 0.2.1
|
||||
*/
|
||||
public class CommandNotFoundException extends Exception {
|
||||
public CommandNotFoundException() { }
|
||||
|
||||
public CommandNotFoundException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
|
|
@ -16,9 +16,13 @@
|
|||
package com.taobao.csp.sentinel.dashboard.client;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
|
@ -29,13 +33,19 @@ import com.alibaba.csp.sentinel.command.vo.NodeVo;
|
|||
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRule;
|
||||
import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;
|
||||
import com.alibaba.csp.sentinel.slots.system.SystemRule;
|
||||
import com.alibaba.csp.sentinel.util.AssertUtil;
|
||||
import com.alibaba.csp.sentinel.util.StringUtil;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
|
||||
import com.taobao.csp.sentinel.dashboard.datasource.entity.DegradeRuleEntity;
|
||||
import com.taobao.csp.sentinel.dashboard.datasource.entity.FlowRuleEntity;
|
||||
import com.taobao.csp.sentinel.dashboard.datasource.entity.SystemRuleEntity;
|
||||
import com.taobao.csp.sentinel.dashboard.datasource.entity.rule.AuthorityRuleEntity;
|
||||
import com.taobao.csp.sentinel.dashboard.datasource.entity.rule.DegradeRuleEntity;
|
||||
import com.taobao.csp.sentinel.dashboard.datasource.entity.rule.FlowRuleEntity;
|
||||
import com.taobao.csp.sentinel.dashboard.datasource.entity.rule.ParamFlowRuleEntity;
|
||||
import com.taobao.csp.sentinel.dashboard.datasource.entity.rule.SystemRuleEntity;
|
||||
import com.taobao.csp.sentinel.dashboard.util.RuleUtils;
|
||||
import org.apache.http.HttpResponse;
|
||||
import org.apache.http.client.methods.HttpGet;
|
||||
import org.apache.http.client.utils.URIBuilder;
|
||||
import org.apache.http.concurrent.FutureCallback;
|
||||
import org.apache.http.entity.ContentType;
|
||||
import org.apache.http.impl.client.DefaultRedirectStrategy;
|
||||
|
|
@ -56,17 +66,24 @@ import org.springframework.stereotype.Component;
|
|||
public class SentinelApiClient {
|
||||
|
||||
private static Logger logger = LoggerFactory.getLogger(SentinelApiClient.class);
|
||||
private static final Charset defaultCharset = Charset.forName(SentinelConfig.charset());
|
||||
|
||||
private static final Charset DEFAULT_CHARSET = Charset.forName(SentinelConfig.charset());
|
||||
|
||||
private static final String RESOURCE_URL_PATH = "jsonTree";
|
||||
private static final String CLUSTER_NODE_PATH = "clusterNode";
|
||||
private static final String GET_RULES_PATH = "getRules";
|
||||
private static final String SET_RULES_PATH = "setRules";
|
||||
private static final String GET_PARAM_RULE_PATH = "getParamFlowRules";
|
||||
private static final String SET_PARAM_RULE_PATH = "setParamFlowRules";
|
||||
|
||||
private static final String FLOW_RULE_TYPE = "flow";
|
||||
private static final String DEGRADE_RULE_TYPE = "degrade";
|
||||
private static final String SYSTEM_RULE_TYPE = "system";
|
||||
private static final String AUTHORITY_TYPE = "authority";
|
||||
|
||||
private CloseableHttpAsyncClient httpClient;
|
||||
|
||||
private final String resourceUrlPath = "jsonTree";
|
||||
private final String clusterNodePath = "clusterNode";
|
||||
private final String getRulesPath = "getRules";
|
||||
private final String setRulesPath = "setRules";
|
||||
private final String flowRuleType = "flow";
|
||||
private final String degradeRuleType = "degrade";
|
||||
private final String systemRuleType = "system";
|
||||
private final boolean enableHttps = false;
|
||||
|
||||
public SentinelApiClient() {
|
||||
IOReactorConfig ioConfig = IOReactorConfig.custom().setConnectTimeout(3000).setSoTimeout(3000)
|
||||
|
|
@ -81,7 +98,7 @@ public class SentinelApiClient {
|
|||
}
|
||||
|
||||
public List<NodeVo> fetchResourceOfMachine(String ip, int port, String type) {
|
||||
String url = "http://" + ip + ":" + port + "/" + resourceUrlPath + "?type=" + type;
|
||||
String url = "http://" + ip + ":" + port + "/" + RESOURCE_URL_PATH + "?type=" + type;
|
||||
String body = httpGetContent(url);
|
||||
if (body == null) {
|
||||
return null;
|
||||
|
|
@ -107,7 +124,7 @@ public class SentinelApiClient {
|
|||
if (includeZero) {
|
||||
type = "zero";
|
||||
}
|
||||
String url = "http://" + ip + ":" + port + "/" + clusterNodePath + "?type=" + type;
|
||||
String url = "http://" + ip + ":" + port + "/" + CLUSTER_NODE_PATH + "?type=" + type;
|
||||
String body = httpGetContent(url);
|
||||
if (body == null) {
|
||||
return null;
|
||||
|
|
@ -121,10 +138,10 @@ public class SentinelApiClient {
|
|||
}
|
||||
|
||||
public List<FlowRuleEntity> fetchFlowRuleOfMachine(String app, String ip, int port) {
|
||||
String url = "http://" + ip + ":" + port + "/" + getRulesPath + "?type=" + flowRuleType;
|
||||
String url = "http://" + ip + ":" + port + "/" + GET_RULES_PATH + "?type=" + FLOW_RULE_TYPE;
|
||||
String body = httpGetContent(url);
|
||||
logger.info("FlowRule Body:{}", body);
|
||||
List<FlowRule> rules = parseFlowRule(body);
|
||||
List<FlowRule> rules = RuleUtils.parseFlowRule(body);
|
||||
if (rules != null) {
|
||||
return rules.stream().map(rule -> FlowRuleEntity.fromFlowRule(app, ip, port, rule))
|
||||
.collect(Collectors.toList());
|
||||
|
|
@ -134,10 +151,10 @@ public class SentinelApiClient {
|
|||
}
|
||||
|
||||
public List<DegradeRuleEntity> fetchDegradeRuleOfMachine(String app, String ip, int port) {
|
||||
String url = "http://" + ip + ":" + port + "/" + getRulesPath + "?type=" + degradeRuleType;
|
||||
String url = "http://" + ip + ":" + port + "/" + GET_RULES_PATH + "?type=" + DEGRADE_RULE_TYPE;
|
||||
String body = httpGetContent(url);
|
||||
logger.info("Degrade Body:{}", body);
|
||||
List<DegradeRule> rules = parseDegradeRule(body);
|
||||
List<DegradeRule> rules = RuleUtils.parseDegradeRule(body);
|
||||
if (rules != null) {
|
||||
return rules.stream().map(rule -> DegradeRuleEntity.fromDegradeRule(app, ip, port, rule))
|
||||
.collect(Collectors.toList());
|
||||
|
|
@ -147,10 +164,10 @@ public class SentinelApiClient {
|
|||
}
|
||||
|
||||
public List<SystemRuleEntity> fetchSystemRuleOfMachine(String app, String ip, int port) {
|
||||
String url = "http://" + ip + ":" + port + "/" + getRulesPath + "?type=" + systemRuleType;
|
||||
String url = "http://" + ip + ":" + port + "/" + GET_RULES_PATH + "?type=" + SYSTEM_RULE_TYPE;
|
||||
String body = httpGetContent(url);
|
||||
logger.info("SystemRule Body:{}", body);
|
||||
List<SystemRule> rules = parseSystemRule(body);
|
||||
List<SystemRule> rules = RuleUtils.parseSystemRule(body);
|
||||
if (rules != null) {
|
||||
return rules.stream().map(rule -> SystemRuleEntity.fromSystemRule(app, ip, port, rule))
|
||||
.collect(Collectors.toList());
|
||||
|
|
@ -159,6 +176,68 @@ public class SentinelApiClient {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch all parameter flow rules from provided machine.
|
||||
*
|
||||
* @param app application name
|
||||
* @param ip machine client IP
|
||||
* @param port machine client port
|
||||
* @return all retrieved parameter flow rules
|
||||
* @since 0.2.1
|
||||
*/
|
||||
public CompletableFuture<List<ParamFlowRuleEntity>> fetchParamFlowRulesOfMachine(String app, String ip, int port) {
|
||||
try {
|
||||
AssertUtil.notEmpty(app, "Bad app name");
|
||||
AssertUtil.notEmpty(ip, "Bad machine IP");
|
||||
AssertUtil.isTrue(port > 0, "Bad machine port");
|
||||
URIBuilder uriBuilder = new URIBuilder();
|
||||
String commandName = GET_PARAM_RULE_PATH;
|
||||
uriBuilder.setScheme("http").setHost(ip).setPort(port)
|
||||
.setPath(commandName);
|
||||
return executeCommand(commandName, uriBuilder.build())
|
||||
.thenApply(RuleUtils::parseParamFlowRule)
|
||||
.thenApply(rules -> rules.stream()
|
||||
.map(e -> ParamFlowRuleEntity.fromAuthorityRule(app, ip, port, e))
|
||||
.collect(Collectors.toList())
|
||||
);
|
||||
} catch (Exception e) {
|
||||
logger.error("Error when fetching parameter flow rules", e);
|
||||
return newFailedFuture(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch all authority rules from provided machine.
|
||||
*
|
||||
* @param app application name
|
||||
* @param ip machine client IP
|
||||
* @param port machine client port
|
||||
* @return all retrieved authority rules
|
||||
* @since 0.2.1
|
||||
*/
|
||||
public List<AuthorityRuleEntity> fetchAuthorityRulesOfMachine(String app, String ip, int port) {
|
||||
AssertUtil.notEmpty(app, "Bad app name");
|
||||
AssertUtil.notEmpty(ip, "Bad machine IP");
|
||||
AssertUtil.isTrue(port > 0, "Bad machine port");
|
||||
URIBuilder uriBuilder = new URIBuilder();
|
||||
uriBuilder.setScheme("http").setHost(ip).setPort(port)
|
||||
.setPath(GET_RULES_PATH)
|
||||
.setParameter("type", AUTHORITY_TYPE);
|
||||
try {
|
||||
String body = httpGetContent(uriBuilder.build().toString());
|
||||
return Optional.ofNullable(body)
|
||||
.map(RuleUtils::parseAuthorityRule)
|
||||
.map(rules -> rules.stream()
|
||||
.map(e -> AuthorityRuleEntity.fromAuthorityRule(app, ip, port, e))
|
||||
.collect(Collectors.toList())
|
||||
)
|
||||
.orElse(null);
|
||||
} catch (URISyntaxException e) {
|
||||
logger.error("Error when fetching authority rules", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* set rules of the machine. rules == null will return immediately;
|
||||
* rules.isEmpty() means setting the rules to empty.
|
||||
|
|
@ -178,12 +257,12 @@ public class SentinelApiClient {
|
|||
}
|
||||
String data = JSON.toJSONString(rules.stream().map(FlowRuleEntity::toFlowRule).collect(Collectors.toList()));
|
||||
try {
|
||||
data = URLEncoder.encode(data, defaultCharset.name());
|
||||
data = URLEncoder.encode(data, DEFAULT_CHARSET.name());
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
logger.info("encode rule error", e);
|
||||
return false;
|
||||
}
|
||||
String url = "http://" + ip + ":" + port + "/" + setRulesPath + "?type=" + flowRuleType + "&data=" + data;
|
||||
String url = "http://" + ip + ":" + port + "/" + SET_RULES_PATH + "?type=" + FLOW_RULE_TYPE + "&data=" + data;
|
||||
String result = httpGetContent(url);
|
||||
logger.info("setFlowRule: " + result);
|
||||
return true;
|
||||
|
|
@ -209,12 +288,13 @@ public class SentinelApiClient {
|
|||
String data = JSON.toJSONString(
|
||||
rules.stream().map(DegradeRuleEntity::toDegradeRule).collect(Collectors.toList()));
|
||||
try {
|
||||
data = URLEncoder.encode(data, defaultCharset.name());
|
||||
data = URLEncoder.encode(data, DEFAULT_CHARSET.name());
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
logger.info("encode rule error", e);
|
||||
return false;
|
||||
}
|
||||
String url = "http://" + ip + ":" + port + "/" + setRulesPath + "?type=" + degradeRuleType + "&data=" + data;
|
||||
String url = "http://" + ip + ":" + port + "/" + SET_RULES_PATH + "?type=" + DEGRADE_RULE_TYPE + "&data="
|
||||
+ data;
|
||||
String result = httpGetContent(url);
|
||||
logger.info("setDegradeRule: " + result);
|
||||
return true;
|
||||
|
|
@ -240,42 +320,93 @@ public class SentinelApiClient {
|
|||
String data = JSON.toJSONString(
|
||||
rules.stream().map(SystemRuleEntity::toSystemRule).collect(Collectors.toList()));
|
||||
try {
|
||||
data = URLEncoder.encode(data, defaultCharset.name());
|
||||
data = URLEncoder.encode(data, DEFAULT_CHARSET.name());
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
logger.info("encode rule error", e);
|
||||
return false;
|
||||
}
|
||||
String url = "http://" + ip + ":" + port + "/" + setRulesPath + "?type=" + systemRuleType + "&data=" + data;
|
||||
String url = "http://" + ip + ":" + port + "/" + SET_RULES_PATH + "?type=" + SYSTEM_RULE_TYPE + "&data=" + data;
|
||||
String result = httpGetContent(url);
|
||||
logger.info("setSystemRule: " + result);
|
||||
return true;
|
||||
}
|
||||
|
||||
private List<FlowRule> parseFlowRule(String body) {
|
||||
public CompletableFuture<Void> setParamFlowRuleOfMachine(String app, String ip, int port, List<ParamFlowRuleEntity> rules) {
|
||||
if (rules == null) {
|
||||
return CompletableFuture.completedFuture(null);
|
||||
}
|
||||
if (StringUtil.isBlank(ip) || port <= 0) {
|
||||
return newFailedFuture(new IllegalArgumentException("Invalid parameter"));
|
||||
}
|
||||
try {
|
||||
return JSON.parseArray(body, FlowRule.class);
|
||||
} catch (Exception e) {
|
||||
logger.info("parser FlowRule error: ", e);
|
||||
return null;
|
||||
String data = JSON.toJSONString(
|
||||
rules.stream().map(ParamFlowRuleEntity::getRule).collect(Collectors.toList())
|
||||
);
|
||||
data = URLEncoder.encode(data, DEFAULT_CHARSET.name());
|
||||
URIBuilder uriBuilder = new URIBuilder();
|
||||
uriBuilder.setScheme("http").setHost(ip).setPort(port)
|
||||
.setPath(SET_PARAM_RULE_PATH)
|
||||
.setParameter("data", data);
|
||||
return executeCommand(SET_PARAM_RULE_PATH, uriBuilder.build())
|
||||
.thenCompose(e -> {
|
||||
if ("success".equals(e)) {
|
||||
return CompletableFuture.completedFuture(null);
|
||||
} else {
|
||||
logger.warn("Push parameter flow rules to client failed: " + e);
|
||||
return newFailedFuture(new RuntimeException(e));
|
||||
}
|
||||
});
|
||||
} catch (Exception ex) {
|
||||
logger.warn("Error when setting parameter flow rule", ex);
|
||||
return newFailedFuture(ex);
|
||||
}
|
||||
}
|
||||
|
||||
private List<DegradeRule> parseDegradeRule(String body) {
|
||||
try {
|
||||
return JSON.parseArray(body, DegradeRule.class);
|
||||
} catch (Exception e) {
|
||||
logger.info("parser DegradeRule error: ", e);
|
||||
return null;
|
||||
}
|
||||
private boolean isSuccess(int statusCode) {
|
||||
return statusCode >= 200 && statusCode < 300;
|
||||
}
|
||||
|
||||
private List<SystemRule> parseSystemRule(String body) {
|
||||
try {
|
||||
return JSON.parseArray(body, SystemRule.class);
|
||||
} catch (Exception e) {
|
||||
logger.info("parser SystemRule error: ", e);
|
||||
return null;
|
||||
private CompletableFuture<String> executeCommand(String command, URI uri) {
|
||||
CompletableFuture<String> future = new CompletableFuture<>();
|
||||
if (StringUtil.isBlank(command) || uri == null) {
|
||||
future.completeExceptionally(new IllegalArgumentException("Bad URL or command name"));
|
||||
return future;
|
||||
}
|
||||
final HttpGet httpGet = new HttpGet(uri);
|
||||
httpClient.execute(httpGet, new FutureCallback<HttpResponse>() {
|
||||
@Override
|
||||
public void completed(final HttpResponse response) {
|
||||
int statusCode = response.getStatusLine().getStatusCode();
|
||||
try {
|
||||
String value = getBody(response);
|
||||
if (isSuccess(statusCode)) {
|
||||
future.complete(value);
|
||||
} else {
|
||||
if (statusCode == 400) {
|
||||
future.completeExceptionally(new CommandNotFoundException(command));
|
||||
} else {
|
||||
future.completeExceptionally(new IllegalStateException(value));
|
||||
}
|
||||
}
|
||||
|
||||
} catch (Exception ex) {
|
||||
future.completeExceptionally(ex);
|
||||
logger.error("HTTP request failed: " + uri.toString(), ex);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void failed(final Exception ex) {
|
||||
future.completeExceptionally(ex);
|
||||
logger.error("HTTP request failed: " + uri.toString(), ex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cancelled() {
|
||||
future.complete(null);
|
||||
}
|
||||
});
|
||||
return future;
|
||||
}
|
||||
|
||||
private String httpGetContent(String url) {
|
||||
|
|
@ -321,10 +452,16 @@ public class SentinelApiClient {
|
|||
charset = contentType.getCharset();
|
||||
} catch (Exception ignore) {
|
||||
}
|
||||
return EntityUtils.toString(response.getEntity(), charset != null ? charset : defaultCharset);
|
||||
return EntityUtils.toString(response.getEntity(), charset != null ? charset : DEFAULT_CHARSET);
|
||||
}
|
||||
|
||||
public void close() throws Exception {
|
||||
httpClient.close();
|
||||
}
|
||||
|
||||
private <R> CompletableFuture<R> newFailedFuture(Throwable ex) {
|
||||
CompletableFuture<R> future = new CompletableFuture<>();
|
||||
future.completeExceptionally(ex);
|
||||
return future;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,114 @@
|
|||
/*
|
||||
* 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.taobao.csp.sentinel.dashboard.datasource.entity;
|
||||
|
||||
/**
|
||||
* @author Eric Zhao
|
||||
* @since 0.2.1
|
||||
*/
|
||||
public class SentinelVersion {
|
||||
|
||||
private int majorVersion;
|
||||
private int minorVersion;
|
||||
private int fixVersion;
|
||||
private String postfix;
|
||||
|
||||
public int getMajorVersion() {
|
||||
return majorVersion;
|
||||
}
|
||||
|
||||
public SentinelVersion setMajorVersion(int majorVersion) {
|
||||
this.majorVersion = majorVersion;
|
||||
return this;
|
||||
}
|
||||
|
||||
public int getMinorVersion() {
|
||||
return minorVersion;
|
||||
}
|
||||
|
||||
public SentinelVersion setMinorVersion(int minorVersion) {
|
||||
this.minorVersion = minorVersion;
|
||||
return this;
|
||||
}
|
||||
|
||||
public int getFixVersion() {
|
||||
return fixVersion;
|
||||
}
|
||||
|
||||
public SentinelVersion setFixVersion(int fixVersion) {
|
||||
this.fixVersion = fixVersion;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getPostfix() {
|
||||
return postfix;
|
||||
}
|
||||
|
||||
public SentinelVersion setPostfix(String postfix) {
|
||||
this.postfix = postfix;
|
||||
return this;
|
||||
}
|
||||
|
||||
public boolean greaterThan(SentinelVersion version) {
|
||||
if (version == null) {
|
||||
return true;
|
||||
}
|
||||
return this.majorVersion > version.majorVersion
|
||||
|| this.minorVersion > version.minorVersion
|
||||
|| this.fixVersion > version.fixVersion;
|
||||
}
|
||||
|
||||
public boolean greaterOrEqual(SentinelVersion version) {
|
||||
if (version == null) {
|
||||
return true;
|
||||
}
|
||||
return this.majorVersion >= version.majorVersion
|
||||
|| this.minorVersion >= version.minorVersion
|
||||
|| this.fixVersion >= version.fixVersion;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) { return true; }
|
||||
if (o == null || getClass() != o.getClass()) { return false; }
|
||||
|
||||
SentinelVersion that = (SentinelVersion)o;
|
||||
|
||||
if (majorVersion != that.majorVersion) { return false; }
|
||||
if (minorVersion != that.minorVersion) { return false; }
|
||||
if (fixVersion != that.fixVersion) { return false; }
|
||||
return postfix != null ? postfix.equals(that.postfix) : that.postfix == null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = majorVersion;
|
||||
result = 31 * result + minorVersion;
|
||||
result = 31 * result + fixVersion;
|
||||
result = 31 * result + (postfix != null ? postfix.hashCode() : 0);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "SentinelVersion{" +
|
||||
"majorVersion=" + majorVersion +
|
||||
", minorVersion=" + minorVersion +
|
||||
", fixVersion=" + fixVersion +
|
||||
", postfix='" + postfix + '\'' +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,106 @@
|
|||
/*
|
||||
* 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.taobao.csp.sentinel.dashboard.datasource.entity.rule;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
import com.alibaba.csp.sentinel.slots.block.AbstractRule;
|
||||
|
||||
/**
|
||||
* @author Eric Zhao
|
||||
* @since 0.2.1
|
||||
*/
|
||||
public abstract class AbstractRuleEntity<T extends AbstractRule> implements RuleEntity {
|
||||
|
||||
protected Long id;
|
||||
|
||||
protected String app;
|
||||
protected String ip;
|
||||
protected Integer port;
|
||||
|
||||
protected T rule;
|
||||
|
||||
private Date gmtCreate;
|
||||
private Date gmtModified;
|
||||
|
||||
@Override
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getApp() {
|
||||
return app;
|
||||
}
|
||||
|
||||
public AbstractRuleEntity<T> setApp(String app) {
|
||||
this.app = app;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getIp() {
|
||||
return ip;
|
||||
}
|
||||
|
||||
public AbstractRuleEntity<T> setIp(String ip) {
|
||||
this.ip = ip;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer getPort() {
|
||||
return port;
|
||||
}
|
||||
|
||||
public AbstractRuleEntity<T> setPort(Integer port) {
|
||||
this.port = port;
|
||||
return this;
|
||||
}
|
||||
|
||||
public T getRule() {
|
||||
return rule;
|
||||
}
|
||||
|
||||
public AbstractRuleEntity<T> setRule(T rule) {
|
||||
this.rule = rule;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Date getGmtCreate() {
|
||||
return gmtCreate;
|
||||
}
|
||||
|
||||
public AbstractRuleEntity<T> setGmtCreate(Date gmtCreate) {
|
||||
this.gmtCreate = gmtCreate;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Date getGmtModified() {
|
||||
return gmtModified;
|
||||
}
|
||||
|
||||
public AbstractRuleEntity<T> setGmtModified(Date gmtModified) {
|
||||
this.gmtModified = gmtModified;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
* 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.taobao.csp.sentinel.dashboard.datasource.entity.rule;
|
||||
|
||||
import com.alibaba.csp.sentinel.slots.block.authority.AuthorityRule;
|
||||
import com.alibaba.csp.sentinel.util.AssertUtil;
|
||||
|
||||
/**
|
||||
* @author Eric Zhao
|
||||
* @since 0.2.1
|
||||
*/
|
||||
public class AuthorityRuleEntity extends AbstractRuleEntity<AuthorityRule> {
|
||||
|
||||
public AuthorityRuleEntity(AuthorityRule authorityRule) {
|
||||
AssertUtil.notNull(authorityRule, "Authority rule should not be null");
|
||||
this.rule = authorityRule;
|
||||
}
|
||||
|
||||
public static AuthorityRuleEntity fromAuthorityRule(String app, String ip, Integer port, AuthorityRule rule) {
|
||||
AuthorityRuleEntity entity = new AuthorityRuleEntity(rule);
|
||||
entity.setApp(app);
|
||||
entity.setIp(ip);
|
||||
entity.setPort(port);
|
||||
return entity;
|
||||
}
|
||||
|
||||
public String getLimitApp() {
|
||||
return rule.getLimitApp();
|
||||
}
|
||||
|
||||
public String getResource() {
|
||||
return rule.getResource();
|
||||
}
|
||||
|
||||
public int getStrategy() {
|
||||
return rule.getStrategy();
|
||||
}
|
||||
}
|
||||
|
|
@ -13,7 +13,7 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.taobao.csp.sentinel.dashboard.datasource.entity;
|
||||
package com.taobao.csp.sentinel.dashboard.datasource.entity.rule;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
|
|
@ -13,7 +13,7 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.taobao.csp.sentinel.dashboard.datasource.entity;
|
||||
package com.taobao.csp.sentinel.dashboard.datasource.entity.rule;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
|
|
@ -0,0 +1,76 @@
|
|||
/*
|
||||
* 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.taobao.csp.sentinel.dashboard.datasource.entity.rule;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowItem;
|
||||
import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowRule;
|
||||
import com.alibaba.csp.sentinel.util.AssertUtil;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
|
||||
/**
|
||||
* @author Eric Zhao
|
||||
* @since 0.2.1
|
||||
*/
|
||||
public class ParamFlowRuleEntity extends AbstractRuleEntity<ParamFlowRule> {
|
||||
|
||||
public ParamFlowRuleEntity() {}
|
||||
|
||||
public ParamFlowRuleEntity(ParamFlowRule rule) {
|
||||
AssertUtil.notNull(rule, "Authority rule should not be null");
|
||||
this.rule = rule;
|
||||
}
|
||||
|
||||
public static ParamFlowRuleEntity fromAuthorityRule(String app, String ip, Integer port, ParamFlowRule rule) {
|
||||
ParamFlowRuleEntity entity = new ParamFlowRuleEntity(rule);
|
||||
entity.setApp(app);
|
||||
entity.setIp(ip);
|
||||
entity.setPort(port);
|
||||
return entity;
|
||||
}
|
||||
|
||||
@JsonIgnore
|
||||
public String getLimitApp() {
|
||||
return rule.getLimitApp();
|
||||
}
|
||||
|
||||
@JsonIgnore
|
||||
public String getResource() {
|
||||
return rule.getResource();
|
||||
}
|
||||
|
||||
@JsonIgnore
|
||||
public int getBlockGrade() {
|
||||
return rule.getBlockGrade();
|
||||
}
|
||||
|
||||
@JsonIgnore
|
||||
public Integer getParamIdx() {
|
||||
return rule.getParamIdx();
|
||||
}
|
||||
|
||||
@JsonIgnore
|
||||
public double getCount() {
|
||||
return rule.getCount();
|
||||
}
|
||||
|
||||
@JsonIgnore
|
||||
public List<ParamFlowItem> getParamFlowItemList() {
|
||||
return rule.getParamFlowItemList();
|
||||
}
|
||||
}
|
||||
|
|
@ -13,7 +13,7 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.taobao.csp.sentinel.dashboard.datasource.entity;
|
||||
package com.taobao.csp.sentinel.dashboard.datasource.entity.rule;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
|
|
@ -13,7 +13,7 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.taobao.csp.sentinel.dashboard.datasource.entity;
|
||||
package com.taobao.csp.sentinel.dashboard.datasource.entity.rule;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
|
|
@ -15,6 +15,7 @@
|
|||
*/
|
||||
package com.taobao.csp.sentinel.dashboard.discovery;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
|
||||
|
|
@ -53,4 +54,9 @@ public class AppInfo {
|
|||
return machines.add(machineInfo);
|
||||
}
|
||||
|
||||
public Optional<MachineInfo> getMachine(String ip, int port) {
|
||||
return machines.stream()
|
||||
.filter(e -> e.getIp().equals(ip) && e.getPort().equals(port))
|
||||
.findFirst();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ package com.taobao.csp.sentinel.dashboard.repository.rule;
|
|||
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
import com.taobao.csp.sentinel.dashboard.datasource.entity.DegradeRuleEntity;
|
||||
import com.taobao.csp.sentinel.dashboard.datasource.entity.rule.DegradeRuleEntity;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ package com.taobao.csp.sentinel.dashboard.repository.rule;
|
|||
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
import com.taobao.csp.sentinel.dashboard.datasource.entity.FlowRuleEntity;
|
||||
import com.taobao.csp.sentinel.dashboard.datasource.entity.rule.FlowRuleEntity;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
|
|
@ -27,6 +27,7 @@ import org.springframework.stereotype.Component;
|
|||
*/
|
||||
@Component
|
||||
public class InMemFlowRuleStore extends InMemoryRuleRepositoryAdapter<FlowRuleEntity> {
|
||||
|
||||
private static AtomicLong ids = new AtomicLong(0);
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
* 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.taobao.csp.sentinel.dashboard.repository.rule;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
import com.taobao.csp.sentinel.dashboard.datasource.entity.rule.ParamFlowRuleEntity;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* @author Eric Zhao
|
||||
* @since 0.2.1
|
||||
*/
|
||||
@Component
|
||||
public class InMemParamFlowRuleStore extends InMemoryRuleRepositoryAdapter<ParamFlowRuleEntity> {
|
||||
|
||||
private static AtomicLong ids = new AtomicLong(0);
|
||||
|
||||
@Override
|
||||
protected long nextId() {
|
||||
return ids.incrementAndGet();
|
||||
}
|
||||
}
|
||||
|
|
@ -17,7 +17,7 @@ package com.taobao.csp.sentinel.dashboard.repository.rule;
|
|||
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
import com.taobao.csp.sentinel.dashboard.datasource.entity.SystemRuleEntity;
|
||||
import com.taobao.csp.sentinel.dashboard.datasource.entity.rule.SystemRuleEntity;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ import java.util.Map;
|
|||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import com.taobao.csp.sentinel.dashboard.datasource.entity.RuleEntity;
|
||||
import com.taobao.csp.sentinel.dashboard.datasource.entity.rule.RuleEntity;
|
||||
import com.taobao.csp.sentinel.dashboard.discovery.MachineInfo;
|
||||
|
||||
/**
|
||||
|
|
@ -83,14 +83,13 @@ public abstract class InMemoryRuleRepositoryAdapter<T extends RuleEntity> implem
|
|||
if (entities == null) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
return entities.values().stream()
|
||||
.collect(Collectors.toList());
|
||||
return new ArrayList<>(entities.values());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get next unused id.
|
||||
*
|
||||
* @return
|
||||
* @return next unused id
|
||||
*/
|
||||
abstract protected long nextId();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,97 @@
|
|||
/*
|
||||
* 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.taobao.csp.sentinel.dashboard.util;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import com.alibaba.csp.sentinel.slots.block.authority.AuthorityRule;
|
||||
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRule;
|
||||
import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;
|
||||
import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowRule;
|
||||
import com.alibaba.csp.sentinel.slots.system.SystemRule;
|
||||
import com.alibaba.csp.sentinel.util.StringUtil;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* @author Eric Zhao
|
||||
* @since 0.2.1
|
||||
*/
|
||||
public final class RuleUtils {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(RuleUtils.class);
|
||||
|
||||
public static List<FlowRule> parseFlowRule(String body) {
|
||||
try {
|
||||
return JSON.parseArray(body, FlowRule.class);
|
||||
} catch (Exception e) {
|
||||
LOGGER.error("parser FlowRule error: ", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static List<DegradeRule> parseDegradeRule(String body) {
|
||||
try {
|
||||
return JSON.parseArray(body, DegradeRule.class);
|
||||
} catch (Exception e) {
|
||||
LOGGER.error("parser DegradeRule error: ", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static List<AuthorityRule> parseAuthorityRule(String body) {
|
||||
if (StringUtil.isBlank(body)) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return JSON.parseArray(body, AuthorityRule.class);
|
||||
} catch (Exception e) {
|
||||
LOGGER.error("Error when parsing authority rules", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse parameter flow rules.
|
||||
*
|
||||
* @param body raw string content
|
||||
* @return parsed rule list; null if error occurs or empty content
|
||||
*/
|
||||
public static List<ParamFlowRule> parseParamFlowRule(String body) {
|
||||
if (StringUtil.isBlank(body)) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return JSON.parseArray(body, ParamFlowRule.class);
|
||||
} catch (Exception e) {
|
||||
LOGGER.error("Error when parsing parameter flow rules", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static List<SystemRule> parseSystemRule(String body) {
|
||||
try {
|
||||
return JSON.parseArray(body, SystemRule.class);
|
||||
} catch (Exception e) {
|
||||
LOGGER.info("parser SystemRule error: ", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private RuleUtils() {}
|
||||
}
|
||||
|
|
@ -0,0 +1,70 @@
|
|||
/*
|
||||
* 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.taobao.csp.sentinel.dashboard.util;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import com.alibaba.csp.sentinel.util.StringUtil;
|
||||
|
||||
import com.taobao.csp.sentinel.dashboard.datasource.entity.SentinelVersion;
|
||||
|
||||
/**
|
||||
* Util class for parsing version.
|
||||
*
|
||||
* @author Eric Zhao
|
||||
* @since 0.2.1
|
||||
*/
|
||||
public final class VersionUtils {
|
||||
|
||||
/**
|
||||
* Parse version of Sentinel from raw string.
|
||||
*
|
||||
* @param s version string
|
||||
* @return parsed {@link SentinelVersion} if the version is valid; empty if
|
||||
* there is something wrong with the format
|
||||
*/
|
||||
public static Optional<SentinelVersion> parseVersion(String s) {
|
||||
if (StringUtil.isBlank(s)) {
|
||||
return Optional.empty();
|
||||
}
|
||||
try {
|
||||
SentinelVersion version = new SentinelVersion();
|
||||
String[] postArr = s.split("-");
|
||||
if (postArr.length > 1) {
|
||||
version.setPostfix(postArr[1]);
|
||||
}
|
||||
String[] arr = postArr[0].split("\\.");
|
||||
if (arr.length == 2) {
|
||||
version.setMajorVersion(Integer.valueOf(arr[0]))
|
||||
.setMinorVersion(Integer.valueOf(arr[1]))
|
||||
.setFixVersion(0);
|
||||
} else if (arr.length == 3) {
|
||||
version.setMajorVersion(Integer.valueOf(arr[0]))
|
||||
.setMinorVersion(Integer.valueOf(arr[1]))
|
||||
.setFixVersion(Integer.valueOf(arr[2]));
|
||||
} else {
|
||||
// Wrong format, return empty.
|
||||
return Optional.empty();
|
||||
}
|
||||
return Optional.of(version);
|
||||
} catch (Exception ex) {
|
||||
// Parse fail, return empty.
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
|
||||
private VersionUtils() {}
|
||||
}
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
* 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.taobao.csp.sentinel.dashboard.view;
|
||||
|
||||
import com.taobao.csp.sentinel.dashboard.client.SentinelApiClient;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/**
|
||||
* @author Eric Zhao
|
||||
* @since 0.2.1
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping(value = "/authority")
|
||||
public class AuthorityRuleController {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(AuthorityRuleController.class);
|
||||
|
||||
@Autowired
|
||||
private SentinelApiClient sentinelApiClient;
|
||||
}
|
||||
|
|
@ -20,7 +20,7 @@ import java.util.List;
|
|||
|
||||
import com.alibaba.csp.sentinel.util.StringUtil;
|
||||
|
||||
import com.taobao.csp.sentinel.dashboard.datasource.entity.DegradeRuleEntity;
|
||||
import com.taobao.csp.sentinel.dashboard.datasource.entity.rule.DegradeRuleEntity;
|
||||
import com.taobao.csp.sentinel.dashboard.discovery.MachineInfo;
|
||||
import com.taobao.csp.sentinel.dashboard.client.SentinelApiClient;
|
||||
import com.taobao.csp.sentinel.dashboard.repository.rule.InMemDegradeRuleStore;
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ import java.util.List;
|
|||
|
||||
import com.alibaba.csp.sentinel.util.StringUtil;
|
||||
|
||||
import com.taobao.csp.sentinel.dashboard.datasource.entity.FlowRuleEntity;
|
||||
import com.taobao.csp.sentinel.dashboard.datasource.entity.rule.FlowRuleEntity;
|
||||
import com.taobao.csp.sentinel.dashboard.discovery.MachineInfo;
|
||||
import com.taobao.csp.sentinel.dashboard.client.SentinelApiClient;
|
||||
import com.taobao.csp.sentinel.dashboard.repository.rule.InMemFlowRuleStore;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,246 @@
|
|||
/*
|
||||
* 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.taobao.csp.sentinel.dashboard.view;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
|
||||
import com.alibaba.csp.sentinel.slots.block.RuleConstant;
|
||||
import com.alibaba.csp.sentinel.util.StringUtil;
|
||||
|
||||
import com.taobao.csp.sentinel.dashboard.client.CommandNotFoundException;
|
||||
import com.taobao.csp.sentinel.dashboard.client.SentinelApiClient;
|
||||
import com.taobao.csp.sentinel.dashboard.datasource.entity.SentinelVersion;
|
||||
import com.taobao.csp.sentinel.dashboard.datasource.entity.rule.ParamFlowRuleEntity;
|
||||
import com.taobao.csp.sentinel.dashboard.discovery.AppManagement;
|
||||
import com.taobao.csp.sentinel.dashboard.discovery.MachineInfo;
|
||||
import com.taobao.csp.sentinel.dashboard.repository.rule.RuleRepository;
|
||||
import com.taobao.csp.sentinel.dashboard.util.VersionUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.PutMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/**
|
||||
* @author Eric Zhao
|
||||
* @since 0.2.1
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping(value = "/paramFlow")
|
||||
public class ParamFlowRuleController {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(ParamFlowRuleController.class);
|
||||
|
||||
@Autowired
|
||||
private SentinelApiClient sentinelApiClient;
|
||||
@Autowired
|
||||
private AppManagement appManagement;
|
||||
@Autowired
|
||||
private RuleRepository<ParamFlowRuleEntity, Long> repository;
|
||||
|
||||
private boolean checkIfSupported(String app, String ip, int port) {
|
||||
try {
|
||||
return Optional.ofNullable(appManagement.getDetailApp(app))
|
||||
.flatMap(e -> e.getMachine(ip, port))
|
||||
.flatMap(m -> VersionUtils.parseVersion(m.getVersion())
|
||||
.map(v -> v.greaterOrEqual(version020)))
|
||||
.orElse(true);
|
||||
// If error occurred or cannot retrieve machine info, return true.
|
||||
} catch (Exception ex) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@GetMapping("/rules")
|
||||
public Result<List<ParamFlowRuleEntity>> apiQueryAllRulesForMachine(@RequestParam String app,
|
||||
@RequestParam String ip,
|
||||
@RequestParam Integer port) {
|
||||
if (StringUtil.isEmpty(app)) {
|
||||
return Result.ofFail(-1, "app cannot be null or empty");
|
||||
}
|
||||
if (StringUtil.isEmpty(ip)) {
|
||||
return Result.ofFail(-1, "ip cannot be null or empty");
|
||||
}
|
||||
if (port == null || port <= 0) {
|
||||
return Result.ofFail(-1, "Invalid parameter: port");
|
||||
}
|
||||
if (!checkIfSupported(app, ip, port)) {
|
||||
return unsupportedVersion();
|
||||
}
|
||||
try {
|
||||
return sentinelApiClient.fetchParamFlowRulesOfMachine(app, ip, port)
|
||||
.thenApply(repository::saveAll)
|
||||
.thenApply(Result::ofSuccess)
|
||||
.get();
|
||||
} catch (ExecutionException ex) {
|
||||
logger.error("Error when querying parameter flow rules", ex.getCause());
|
||||
if (isNotSupported(ex.getCause())) {
|
||||
return unsupportedVersion();
|
||||
} else {
|
||||
return Result.ofThrowable(-1, ex.getCause());
|
||||
}
|
||||
} catch (Throwable throwable) {
|
||||
logger.error("Error when querying parameter flow rules", throwable);
|
||||
return Result.ofFail(-1, throwable.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isNotSupported(Throwable ex) {
|
||||
return ex instanceof CommandNotFoundException;
|
||||
}
|
||||
|
||||
@PostMapping("/rule")
|
||||
public Result<ParamFlowRuleEntity> apiAddParamFlowRule(@RequestBody ParamFlowRuleEntity entity) {
|
||||
Result<ParamFlowRuleEntity> checkResult = checkEntityInternal(entity);
|
||||
if (checkResult != null) {
|
||||
return checkResult;
|
||||
}
|
||||
if (!checkIfSupported(entity.getApp(), entity.getIp(), entity.getPort())) {
|
||||
return unsupportedVersion();
|
||||
}
|
||||
entity.setId(null);
|
||||
Date date = new Date();
|
||||
entity.setGmtCreate(date);
|
||||
entity.setGmtModified(date);
|
||||
try {
|
||||
entity = repository.save(entity);
|
||||
publishRules(entity.getApp(), entity.getIp(), entity.getPort()).get();
|
||||
return Result.ofSuccess(entity);
|
||||
} catch (ExecutionException ex) {
|
||||
logger.error("Error when adding new parameter flow rules", ex.getCause());
|
||||
if (isNotSupported(ex.getCause())) {
|
||||
return unsupportedVersion();
|
||||
} else {
|
||||
return Result.ofThrowable(-1, ex.getCause());
|
||||
}
|
||||
} catch (Throwable throwable) {
|
||||
logger.error("Error when adding new parameter flow rules", throwable);
|
||||
return Result.ofFail(-1, throwable.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private <R> Result<R> checkEntityInternal(ParamFlowRuleEntity entity) {
|
||||
if (entity == null) {
|
||||
return Result.ofFail(-1, "bad rule body");
|
||||
}
|
||||
if (StringUtil.isBlank(entity.getApp())) {
|
||||
return Result.ofFail(-1, "app can't be null or empty");
|
||||
}
|
||||
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, "port can't be null");
|
||||
}
|
||||
if (entity.getRule() == null) {
|
||||
return Result.ofFail(-1, "rule can't be null");
|
||||
}
|
||||
if (StringUtil.isBlank(entity.getResource())) {
|
||||
return Result.ofFail(-1, "resource name cannot be null or empty");
|
||||
}
|
||||
if (entity.getCount() < 0) {
|
||||
return Result.ofFail(-1, "count should be valid");
|
||||
}
|
||||
if (entity.getBlockGrade() != RuleConstant.FLOW_GRADE_QPS) {
|
||||
return Result.ofFail(-1, "Unknown mode (blockGrade) for parameter flow control");
|
||||
}
|
||||
if (entity.getParamIdx() == null || entity.getParamIdx() < 0) {
|
||||
return Result.ofFail(-1, "paramIdx should be valid");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@PutMapping("/rule/{id}")
|
||||
public Result<ParamFlowRuleEntity> apiUpdateParamFlowRule(@PathVariable("id") Long id, @RequestBody ParamFlowRuleEntity entity) {
|
||||
if (id == null || id <= 0) {
|
||||
return Result.ofFail(-1, "Invalid id");
|
||||
}
|
||||
Result<ParamFlowRuleEntity> checkResult = checkEntityInternal(entity);
|
||||
if (checkResult != null) {
|
||||
return checkResult;
|
||||
}
|
||||
if (!checkIfSupported(entity.getApp(), entity.getIp(), entity.getPort())) {
|
||||
return unsupportedVersion();
|
||||
}
|
||||
entity.setId(id);
|
||||
Date date = new Date();
|
||||
entity.setGmtCreate(null);
|
||||
entity.setGmtModified(date);
|
||||
try {
|
||||
entity = repository.save(entity);
|
||||
publishRules(entity.getApp(), entity.getIp(), entity.getPort()).get();
|
||||
return Result.ofSuccess(entity);
|
||||
} catch (ExecutionException ex) {
|
||||
logger.error("Error when updating parameter flow rules, id=" + id, ex.getCause());
|
||||
if (isNotSupported(ex.getCause())) {
|
||||
return unsupportedVersion();
|
||||
} else {
|
||||
return Result.ofThrowable(-1, ex.getCause());
|
||||
}
|
||||
} catch (Throwable throwable) {
|
||||
logger.error("Error when updating parameter flow rules, id=" + id, throwable);
|
||||
return Result.ofFail(-1, throwable.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@DeleteMapping("/rule/{id}")
|
||||
public Result<Long> apiDeleteRule(@PathVariable("id") Long id) {
|
||||
if (id == null) {
|
||||
return Result.ofFail(-1, "id cannot be null");
|
||||
}
|
||||
ParamFlowRuleEntity oldEntity = repository.findById(id);
|
||||
if (oldEntity == null) {
|
||||
return Result.ofSuccess(null);
|
||||
}
|
||||
try {
|
||||
repository.delete(id);
|
||||
publishRules(oldEntity.getApp(), oldEntity.getIp(), oldEntity.getPort()).get();
|
||||
return Result.ofSuccess(id);
|
||||
} catch (ExecutionException ex) {
|
||||
logger.error("Error when deleting parameter flow rules", ex.getCause());
|
||||
if (isNotSupported(ex.getCause())) {
|
||||
return unsupportedVersion();
|
||||
} else {
|
||||
return Result.ofThrowable(-1, ex.getCause());
|
||||
}
|
||||
} catch (Throwable throwable) {
|
||||
logger.error("Error when deleting parameter flow rules", throwable);
|
||||
return Result.ofFail(-1, throwable.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private CompletableFuture<Void> publishRules(String app, String ip, Integer port) {
|
||||
List<ParamFlowRuleEntity> rules = repository.findAllByMachine(MachineInfo.of(app, ip, port));
|
||||
return sentinelApiClient.setParamFlowRuleOfMachine(app, ip, port, rules);
|
||||
}
|
||||
|
||||
private <R> Result<R> unsupportedVersion() {
|
||||
return Result.ofFail(4041, "Sentinel client version not supported for parameter flow control");
|
||||
}
|
||||
|
||||
private final SentinelVersion version020 = new SentinelVersion().setMinorVersion(2);
|
||||
}
|
||||
|
|
@ -17,27 +17,31 @@ package com.taobao.csp.sentinel.dashboard.view;
|
|||
|
||||
/**
|
||||
* @author leyou
|
||||
* @author Eric Zhao
|
||||
*/
|
||||
public class Result<R> {
|
||||
|
||||
private boolean success;
|
||||
private int code;
|
||||
private String msg;
|
||||
private R data;
|
||||
|
||||
public static <R> Result<R> ofSuccess(R data) {
|
||||
Result<R> result = new Result<>();
|
||||
result.setMsg("success");
|
||||
result.setData(data);
|
||||
return result;
|
||||
return new Result<R>()
|
||||
.setSuccess(true)
|
||||
.setMsg("success")
|
||||
.setData(data);
|
||||
}
|
||||
|
||||
public static <R> Result<R> ofSuccessMsg(String msg) {
|
||||
Result<R> result = new Result<>();
|
||||
result.setMsg(msg);
|
||||
return result;
|
||||
return new Result<R>()
|
||||
.setSuccess(true)
|
||||
.setMsg(msg);
|
||||
}
|
||||
|
||||
public static <R> Result<R> ofFail(int code, String msg) {
|
||||
Result<R> result = new Result<>();
|
||||
result.setSuccess(false);
|
||||
result.setCode(code);
|
||||
result.setMsg(msg);
|
||||
return result;
|
||||
|
|
@ -45,32 +49,55 @@ public class Result<R> {
|
|||
|
||||
public static <R> Result<R> ofThrowable(int code, Throwable throwable) {
|
||||
Result<R> result = new Result<>();
|
||||
result.setSuccess(false);
|
||||
result.setCode(code);
|
||||
result.setMsg(throwable.getClass().getName() + ", " + throwable.getMessage());
|
||||
return result;
|
||||
}
|
||||
|
||||
public boolean isSuccess() {
|
||||
return success;
|
||||
}
|
||||
|
||||
public Result<R> setSuccess(boolean success) {
|
||||
this.success = success;
|
||||
return this;
|
||||
}
|
||||
|
||||
public int getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public void setCode(int code) {
|
||||
public Result<R> setCode(int code) {
|
||||
this.code = code;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getMsg() {
|
||||
return msg;
|
||||
}
|
||||
|
||||
public void setMsg(String msg) {
|
||||
public Result<R> setMsg(String msg) {
|
||||
this.msg = msg;
|
||||
return this;
|
||||
}
|
||||
|
||||
public R getData() {
|
||||
return data;
|
||||
}
|
||||
|
||||
public void setData(R data) {
|
||||
public Result<R> setData(R data) {
|
||||
this.data = data;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Result{" +
|
||||
"success=" + success +
|
||||
", code=" + code +
|
||||
", msg='" + msg + '\'' +
|
||||
", data=" + data +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ import java.util.List;
|
|||
|
||||
import com.alibaba.csp.sentinel.util.StringUtil;
|
||||
|
||||
import com.taobao.csp.sentinel.dashboard.datasource.entity.SystemRuleEntity;
|
||||
import com.taobao.csp.sentinel.dashboard.datasource.entity.rule.SystemRuleEntity;
|
||||
import com.taobao.csp.sentinel.dashboard.discovery.MachineInfo;
|
||||
import com.taobao.csp.sentinel.dashboard.client.SentinelApiClient;
|
||||
import com.taobao.csp.sentinel.dashboard.repository.rule.InMemSystemRuleStore;
|
||||
|
|
|
|||
|
|
@ -82,6 +82,22 @@ angular
|
|||
}
|
||||
})
|
||||
|
||||
.state('dashboard.paramFlow', {
|
||||
templateUrl: 'app/views/param_flow.html',
|
||||
url: '/paramFlow/:app',
|
||||
controller: 'ParamFlowController',
|
||||
resolve: {
|
||||
loadMyFiles: ['$ocLazyLoad', function ($ocLazyLoad) {
|
||||
return $ocLazyLoad.load({
|
||||
name: 'sentinelDashboardApp',
|
||||
files: [
|
||||
'app/scripts/controllers/param_flow.js',
|
||||
]
|
||||
});
|
||||
}]
|
||||
}
|
||||
})
|
||||
|
||||
.state('dashboard.degrade', {
|
||||
templateUrl: 'app/views/degrade.html',
|
||||
url: '/degrade/:app',
|
||||
|
|
|
|||
|
|
@ -0,0 +1,344 @@
|
|||
/**
|
||||
* Parameter flow control controller.
|
||||
*
|
||||
* @author Eric Zhao
|
||||
*/
|
||||
angular.module('sentinelDashboardApp').controller('ParamFlowController', ['$scope', '$stateParams', 'ParamFlowService', 'ngDialog',
|
||||
'MachineService',
|
||||
function ($scope, $stateParams, ParamFlowService, ngDialog,
|
||||
MachineService) {
|
||||
const UNSUPPORTED_CODE = 4041;
|
||||
$scope.app = $stateParams.app;
|
||||
$scope.curExItem = {};
|
||||
|
||||
$scope.paramItemClassTypeList = [
|
||||
'int', 'double', 'java.lang.String', 'long', 'float', 'char', 'byte'
|
||||
];
|
||||
|
||||
$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;
|
||||
}
|
||||
};
|
||||
|
||||
function updateSingleParamItem(arr, v, t, c) {
|
||||
for (let i = 0; i < arr.length; i++) {
|
||||
if (arr[i].object === v && arr[i].classType === t) {
|
||||
arr[i].count = c;
|
||||
return;
|
||||
}
|
||||
}
|
||||
arr.push({object: v, classType: t, count: c});
|
||||
}
|
||||
|
||||
function removeSingleParamItem(arr, v, t) {
|
||||
for (let i = 0; i < arr.length; i++) {
|
||||
if (arr[i].object === v && arr[i].classType === t) {
|
||||
arr.splice(i, 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function isNumberClass(classType) {
|
||||
return classType === 'int' || classType === 'double' ||
|
||||
classType === 'float' || classType === 'long' || classType === 'short';
|
||||
}
|
||||
|
||||
function isByteClass(classType) {
|
||||
return classType === 'byte';
|
||||
}
|
||||
|
||||
function notNumberAtLeastZero(num) {
|
||||
return num === undefined || num === '' || isNaN(num) || num < 0;
|
||||
}
|
||||
|
||||
function notGoodNumber(num) {
|
||||
return num === undefined || num === '' || isNaN(num);
|
||||
}
|
||||
|
||||
function notGoodNumberBetweenExclusive(num, l ,r) {
|
||||
return num === undefined || num === '' || isNaN(num) || num < l || num > r;
|
||||
}
|
||||
|
||||
$scope.notValidParamItem = (curExItem) => {
|
||||
if (isNumberClass(curExItem.classType) && notGoodNumber(curExItem.object)) {
|
||||
return true;
|
||||
}
|
||||
if (isByteClass(curExItem.classType) && notGoodNumberBetweenExclusive(curExItem.object, -128, 127)) {
|
||||
return true;
|
||||
}
|
||||
return curExItem.object === undefined || curExItem.classType === undefined ||
|
||||
notNumberAtLeastZero(curExItem.count);
|
||||
};
|
||||
|
||||
$scope.addParamItem = () => {
|
||||
updateSingleParamItem($scope.currentRule.rule.paramFlowItemList,
|
||||
$scope.curExItem.object, $scope.curExItem.classType, $scope.curExItem.count);
|
||||
let oldItem = $scope.curExItem;
|
||||
$scope.curExItem = {classType: oldItem.classType};
|
||||
};
|
||||
|
||||
$scope.removeParamItem = (v, t) => {
|
||||
removeSingleParamItem($scope.currentRule.rule.paramFlowItemList, v, t);
|
||||
};
|
||||
|
||||
function getMachineRules() {
|
||||
if (!$scope.macInputModel) {
|
||||
return;
|
||||
}
|
||||
let mac = $scope.macInputModel.split(':');
|
||||
ParamFlowService.queryMachineRules($scope.app, mac[0], mac[1])
|
||||
.success(function (data) {
|
||||
if (data.code == 0 && data.data) {
|
||||
$scope.loadError = undefined;
|
||||
$scope.rules = data.data;
|
||||
$scope.rulesPageConfig.totalCount = $scope.rules.length;
|
||||
} else {
|
||||
$scope.rules = [];
|
||||
$scope.rulesPageConfig.totalCount = 0;
|
||||
if (data.code === UNSUPPORTED_CODE) {
|
||||
$scope.loadError = {message: "机器 " + mac[0] + ":" + mac[1] + " 的 Sentinel 客户端版本不支持热点参数限流功能,请升级至 0.2.0 以上版本并引入 sentinel-parameter-flow-control 依赖。"}
|
||||
} else {
|
||||
$scope.loadError = {message: data.msg}
|
||||
}
|
||||
}
|
||||
})
|
||||
.error((data, header, config, status) => {
|
||||
$scope.loadError = {message: "未知错误"}
|
||||
});
|
||||
};
|
||||
$scope.getMachineRules = getMachineRules;
|
||||
getMachineRules();
|
||||
|
||||
var paramFlowRuleDialog;
|
||||
|
||||
$scope.editRule = function (rule) {
|
||||
$scope.currentRule = rule;
|
||||
$scope.paramFlowRuleDialog = {
|
||||
title: '编辑热点规则',
|
||||
type: 'edit',
|
||||
confirmBtnText: '保存',
|
||||
showAdvanceButton: rule.rule.paramFlowItemList === undefined || rule.rule.paramFlowItemList.length <= 0
|
||||
};
|
||||
paramFlowRuleDialog = ngDialog.open({
|
||||
template: '/app/views/dialog/param-flow-rule-dialog.html',
|
||||
width: 680,
|
||||
overlay: true,
|
||||
scope: $scope
|
||||
});
|
||||
$scope.curExItem = {};
|
||||
};
|
||||
|
||||
$scope.addNewRule = function () {
|
||||
var mac = $scope.macInputModel.split(':');
|
||||
$scope.currentRule = {
|
||||
app: $scope.app,
|
||||
ip: mac[0],
|
||||
port: mac[1],
|
||||
rule: {
|
||||
blockGrade: 1,
|
||||
paramFlowItemList: [],
|
||||
count: 0,
|
||||
limitApp: 'default',
|
||||
}
|
||||
};
|
||||
$scope.paramFlowRuleDialog = {
|
||||
title: '新增热点规则',
|
||||
type: 'add',
|
||||
confirmBtnText: '新增',
|
||||
showAdvanceButton: true,
|
||||
};
|
||||
paramFlowRuleDialog = ngDialog.open({
|
||||
template: '/app/views/dialog/param-flow-rule-dialog.html',
|
||||
width: 680,
|
||||
overlay: true,
|
||||
scope: $scope
|
||||
});
|
||||
$scope.curExItem = {};
|
||||
};
|
||||
|
||||
$scope.onOpenAdvanceClick = function () {
|
||||
$scope.paramFlowRuleDialog.showAdvanceButton = false;
|
||||
};
|
||||
$scope.onCloseAdvanceClick = function () {
|
||||
$scope.paramFlowRuleDialog.showAdvanceButton = true;
|
||||
};
|
||||
|
||||
function checkRuleValid(rule) {
|
||||
if (!rule.resource || rule.resource === '') {
|
||||
alert('资源名称不能为空');
|
||||
return false;
|
||||
}
|
||||
if (rule.blockGrade !== 1) {
|
||||
alert('未知的限流模式');
|
||||
return false;
|
||||
}
|
||||
if (rule.count < 0) {
|
||||
alert('限流阈值必须大于等于 0');
|
||||
return false;
|
||||
}
|
||||
if (rule.paramIdx === undefined || rule.paramIdx === '' || isNaN(rule.paramIdx) || rule.paramIdx < 0) {
|
||||
alert('热点参数索引必须大于等于 0');
|
||||
return false;
|
||||
}
|
||||
if (rule.paramFlowItemList !== undefined) {
|
||||
for (let i = 0; i < rule.paramFlowItemList.length; i++) {
|
||||
let item = rule.paramFlowItemList[i];
|
||||
if ($scope.notValidParamItem(item)) {
|
||||
alert('热点参数例外项不合法,请检查值和类型是否正确:参数为 ' + item.object + ', 类型为 ' +
|
||||
item.classType + ', 限流阈值为 ' + item.count);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
$scope.saveRule = function () {
|
||||
if (!checkRuleValid($scope.currentRule.rule)) {
|
||||
return;
|
||||
}
|
||||
if ($scope.paramFlowRuleDialog.type === 'add') {
|
||||
addNewRuleAndPush($scope.currentRule);
|
||||
} else if ($scope.paramFlowRuleDialog.type === 'edit') {
|
||||
saveRuleAndPush($scope.currentRule, true);
|
||||
}
|
||||
};
|
||||
|
||||
function addNewRuleAndPush(rule) {
|
||||
ParamFlowService.addNewRule(rule).success((data) => {
|
||||
if (data.success) {
|
||||
getMachineRules();
|
||||
paramFlowRuleDialog.close();
|
||||
} else {
|
||||
alert('添加规则失败:' + data.msg);
|
||||
}
|
||||
}).error((data) => {
|
||||
if (data) {
|
||||
alert('添加规则失败:' + data.msg);
|
||||
} else {
|
||||
alert("添加规则失败:未知错误");
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
function saveRuleAndPush(rule, edit) {
|
||||
ParamFlowService.saveRule(rule).success(function (data) {
|
||||
if (data.success) {
|
||||
alert("修改规则成功");
|
||||
getMachineRules();
|
||||
if (edit) {
|
||||
paramFlowRuleDialog.close();
|
||||
} else {
|
||||
confirmDialog.close();
|
||||
}
|
||||
} else {
|
||||
alert('修改规则失败:' + data.msg);
|
||||
}
|
||||
}).error((data) => {
|
||||
if (data) {
|
||||
alert('修改规则失败:' + data.msg);
|
||||
} else {
|
||||
alert("修改规则失败:未知错误");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function deleteRuleAndPush(entity) {
|
||||
if (entity.id === undefined || isNaN(entity.id)) {
|
||||
alert('规则 ID 不合法!');
|
||||
return;
|
||||
}
|
||||
ParamFlowService.deleteRule(entity).success((data) => {
|
||||
if (data.code == 0) {
|
||||
getMachineRules();
|
||||
confirmDialog.close();
|
||||
} else {
|
||||
alert('删除规则失败:' + data.msg);
|
||||
}
|
||||
}).error((data) => {
|
||||
if (data) {
|
||||
alert('删除规则失败:' + data.msg);
|
||||
} else {
|
||||
alert("删除规则失败:未知错误");
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
var confirmDialog;
|
||||
$scope.deleteRule = function (ruleEntity) {
|
||||
$scope.currentRule = ruleEntity;
|
||||
console.log('deleting: ' + ruleEntity);
|
||||
$scope.confirmDialog = {
|
||||
title: '删除热点规则',
|
||||
type: 'delete_rule',
|
||||
attentionTitle: '请确认是否删除如下热点参数限流规则',
|
||||
attention: '资源名: ' + ruleEntity.rule.resource + ', 热点参数索引: ' + ruleEntity.rule.paramIdx +
|
||||
', 限流模式: ' + (ruleEntity.rule.blockGrade === 1 ? 'QPS' : '未知') + ', 限流阈值: ' + ruleEntity.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') {
|
||||
deleteRuleAndPush($scope.currentRule);
|
||||
} else {
|
||||
console.error('error');
|
||||
}
|
||||
};
|
||||
|
||||
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();
|
||||
}
|
||||
});
|
||||
}]);
|
||||
|
|
@ -9,21 +9,15 @@
|
|||
</span>
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<!--<li ui-sref-active="active"><a ui-sref="dashboard.home"><i-->
|
||||
<!--style="color: #000;" class="fa fa-dashboard fa-fw"></i> <span-->
|
||||
<!--style="color: black; font-size:16px;">主控制板</span> </a></li>-->
|
||||
<li ui-sref-active="active">
|
||||
<a ui-sref="dashboard.home" style="font-size:16px;">
|
||||
<span class="glyphicon glyphicon-dashboard"></span>
|
||||
首页</a>
|
||||
</li>
|
||||
|
||||
<li ng-class="{active: true}" ng-repeat="entry in apps | filter: { app: searchApp}">{{dropDown}}
|
||||
<a href="" ng-click="click($event)" collapse="{{collpaseall == 1}}" style="font-size: 16px;">
|
||||
<!--<i class="glyphicon glyphicon-chevron-right"></i>-->
|
||||
<!--<span class="fa arrow"></span>-->
|
||||
{{entry.app}}
|
||||
<li ng-class="{active: true}" ng-repeat="entry in apps | filter: { app: searchApp }">{{dropDown}}
|
||||
<a href="#" ng-click="click($event)" collapse="{{collpaseall == 1}}" style="font-size: 16px;word-break: break-word;">
|
||||
{{entry.app}}
|
||||
<span class="fa arrow"></span>
|
||||
</a>
|
||||
|
||||
|
|
@ -46,6 +40,10 @@
|
|||
<a ui-sref="dashboard.degrade({app: entry.app})">
|
||||
<i class="glyphicon glyphicon-flash"></i> 降级规则</a>
|
||||
</li>
|
||||
<li ui-sref-active="active">
|
||||
<a ui-sref="dashboard.paramFlow({app: entry.app})">
|
||||
<i class="glyphicon glyphicon-fire"></i> 热点规则</a>
|
||||
</li>
|
||||
<li ui-sref-active="active">
|
||||
<a ui-sref="dashboard.system({app: entry.app})">
|
||||
<i class="glyphicon glyphicon-lock"></i> 系统规则</a>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,42 @@
|
|||
/**
|
||||
* Parameter flow control service.
|
||||
*
|
||||
* @author Eric Zhao
|
||||
*/
|
||||
angular.module('sentinelDashboardApp').service('ParamFlowService', ['$http', function ($http) {
|
||||
this.queryMachineRules = function(app, ip, port) {
|
||||
var param = {
|
||||
app: app,
|
||||
ip: ip,
|
||||
port: port
|
||||
};
|
||||
return $http({
|
||||
url: '/paramFlow/rules',
|
||||
params: param,
|
||||
method: 'GET'
|
||||
});
|
||||
};
|
||||
|
||||
this.addNewRule = function(rule) {
|
||||
return $http({
|
||||
url: '/paramFlow/rule',
|
||||
data: rule,
|
||||
method: 'POST'
|
||||
});
|
||||
};
|
||||
|
||||
this.saveRule = function (entity) {
|
||||
return $http({
|
||||
url: '/paramFlow/rule/' + entity.id,
|
||||
data: entity,
|
||||
method: 'PUT'
|
||||
});
|
||||
};
|
||||
|
||||
this.deleteRule = function (entity) {
|
||||
return $http({
|
||||
url: '/paramFlow/rule/' + entity.id,
|
||||
method: 'DELETE'
|
||||
});
|
||||
};
|
||||
}]);
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
<div>
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
<h1 class="page-header">欢迎使用Sentinel控制台</h1>
|
||||
<h1 class="page-header">欢迎使用 Sentinel 控制台</h1>
|
||||
</div>
|
||||
<!-- /.col-lg-12 -->
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,119 @@
|
|||
<div>
|
||||
<span class="brand" style="font-weight:bold;">{{paramFlowRuleDialog.title}}</span>
|
||||
<div class="card" style="margin-top: 20px;margin-bottom: 10px;">
|
||||
<div class="panel-body">
|
||||
<div class="clearfix">
|
||||
<form role="form" class="form-horizontal">
|
||||
<div class="form-group">
|
||||
<label class="col-sm-2 control-label">资源名</label>
|
||||
<div class="col-sm-9">
|
||||
<input type="text" ng-if="paramFlowRuleDialog.type == 'edit'" class="form-control" placeholder="资源名" ng-model='currentRule.rule.resource' disabled="" />
|
||||
<input type="text" ng-if="paramFlowRuleDialog.type == 'add'" class="form-control highlight-border" placeholder="资源名" ng-model='currentRule.rule.resource' required />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="col-sm-2 control-label">限流模式</label>
|
||||
<p class="col-sm-9 control-label" style="text-align: left; font-weight: normal;">QPS 模式</p>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="col-sm-2 control-label">参数索引</label>
|
||||
<div class="col-sm-9">
|
||||
<input type="number" class="form-control highlight-border" ng-model='currentRule.rule.paramIdx' placeholder='请填入传入的热点参数的索引(从 0 开始)' />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="col-sm-2 control-label">限流阈值</label>
|
||||
<div class="col-sm-9">
|
||||
<input type="number" class="form-control highlight-border" ng-model='currentRule.rule.count' placeholder='请填入限流阈值' />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- exclusion item part start -->
|
||||
<div ng-if="!paramFlowRuleDialog.showAdvanceButton">
|
||||
<hr />
|
||||
<div class="form-group">
|
||||
<div class="form-group" style="text-align: center">
|
||||
<label class="control-label">参数例外项</label>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="col-sm-2 control-label">参数类型</label>
|
||||
<div class="col-md-9">
|
||||
<select ng-model="curExItem.classType" ng-options="classType for classType in paramItemClassTypeList" class="form-control" placeholder="请选择参数类型">
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="col-sm-2 control-label">参数值</label>
|
||||
<div class="col-md-3">
|
||||
<input ng-model="curExItem.object" type="text" class="form-control" placeholder="例外项参数值">
|
||||
</div>
|
||||
|
||||
<label class="col-sm-2 control-label">限流阈值</label>
|
||||
<div class="col-md-3">
|
||||
<input type="number" ng-model="curExItem.count" class="form-control" placeholder="限流阈值">
|
||||
</div>
|
||||
|
||||
<div class="col-md-2">
|
||||
<button type="button" class="btn btn-success"
|
||||
ng-disabled="notValidParamItem(curExItem)"
|
||||
ng-click="addParamItem()">
|
||||
<span class="fa fa-plus"> 添加</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class="col-md-12">
|
||||
<table class="table table-condensed table-hover">
|
||||
<thead>
|
||||
<th>参数值</th>
|
||||
<th>参数类型</th>
|
||||
<th>限流阈值</th>
|
||||
<th>操作</th>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr ng-repeat="paramItem in currentRule.rule.paramFlowItemList">
|
||||
<td><input ng-model="paramItem.object" type="text" class="form-control" placeholder="例外项参数"></td>
|
||||
<td>
|
||||
<p>{{paramItem.classType}}</p>
|
||||
</td>
|
||||
<td>
|
||||
<input type="number" ng-model="paramItem.count" class="form-control" placeholder="限流阈值">
|
||||
</td>
|
||||
<td>
|
||||
<button type="button" class="btn btn-danger"
|
||||
ng-click="removeParamItem(paramItem.object, paramItem.classType)"><span
|
||||
class="fa fa-trash-o"> 删除</span></button>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- exclusion item part end -->
|
||||
<div class="form-group text-center">
|
||||
<a ng-click="onOpenAdvanceClick()" ng-if="paramFlowRuleDialog.showAdvanceButton">高级选项</a>
|
||||
<a ng-click="onCloseAdvanceClick()" ng-if="!paramFlowRuleDialog.showAdvanceButton">关闭高级选项</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="separator"></div>
|
||||
<div clss="row" style="margin-top: 20px;">
|
||||
<button class="btn btn-default-inverse" style="float:right; height: 30px;font-size: 12px;margin-left: 10px;" ng-click="closeThisDialog()">取消</button>
|
||||
<button class="btn btn-danger-inverse" style="float:right; height: 30px;font-size: 12px;margin-left: 10px;" ng-click="saveRule()">{{paramFlowRuleDialog.confirmBtnText}}</button>
|
||||
<button ng-if="paramFlowRuleDialog.saveAndContinueBtnText" class="btn btn-default" style="float:right; height: 30px;font-size: 12px;"
|
||||
ng-click="saveRuleAndContinue()">{{paramFlowRuleDialog.saveAndContinueBtnText}}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -0,0 +1,111 @@
|
|||
<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" ng-if="!loadError">
|
||||
<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>
|
||||
</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>
|
||||
|
||||
<!-- Table and pagination start -->
|
||||
<!--.tools-header -->
|
||||
<div class="card-body" style="padding: 0px 0px;" ng-if="!loadError">
|
||||
<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: 12%;">
|
||||
操作
|
||||
</td>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr dir-paginate="ruleEntity in rules | filter: searchKey | itemsPerPage: rulesPageConfig.pageSize " current-page="rulesPageConfig.currentPageIndex"
|
||||
pagination-id="entriesPagination">
|
||||
<td style="word-wrap:break-word;word-break:break-all;">{{ruleEntity.rule.resource}}</td>
|
||||
<td style="word-wrap:break-word;word-break:break-all;">{{ruleEntity.rule.paramIdx}}</td>
|
||||
<td>
|
||||
{{ruleEntity.rule.blockGrade == 1 ? 'QPS' : '未知'}}
|
||||
</td>
|
||||
<td style="word-wrap:break-word;word-break:break-all;">
|
||||
{{ruleEntity.rule.count}}
|
||||
</td>
|
||||
<td>
|
||||
{{ruleEntity.rule.paramFlowItemList == undefined ? 0 : ruleEntity.rule.paramFlowItemList.length}}
|
||||
</td>
|
||||
<td>
|
||||
<button class="btn btn-xs btn-default" type="button" ng-click="editRule(ruleEntity)" style="font-size: 12px; height:25px;">编辑</button>
|
||||
<button class="btn btn-xs btn-default" type="button" ng-click="deleteRule(ruleEntity)" style="font-size: 12px; height:25px;">删除</button>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<!-- .card-body -->
|
||||
<div class="pagination-footer" ng-if="!loadError">
|
||||
<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 -->
|
||||
<!-- Table and pagination end -->
|
||||
|
||||
</div>
|
||||
<!-- .card -->
|
||||
</div>
|
||||
<!-- .col-md-12 -->
|
||||
</div>
|
||||
<!-- -->
|
||||
</div>
|
||||
<!-- .container-fluid -->
|
||||
|
||||
2
sentinel-dashboard/src/main/webapp/resources/dist/js/app.js
vendored
Executable file → Normal file
2
sentinel-dashboard/src/main/webapp/resources/dist/js/app.js
vendored
Executable file → Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
|
@ -50,6 +50,7 @@ const JS_APP = [
|
|||
'app/scripts/services/machineservice.js',
|
||||
'app/scripts/services/identityservice.js',
|
||||
'app/scripts/services/metricservice.js',
|
||||
'app/scripts/services/param_flow_service.js',
|
||||
];
|
||||
|
||||
gulp.task('lib', function () {
|
||||
|
|
|
|||
|
|
@ -2220,7 +2220,7 @@
|
|||
},
|
||||
"gulp-uglify": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "http://registry.npm.alibaba-inc.com/gulp-uglify/download/gulp-uglify-3.0.1.tgz",
|
||||
"resolved": "http://registry.npm.taobao.org/gulp-uglify/download/gulp-uglify-3.0.1.tgz",
|
||||
"integrity": "sha1-jT7uRmUhvqaxD9dd/3Kt+LfqLZc=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
|
|
@ -3017,14 +3017,14 @@
|
|||
"dev": true
|
||||
},
|
||||
"make-error": {
|
||||
"version": "1.3.4",
|
||||
"resolved": "http://registry.npm.alibaba-inc.com/make-error/download/make-error-1.3.4.tgz",
|
||||
"integrity": "sha1-GZeO1XX56VRdL/jBPjO10Ypn1TU=",
|
||||
"version": "1.3.5",
|
||||
"resolved": "http://registry.npm.taobao.org/make-error/download/make-error-1.3.5.tgz",
|
||||
"integrity": "sha1-7+ToH22yjK3WBccPKcgxtY73dsg=",
|
||||
"dev": true
|
||||
},
|
||||
"make-error-cause": {
|
||||
"version": "1.2.2",
|
||||
"resolved": "http://registry.npm.alibaba-inc.com/make-error-cause/download/make-error-cause-1.2.2.tgz",
|
||||
"resolved": "http://registry.npm.taobao.org/make-error-cause/download/make-error-cause-1.2.2.tgz",
|
||||
"integrity": "sha1-3wOI/NCzeBbf8KX7gQiTl3fcvJ0=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
|
|
@ -4659,24 +4659,24 @@
|
|||
}
|
||||
},
|
||||
"uglify-js": {
|
||||
"version": "3.4.5",
|
||||
"resolved": "http://registry.npm.alibaba-inc.com/uglify-js/download/uglify-js-3.4.5.tgz",
|
||||
"integrity": "sha1-ZQiJwHZs8Pb9U0bOoJzSEvVEvmk=",
|
||||
"version": "3.4.9",
|
||||
"resolved": "http://registry.npm.taobao.org/uglify-js/download/uglify-js-3.4.9.tgz",
|
||||
"integrity": "sha1-rwLxgMEgfXZDLkc+0koo9KeCuuM=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"commander": "~2.16.0",
|
||||
"commander": "~2.17.1",
|
||||
"source-map": "~0.6.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"commander": {
|
||||
"version": "2.16.0",
|
||||
"resolved": "http://registry.npm.alibaba-inc.com/commander/download/commander-2.16.0.tgz",
|
||||
"integrity": "sha1-8WOQWTmWzrTz7rAgsx14Uo9/ilA=",
|
||||
"version": "2.17.1",
|
||||
"resolved": "http://registry.npm.taobao.org/commander/download/commander-2.17.1.tgz",
|
||||
"integrity": "sha1-vXerfebelCBc6sxy8XFtKfIKd78=",
|
||||
"dev": true
|
||||
},
|
||||
"source-map": {
|
||||
"version": "0.6.1",
|
||||
"resolved": "http://registry.npm.alibaba-inc.com/source-map/download/source-map-0.6.1.tgz",
|
||||
"resolved": "http://registry.npm.taobao.org/source-map/download/source-map-0.6.1.tgz",
|
||||
"integrity": "sha1-dHIq8y6WFOnCh6jQu95IteLxomM=",
|
||||
"dev": true
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue