diff --git a/sentinel-dashboard/pom.xml b/sentinel-dashboard/pom.xml index f91dd6b2..401b7b1f 100755 --- a/sentinel-dashboard/pom.xml +++ b/sentinel-dashboard/pom.xml @@ -40,6 +40,12 @@ ${project.version} + + com.alibaba.csp + sentinel-datasource-nacos + test + + org.springframework.boot spring-boot-starter-web diff --git a/sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/DashboardApplication.java b/sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/DashboardApplication.java index 5a921727..dc6d2697 100755 --- a/sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/DashboardApplication.java +++ b/sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/DashboardApplication.java @@ -18,6 +18,11 @@ package com.taobao.csp.sentinel.dashboard; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +/** + * Sentinel dashboard application. + * + * @author Carpenter Lee + */ @SpringBootApplication public class DashboardApplication { diff --git a/sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/client/SentinelApiClient.java b/sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/client/SentinelApiClient.java index ee5ec099..6e82e787 100755 --- a/sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/client/SentinelApiClient.java +++ b/sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/client/SentinelApiClient.java @@ -22,6 +22,7 @@ import java.net.URLEncoder; import java.nio.charset.Charset; import java.util.List; import java.util.Optional; +import java.util.Set; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; @@ -42,6 +43,11 @@ import com.taobao.csp.sentinel.dashboard.datasource.entity.rule.DegradeRuleEntit 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.domain.cluster.ClusterServerStateVO; +import com.taobao.csp.sentinel.dashboard.domain.cluster.ClusterStateSimpleEntity; +import com.taobao.csp.sentinel.dashboard.domain.cluster.config.ClusterClientConfig; +import com.taobao.csp.sentinel.dashboard.domain.cluster.config.ServerFlowConfig; +import com.taobao.csp.sentinel.dashboard.domain.cluster.config.ServerTransportConfig; import com.taobao.csp.sentinel.dashboard.util.RuleUtils; import org.apache.http.HttpResponse; import org.apache.http.client.methods.HttpGet; @@ -76,6 +82,18 @@ public class SentinelApiClient { private static final String GET_PARAM_RULE_PATH = "getParamFlowRules"; private static final String SET_PARAM_RULE_PATH = "setParamFlowRules"; + private static final String FETCH_CLUSTER_MODE_PATH = "getClusterMode"; + private static final String MODIFY_CLUSTER_MODE_PATH = "setClusterMode"; + private static final String FETCH_CLUSTER_CLIENT_CONFIG_PATH = "cluster/client/fetchConfig"; + private static final String MODIFY_CLUSTER_CLIENT_CONFIG_PATH = "cluster/client/modifyConfig"; + + private static final String FETCH_CLUSTER_SERVER_ALL_CONFIG_PATH = "cluster/server/fetchConfig"; + private static final String FETCH_CLUSTER_SERVER_BASIC_INFO_PATH = "cluster/server/info"; + + private static final String MODIFY_CLUSTER_SERVER_TRANSPORT_CONFIG_PATH = "cluster/server/modifyTransportConfig"; + private static final String MODIFY_CLUSTER_SERVER_FLOW_CONFIG_PATH = "cluster/server/modifyFlowConfig"; + private static final String MODIFY_CLUSTER_SERVER_NAMESPACE_SET_PATH = "cluster/server/modifyNamespaceSet"; + private static final String FLOW_RULE_TYPE = "flow"; private static final String DEGRADE_RULE_TYPE = "degrade"; private static final String SYSTEM_RULE_TYPE = "system"; @@ -485,4 +503,175 @@ public class SentinelApiClient { future.completeExceptionally(ex); return future; } + + // Cluster related + + public CompletableFuture fetchClusterMode(String app, String ip, int port) { + if (StringUtil.isBlank(ip) || port <= 0) { + return newFailedFuture(new IllegalArgumentException("Invalid parameter")); + } + try { + URIBuilder uriBuilder = new URIBuilder(); + uriBuilder.setScheme("http").setHost(ip).setPort(port) + .setPath(FETCH_CLUSTER_MODE_PATH); + return executeCommand(FETCH_CLUSTER_MODE_PATH, uriBuilder.build()) + .thenApply(r -> JSON.parseObject(r, ClusterStateSimpleEntity.class)); + } catch (Exception ex) { + logger.warn("Error when fetching cluster mode", ex); + return newFailedFuture(ex); + } + } + + public CompletableFuture modifyClusterMode(String app, String ip, int port, int mode) { + if (StringUtil.isBlank(ip) || port <= 0) { + return newFailedFuture(new IllegalArgumentException("Invalid parameter")); + } + try { + URIBuilder uriBuilder = new URIBuilder(); + uriBuilder.setScheme("http").setHost(ip).setPort(port) + .setPath(MODIFY_CLUSTER_MODE_PATH) + .setParameter("mode", String.valueOf(mode)); + return executeCommand(MODIFY_CLUSTER_MODE_PATH, uriBuilder.build()) + .thenCompose(e -> { + if ("success".equals(e)) { + return CompletableFuture.completedFuture(null); + } else { + logger.warn("Error when modifying cluster mode: " + e); + return newFailedFuture(new RuntimeException(e)); + } + }); + } catch (Exception ex) { + logger.warn("Error when modifying cluster mode", ex); + return newFailedFuture(ex); + } + } + + public CompletableFuture fetchClusterClientConfig(String app, String ip, int port) { + if (StringUtil.isBlank(ip) || port <= 0) { + return newFailedFuture(new IllegalArgumentException("Invalid parameter")); + } + try { + URIBuilder uriBuilder = new URIBuilder(); + uriBuilder.setScheme("http").setHost(ip).setPort(port) + .setPath(FETCH_CLUSTER_CLIENT_CONFIG_PATH); + return executeCommand(FETCH_CLUSTER_CLIENT_CONFIG_PATH, uriBuilder.build()) + .thenApply(r -> JSON.parseObject(r, ClusterClientConfig.class)); + } catch (Exception ex) { + logger.warn("Error when fetching cluster client config", ex); + return newFailedFuture(ex); + } + } + + public CompletableFuture modifyClusterClientConfig(String app, String ip, int port, ClusterClientConfig config) { + if (StringUtil.isBlank(ip) || port <= 0) { + return newFailedFuture(new IllegalArgumentException("Invalid parameter")); + } + try { + URIBuilder uriBuilder = new URIBuilder(); + uriBuilder.setScheme("http").setHost(ip).setPort(port) + .setPath(MODIFY_CLUSTER_CLIENT_CONFIG_PATH) + .setParameter("data", JSON.toJSONString(config)); + return executeCommand(MODIFY_CLUSTER_MODE_PATH, uriBuilder.build()) + .thenCompose(e -> { + if ("success".equals(e)) { + return CompletableFuture.completedFuture(null); + } else { + logger.warn("Error when modifying cluster client config: " + e); + return newFailedFuture(new RuntimeException(e)); + } + }); + } catch (Exception ex) { + logger.warn("Error when modifying cluster client config", ex); + return newFailedFuture(ex); + } + } + + public CompletableFuture modifyClusterServerFlowConfig(String app, String ip, int port, ServerFlowConfig config) { + if (StringUtil.isBlank(ip) || port <= 0) { + return newFailedFuture(new IllegalArgumentException("Invalid parameter")); + } + try { + URIBuilder uriBuilder = new URIBuilder(); + uriBuilder.setScheme("http").setHost(ip).setPort(port) + .setPath(MODIFY_CLUSTER_SERVER_FLOW_CONFIG_PATH) + .setParameter("data", JSON.toJSONString(config)); + return executeCommand(MODIFY_CLUSTER_SERVER_FLOW_CONFIG_PATH, uriBuilder.build()) + .thenCompose(e -> { + if ("success".equals(e)) { + return CompletableFuture.completedFuture(null); + } else { + logger.warn("Error when modifying cluster server flow config: " + e); + return newFailedFuture(new RuntimeException(e)); + } + }); + } catch (Exception ex) { + logger.warn("Error when modifying cluster server flow config", ex); + return newFailedFuture(ex); + } + } + + public CompletableFuture modifyClusterServerTransportConfig(String app, String ip, int port, ServerTransportConfig config) { + if (StringUtil.isBlank(ip) || port <= 0) { + return newFailedFuture(new IllegalArgumentException("Invalid parameter")); + } + try { + URIBuilder uriBuilder = new URIBuilder(); + uriBuilder.setScheme("http").setHost(ip).setPort(port) + .setPath(MODIFY_CLUSTER_SERVER_TRANSPORT_CONFIG_PATH) + .setParameter("port", config.getPort().toString()) + .setParameter("idleSeconds", config.getIdleSeconds().toString()); + return executeCommand(MODIFY_CLUSTER_SERVER_TRANSPORT_CONFIG_PATH, uriBuilder.build()) + .thenCompose(e -> { + if ("success".equals(e)) { + return CompletableFuture.completedFuture(null); + } else { + logger.warn("Error when modifying cluster server transport config: " + e); + return newFailedFuture(new RuntimeException(e)); + } + }); + } catch (Exception ex) { + logger.warn("Error when modifying cluster server transport config", ex); + return newFailedFuture(ex); + } + } + + public CompletableFuture modifyClusterServerNamespaceSet(String app, String ip, int port, Set set) { + if (StringUtil.isBlank(ip) || port <= 0) { + return newFailedFuture(new IllegalArgumentException("Invalid parameter")); + } + try { + URIBuilder uriBuilder = new URIBuilder(); + uriBuilder.setScheme("http").setHost(ip).setPort(port) + .setPath(MODIFY_CLUSTER_SERVER_NAMESPACE_SET_PATH) + .setParameter("data", JSON.toJSONString(set)); + return executeCommand(MODIFY_CLUSTER_SERVER_NAMESPACE_SET_PATH, uriBuilder.build()) + .thenCompose(e -> { + if ("success".equals(e)) { + return CompletableFuture.completedFuture(null); + } else { + logger.warn("Error when modifying cluster server NamespaceSet: " + e); + return newFailedFuture(new RuntimeException(e)); + } + }); + } catch (Exception ex) { + logger.warn("Error when modifying cluster server NamespaceSet", ex); + return newFailedFuture(ex); + } + } + + public CompletableFuture fetchClusterServerBasicInfo(String app, String ip, int port) { + if (StringUtil.isBlank(ip) || port <= 0) { + return newFailedFuture(new IllegalArgumentException("Invalid parameter")); + } + try { + URIBuilder uriBuilder = new URIBuilder(); + uriBuilder.setScheme("http").setHost(ip).setPort(port) + .setPath(FETCH_CLUSTER_SERVER_BASIC_INFO_PATH); + return executeCommand(FETCH_CLUSTER_SERVER_BASIC_INFO_PATH, uriBuilder.build()) + .thenApply(r -> JSON.parseObject(r, ClusterServerStateVO.class)); + } catch (Exception ex) { + logger.warn("Error when fetching cluster sever all config and basic info", ex); + return newFailedFuture(ex); + } + } } diff --git a/sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/config/WebConfig.java b/sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/config/WebConfig.java index ba9b095d..6fa051f0 100755 --- a/sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/config/WebConfig.java +++ b/sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/config/WebConfig.java @@ -15,6 +15,8 @@ */ package com.taobao.csp.sentinel.dashboard.config; +import javax.servlet.Filter; + import com.alibaba.csp.sentinel.adapter.servlet.CommonFilter; import org.slf4j.Logger; @@ -31,7 +33,8 @@ import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter */ @Configuration public class WebConfig extends WebMvcConfigurerAdapter { - private static Logger logger = LoggerFactory.getLogger(WebConfig.class); + + private final Logger logger = LoggerFactory.getLogger(WebConfig.class); @Override public void addResourceHandlers(ResourceHandlerRegistry registry) { @@ -46,19 +49,17 @@ public class WebConfig extends WebMvcConfigurerAdapter { /** * Add {@link CommonFilter} to the server, this is the simplest way to use Sentinel * for Web application. - * - * @return */ @Bean public FilterRegistrationBean sentinelFilterRegistration() { - logger.info("sentinelFilterRegistration(), add CommonFilter"); - FilterRegistrationBean registration = new FilterRegistrationBean(); + FilterRegistrationBean registration = new FilterRegistrationBean<>(); registration.setFilter(new CommonFilter()); registration.addUrlPatterns("/*"); - registration.addInitParameter("paramName", "paramValue"); registration.setName("sentinelFilter"); registration.setOrder(1); + logger.info("Sentinel servlet CommonFilter registered"); + return registration; } diff --git a/sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/datasource/entity/rule/FlowRuleEntity.java b/sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/datasource/entity/rule/FlowRuleEntity.java index 98f976b8..eb2eced9 100755 --- a/sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/datasource/entity/rule/FlowRuleEntity.java +++ b/sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/datasource/entity/rule/FlowRuleEntity.java @@ -17,6 +17,7 @@ package com.taobao.csp.sentinel.dashboard.datasource.entity.rule; import java.util.Date; +import com.alibaba.csp.sentinel.slots.block.flow.ClusterFlowConfig; import com.alibaba.csp.sentinel.slots.block.flow.FlowRule; /** @@ -49,6 +50,13 @@ public class FlowRuleEntity implements RuleEntity { * max queueing time in rate limiter behavior */ private Integer maxQueueingTimeMs; + + private boolean clusterMode; + /** + * Flow rule config for cluster mode. + */ + private ClusterFlowConfig clusterConfig; + private Date gmtCreate; private Date gmtModified; @@ -66,6 +74,8 @@ public class FlowRuleEntity implements RuleEntity { entity.setControlBehavior(rule.getControlBehavior()); entity.setWarmUpPeriodSec(rule.getWarmUpPeriodSec()); entity.setMaxQueueingTimeMs(rule.getMaxQueueingTimeMs()); + entity.setClusterMode(rule.isClusterMode()); + entity.setClusterConfig(rule.getClusterConfig()); return entity; } @@ -178,6 +188,24 @@ public class FlowRuleEntity implements RuleEntity { this.maxQueueingTimeMs = maxQueueingTimeMs; } + public boolean isClusterMode() { + return clusterMode; + } + + public FlowRuleEntity setClusterMode(boolean clusterMode) { + this.clusterMode = clusterMode; + return this; + } + + public ClusterFlowConfig getClusterConfig() { + return clusterConfig; + } + + public FlowRuleEntity setClusterConfig(ClusterFlowConfig clusterConfig) { + this.clusterConfig = clusterConfig; + return this; + } + @Override public Date getGmtCreate() { return gmtCreate; @@ -212,6 +240,8 @@ public class FlowRuleEntity implements RuleEntity { if (this.maxQueueingTimeMs != null) { flowRule.setMaxQueueingTimeMs(maxQueueingTimeMs); } + flowRule.setClusterMode(clusterMode); + flowRule.setClusterConfig(clusterConfig); return flowRule; } diff --git a/sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/datasource/entity/rule/ParamFlowRuleEntity.java b/sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/datasource/entity/rule/ParamFlowRuleEntity.java index fde8cfb4..3924f1fd 100644 --- a/sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/datasource/entity/rule/ParamFlowRuleEntity.java +++ b/sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/datasource/entity/rule/ParamFlowRuleEntity.java @@ -17,6 +17,7 @@ package com.taobao.csp.sentinel.dashboard.datasource.entity.rule; import java.util.List; +import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowClusterConfig; 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; @@ -73,4 +74,14 @@ public class ParamFlowRuleEntity extends AbstractRuleEntity { public List getParamFlowItemList() { return rule.getParamFlowItemList(); } + + @JsonIgnore + public boolean isClusterMode() { + return rule.isClusterMode(); + } + + @JsonIgnore + public ParamFlowClusterConfig getClusterConfig() { + return rule.getClusterConfig(); + } } diff --git a/sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/domain/cluster/ClusterClientModifyRequest.java b/sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/domain/cluster/ClusterClientModifyRequest.java new file mode 100644 index 00000000..924e2fd9 --- /dev/null +++ b/sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/domain/cluster/ClusterClientModifyRequest.java @@ -0,0 +1,82 @@ +/* + * 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.domain.cluster; + +import com.taobao.csp.sentinel.dashboard.domain.cluster.config.ClusterClientConfig; + +/** + * @author Eric Zhao + * @since 1.4.0 + */ +public class ClusterClientModifyRequest implements ClusterModifyRequest { + + private String app; + private String ip; + private Integer port; + + private Integer mode; + private ClusterClientConfig clientConfig; + + @Override + public String getApp() { + return app; + } + + public ClusterClientModifyRequest setApp(String app) { + this.app = app; + return this; + } + + @Override + public String getIp() { + return ip; + } + + public ClusterClientModifyRequest setIp(String ip) { + this.ip = ip; + return this; + } + + @Override + public Integer getPort() { + return port; + } + + public ClusterClientModifyRequest setPort(Integer port) { + this.port = port; + return this; + } + + @Override + public Integer getMode() { + return mode; + } + + public ClusterClientModifyRequest setMode(Integer mode) { + this.mode = mode; + return this; + } + + public ClusterClientConfig getClientConfig() { + return clientConfig; + } + + public ClusterClientModifyRequest setClientConfig( + ClusterClientConfig clientConfig) { + this.clientConfig = clientConfig; + return this; + } +} diff --git a/sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/domain/cluster/ClusterClientStateVO.java b/sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/domain/cluster/ClusterClientStateVO.java new file mode 100644 index 00000000..3e9e73f4 --- /dev/null +++ b/sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/domain/cluster/ClusterClientStateVO.java @@ -0,0 +1,44 @@ +/* + * 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.domain.cluster; + +import com.taobao.csp.sentinel.dashboard.domain.cluster.config.ClusterClientConfig; + +/** + * @author Eric Zhao + * @since 1.4.0 + */ +public class ClusterClientStateVO { + + private ClusterClientConfig clientConfig; + + public ClusterClientConfig getClientConfig() { + return clientConfig; + } + + public ClusterClientStateVO setClientConfig( + ClusterClientConfig clientConfig) { + this.clientConfig = clientConfig; + return this; + } + + @Override + public String toString() { + return "ClusterClientStateVO{" + + "clientConfig=" + clientConfig + + '}'; + } +} diff --git a/sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/domain/cluster/ClusterModifyRequest.java b/sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/domain/cluster/ClusterModifyRequest.java new file mode 100644 index 00000000..60fd8325 --- /dev/null +++ b/sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/domain/cluster/ClusterModifyRequest.java @@ -0,0 +1,31 @@ +/* + * 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.domain.cluster; + +/** + * @author Eric Zhao + * @since 1.4.0 + */ +public interface ClusterModifyRequest { + + String getApp(); + + String getIp(); + + Integer getPort(); + + Integer getMode(); +} diff --git a/sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/domain/cluster/ClusterServerModifyRequest.java b/sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/domain/cluster/ClusterServerModifyRequest.java new file mode 100644 index 00000000..a621187a --- /dev/null +++ b/sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/domain/cluster/ClusterServerModifyRequest.java @@ -0,0 +1,119 @@ +/* + * 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.domain.cluster; + +import java.util.Set; + +import com.taobao.csp.sentinel.dashboard.domain.cluster.config.ServerFlowConfig; +import com.taobao.csp.sentinel.dashboard.domain.cluster.config.ServerTransportConfig; + +/** + * @author Eric Zhao + * @since 1.4.0 + */ +public class ClusterServerModifyRequest implements ClusterModifyRequest { + + private String app; + private String ip; + private Integer port; + + private Integer mode; + private ServerFlowConfig flowConfig; + private ServerTransportConfig transportConfig; + private Set namespaceSet; + + @Override + public String getApp() { + return app; + } + + public ClusterServerModifyRequest setApp(String app) { + this.app = app; + return this; + } + + @Override + public String getIp() { + return ip; + } + + public ClusterServerModifyRequest setIp(String ip) { + this.ip = ip; + return this; + } + + @Override + public Integer getPort() { + return port; + } + + public ClusterServerModifyRequest setPort(Integer port) { + this.port = port; + return this; + } + + @Override + public Integer getMode() { + return mode; + } + + public ClusterServerModifyRequest setMode(Integer mode) { + this.mode = mode; + return this; + } + + public ServerFlowConfig getFlowConfig() { + return flowConfig; + } + + public ClusterServerModifyRequest setFlowConfig( + ServerFlowConfig flowConfig) { + this.flowConfig = flowConfig; + return this; + } + + public ServerTransportConfig getTransportConfig() { + return transportConfig; + } + + public ClusterServerModifyRequest setTransportConfig( + ServerTransportConfig transportConfig) { + this.transportConfig = transportConfig; + return this; + } + + public Set getNamespaceSet() { + return namespaceSet; + } + + public ClusterServerModifyRequest setNamespaceSet(Set namespaceSet) { + this.namespaceSet = namespaceSet; + return this; + } + + @Override + public String toString() { + return "ClusterServerModifyRequest{" + + "app='" + app + '\'' + + ", ip='" + ip + '\'' + + ", port=" + port + + ", mode=" + mode + + ", flowConfig=" + flowConfig + + ", transportConfig=" + transportConfig + + ", namespaceSet=" + namespaceSet + + '}'; + } +} diff --git a/sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/domain/cluster/ClusterServerStateVO.java b/sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/domain/cluster/ClusterServerStateVO.java new file mode 100644 index 00000000..5a687893 --- /dev/null +++ b/sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/domain/cluster/ClusterServerStateVO.java @@ -0,0 +1,94 @@ +/* + * 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.domain.cluster; + +import java.util.List; +import java.util.Set; + +import com.taobao.csp.sentinel.dashboard.domain.cluster.config.ServerFlowConfig; +import com.taobao.csp.sentinel.dashboard.domain.cluster.config.ServerTransportConfig; + +/** + * @author Eric Zhao + * @since 1.4.0 + */ +public class ClusterServerStateVO { + + private ServerTransportConfig transport; + private ServerFlowConfig flow; + private Set namespaceSet; + + private Integer port; + private List connection; + + public ServerTransportConfig getTransport() { + return transport; + } + + public ClusterServerStateVO setTransport( + ServerTransportConfig transport) { + this.transport = transport; + return this; + } + + public ServerFlowConfig getFlow() { + return flow; + } + + public ClusterServerStateVO setFlow(ServerFlowConfig flow) { + this.flow = flow; + return this; + } + + public Set getNamespaceSet() { + return namespaceSet; + } + + public ClusterServerStateVO setNamespaceSet(Set namespaceSet) { + this.namespaceSet = namespaceSet; + return this; + } + + public Integer getPort() { + return port; + } + + public ClusterServerStateVO setPort(Integer port) { + this.port = port; + return this; + } + + public List getConnection() { + return connection; + } + + public ClusterServerStateVO setConnection( + List connection) { + this.connection = connection; + return this; + } + + @Override + public String toString() { + return "ClusterServerStateVO{" + + "transport=" + transport + + ", flow=" + flow + + ", namespaceSet=" + namespaceSet + + ", port=" + port + + ", connection=" + connection + + '}'; + } +} diff --git a/sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/domain/cluster/ClusterStateSimpleEntity.java b/sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/domain/cluster/ClusterStateSimpleEntity.java new file mode 100644 index 00000000..52577c60 --- /dev/null +++ b/sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/domain/cluster/ClusterStateSimpleEntity.java @@ -0,0 +1,74 @@ +/* + * 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.domain.cluster; + +/** + * @author Eric Zhao + * @since 1.4.0 + */ +public class ClusterStateSimpleEntity { + + private Integer mode; + private Long lastModified; + private Boolean clientAvailable; + private Boolean serverAvailable; + + public Integer getMode() { + return mode; + } + + public ClusterStateSimpleEntity setMode(Integer mode) { + this.mode = mode; + return this; + } + + public Long getLastModified() { + return lastModified; + } + + public ClusterStateSimpleEntity setLastModified(Long lastModified) { + this.lastModified = lastModified; + return this; + } + + public Boolean getClientAvailable() { + return clientAvailable; + } + + public ClusterStateSimpleEntity setClientAvailable(Boolean clientAvailable) { + this.clientAvailable = clientAvailable; + return this; + } + + public Boolean getServerAvailable() { + return serverAvailable; + } + + public ClusterStateSimpleEntity setServerAvailable(Boolean serverAvailable) { + this.serverAvailable = serverAvailable; + return this; + } + + @Override + public String toString() { + return "ClusterStateSimpleEntity{" + + "mode=" + mode + + ", lastModified=" + lastModified + + ", clientAvailable=" + clientAvailable + + ", serverAvailable=" + serverAvailable + + '}'; + } +} diff --git a/sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/domain/cluster/ClusterUniversalStateVO.java b/sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/domain/cluster/ClusterUniversalStateVO.java new file mode 100644 index 00000000..8038bcc5 --- /dev/null +++ b/sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/domain/cluster/ClusterUniversalStateVO.java @@ -0,0 +1,64 @@ +/* + * 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.domain.cluster; + +/** + * @author Eric Zhao + * @since 1.4.0 + */ +public class ClusterUniversalStateVO { + + private ClusterStateSimpleEntity stateInfo; + private ClusterClientStateVO client; + private ClusterServerStateVO server; + + public ClusterClientStateVO getClient() { + return client; + } + + public ClusterUniversalStateVO setClient(ClusterClientStateVO client) { + this.client = client; + return this; + } + + public ClusterServerStateVO getServer() { + return server; + } + + public ClusterUniversalStateVO setServer(ClusterServerStateVO server) { + this.server = server; + return this; + } + + public ClusterStateSimpleEntity getStateInfo() { + return stateInfo; + } + + public ClusterUniversalStateVO setStateInfo( + ClusterStateSimpleEntity stateInfo) { + this.stateInfo = stateInfo; + return this; + } + + @Override + public String toString() { + return "ClusterUniversalStateVO{" + + "stateInfo=" + stateInfo + + ", client=" + client + + ", server=" + server + + '}'; + } +} diff --git a/sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/domain/cluster/ConnectionDescriptorVO.java b/sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/domain/cluster/ConnectionDescriptorVO.java new file mode 100644 index 00000000..415aaf6f --- /dev/null +++ b/sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/domain/cluster/ConnectionDescriptorVO.java @@ -0,0 +1,53 @@ +/* + * 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.domain.cluster; + + +/** + * @author Eric Zhao + * @since 1.4.0 + */ +public class ConnectionDescriptorVO { + + private String address; + private String host; + + public String getAddress() { + return address; + } + + public ConnectionDescriptorVO setAddress(String address) { + this.address = address; + return this; + } + + public String getHost() { + return host; + } + + public ConnectionDescriptorVO setHost(String host) { + this.host = host; + return this; + } + + @Override + public String toString() { + return "ConnectionDescriptorVO{" + + "address='" + address + '\'' + + ", host='" + host + '\'' + + '}'; + } +} diff --git a/sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/domain/cluster/ConnectionGroupVO.java b/sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/domain/cluster/ConnectionGroupVO.java new file mode 100644 index 00000000..5618c883 --- /dev/null +++ b/sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/domain/cluster/ConnectionGroupVO.java @@ -0,0 +1,66 @@ +/* + * 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.domain.cluster; + +import java.util.List; + +/** + * @author Eric Zhao + * @since 1.4.0 + */ +public class ConnectionGroupVO { + + private String namespace; + private List connectionSet; + private Integer connectedCount; + + public String getNamespace() { + return namespace; + } + + public ConnectionGroupVO setNamespace(String namespace) { + this.namespace = namespace; + return this; + } + + public List getConnectionSet() { + return connectionSet; + } + + public ConnectionGroupVO setConnectionSet( + List connectionSet) { + this.connectionSet = connectionSet; + return this; + } + + public Integer getConnectedCount() { + return connectedCount; + } + + public ConnectionGroupVO setConnectedCount(Integer connectedCount) { + this.connectedCount = connectedCount; + return this; + } + + @Override + public String toString() { + return "ConnectionGroupVO{" + + "namespace='" + namespace + '\'' + + ", connectionSet=" + connectionSet + + ", connectedCount=" + connectedCount + + '}'; + } +} diff --git a/sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/domain/cluster/config/ClusterClientConfig.java b/sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/domain/cluster/config/ClusterClientConfig.java new file mode 100644 index 00000000..61304798 --- /dev/null +++ b/sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/domain/cluster/config/ClusterClientConfig.java @@ -0,0 +1,75 @@ +/* + * 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.domain.cluster.config; + +/** + * @author Eric Zhao + * @since 1.4.0 + */ +public class ClusterClientConfig { + + private String serverHost; + private Integer serverPort; + + private Integer requestTimeout; + private Integer connectTimeout; + + public String getServerHost() { + return serverHost; + } + + public ClusterClientConfig setServerHost(String serverHost) { + this.serverHost = serverHost; + return this; + } + + public Integer getServerPort() { + return serverPort; + } + + public ClusterClientConfig setServerPort(Integer serverPort) { + this.serverPort = serverPort; + return this; + } + + public Integer getRequestTimeout() { + return requestTimeout; + } + + public ClusterClientConfig setRequestTimeout(Integer requestTimeout) { + this.requestTimeout = requestTimeout; + return this; + } + + public Integer getConnectTimeout() { + return connectTimeout; + } + + public ClusterClientConfig setConnectTimeout(Integer connectTimeout) { + this.connectTimeout = connectTimeout; + return this; + } + + @Override + public String toString() { + return "ClusterClientConfig{" + + "serverHost='" + serverHost + '\'' + + ", serverPort=" + serverPort + + ", requestTimeout=" + requestTimeout + + ", connectTimeout=" + connectTimeout + + '}'; + } +} diff --git a/sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/domain/cluster/config/ServerFlowConfig.java b/sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/domain/cluster/config/ServerFlowConfig.java new file mode 100644 index 00000000..f0e2d888 --- /dev/null +++ b/sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/domain/cluster/config/ServerFlowConfig.java @@ -0,0 +1,95 @@ +/* + * 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.domain.cluster.config; + +/** + * @author Eric Zhao + * @since 1.4.0 + */ +public class ServerFlowConfig { + + public static final double DEFAULT_EXCEED_COUNT = 1.0d; + public static final double DEFAULT_MAX_OCCUPY_RATIO = 1.0d; + + public static final int DEFAULT_INTERVAL_MS = 1000; + public static final int DEFAULT_SAMPLE_COUNT= 10; + + private final String namespace; + + private Double exceedCount = DEFAULT_EXCEED_COUNT; + private Double maxOccupyRatio = DEFAULT_MAX_OCCUPY_RATIO; + private Integer intervalMs = DEFAULT_INTERVAL_MS; + private Integer sampleCount = DEFAULT_SAMPLE_COUNT; + + public ServerFlowConfig() { + this("default"); + } + + public ServerFlowConfig(String namespace) { + this.namespace = namespace; + } + + public String getNamespace() { + return namespace; + } + + public Double getExceedCount() { + return exceedCount; + } + + public ServerFlowConfig setExceedCount(Double exceedCount) { + this.exceedCount = exceedCount; + return this; + } + + public Double getMaxOccupyRatio() { + return maxOccupyRatio; + } + + public ServerFlowConfig setMaxOccupyRatio(Double maxOccupyRatio) { + this.maxOccupyRatio = maxOccupyRatio; + return this; + } + + public Integer getIntervalMs() { + return intervalMs; + } + + public ServerFlowConfig setIntervalMs(Integer intervalMs) { + this.intervalMs = intervalMs; + return this; + } + + public Integer getSampleCount() { + return sampleCount; + } + + public ServerFlowConfig setSampleCount(Integer sampleCount) { + this.sampleCount = sampleCount; + return this; + } + + @Override + public String toString() { + return "ServerFlowConfig{" + + "namespace='" + namespace + '\'' + + ", exceedCount=" + exceedCount + + ", maxOccupyRatio=" + maxOccupyRatio + + ", intervalMs=" + intervalMs + + ", sampleCount=" + sampleCount + + '}'; + } +} diff --git a/sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/domain/cluster/config/ServerTransportConfig.java b/sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/domain/cluster/config/ServerTransportConfig.java new file mode 100644 index 00000000..dbf03be3 --- /dev/null +++ b/sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/domain/cluster/config/ServerTransportConfig.java @@ -0,0 +1,64 @@ +/* + * 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.domain.cluster.config; + +/** + * @author Eric Zhao + * @since 1.4.0 + */ +public class ServerTransportConfig { + + public static final int DEFAULT_PORT = 8730; + public static final int DEFAULT_IDLE_SECONDS = 600; + + private Integer port; + private Integer idleSeconds; + + public ServerTransportConfig() { + this(DEFAULT_PORT, DEFAULT_IDLE_SECONDS); + } + + public ServerTransportConfig(Integer port, Integer idleSeconds) { + this.port = port; + this.idleSeconds = idleSeconds; + } + + public Integer getPort() { + return port; + } + + public ServerTransportConfig setPort(Integer port) { + this.port = port; + return this; + } + + public Integer getIdleSeconds() { + return idleSeconds; + } + + public ServerTransportConfig setIdleSeconds(Integer idleSeconds) { + this.idleSeconds = idleSeconds; + return this; + } + + @Override + public String toString() { + return "ServerTransportConfig{" + + "port=" + port + + ", idleSeconds=" + idleSeconds + + '}'; + } +} diff --git a/sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/repository/rule/InMemFlowRuleStore.java b/sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/repository/rule/InMemFlowRuleStore.java index 87d1c009..84742111 100755 --- a/sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/repository/rule/InMemFlowRuleStore.java +++ b/sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/repository/rule/InMemFlowRuleStore.java @@ -17,6 +17,8 @@ package com.taobao.csp.sentinel.dashboard.repository.rule; import java.util.concurrent.atomic.AtomicLong; +import com.alibaba.csp.sentinel.slots.block.flow.ClusterFlowConfig; + import com.taobao.csp.sentinel.dashboard.datasource.entity.rule.FlowRuleEntity; import org.springframework.stereotype.Component; @@ -34,4 +36,18 @@ public class InMemFlowRuleStore extends InMemoryRuleRepositoryAdapter implements RuleRepository { + /** * {@code >} */ private Map> machineRules = new ConcurrentHashMap<>(16); private Map allRules = new ConcurrentHashMap<>(16); + private Map> appRules = new ConcurrentHashMap<>(16); + private static final int MAX_RULES_SIZE = 10000; @Override @@ -41,17 +45,25 @@ public abstract class InMemoryRuleRepositoryAdapter implem if (entity.getId() == null) { entity.setId(nextId()); } - allRules.put(entity.getId(), entity); - machineRules.computeIfAbsent(MachineInfo.of(entity.getApp(), entity.getIp(), entity.getPort()), - e -> new ConcurrentHashMap<>(32)) - .put(entity.getId(), entity); - return entity; + T processedEntity = preProcess(entity); + if (processedEntity != null) { + allRules.put(processedEntity.getId(), processedEntity); + machineRules.computeIfAbsent(MachineInfo.of(processedEntity.getApp(), processedEntity.getIp(), + processedEntity.getPort()), e -> new ConcurrentHashMap<>(32)) + .put(processedEntity.getId(), processedEntity); + appRules.computeIfAbsent(processedEntity.getApp(), v -> new ConcurrentHashMap<>(32)) + .put(processedEntity.getId(), processedEntity); + } + + return processedEntity; } @Override public List saveAll(List rules) { + // TODO: check here. allRules.clear(); machineRules.clear(); + appRules.clear(); if (rules == null) { return null; @@ -67,6 +79,9 @@ public abstract class InMemoryRuleRepositoryAdapter implem public T delete(Long id) { T entity = allRules.remove(id); if (entity != null) { + if (appRules.get(entity.getApp()) != null) { + appRules.get(entity.getApp()).remove(id); + } machineRules.get(MachineInfo.of(entity.getApp(), entity.getIp(), entity.getPort())).remove(id); } return entity; @@ -86,6 +101,20 @@ public abstract class InMemoryRuleRepositoryAdapter implem return new ArrayList<>(entities.values()); } + @Override + public List findAllByApp(String appName) { + AssertUtil.notEmpty(appName, "appName cannot be empty"); + Map entities = appRules.get(appName); + if (entities == null) { + return new ArrayList<>(); + } + return new ArrayList<>(entities.values()); + } + + protected T preProcess(T entity) { + return entity; + } + /** * Get next unused id. * diff --git a/sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/repository/rule/RuleRepository.java b/sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/repository/rule/RuleRepository.java index cb50669f..6cd3f792 100755 --- a/sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/repository/rule/RuleRepository.java +++ b/sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/repository/rule/RuleRepository.java @@ -25,6 +25,7 @@ import com.taobao.csp.sentinel.dashboard.discovery.MachineInfo; * @author leyou */ public interface RuleRepository { + /** * Save one. * @@ -65,6 +66,15 @@ public interface RuleRepository { */ List findAllByMachine(MachineInfo machineInfo); + /** + * Find all by application. + * + * @param appName valid app name + * @return all rules of the application + * @since 1.4.0 + */ + List findAllByApp(String appName); + ///** // * Find all by app and enable switch. // * @param app diff --git a/sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/rule/DynamicRuleProvider.java b/sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/rule/DynamicRuleProvider.java new file mode 100644 index 00000000..20a1e937 --- /dev/null +++ b/sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/rule/DynamicRuleProvider.java @@ -0,0 +1,25 @@ +/* + * 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.rule; + +/** + * @author Eric Zhao + * @since 1.4.0 + */ +public interface DynamicRuleProvider { + + T getRules(String appName) throws Exception; +} diff --git a/sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/rule/DynamicRulePublisher.java b/sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/rule/DynamicRulePublisher.java new file mode 100644 index 00000000..007bb863 --- /dev/null +++ b/sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/rule/DynamicRulePublisher.java @@ -0,0 +1,32 @@ +/* + * 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.rule; + +/** + * @author Eric Zhao + * @since 1.4.0 + */ +public interface DynamicRulePublisher { + + /** + * Publish rules to remote rule configuration center for given application name. + * + * @param app app name + * @param rules list of rules to push + * @throws Exception if some error occurs + */ + void publish(String app, T rules) throws Exception; +} diff --git a/sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/rule/FlowRuleApiProvider.java b/sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/rule/FlowRuleApiProvider.java new file mode 100644 index 00000000..6978d2e7 --- /dev/null +++ b/sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/rule/FlowRuleApiProvider.java @@ -0,0 +1,67 @@ +/* + * 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.rule; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +import com.alibaba.csp.sentinel.util.StringUtil; + +import com.taobao.csp.sentinel.dashboard.client.SentinelApiClient; +import com.taobao.csp.sentinel.dashboard.datasource.entity.rule.FlowRuleEntity; +import com.taobao.csp.sentinel.dashboard.discovery.AppManagement; +import com.taobao.csp.sentinel.dashboard.discovery.MachineInfo; +import com.taobao.csp.sentinel.dashboard.util.MachineUtil; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +/** + * @author Eric Zhao + */ +@Component("flowRuleDefaultProvider") +public class FlowRuleApiProvider implements DynamicRuleProvider> { + + @Autowired + private SentinelApiClient sentinelApiClient; + @Autowired + private AppManagement appManagement; + + @Override + public List getRules(String appName) throws Exception { + if (StringUtil.isBlank(appName)) { + return new ArrayList<>(); + } + List list = appManagement.getDetailApp(appName).getMachines() + .stream() + .filter(MachineUtil::isMachineHealth) + .sorted((e1, e2) -> { + if (e1.getTimestamp().before(e2.getTimestamp())) { + return 1; + } else if (e1.getTimestamp().after(e2.getTimestamp())) { + return -1; + } else { + return 0; + } + }).collect(Collectors.toList()); + if (list.isEmpty()) { + return new ArrayList<>(); + } else { + MachineInfo machine = list.get(0); + return sentinelApiClient.fetchFlowRuleOfMachine(machine.getApp(), machine.getIp(), machine.getPort()); + } + } +} diff --git a/sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/rule/FlowRuleApiPublisher.java b/sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/rule/FlowRuleApiPublisher.java new file mode 100644 index 00000000..05530101 --- /dev/null +++ b/sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/rule/FlowRuleApiPublisher.java @@ -0,0 +1,61 @@ +/* + * 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.rule; + +import java.util.List; +import java.util.Set; + +import com.alibaba.csp.sentinel.util.StringUtil; + +import com.taobao.csp.sentinel.dashboard.client.SentinelApiClient; +import com.taobao.csp.sentinel.dashboard.datasource.entity.rule.FlowRuleEntity; +import com.taobao.csp.sentinel.dashboard.discovery.AppManagement; +import com.taobao.csp.sentinel.dashboard.discovery.MachineInfo; +import com.taobao.csp.sentinel.dashboard.util.MachineUtil; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +/** + * @author Eric Zhao + * @since 1.4.0 + */ +@Component("flowRuleDefaultPublisher") +public class FlowRuleApiPublisher implements DynamicRulePublisher> { + + @Autowired + private SentinelApiClient sentinelApiClient; + @Autowired + private AppManagement appManagement; + + @Override + public void publish(String app, List rules) throws Exception { + if (StringUtil.isBlank(app)) { + return; + } + if (rules == null || rules.isEmpty()) { + return; + } + Set set = appManagement.getDetailApp(app).getMachines(); + + for (MachineInfo machine : set) { + if (!MachineUtil.isMachineHealth(machine)) { + continue; + } + // TODO: parse the results + sentinelApiClient.setFlowRuleOfMachine(app, machine.getIp(), machine.getPort(), rules); + } + } +} diff --git a/sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/service/ClusterConfigService.java b/sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/service/ClusterConfigService.java new file mode 100644 index 00000000..ea334963 --- /dev/null +++ b/sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/service/ClusterConfigService.java @@ -0,0 +1,116 @@ +/* + * 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.service; + +import java.util.Set; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; + +import com.alibaba.csp.sentinel.cluster.ClusterStateManager; +import com.alibaba.csp.sentinel.util.StringUtil; + +import com.taobao.csp.sentinel.dashboard.client.SentinelApiClient; +import com.taobao.csp.sentinel.dashboard.domain.cluster.ClusterClientModifyRequest; +import com.taobao.csp.sentinel.dashboard.domain.cluster.ClusterClientStateVO; +import com.taobao.csp.sentinel.dashboard.domain.cluster.ClusterServerModifyRequest; +import com.taobao.csp.sentinel.dashboard.domain.cluster.ClusterUniversalStateVO; +import com.taobao.csp.sentinel.dashboard.domain.cluster.config.ClusterClientConfig; +import com.taobao.csp.sentinel.dashboard.domain.cluster.config.ServerFlowConfig; +import com.taobao.csp.sentinel.dashboard.domain.cluster.config.ServerTransportConfig; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +/** + * @author Eric Zhao + * @since 1.4.0 + */ +@Service +public class ClusterConfigService { + + @Autowired + private SentinelApiClient sentinelApiClient; + + public CompletableFuture modifyClusterClientConfig(ClusterClientModifyRequest request) { + if (notClientRequestValid(request)) { + throw new IllegalArgumentException("Invalid request"); + } + String app = request.getApp(); + String ip = request.getIp(); + int port = request.getPort(); + return sentinelApiClient.modifyClusterClientConfig(app, ip, port, request.getClientConfig()) + .thenCompose(v -> sentinelApiClient.modifyClusterMode(app, ip, port, ClusterStateManager.CLUSTER_CLIENT)); + } + + private boolean notClientRequestValid(/*@NonNull */ ClusterClientModifyRequest request) { + ClusterClientConfig config = request.getClientConfig(); + return config == null || StringUtil.isEmpty(config.getServerHost()) + || config.getServerPort() == null || config.getServerPort() <= 0 + || config.getRequestTimeout() == null || config.getRequestTimeout() <= 0; + } + + public CompletableFuture modifyClusterServerConfig(ClusterServerModifyRequest request) { + ServerTransportConfig transportConfig = request.getTransportConfig(); + ServerFlowConfig flowConfig = request.getFlowConfig(); + Set namespaceSet = request.getNamespaceSet(); + if (invalidTransportConfig(transportConfig)) { + throw new IllegalArgumentException("Invalid transport config in request"); + } + if (invalidFlowConfig(flowConfig)) { + throw new IllegalArgumentException("Invalid flow config in request"); + } + if (namespaceSet == null) { + throw new IllegalArgumentException("namespace set cannot be null"); + } + String app = request.getApp(); + String ip = request.getIp(); + int port = request.getPort(); + return sentinelApiClient.modifyClusterServerNamespaceSet(app, ip, port, namespaceSet) + .thenCompose(v -> sentinelApiClient.modifyClusterServerTransportConfig(app, ip, port, transportConfig)) + .thenCompose(v -> sentinelApiClient.modifyClusterServerFlowConfig(app, ip, port, flowConfig)) + .thenCompose(v -> sentinelApiClient.modifyClusterMode(app, ip, port, ClusterStateManager.CLUSTER_SERVER)); + } + + public CompletableFuture getClusterUniversalState(String app, String ip, int port) { + return sentinelApiClient.fetchClusterMode(app, ip, port) + .thenApply(e -> new ClusterUniversalStateVO().setStateInfo(e)) + .thenCompose(vo -> { + if (vo.getStateInfo().getClientAvailable()) { + return sentinelApiClient.fetchClusterClientConfig(app, ip, port) + .thenApply(cc -> vo.setClient(new ClusterClientStateVO().setClientConfig(cc))); + } else { + return CompletableFuture.completedFuture(vo); + } + }).thenCompose(vo -> { + if (vo.getStateInfo().getServerAvailable()) { + return sentinelApiClient.fetchClusterServerBasicInfo(app, ip, port) + .thenApply(vo::setServer); + } else { + return CompletableFuture.completedFuture(vo); + } + }); + } + + private boolean invalidTransportConfig(ServerTransportConfig transportConfig) { + return transportConfig == null || transportConfig.getPort() == null || transportConfig.getPort() <= 0 + || transportConfig.getIdleSeconds() == null || transportConfig.getIdleSeconds() <= 0; + } + + private boolean invalidFlowConfig(ServerFlowConfig flowConfig) { + return flowConfig == null || flowConfig.getSampleCount() == null || flowConfig.getSampleCount() <= 0 + || flowConfig.getIntervalMs() == null || flowConfig.getIntervalMs() <= 0 + || flowConfig.getIntervalMs() % flowConfig.getSampleCount() != 0; + } +} diff --git a/sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/util/MachineUtil.java b/sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/util/MachineUtil.java new file mode 100644 index 00000000..ac94dcd6 --- /dev/null +++ b/sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/util/MachineUtil.java @@ -0,0 +1,32 @@ +/* + * 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 com.taobao.csp.sentinel.dashboard.discovery.MachineDiscovery; +import com.taobao.csp.sentinel.dashboard.discovery.MachineInfo; + +/** + * @author Eric Zhao + */ +public final class MachineUtil { + + public static boolean isMachineHealth(MachineInfo machine) { + if (machine == null) { + return false; + } + return System.currentTimeMillis() - machine.getTimestamp().getTime() < MachineDiscovery.MAX_CLIENT_LIVE_TIME_MS; + } +} diff --git a/sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/view/ClusterConfigController.java b/sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/view/ClusterConfigController.java new file mode 100644 index 00000000..b4f1d86d --- /dev/null +++ b/sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/view/ClusterConfigController.java @@ -0,0 +1,182 @@ +/* + * 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.Optional; +import java.util.concurrent.ExecutionException; + +import com.alibaba.csp.sentinel.cluster.ClusterStateManager; +import com.alibaba.csp.sentinel.util.StringUtil; +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONObject; + +import com.taobao.csp.sentinel.dashboard.client.CommandNotFoundException; +import com.taobao.csp.sentinel.dashboard.datasource.entity.SentinelVersion; +import com.taobao.csp.sentinel.dashboard.discovery.AppManagement; +import com.taobao.csp.sentinel.dashboard.domain.cluster.ClusterClientModifyRequest; +import com.taobao.csp.sentinel.dashboard.domain.cluster.ClusterModifyRequest; +import com.taobao.csp.sentinel.dashboard.domain.cluster.ClusterServerModifyRequest; +import com.taobao.csp.sentinel.dashboard.domain.cluster.ClusterUniversalStateVO; +import com.taobao.csp.sentinel.dashboard.service.ClusterConfigService; +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.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +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 1.4.0 + */ +@RestController +@RequestMapping(value = "/cluster") +public class ClusterConfigController { + + private final Logger logger = LoggerFactory.getLogger(ClusterConfigController.class); + + private final SentinelVersion version140 = new SentinelVersion().setMajorVersion(1).setMinorVersion(4); + + @Autowired + private AppManagement appManagement; + + @Autowired + private ClusterConfigService clusterConfigService; + + @PostMapping("/config/modify") + public Result apiModifyClusterConfig(@RequestBody String payload) { + if (StringUtil.isBlank(payload)) { + return Result.ofFail(-1, "empty request body"); + } + try { + JSONObject body = JSON.parseObject(payload); + if (body.containsKey(KEY_MODE)) { + int mode = body.getInteger(KEY_MODE); + switch (mode) { + case ClusterStateManager.CLUSTER_CLIENT: + ClusterClientModifyRequest data = JSON.parseObject(payload, ClusterClientModifyRequest.class); + Result res = checkValidRequest(data); + if (res != null) { + return res; + } + clusterConfigService.modifyClusterClientConfig(data).get(); + return Result.ofSuccess(true); + case ClusterStateManager.CLUSTER_SERVER: + ClusterServerModifyRequest d = JSON.parseObject(payload, ClusterServerModifyRequest.class); + Result r = checkValidRequest(d); + if (r != null) { + return r; + } + // TODO: bad design here, should refactor! + clusterConfigService.modifyClusterServerConfig(d).get(); + return Result.ofSuccess(true); + default: + return Result.ofFail(-1, "invalid mode"); + } + } + return Result.ofFail(-1, "invalid parameter"); + } catch (ExecutionException ex) { + logger.error("Error when modifying cluster config", ex.getCause()); + if (isNotSupported(ex.getCause())) { + return unsupportedVersion(); + } else { + return Result.ofThrowable(-1, ex.getCause()); + } + } catch (Throwable ex) { + logger.error("Error when modifying cluster config", ex); + return Result.ofFail(-1, ex.getMessage()); + } + } + + @GetMapping("/state") + public Result apiGetClusterState(@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 clusterConfigService.getClusterUniversalState(app, ip, port) + .thenApply(Result::ofSuccess) + .get(); + } catch (ExecutionException ex) { + logger.error("Error when fetching cluster state", ex.getCause()); + if (isNotSupported(ex.getCause())) { + return unsupportedVersion(); + } else { + return Result.ofThrowable(-1, ex.getCause()); + } + } catch (Throwable throwable) { + logger.error("Error when fetching cluster state", throwable); + return Result.ofFail(-1, throwable.getMessage()); + } + } + + private boolean isNotSupported(Throwable ex) { + return ex instanceof CommandNotFoundException; + } + + 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(version140))) + .orElse(true); + // If error occurred or cannot retrieve machine info, return true. + } catch (Exception ex) { + return true; + } + } + + private Result checkValidRequest(ClusterModifyRequest request) { + if (StringUtil.isEmpty(request.getApp())) { + return Result.ofFail(-1, "app cannot be empty"); + } + if (StringUtil.isEmpty(request.getIp())) { + return Result.ofFail(-1, "ip cannot be empty"); + } + if (request.getPort() == null || request.getPort() < 0) { + return Result.ofFail(-1, "invalid port"); + } + if (request.getMode() == null || request.getMode() < 0) { + return Result.ofFail(-1, "invalid mode"); + } + if (!checkIfSupported(request.getApp(), request.getIp(), request.getPort())) { + return unsupportedVersion(); + } + return null; + } + + private Result unsupportedVersion() { + return Result.ofFail(4041, "Sentinel client not supported for cluster flow control (unsupported version or dependency absent)"); + } + + private static final String KEY_MODE = "mode"; +} diff --git a/sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/view/FlowController.java b/sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/view/FlowControllerV1.java similarity index 71% rename from sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/view/FlowController.java rename to sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/view/FlowControllerV1.java index 0a6026e3..9813896f 100755 --- a/sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/view/FlowController.java +++ b/sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/view/FlowControllerV1.java @@ -20,36 +20,44 @@ import java.util.List; import com.alibaba.csp.sentinel.util.StringUtil; +import com.taobao.csp.sentinel.dashboard.client.SentinelApiClient; 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; +import com.taobao.csp.sentinel.dashboard.repository.rule.InMemoryRuleRepositoryAdapter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.http.MediaType; -import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +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.ResponseBody; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; /** * Flow rule controller. * * @author leyou + * @author Eric Zhao */ -@Controller -@RequestMapping(value = "/flow", produces = MediaType.APPLICATION_JSON_VALUE) -public class FlowController { - private static Logger logger = LoggerFactory.getLogger(FlowController.class); +@RestController +@RequestMapping(value = "/v1/flow") +public class FlowControllerV1 { + + private final Logger logger = LoggerFactory.getLogger(FlowControllerV1.class); + @Autowired - private InMemFlowRuleStore repository; + private InMemoryRuleRepositoryAdapter repository; @Autowired private SentinelApiClient sentinelApiClient; - @ResponseBody - @RequestMapping("/rules.json") - Result> queryMachineRules(String app, String ip, Integer port) { + @GetMapping("/rules") + public Result> apiQueryMachineRules(@RequestParam String app, + @RequestParam String ip, + @RequestParam Integer port) { if (StringUtil.isEmpty(app)) { return Result.ofFail(-1, "app can't be null or empty"); } @@ -64,88 +72,82 @@ public class FlowController { rules = repository.saveAll(rules); return Result.ofSuccess(rules); } catch (Throwable throwable) { - logger.error("queryApps error:", throwable); + logger.error("Error when querying flow rules", throwable); return Result.ofThrowable(-1, throwable); } } - @ResponseBody - @RequestMapping("/new.json") - Result add(String app, String ip, Integer port, String limitApp, String resource, Integer grade, - Double count, Integer strategy, String refResource, - Integer controlBehavior, Integer warmUpPeriodSec, Integer maxQueueingTimeMs) { - if (StringUtil.isBlank(app)) { + private Result checkEntityInternal(FlowRuleEntity entity) { + if (StringUtil.isBlank(entity.getApp())) { return Result.ofFail(-1, "app can't be null or empty"); } - if (StringUtil.isBlank(ip)) { + if (StringUtil.isBlank(entity.getIp())) { return Result.ofFail(-1, "ip can't be null or empty"); } - if (port == null) { + if (entity.getPort() == null) { return Result.ofFail(-1, "port can't be null"); } - if (StringUtil.isBlank(limitApp)) { + if (StringUtil.isBlank(entity.getLimitApp())) { return Result.ofFail(-1, "limitApp can't be null or empty"); } - if (StringUtil.isBlank(resource)) { + if (StringUtil.isBlank(entity.getResource())) { return Result.ofFail(-1, "resource can't be null or empty"); } - if (grade == null) { + if (entity.getGrade() == null) { return Result.ofFail(-1, "grade can't be null"); } - if (grade != 0 && grade != 1) { - return Result.ofFail(-1, "grade must be 0 or 1, but " + grade + " got"); + if (entity.getGrade() != 0 && entity.getGrade() != 1) { + return Result.ofFail(-1, "grade must be 0 or 1, but " + entity.getGrade() + " got"); } - if (count == null) { - return Result.ofFail(-1, "count can't be null"); + if (entity.getCount() == null || entity.getCount() < 0) { + return Result.ofFail(-1, "count should be at lease zero"); } - if (strategy == null) { + if (entity.getStrategy() == null) { return Result.ofFail(-1, "strategy can't be null"); } - if (strategy != 0 && StringUtil.isBlank(refResource)) { + if (entity.getStrategy() != 0 && StringUtil.isBlank(entity.getRefResource())) { return Result.ofFail(-1, "refResource can't be null or empty when strategy!=0"); } - if (controlBehavior == null) { + if (entity.getControlBehavior() == null) { return Result.ofFail(-1, "controlBehavior can't be null"); } - if (controlBehavior == 1 && warmUpPeriodSec == null) { + int controlBehavior = entity.getControlBehavior(); + if (controlBehavior == 1 && entity.getWarmUpPeriodSec() == null) { return Result.ofFail(-1, "warmUpPeriodSec can't be null when controlBehavior==1"); } - if (controlBehavior == 2 && maxQueueingTimeMs == null) { + if (controlBehavior == 2 && entity.getMaxQueueingTimeMs() == null) { return Result.ofFail(-1, "maxQueueingTimeMs can't be null when controlBehavior==2"); } - FlowRuleEntity entity = new FlowRuleEntity(); - entity.setApp(app.trim()); - entity.setIp(ip.trim()); - entity.setPort(port); - entity.setLimitApp(limitApp.trim()); - entity.setResource(resource.trim()); - entity.setGrade(grade); - entity.setCount(count); - entity.setStrategy(strategy); - entity.setControlBehavior(controlBehavior); - entity.setWarmUpPeriodSec(warmUpPeriodSec); - entity.setMaxQueueingTimeMs(maxQueueingTimeMs); - if (strategy != 0) { - entity.setRefResource(refResource.trim()); + if (entity.isClusterMode() && entity.getClusterConfig() == null) { + return Result.ofFail(-1, "cluster config should be valid"); } + return null; + } + + @PostMapping("/rule") + public Result apiAddFlowRule(@RequestBody FlowRuleEntity entity) { + Result checkResult = checkEntityInternal(entity); + if (checkResult != null) { + return checkResult; + } + entity.setId(null); Date date = new Date(); entity.setGmtCreate(date); entity.setGmtModified(date); try { entity = repository.save(entity); } catch (Throwable throwable) { - logger.error("add error:", throwable); + logger.error("Failed to add flow rule", throwable); return Result.ofThrowable(-1, throwable); } - if (!publishRules(app, ip, port)) { - logger.info("publish flow rules fail after rule add"); + if (!publishRules(entity.getApp(), entity.getIp(), entity.getPort())) { + logger.error("Publish flow rules failed after rule add"); } return Result.ofSuccess(entity); } - @ResponseBody - @RequestMapping("/save.json") - Result updateIfNotNull(Long id, String app, + @PutMapping("/save.json") + public Result updateIfNotNull(Long id, String app, String limitApp, String resource, Integer grade, Double count, Integer strategy, String refResource, Integer controlBehavior, Integer warmUpPeriodSec, Integer maxQueueingTimeMs) { @@ -221,8 +223,7 @@ public class FlowController { return Result.ofSuccess(entity); } - @ResponseBody - @RequestMapping("/delete.json") + @DeleteMapping("/delete.json") Result delete(Long id) { if (id == null) { return Result.ofFail(-1, "id can't be null"); diff --git a/sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/view/FlowControllerV2.java b/sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/view/FlowControllerV2.java new file mode 100755 index 00000000..52e3cf93 --- /dev/null +++ b/sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/view/FlowControllerV2.java @@ -0,0 +1,209 @@ +/* + * 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 com.alibaba.csp.sentinel.util.StringUtil; + +import com.taobao.csp.sentinel.dashboard.datasource.entity.rule.FlowRuleEntity; +import com.taobao.csp.sentinel.dashboard.repository.rule.InMemoryRuleRepositoryAdapter; +import com.taobao.csp.sentinel.dashboard.rule.DynamicRuleProvider; +import com.taobao.csp.sentinel.dashboard.rule.DynamicRulePublisher; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +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; + +/** + * Flow rule controller (v2). + * + * @author Eric Zhao + * @since 1.4.0 + */ +@RestController +@RequestMapping(value = "/v2/flow") +public class FlowControllerV2 { + + private final Logger logger = LoggerFactory.getLogger(FlowControllerV2.class); + + @Autowired + private InMemoryRuleRepositoryAdapter repository; + + @Autowired + @Qualifier("flowRuleDefaultProvider") + private DynamicRuleProvider> ruleProvider; + @Autowired + @Qualifier("flowRuleDefaultPublisher") + private DynamicRulePublisher> rulePublisher; + + @GetMapping("/rules") + public Result> apiQueryMachineRules(@RequestParam String app) { + if (StringUtil.isEmpty(app)) { + return Result.ofFail(-1, "app can't be null or empty"); + } + try { + List rules = ruleProvider.getRules(app); + if (rules != null && !rules.isEmpty()) { + for (FlowRuleEntity entity : rules) { + entity.setApp(app); + if (entity.getClusterConfig() != null && entity.getClusterConfig().getFlowId() != null) { + entity.setId(entity.getClusterConfig().getFlowId()); + } + } + } + rules = repository.saveAll(rules); + return Result.ofSuccess(rules); + } catch (Throwable throwable) { + logger.error("Error when querying flow rules", throwable); + return Result.ofThrowable(-1, throwable); + } + } + + private Result checkEntityInternal(FlowRuleEntity entity) { + if (entity == null) { + return Result.ofFail(-1, "invalid body"); + } + if (StringUtil.isBlank(entity.getApp())) { + return Result.ofFail(-1, "app can't be null or empty"); + } + if (StringUtil.isBlank(entity.getLimitApp())) { + return Result.ofFail(-1, "limitApp can't be null or empty"); + } + if (StringUtil.isBlank(entity.getResource())) { + return Result.ofFail(-1, "resource can't be null or empty"); + } + if (entity.getGrade() == null) { + return Result.ofFail(-1, "grade can't be null"); + } + if (entity.getGrade() != 0 && entity.getGrade() != 1) { + return Result.ofFail(-1, "grade must be 0 or 1, but " + entity.getGrade() + " got"); + } + if (entity.getCount() == null || entity.getCount() < 0) { + return Result.ofFail(-1, "count should be at lease zero"); + } + if (entity.getStrategy() == null) { + return Result.ofFail(-1, "strategy can't be null"); + } + if (entity.getStrategy() != 0 && StringUtil.isBlank(entity.getRefResource())) { + return Result.ofFail(-1, "refResource can't be null or empty when strategy!=0"); + } + if (entity.getControlBehavior() == null) { + return Result.ofFail(-1, "controlBehavior can't be null"); + } + int controlBehavior = entity.getControlBehavior(); + if (controlBehavior == 1 && entity.getWarmUpPeriodSec() == null) { + return Result.ofFail(-1, "warmUpPeriodSec can't be null when controlBehavior==1"); + } + if (controlBehavior == 2 && entity.getMaxQueueingTimeMs() == null) { + return Result.ofFail(-1, "maxQueueingTimeMs can't be null when controlBehavior==2"); + } + if (entity.isClusterMode() && entity.getClusterConfig() == null) { + return Result.ofFail(-1, "cluster config should be valid"); + } + return null; + } + + @PostMapping("/rule") + public Result apiAddFlowRule(@RequestBody FlowRuleEntity entity) { + Result checkResult = checkEntityInternal(entity); + if (checkResult != null) { + return checkResult; + } + entity.setId(null); + Date date = new Date(); + entity.setGmtCreate(date); + entity.setGmtModified(date); + try { + entity = repository.save(entity); + publishRules(entity.getApp()); + } catch (Throwable throwable) { + logger.error("Failed to add flow rule", throwable); + return Result.ofThrowable(-1, throwable); + } + return Result.ofSuccess(entity); + } + + @PutMapping("/rule/{id}") + public Result apiUpdateFlowRule(@PathVariable("id") Long id, @RequestBody FlowRuleEntity entity) { + if (id == null || id <= 0) { + return Result.ofFail(-1, "Invalid id"); + } + FlowRuleEntity oldEntity = repository.findById(id); + if (oldEntity == null) { + return Result.ofFail(-1, "id " + id + " does not exist"); + } + if (entity == null) { + return Result.ofFail(-1, "invalid body"); + } + entity.setApp(oldEntity.getApp()); + entity.setIp(oldEntity.getIp()); + entity.setPort(oldEntity.getPort()); + Result checkResult = checkEntityInternal(entity); + if (checkResult != null) { + return checkResult; + } + + entity.setId(id); + Date date = new Date(); + entity.setGmtCreate(oldEntity.getGmtCreate()); + entity.setGmtModified(date); + try { + entity = repository.save(entity); + if (entity == null) { + return Result.ofFail(-1, "save entity fail"); + } + publishRules(oldEntity.getApp()); + } catch (Throwable throwable) { + logger.error("Failed to update flow rule", throwable); + return Result.ofThrowable(-1, throwable); + } + return Result.ofSuccess(entity); + } + + @DeleteMapping("/rule/{id}") + public Result apiDeleteRule(@PathVariable("id") Long id) { + if (id == null || id <= 0) { + return Result.ofFail(-1, "Invalid id"); + } + FlowRuleEntity oldEntity = repository.findById(id); + if (oldEntity == null) { + return Result.ofSuccess(null); + } + try { + repository.delete(id); + publishRules(oldEntity.getApp()); + } catch (Exception e) { + return Result.ofFail(-1, e.getMessage()); + } + return Result.ofSuccess(id); + } + + private void publishRules(/*@NonNull*/ String app) throws Exception { + List rules = repository.findAllByApp(app); + rulePublisher.publish(app, rules); + } +} diff --git a/sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/view/MetricController.java b/sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/view/MetricController.java index bd683c01..ee336664 100755 --- a/sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/view/MetricController.java +++ b/sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/view/MetricController.java @@ -84,7 +84,8 @@ public class MetricController { return Result.ofFail(-1, "time intervalMs is too big, must <= 1h"); } List resources = metricStore.listResourcesOfApp(app); - logger.info("queryTopResourceMetric(), resources.size()={}", resources.size()); + logger.debug("queryTopResourceMetric(), resources.size()={}", resources.size()); + if (resources == null || resources.isEmpty()) { return Result.ofSuccess(null); } @@ -107,17 +108,17 @@ public class MetricController { Math.min(pageIndex * pageSize, resources.size())); } final Map> map = new ConcurrentHashMap<>(); - logger.info("topResource={}", topResource); + logger.debug("topResource={}", topResource); long time = System.currentTimeMillis(); for (final String resource : topResource) { List entities = metricStore.queryByAppAndResourceBetween( app, resource, startTime, endTime); - logger.info("resource={}, entities.size()={}", resource, entities == null ? "null" : entities.size()); + logger.debug("resource={}, entities.size()={}", resource, entities == null ? "null" : entities.size()); List vos = MetricVo.fromMetricEntities(entities, resource); Iterable vosSorted = sortMetricVoAndDistinct(vos); map.put(resource, vosSorted); } - logger.info("queryTopResourceMetric() total query time={} ms", System.currentTimeMillis() - time); + logger.debug("queryTopResourceMetric() total query time={} ms", System.currentTimeMillis() - time); Map resultMap = new HashMap<>(16); resultMap.put("totalCount", resources.size()); resultMap.put("totalPage", totalPage); diff --git a/sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/view/ParamFlowRuleController.java b/sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/view/ParamFlowRuleController.java index 817fdafc..c24b605d 100644 --- a/sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/view/ParamFlowRuleController.java +++ b/sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/view/ParamFlowRuleController.java @@ -179,6 +179,10 @@ public class ParamFlowRuleController { if (id == null || id <= 0) { return Result.ofFail(-1, "Invalid id"); } + ParamFlowRuleEntity oldEntity = repository.findById(id); + if (oldEntity == null) { + return Result.ofFail(-1, "id " + id + " does not exist"); + } Result checkResult = checkEntityInternal(entity); if (checkResult != null) { return checkResult; @@ -188,7 +192,7 @@ public class ParamFlowRuleController { } entity.setId(id); Date date = new Date(); - entity.setGmtCreate(null); + entity.setGmtCreate(oldEntity.getGmtCreate()); entity.setGmtModified(date); try { entity = repository.save(entity); diff --git a/sentinel-dashboard/src/test/java/com/taobao/csp/sentinel/dashboard/rule/nacos/FlowRuleNacosProvider.java b/sentinel-dashboard/src/test/java/com/taobao/csp/sentinel/dashboard/rule/nacos/FlowRuleNacosProvider.java new file mode 100644 index 00000000..0c5fe27e --- /dev/null +++ b/sentinel-dashboard/src/test/java/com/taobao/csp/sentinel/dashboard/rule/nacos/FlowRuleNacosProvider.java @@ -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.rule.nacos; + +import java.util.ArrayList; +import java.util.List; + +import com.alibaba.csp.sentinel.datasource.Converter; +import com.alibaba.csp.sentinel.util.StringUtil; +import com.alibaba.nacos.api.config.ConfigService; + +import com.taobao.csp.sentinel.dashboard.datasource.entity.rule.FlowRuleEntity; +import com.taobao.csp.sentinel.dashboard.rule.DynamicRuleProvider; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +/** + * @author Eric Zhao + * @since 1.4.0 + */ +@Component("flowRuleNacosProvider") +public class FlowRuleNacosProvider implements DynamicRuleProvider> { + + @Autowired + private ConfigService configService; + @Autowired + private Converter> converter; + + @Override + public List getRules(String appName) throws Exception { + String rules = configService.getConfig(appName + NacosConfigUtil.FLOW_DATA_ID_POSTFIX, + NacosConfigUtil.GROUP_ID, 3000); + if (StringUtil.isEmpty(rules)) { + return new ArrayList<>(); + } + return converter.convert(rules); + } +} diff --git a/sentinel-dashboard/src/test/java/com/taobao/csp/sentinel/dashboard/rule/nacos/FlowRuleNacosPublisher.java b/sentinel-dashboard/src/test/java/com/taobao/csp/sentinel/dashboard/rule/nacos/FlowRuleNacosPublisher.java new file mode 100644 index 00000000..4b3bf840 --- /dev/null +++ b/sentinel-dashboard/src/test/java/com/taobao/csp/sentinel/dashboard/rule/nacos/FlowRuleNacosPublisher.java @@ -0,0 +1,50 @@ +/* + * 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.rule.nacos; + +import java.util.List; + +import com.alibaba.csp.sentinel.datasource.Converter; +import com.alibaba.csp.sentinel.util.AssertUtil; +import com.alibaba.nacos.api.config.ConfigService; + +import com.taobao.csp.sentinel.dashboard.datasource.entity.rule.FlowRuleEntity; +import com.taobao.csp.sentinel.dashboard.rule.DynamicRulePublisher; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +/** + * @author Eric Zhao + * @since 1.4.0 + */ +@Component("flowRuleNacosPublisher") +public class FlowRuleNacosPublisher implements DynamicRulePublisher> { + + @Autowired + private ConfigService configService; + @Autowired + private Converter, String> converter; + + @Override + public void publish(String app, List rules) throws Exception { + AssertUtil.notEmpty(app, "app name cannot be empty"); + if (rules == null) { + return; + } + configService.publishConfig(app + NacosConfigUtil.FLOW_DATA_ID_POSTFIX, + NacosConfigUtil.GROUP_ID, converter.convert(rules)); + } +} diff --git a/sentinel-dashboard/src/test/java/com/taobao/csp/sentinel/dashboard/rule/nacos/NacosConfig.java b/sentinel-dashboard/src/test/java/com/taobao/csp/sentinel/dashboard/rule/nacos/NacosConfig.java new file mode 100644 index 00000000..146b8697 --- /dev/null +++ b/sentinel-dashboard/src/test/java/com/taobao/csp/sentinel/dashboard/rule/nacos/NacosConfig.java @@ -0,0 +1,50 @@ +/* + * 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.rule.nacos; + +import java.util.List; + +import com.alibaba.csp.sentinel.datasource.Converter; +import com.alibaba.fastjson.JSON; +import com.alibaba.nacos.api.config.ConfigFactory; +import com.alibaba.nacos.api.config.ConfigService; + +import com.taobao.csp.sentinel.dashboard.datasource.entity.rule.FlowRuleEntity; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * @author Eric Zhao + * @since 1.4.0 + */ +@Configuration +public class NacosConfig { + + @Bean + public Converter, String> flowRuleEntityEncoder() { + return JSON::toJSONString; + } + + @Bean + public Converter> flowRuleEntityDecoder() { + return s -> JSON.parseArray(s, FlowRuleEntity.class); + } + + @Bean + public ConfigService nacosConfigService() throws Exception { + return ConfigFactory.createConfigService("localhost"); + } +} diff --git a/sentinel-dashboard/src/test/java/com/taobao/csp/sentinel/dashboard/rule/nacos/NacosConfigUtil.java b/sentinel-dashboard/src/test/java/com/taobao/csp/sentinel/dashboard/rule/nacos/NacosConfigUtil.java new file mode 100644 index 00000000..d17d2475 --- /dev/null +++ b/sentinel-dashboard/src/test/java/com/taobao/csp/sentinel/dashboard/rule/nacos/NacosConfigUtil.java @@ -0,0 +1,41 @@ +/* + * 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.rule.nacos; + +/** + * @author Eric Zhao + * @since 1.4.0 + */ +public final class NacosConfigUtil { + + public static final String GROUP_ID = "SENTINEL_GROUP"; + + public static final String FLOW_DATA_ID_POSTFIX = "-flow-rules"; + public static final String PARAM_FLOW_DATA_ID_POSTFIX = "-param-rules"; + + /** + * cc for `cluster-client` + */ + public static final String CLIENT_CONFIG_DATA_ID_POSTFIX = "-cc-config"; + /** + * cs for `cluster-server` + */ + public static final String SERVER_TRANSPORT_CONFIG_DATA_ID_POSTFIX = "-cs-transport-config"; + public static final String SERVER_FLOW_CONFIG_DATA_ID_POSTFIX = "-cs-flow-config"; + public static final String SERVER_NAMESPACE_SET_DATA_ID_POSTFIX = "-cs-namespace-set"; + + private NacosConfigUtil() {} +}