Browse Source

Polish Sentinel dashboard backend for cluster flow control enhancement

- Add cluster token server management controller and service for app
- Other enhancements and fixes

Signed-off-by: Eric Zhao <sczyh16@gmail.com>
master
Eric Zhao 5 years ago
parent
commit
8d413e1645
42 changed files with 1964 additions and 183 deletions
  1. +6
    -6
      sentinel-dashboard/pom.xml
  2. +7
    -0
      sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/DashboardApplication.java
  3. +11
    -10
      sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/client/CommandFailedException.java
  4. +6
    -0
      sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/client/CommandNotFoundException.java
  5. +118
    -114
      sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/client/SentinelApiClient.java
  6. +1
    -1
      sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/discovery/MachineDiscovery.java
  7. +4
    -0
      sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/discovery/MachineInfo.java
  8. +66
    -0
      sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/domain/cluster/ClusterAppAssignResultVO.java
  9. +58
    -0
      sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/domain/cluster/ClusterAppFullAssignRequest.java
  10. +56
    -0
      sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/domain/cluster/ClusterAppSingleServerAssignRequest.java
  11. +76
    -0
      sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/domain/cluster/ClusterClientInfoVO.java
  12. +91
    -0
      sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/domain/cluster/ClusterGroupEntity.java
  13. +63
    -0
      sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/domain/cluster/ClusterStateSingleVO.java
  14. +13
    -0
      sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/domain/cluster/config/ServerFlowConfig.java
  15. +1
    -1
      sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/domain/cluster/config/ServerTransportConfig.java
  16. +112
    -0
      sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/domain/cluster/request/ClusterAppAssignMap.java
  17. +1
    -1
      sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/domain/cluster/request/ClusterClientModifyRequest.java
  18. +1
    -1
      sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/domain/cluster/request/ClusterModifyRequest.java
  19. +1
    -1
      sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/domain/cluster/request/ClusterServerModifyRequest.java
  20. +79
    -0
      sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/domain/cluster/state/AppClusterClientStateWrapVO.java
  21. +102
    -0
      sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/domain/cluster/state/AppClusterServerStateWrapVO.java
  22. +8
    -6
      sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/domain/cluster/state/ClusterClientStateVO.java
  23. +63
    -0
      sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/domain/cluster/state/ClusterRequestLimitVO.java
  24. +28
    -5
      sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/domain/cluster/state/ClusterServerStateVO.java
  25. +1
    -1
      sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/domain/cluster/state/ClusterStateSimpleEntity.java
  26. +72
    -0
      sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/domain/cluster/state/ClusterUniversalStatePairVO.java
  27. +1
    -1
      sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/domain/cluster/state/ClusterUniversalStateVO.java
  28. +15
    -5
      sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/metric/MetricFetcher.java
  29. +2
    -2
      sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/rule/FlowRuleApiProvider.java
  30. +2
    -2
      sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/rule/FlowRuleApiPublisher.java
  31. +58
    -0
      sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/service/ClusterAssignService.java
  32. +235
    -0
      sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/service/ClusterAssignServiceImpl.java
  33. +72
    -8
      sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/service/ClusterConfigService.java
  34. +72
    -0
      sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/util/AsyncUtils.java
  35. +138
    -0
      sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/util/ClusterEntityUtils.java
  36. +72
    -0
      sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/util/MachineUtils.java
  37. +3
    -1
      sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/view/FlowControllerV1.java
  38. +2
    -0
      sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/view/FlowControllerV2.java
  39. +104
    -0
      sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/view/cluster/ClusterAssignController.java
  40. +80
    -17
      sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/view/cluster/ClusterConfigController.java
  41. +1
    -0
      sentinel-dashboard/src/test/java/com/taobao/csp/sentinel/dashboard/rule/nacos/NacosConfigUtil.java
  42. +62
    -0
      sentinel-demo/sentinel-demo-cluster/sentinel-demo-cluster-embedded/src/main/java/com/alibaba/csp/sentinel/demo/cluster/ClusterClientDemo.java

+ 6
- 6
sentinel-dashboard/pom.xml View File

@@ -56,12 +56,6 @@
<artifactId>spring-boot-starter-logging</artifactId>
<version>${spring.boot.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<version>${spring.boot.version}</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
@@ -105,11 +99,17 @@
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
</dependency>

<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

<build>


+ 7
- 0
sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/DashboardApplication.java View File

@@ -15,6 +15,8 @@
*/
package com.taobao.csp.sentinel.dashboard;

import com.alibaba.csp.sentinel.init.InitExecutor;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@@ -27,6 +29,11 @@ import org.springframework.boot.autoconfigure.SpringBootApplication;
public class DashboardApplication {

public static void main(String[] args) {
triggerSentinelInit();
SpringApplication.run(DashboardApplication.class, args);
}

private static void triggerSentinelInit() {
new Thread(() -> InitExecutor.doInit()).start();
}
}

sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/util/MachineUtil.java → sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/client/CommandFailedException.java View File

@@ -13,20 +13,21 @@
* 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;
package com.taobao.csp.sentinel.dashboard.client;

/**
* @author Eric Zhao
*/
public final class MachineUtil {
public class CommandFailedException extends RuntimeException {

public CommandFailedException() {}

public CommandFailedException(String message) {
super(message);
}

public static boolean isMachineHealth(MachineInfo machine) {
if (machine == null) {
return false;
}
return System.currentTimeMillis() - machine.getTimestamp().getTime() < MachineDiscovery.MAX_CLIENT_LIVE_TIME_MS;
@Override
public synchronized Throwable fillInStackTrace() {
return this;
}
}

+ 6
- 0
sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/client/CommandNotFoundException.java View File

@@ -20,9 +20,15 @@ package com.taobao.csp.sentinel.dashboard.client;
* @since 0.2.1
*/
public class CommandNotFoundException extends Exception {

public CommandNotFoundException() { }

public CommandNotFoundException(String message) {
super(message);
}

@Override
public synchronized Throwable fillInStackTrace() {
return this;
}
}

+ 118
- 114
sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/client/SentinelApiClient.java View File

@@ -29,6 +29,7 @@ import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;

import com.alibaba.csp.sentinel.command.CommandConstants;
import com.alibaba.csp.sentinel.config.SentinelConfig;
import com.alibaba.csp.sentinel.command.vo.NodeVo;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRule;
@@ -43,8 +44,9 @@ 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.ClusterClientInfoVO;
import com.taobao.csp.sentinel.dashboard.domain.cluster.state.ClusterServerStateVO;
import com.taobao.csp.sentinel.dashboard.domain.cluster.state.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;
@@ -63,6 +65,8 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import static com.taobao.csp.sentinel.dashboard.util.AsyncUtils.*;

/**
* Communicate with Sentinel client.
*
@@ -104,7 +108,7 @@ public class SentinelApiClient {
private final boolean enableHttps = false;

public SentinelApiClient() {
IOReactorConfig ioConfig = IOReactorConfig.custom().setConnectTimeout(3000).setSoTimeout(3000)
IOReactorConfig ioConfig = IOReactorConfig.custom().setConnectTimeout(3000).setSoTimeout(10000)
.setIoThreadCount(Runtime.getRuntime().availableProcessors() * 2).build();
httpClient = HttpAsyncClients.custom().setRedirectStrategy(new DefaultRedirectStrategy() {
@Override
@@ -115,6 +119,109 @@ public class SentinelApiClient {
httpClient.start();
}

private boolean isSuccess(int statusCode) {
return statusCode >= 200 && statusCode < 300;
}

private boolean isCommandNotFound(int statusCode, String body) {
return statusCode == 400 && StringUtil.isNotEmpty(body) && body.contains(CommandConstants.MSG_UNKNOWN_COMMAND_PREFIX);
}

private CompletableFuture<String> executeCommand(String command, URI uri) {
CompletableFuture<String> future = new CompletableFuture<>();
if (StringUtil.isBlank(command) || uri == null) {
future.completeExceptionally(new IllegalArgumentException("Bad URL or command name"));
return future;
}
final HttpGet httpGet = new HttpGet(uri);
httpClient.execute(httpGet, new FutureCallback<HttpResponse>() {
@Override
public void completed(final HttpResponse response) {
int statusCode = response.getStatusLine().getStatusCode();
try {
String value = getBody(response);
if (isSuccess(statusCode)) {
future.complete(value);
} else {
if (isCommandNotFound(statusCode, value)) {
future.completeExceptionally(new CommandNotFoundException(command));
} else {
future.completeExceptionally(new CommandFailedException(value));
}
}

} catch (Exception ex) {
future.completeExceptionally(ex);
logger.error("HTTP request failed: " + uri.toString(), ex);
}
}

@Override
public void failed(final Exception ex) {
future.completeExceptionally(ex);
logger.error("HTTP request failed: " + uri.toString(), ex);
}

@Override
public void cancelled() {
future.complete(null);
}
});
return future;
}

private String httpGetContent(String url) {
final HttpGet httpGet = new HttpGet(url);
final CountDownLatch latch = new CountDownLatch(1);
final AtomicReference<String> reference = new AtomicReference<>();
httpClient.execute(httpGet, new FutureCallback<HttpResponse>() {
@Override
public void completed(final HttpResponse response) {
try {
reference.set(getBody(response));
} catch (Exception e) {
logger.info("httpGetContent " + url + " error:", e);
} finally {
latch.countDown();
}
}

@Override
public void failed(final Exception ex) {
latch.countDown();
logger.info("httpGetContent " + url + " failed:", ex);
}

@Override
public void cancelled() {
latch.countDown();
}
});
try {
latch.await(5, TimeUnit.SECONDS);
} catch (Exception e) {
logger.info("wait http client error:", e);
}
return reference.get();
}

private String getBody(HttpResponse response) throws Exception {
Charset charset = null;
try {
String contentTypeStr = response.getFirstHeader("Content-type").getValue();
if (StringUtil.isNotEmpty(contentTypeStr)) {
ContentType contentType = ContentType.parse(contentTypeStr);
charset = contentType.getCharset();
}
} catch (Exception ignore) {
}
return EntityUtils.toString(response.getEntity(), charset != null ? charset : DEFAULT_CHARSET);
}

public void close() throws Exception {
httpClient.close();
}

public List<NodeVo> fetchResourceOfMachine(String ip, int port, String type) {
String url = "http://" + ip + ":" + port + "/" + RESOURCE_URL_PATH + "?type=" + type;
String body = httpGetContent(url);
@@ -388,7 +495,7 @@ public class SentinelApiClient {
.setParameter("data", data);
return executeCommand(SET_PARAM_RULE_PATH, uriBuilder.build())
.thenCompose(e -> {
if ("success".equals(e)) {
if (CommandConstants.MSG_SUCCESS.equals(e)) {
return CompletableFuture.completedFuture(null);
} else {
logger.warn("Push parameter flow rules to client failed: " + e);
@@ -401,109 +508,6 @@ public class SentinelApiClient {
}
}

private boolean isSuccess(int statusCode) {
return statusCode >= 200 && statusCode < 300;
}

private CompletableFuture<String> executeCommand(String command, URI uri) {
CompletableFuture<String> future = new CompletableFuture<>();
if (StringUtil.isBlank(command) || uri == null) {
future.completeExceptionally(new IllegalArgumentException("Bad URL or command name"));
return future;
}
final HttpGet httpGet = new HttpGet(uri);
httpClient.execute(httpGet, new FutureCallback<HttpResponse>() {
@Override
public void completed(final HttpResponse response) {
int statusCode = response.getStatusLine().getStatusCode();
try {
String value = getBody(response);
if (isSuccess(statusCode)) {
future.complete(value);
} else {
if (statusCode == 400) {
future.completeExceptionally(new CommandNotFoundException(command));
} else {
future.completeExceptionally(new IllegalStateException(value));
}
}

} catch (Exception ex) {
future.completeExceptionally(ex);
logger.error("HTTP request failed: " + uri.toString(), ex);
}
}

@Override
public void failed(final Exception ex) {
future.completeExceptionally(ex);
logger.error("HTTP request failed: " + uri.toString(), ex);
}

@Override
public void cancelled() {
future.complete(null);
}
});
return future;
}

private String httpGetContent(String url) {
final HttpGet httpGet = new HttpGet(url);
final CountDownLatch latch = new CountDownLatch(1);
final AtomicReference<String> reference = new AtomicReference<>();
httpClient.execute(httpGet, new FutureCallback<HttpResponse>() {
@Override
public void completed(final HttpResponse response) {
try {
reference.set(getBody(response));
} catch (Exception e) {
logger.info("httpGetContent " + url + " error:", e);
} finally {
latch.countDown();
}
}

@Override
public void failed(final Exception ex) {
latch.countDown();
logger.info("httpGetContent " + url + " failed:", ex);
}

@Override
public void cancelled() {
latch.countDown();
}
});
try {
latch.await(5, TimeUnit.SECONDS);
} catch (Exception e) {
logger.info("wait http client error:", e);
}
return reference.get();
}

private String getBody(HttpResponse response) throws Exception {
Charset charset = null;
try {
String contentTypeStr = response.getFirstHeader("Content-type").getValue();
ContentType contentType = ContentType.parse(contentTypeStr);
charset = contentType.getCharset();
} catch (Exception ignore) {
}
return EntityUtils.toString(response.getEntity(), charset != null ? charset : DEFAULT_CHARSET);
}

public void close() throws Exception {
httpClient.close();
}

private <R> CompletableFuture<R> newFailedFuture(Throwable ex) {
CompletableFuture<R> future = new CompletableFuture<>();
future.completeExceptionally(ex);
return future;
}

// Cluster related

public CompletableFuture<ClusterStateSimpleEntity> fetchClusterMode(String app, String ip, int port) {
@@ -533,7 +537,7 @@ public class SentinelApiClient {
.setParameter("mode", String.valueOf(mode));
return executeCommand(MODIFY_CLUSTER_MODE_PATH, uriBuilder.build())
.thenCompose(e -> {
if ("success".equals(e)) {
if (CommandConstants.MSG_SUCCESS.equals(e)) {
return CompletableFuture.completedFuture(null);
} else {
logger.warn("Error when modifying cluster mode: " + e);
@@ -546,7 +550,7 @@ public class SentinelApiClient {
}
}

public CompletableFuture<ClusterClientConfig> fetchClusterClientConfig(String app, String ip, int port) {
public CompletableFuture<ClusterClientInfoVO> fetchClusterClientInfoAndConfig(String app, String ip, int port) {
if (StringUtil.isBlank(ip) || port <= 0) {
return newFailedFuture(new IllegalArgumentException("Invalid parameter"));
}
@@ -555,7 +559,7 @@ public class SentinelApiClient {
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));
.thenApply(r -> JSON.parseObject(r, ClusterClientInfoVO.class));
} catch (Exception ex) {
logger.warn("Error when fetching cluster client config", ex);
return newFailedFuture(ex);
@@ -573,7 +577,7 @@ public class SentinelApiClient {
.setParameter("data", JSON.toJSONString(config));
return executeCommand(MODIFY_CLUSTER_MODE_PATH, uriBuilder.build())
.thenCompose(e -> {
if ("success".equals(e)) {
if (CommandConstants.MSG_SUCCESS.equals(e)) {
return CompletableFuture.completedFuture(null);
} else {
logger.warn("Error when modifying cluster client config: " + e);
@@ -597,7 +601,7 @@ public class SentinelApiClient {
.setParameter("data", JSON.toJSONString(config));
return executeCommand(MODIFY_CLUSTER_SERVER_FLOW_CONFIG_PATH, uriBuilder.build())
.thenCompose(e -> {
if ("success".equals(e)) {
if (CommandConstants.MSG_SUCCESS.equals(e)) {
return CompletableFuture.completedFuture(null);
} else {
logger.warn("Error when modifying cluster server flow config: " + e);
@@ -622,7 +626,7 @@ public class SentinelApiClient {
.setParameter("idleSeconds", config.getIdleSeconds().toString());
return executeCommand(MODIFY_CLUSTER_SERVER_TRANSPORT_CONFIG_PATH, uriBuilder.build())
.thenCompose(e -> {
if ("success".equals(e)) {
if (CommandConstants.MSG_SUCCESS.equals(e)) {
return CompletableFuture.completedFuture(null);
} else {
logger.warn("Error when modifying cluster server transport config: " + e);
@@ -646,7 +650,7 @@ public class SentinelApiClient {
.setParameter("data", JSON.toJSONString(set));
return executeCommand(MODIFY_CLUSTER_SERVER_NAMESPACE_SET_PATH, uriBuilder.build())
.thenCompose(e -> {
if ("success".equals(e)) {
if (CommandConstants.MSG_SUCCESS.equals(e)) {
return CompletableFuture.completedFuture(null);
} else {
logger.warn("Error when modifying cluster server NamespaceSet: " + e);


+ 1
- 1
sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/discovery/MachineDiscovery.java View File

@@ -21,7 +21,7 @@ import java.util.Set;
public interface MachineDiscovery {

long MAX_CLIENT_LIVE_TIME_MS = 1000 * 60 * 5;
String UNKNOWN_APP_NAME = "UNKNOWN";
String UNKNOWN_APP_NAME = "CLUSTER_NOT_STARTED";

List<String> getAppNames();



+ 4
- 0
sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/discovery/MachineInfo.java View File

@@ -41,6 +41,10 @@ public class MachineInfo implements Comparable<MachineInfo> {
return machineInfo;
}

public String toHostPort() {
return ip + ":" + port;
}

public Integer getPort() {
return port;
}


+ 66
- 0
sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/domain/cluster/ClusterAppAssignResultVO.java View File

@@ -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.Set;

/**
* @author Eric Zhao
* @since 1.4.1
*/
public class ClusterAppAssignResultVO {

private Set<String> failedServerSet;
private Set<String> failedClientSet;

private Integer totalCount;

public Set<String> getFailedServerSet() {
return failedServerSet;
}

public ClusterAppAssignResultVO setFailedServerSet(Set<String> failedServerSet) {
this.failedServerSet = failedServerSet;
return this;
}

public Set<String> getFailedClientSet() {
return failedClientSet;
}

public ClusterAppAssignResultVO setFailedClientSet(Set<String> failedClientSet) {
this.failedClientSet = failedClientSet;
return this;
}

public Integer getTotalCount() {
return totalCount;
}

public ClusterAppAssignResultVO setTotalCount(Integer totalCount) {
this.totalCount = totalCount;
return this;
}

@Override
public String toString() {
return "ClusterAppAssignResultVO{" +
"failedServerSet=" + failedServerSet +
", failedClientSet=" + failedClientSet +
", totalCount=" + totalCount +
'}';
}
}

+ 58
- 0
sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/domain/cluster/ClusterAppFullAssignRequest.java View File

@@ -0,0 +1,58 @@
/*
* 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.request.ClusterAppAssignMap;

/**
* @author Eric Zhao
* @since 1.4.1
*/
public class ClusterAppFullAssignRequest {

private List<ClusterAppAssignMap> clusterMap;
private Set<String> remainingList;

public List<ClusterAppAssignMap> getClusterMap() {
return clusterMap;
}

public ClusterAppFullAssignRequest setClusterMap(
List<ClusterAppAssignMap> clusterMap) {
this.clusterMap = clusterMap;
return this;
}

public Set<String> getRemainingList() {
return remainingList;
}

public ClusterAppFullAssignRequest setRemainingList(Set<String> remainingList) {
this.remainingList = remainingList;
return this;
}

@Override
public String toString() {
return "ClusterAppFullAssignRequest{" +
"clusterMap=" + clusterMap +
", remainingList=" + remainingList +
'}';
}
}

+ 56
- 0
sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/domain/cluster/ClusterAppSingleServerAssignRequest.java View File

@@ -0,0 +1,56 @@
/*
* 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.request.ClusterAppAssignMap;

/**
* @author Eric Zhao
* @since 1.4.1
*/
public class ClusterAppSingleServerAssignRequest {

private ClusterAppAssignMap clusterMap;
private Set<String> remainingList;

public ClusterAppAssignMap getClusterMap() {
return clusterMap;
}

public ClusterAppSingleServerAssignRequest setClusterMap(ClusterAppAssignMap clusterMap) {
this.clusterMap = clusterMap;
return this;
}

public Set<String> getRemainingList() {
return remainingList;
}

public ClusterAppSingleServerAssignRequest setRemainingList(Set<String> remainingList) {
this.remainingList = remainingList;
return this;
}

@Override
public String toString() {
return "ClusterAppSingleServerAssignRequest{" +
"clusterMap=" + clusterMap +
", remainingList=" + remainingList +
'}';
}
}

+ 76
- 0
sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/domain/cluster/ClusterClientInfoVO.java View File

@@ -0,0 +1,76 @@
/*
* Copyright 1999-2018 Alibaba Group Holding Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.taobao.csp.sentinel.dashboard.domain.cluster;

/**
* @author Eric Zhao
* @since 1.4.1
*/
public class ClusterClientInfoVO {

private String serverHost;
private Integer serverPort;

private Integer clientState;

private Integer requestTimeout;

public String getServerHost() {
return serverHost;
}

public ClusterClientInfoVO setServerHost(String serverHost) {
this.serverHost = serverHost;
return this;
}

public Integer getServerPort() {
return serverPort;
}

public ClusterClientInfoVO setServerPort(Integer serverPort) {
this.serverPort = serverPort;
return this;
}

public Integer getClientState() {
return clientState;
}

public ClusterClientInfoVO setClientState(Integer clientState) {
this.clientState = clientState;
return this;
}

public Integer getRequestTimeout() {
return requestTimeout;
}

public ClusterClientInfoVO setRequestTimeout(Integer requestTimeout) {
this.requestTimeout = requestTimeout;
return this;
}

@Override
public String toString() {
return "ClusterClientInfoVO{" +
"serverHost='" + serverHost + '\'' +
", serverPort=" + serverPort +
", clientState=" + clientState +
", requestTimeout=" + requestTimeout +
'}';
}
}

+ 91
- 0
sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/domain/cluster/ClusterGroupEntity.java View File

@@ -0,0 +1,91 @@
/*
* 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.HashSet;
import java.util.Set;

/**
* @author Eric Zhao
* @since 1.4.1
*/
public class ClusterGroupEntity {

private String machineId;

private String ip;
private Integer port;

private Set<String> clientSet = new HashSet<>();

private Boolean belongToApp;

public String getMachineId() {
return machineId;
}

public ClusterGroupEntity setMachineId(String machineId) {
this.machineId = machineId;
return this;
}

public String getIp() {
return ip;
}

public ClusterGroupEntity setIp(String ip) {
this.ip = ip;
return this;
}

public Integer getPort() {
return port;
}

public ClusterGroupEntity setPort(Integer port) {
this.port = port;
return this;
}

public Set<String> getClientSet() {
return clientSet;
}

public ClusterGroupEntity setClientSet(Set<String> clientSet) {
this.clientSet = clientSet;
return this;
}

public Boolean getBelongToApp() {
return belongToApp;
}

public ClusterGroupEntity setBelongToApp(Boolean belongToApp) {
this.belongToApp = belongToApp;
return this;
}

@Override
public String toString() {
return "ClusterGroupEntity{" +
"machineId='" + machineId + '\'' +
", ip='" + ip + '\'' +
", port=" + port +
", clientSet=" + clientSet +
", belongToApp=" + belongToApp +
'}';
}
}

+ 63
- 0
sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/domain/cluster/ClusterStateSingleVO.java View File

@@ -0,0 +1,63 @@
/*
* 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.1
*/
public class ClusterStateSingleVO {

private String address;
private Integer mode;
private String target;

public String getAddress() {
return address;
}

public ClusterStateSingleVO setAddress(String address) {
this.address = address;
return this;
}

public Integer getMode() {
return mode;
}

public ClusterStateSingleVO setMode(Integer mode) {
this.mode = mode;
return this;
}

public String getTarget() {
return target;
}

public ClusterStateSingleVO setTarget(String target) {
this.target = target;
return this;
}

@Override
public String toString() {
return "ClusterStateSingleVO{" +
"address='" + address + '\'' +
", mode=" + mode +
", target='" + target + '\'' +
'}';
}
}

+ 13
- 0
sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/domain/cluster/config/ServerFlowConfig.java View File

@@ -26,6 +26,7 @@ public class ServerFlowConfig {

public static final int DEFAULT_INTERVAL_MS = 1000;
public static final int DEFAULT_SAMPLE_COUNT= 10;
public static final double DEFAULT_MAX_ALLOWED_QPS= 30000;

private final String namespace;

@@ -34,6 +35,8 @@ public class ServerFlowConfig {
private Integer intervalMs = DEFAULT_INTERVAL_MS;
private Integer sampleCount = DEFAULT_SAMPLE_COUNT;

private Double maxAllowedQps = DEFAULT_MAX_ALLOWED_QPS;

public ServerFlowConfig() {
this("default");
}
@@ -82,6 +85,15 @@ public class ServerFlowConfig {
return this;
}

public Double getMaxAllowedQps() {
return maxAllowedQps;
}

public ServerFlowConfig setMaxAllowedQps(Double maxAllowedQps) {
this.maxAllowedQps = maxAllowedQps;
return this;
}

@Override
public String toString() {
return "ServerFlowConfig{" +
@@ -90,6 +102,7 @@ public class ServerFlowConfig {
", maxOccupyRatio=" + maxOccupyRatio +
", intervalMs=" + intervalMs +
", sampleCount=" + sampleCount +
", maxAllowedQps=" + maxAllowedQps +
'}';
}
}

+ 1
- 1
sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/domain/cluster/config/ServerTransportConfig.java View File

@@ -21,7 +21,7 @@ package com.taobao.csp.sentinel.dashboard.domain.cluster.config;
*/
public class ServerTransportConfig {

public static final int DEFAULT_PORT = 8730;
public static final int DEFAULT_PORT = 18730;
public static final int DEFAULT_IDLE_SECONDS = 600;

private Integer port;


+ 112
- 0
sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/domain/cluster/request/ClusterAppAssignMap.java View File

@@ -0,0 +1,112 @@
/*
* 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.request;

import java.util.Set;

/**
* @author Eric Zhao
* @since 1.4.1
*/
public class ClusterAppAssignMap {

private String machineId;
private String ip;
private Integer port;

private Boolean belongToApp;

private Set<String> clientSet;

private Set<String> namespaceSet;
private Double maxAllowedQps;

public String getMachineId() {
return machineId;
}

public ClusterAppAssignMap setMachineId(String machineId) {
this.machineId = machineId;
return this;
}

public String getIp() {
return ip;
}

public ClusterAppAssignMap setIp(String ip) {
this.ip = ip;
return this;
}

public Integer getPort() {
return port;
}

public ClusterAppAssignMap setPort(Integer port) {
this.port = port;
return this;
}

public Set<String> getClientSet() {
return clientSet;
}

public ClusterAppAssignMap setClientSet(Set<String> clientSet) {
this.clientSet = clientSet;
return this;
}

public Set<String> getNamespaceSet() {
return namespaceSet;
}

public ClusterAppAssignMap setNamespaceSet(Set<String> namespaceSet) {
this.namespaceSet = namespaceSet;
return this;
}

public Boolean getBelongToApp() {
return belongToApp;
}

public ClusterAppAssignMap setBelongToApp(Boolean belongToApp) {
this.belongToApp = belongToApp;
return this;
}

public Double getMaxAllowedQps() {
return maxAllowedQps;
}

public ClusterAppAssignMap setMaxAllowedQps(Double maxAllowedQps) {
this.maxAllowedQps = maxAllowedQps;
return this;
}

@Override
public String toString() {
return "ClusterAppAssignMap{" +
"machineId='" + machineId + '\'' +
", ip='" + ip + '\'' +
", port=" + port +
", belongToApp=" + belongToApp +
", clientSet=" + clientSet +
", namespaceSet=" + namespaceSet +
", maxAllowedQps=" + maxAllowedQps +
'}';
}
}

sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/domain/cluster/ClusterClientModifyRequest.java → sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/domain/cluster/request/ClusterClientModifyRequest.java View File

@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.taobao.csp.sentinel.dashboard.domain.cluster;
package com.taobao.csp.sentinel.dashboard.domain.cluster.request;

import com.taobao.csp.sentinel.dashboard.domain.cluster.config.ClusterClientConfig;


sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/domain/cluster/ClusterModifyRequest.java → sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/domain/cluster/request/ClusterModifyRequest.java View File

@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.taobao.csp.sentinel.dashboard.domain.cluster;
package com.taobao.csp.sentinel.dashboard.domain.cluster.request;

/**
* @author Eric Zhao

sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/domain/cluster/ClusterServerModifyRequest.java → sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/domain/cluster/request/ClusterServerModifyRequest.java View File

@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.taobao.csp.sentinel.dashboard.domain.cluster;
package com.taobao.csp.sentinel.dashboard.domain.cluster.request;

import java.util.Set;


+ 79
- 0
sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/domain/cluster/state/AppClusterClientStateWrapVO.java View File

@@ -0,0 +1,79 @@
/*
* 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.state;

/**
* @author Eric Zhao
* @since 1.4.1
*/
public class AppClusterClientStateWrapVO {

/**
* {ip}@{transport_command_port}.
*/
private String id;

private Integer commandPort;
private String ip;

private ClusterClientStateVO state;

public String getId() {
return id;
}

public AppClusterClientStateWrapVO setId(String id) {
this.id = id;
return this;
}

public String getIp() {
return ip;
}

public AppClusterClientStateWrapVO setIp(String ip) {
this.ip = ip;
return this;
}

public ClusterClientStateVO getState() {
return state;
}

public AppClusterClientStateWrapVO setState(ClusterClientStateVO state) {
this.state = state;
return this;
}

public Integer getCommandPort() {
return commandPort;
}

public AppClusterClientStateWrapVO setCommandPort(Integer commandPort) {
this.commandPort = commandPort;
return this;
}

@Override
public String toString() {
return "AppClusterClientStateWrapVO{" +
"id='" + id + '\'' +
", commandPort=" + commandPort +
", ip='" + ip + '\'' +
", state=" + state +
'}';
}
}

+ 102
- 0
sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/domain/cluster/state/AppClusterServerStateWrapVO.java View File

@@ -0,0 +1,102 @@
/*
* 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.state;

/**
* @author Eric Zhao
* @since 1.4.1
*/
public class AppClusterServerStateWrapVO {

/**
* {ip}@{transport_command_port}.
*/
private String id;

private String ip;
private Integer port;

private Integer connectedCount;

private Boolean belongToApp;

private ClusterServerStateVO state;

public String getId() {
return id;
}

public AppClusterServerStateWrapVO setId(String id) {
this.id = id;
return this;
}

public String getIp() {
return ip;
}

public AppClusterServerStateWrapVO setIp(String ip) {
this.ip = ip;
return this;
}

public Integer getPort() {
return port;
}

public AppClusterServerStateWrapVO setPort(Integer port) {
this.port = port;
return this;
}

public Boolean getBelongToApp() {
return belongToApp;
}

public AppClusterServerStateWrapVO setBelongToApp(Boolean belongToApp) {
this.belongToApp = belongToApp;
return this;
}

public Integer getConnectedCount() {
return connectedCount;
}

public AppClusterServerStateWrapVO setConnectedCount(Integer connectedCount) {
this.connectedCount = connectedCount;
return this;
}

public ClusterServerStateVO getState() {
return state;
}

public AppClusterServerStateWrapVO setState(ClusterServerStateVO state) {
this.state = state;
return this;
}

@Override
public String toString() {
return "AppClusterServerStateWrapVO{" +
"id='" + id + '\'' +
", ip='" + ip + '\'' +
", port='" + port + '\'' +
", belongToApp=" + belongToApp +
", state=" + state +
'}';
}
}

sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/domain/cluster/ClusterClientStateVO.java → sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/domain/cluster/state/ClusterClientStateVO.java View File

@@ -13,9 +13,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.taobao.csp.sentinel.dashboard.domain.cluster;
package com.taobao.csp.sentinel.dashboard.domain.cluster.state;

import com.taobao.csp.sentinel.dashboard.domain.cluster.config.ClusterClientConfig;
import com.taobao.csp.sentinel.dashboard.domain.cluster.ClusterClientInfoVO;

/**
* @author Eric Zhao
@@ -23,14 +23,16 @@ import com.taobao.csp.sentinel.dashboard.domain.cluster.config.ClusterClientConf
*/
public class ClusterClientStateVO {

private ClusterClientConfig clientConfig;
/**
* Cluster token client state.
*/
private ClusterClientInfoVO clientConfig;

public ClusterClientConfig getClientConfig() {
public ClusterClientInfoVO getClientConfig() {
return clientConfig;
}

public ClusterClientStateVO setClientConfig(
ClusterClientConfig clientConfig) {
public ClusterClientStateVO setClientConfig(ClusterClientInfoVO clientConfig) {
this.clientConfig = clientConfig;
return this;
}

+ 63
- 0
sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/domain/cluster/state/ClusterRequestLimitVO.java View File

@@ -0,0 +1,63 @@
/*
* 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.state;

/**
* @author Eric Zhao
* @since 1.4.1
*/
public class ClusterRequestLimitVO {

private String namespace;
private Double currentQps;
private Double maxAllowedQps;

public String getNamespace() {
return namespace;
}

public ClusterRequestLimitVO setNamespace(String namespace) {
this.namespace = namespace;
return this;
}

public Double getCurrentQps() {
return currentQps;
}

public ClusterRequestLimitVO setCurrentQps(Double currentQps) {
this.currentQps = currentQps;
return this;
}

public Double getMaxAllowedQps() {
return maxAllowedQps;
}

public ClusterRequestLimitVO setMaxAllowedQps(Double maxAllowedQps) {
this.maxAllowedQps = maxAllowedQps;
return this;
}

@Override
public String toString() {
return "ClusterRequestLimitVO{" +
"namespace='" + namespace + '\'' +
", currentQps=" + currentQps +
", maxAllowedQps=" + maxAllowedQps +
'}';
}
}

sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/domain/cluster/ClusterServerStateVO.java → sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/domain/cluster/state/ClusterServerStateVO.java View File

@@ -13,11 +13,12 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.taobao.csp.sentinel.dashboard.domain.cluster;
package com.taobao.csp.sentinel.dashboard.domain.cluster.state;

import java.util.List;
import java.util.Set;

import com.taobao.csp.sentinel.dashboard.domain.cluster.ConnectionGroupVO;
import com.taobao.csp.sentinel.dashboard.domain.cluster.config.ServerFlowConfig;
import com.taobao.csp.sentinel.dashboard.domain.cluster.config.ServerTransportConfig;

@@ -32,14 +33,17 @@ public class ClusterServerStateVO {
private Set<String> namespaceSet;

private Integer port;

private List<ConnectionGroupVO> connection;
private List<ClusterRequestLimitVO> requestLimitData;

private Boolean embedded;

public ServerTransportConfig getTransport() {
return transport;
}

public ClusterServerStateVO setTransport(
ServerTransportConfig transport) {
public ClusterServerStateVO setTransport(ServerTransportConfig transport) {
this.transport = transport;
return this;
}
@@ -75,12 +79,29 @@ public class ClusterServerStateVO {
return connection;
}

public ClusterServerStateVO setConnection(
List<ConnectionGroupVO> connection) {
public ClusterServerStateVO setConnection(List<ConnectionGroupVO> connection) {
this.connection = connection;
return this;
}

public List<ClusterRequestLimitVO> getRequestLimitData() {
return requestLimitData;
}

public ClusterServerStateVO setRequestLimitData(List<ClusterRequestLimitVO> requestLimitData) {
this.requestLimitData = requestLimitData;
return this;
}

public Boolean getEmbedded() {
return embedded;
}

public ClusterServerStateVO setEmbedded(Boolean embedded) {
this.embedded = embedded;
return this;
}

@Override
public String toString() {
return "ClusterServerStateVO{" +
@@ -89,6 +110,8 @@ public class ClusterServerStateVO {
", namespaceSet=" + namespaceSet +
", port=" + port +
", connection=" + connection +
", requestLimitData=" + requestLimitData +
", embedded=" + embedded +
'}';
}
}

sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/domain/cluster/ClusterStateSimpleEntity.java → sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/domain/cluster/state/ClusterStateSimpleEntity.java View File

@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.taobao.csp.sentinel.dashboard.domain.cluster;
package com.taobao.csp.sentinel.dashboard.domain.cluster.state;

/**
* @author Eric Zhao

+ 72
- 0
sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/domain/cluster/state/ClusterUniversalStatePairVO.java View File

@@ -0,0 +1,72 @@
/*
* 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.state;

/**
* @author Eric Zhao
* @since 1.4.1
*/
public class ClusterUniversalStatePairVO {

private String ip;
private Integer commandPort;

private ClusterUniversalStateVO state;

public ClusterUniversalStatePairVO() {}

public ClusterUniversalStatePairVO(String ip, Integer commandPort, ClusterUniversalStateVO state) {
this.ip = ip;
this.commandPort = commandPort;
this.state = state;
}

public String getIp() {
return ip;
}

public ClusterUniversalStatePairVO setIp(String ip) {
this.ip = ip;
return this;
}

public Integer getCommandPort() {
return commandPort;
}

public ClusterUniversalStatePairVO setCommandPort(Integer commandPort) {
this.commandPort = commandPort;
return this;
}

public ClusterUniversalStateVO getState() {
return state;
}

public ClusterUniversalStatePairVO setState(ClusterUniversalStateVO state) {
this.state = state;
return this;
}

@Override
public String toString() {
return "ClusterUniversalStatePairVO{" +
"ip='" + ip + '\'' +
", commandPort=" + commandPort +
", state=" + state +
'}';
}
}

sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/domain/cluster/ClusterUniversalStateVO.java → sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/domain/cluster/state/ClusterUniversalStateVO.java View File

@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.taobao.csp.sentinel.dashboard.domain.cluster;
package com.taobao.csp.sentinel.dashboard.domain.cluster.state;

/**
* @author Eric Zhao

+ 15
- 5
sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/metric/MetricFetcher.java View File

@@ -15,6 +15,8 @@
*/
package com.taobao.csp.sentinel.dashboard.metric;

import java.net.ConnectException;
import java.net.SocketTimeoutException;
import java.nio.charset.Charset;
import java.util.Date;
import java.util.List;
@@ -41,6 +43,7 @@ import com.taobao.csp.sentinel.dashboard.datasource.entity.MetricEntity;
import com.taobao.csp.sentinel.dashboard.discovery.AppManagement;
import com.taobao.csp.sentinel.dashboard.discovery.MachineInfo;
import com.taobao.csp.sentinel.dashboard.repository.metric.MetricsRepository;
import com.taobao.csp.sentinel.dashboard.util.MachineUtils;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.concurrent.FutureCallback;
@@ -64,7 +67,6 @@ import org.springframework.stereotype.Component;
@Component
public class MetricFetcher {

public static final long MAX_CLIENT_LIVE_TIME_MS = 1000 * 60 * 5;
public static final String NO_METRICS = "No metrics";
private static final int HTTP_OK = 200;
private static final long MAX_LAST_FETCH_INTERVAL_MS = 1000 * 15;
@@ -183,7 +185,7 @@ public class MetricFetcher {
final CountDownLatch latch = new CountDownLatch(machines.size());
for (final MachineInfo machine : machines) {
// dead
if (System.currentTimeMillis() - machine.getTimestamp().getTime() > MAX_CLIENT_LIVE_TIME_MS) {
if (System.currentTimeMillis() - machine.getTimestamp().getTime() > MachineUtils.getMaxClientTimeout()) {
latch.countDown();
dead.incrementAndGet();
continue;
@@ -210,7 +212,13 @@ public class MetricFetcher {
latch.countDown();
fail.incrementAndGet();
httpGet.abort();
logger.error(msg + " metric " + url + " failed:", ex);
if (ex instanceof SocketTimeoutException) {
logger.error("Failed to fetch metric from <{}>: socket timeout", url);
} else if (ex instanceof ConnectException) {
logger.error("Failed to fetch metric from <{}> (ConnectionException: {})", url, ex.getMessage());
} else {
logger.error(msg + " metric " + url + " error", ex);
}
}

@Override
@@ -273,8 +281,10 @@ public class MetricFetcher {
Charset charset = null;
try {
String contentTypeStr = response.getFirstHeader("Content-type").getValue();
ContentType contentType = ContentType.parse(contentTypeStr);
charset = contentType.getCharset();
if (StringUtil.isNotEmpty(contentTypeStr)) {
ContentType contentType = ContentType.parse(contentTypeStr);
charset = contentType.getCharset();
}
} catch (Exception ignore) {
}
String body = EntityUtils.toString(response.getEntity(), charset != null ? charset : DEFAULT_CHARSET);


+ 2
- 2
sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/rule/FlowRuleApiProvider.java View File

@@ -25,7 +25,7 @@ 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 com.taobao.csp.sentinel.dashboard.util.MachineUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@@ -47,7 +47,7 @@ public class FlowRuleApiProvider implements DynamicRuleProvider<List<FlowRuleEnt
}
List<MachineInfo> list = appManagement.getDetailApp(appName).getMachines()
.stream()
.filter(MachineUtil::isMachineHealth)
.filter(MachineUtils::isMachineHealth)
.sorted((e1, e2) -> {
if (e1.getTimestamp().before(e2.getTimestamp())) {
return 1;


+ 2
- 2
sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/rule/FlowRuleApiPublisher.java View File

@@ -24,7 +24,7 @@ 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 com.taobao.csp.sentinel.dashboard.util.MachineUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@@ -51,7 +51,7 @@ public class FlowRuleApiPublisher implements DynamicRulePublisher<List<FlowRuleE
Set<MachineInfo> set = appManagement.getDetailApp(app).getMachines();

for (MachineInfo machine : set) {
if (!MachineUtil.isMachineHealth(machine)) {
if (!MachineUtils.isMachineHealth(machine)) {
continue;
}
// TODO: parse the results


+ 58
- 0
sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/service/ClusterAssignService.java View File

@@ -0,0 +1,58 @@
/*
* 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.List;
import java.util.Set;

import com.taobao.csp.sentinel.dashboard.domain.cluster.ClusterAppAssignResultVO;
import com.taobao.csp.sentinel.dashboard.domain.cluster.request.ClusterAppAssignMap;

/**
* @author Eric Zhao
* @since 1.4.1
*/
public interface ClusterAssignService {

/**
* Unbind a specific cluster server and its clients.
*
* @param app app name
* @param machineId valid machine ID ({@code host@commandPort})
* @return assign result
*/
ClusterAppAssignResultVO unbindClusterServer(String app, String machineId);

/**
* Unbind a set of cluster servers and its clients.
*
* @param app app name
* @param machineIdSet set of valid machine ID ({@code host@commandPort})
* @return assign result
*/
ClusterAppAssignResultVO unbindClusterServers(String app, Set<String> machineIdSet);

/**
* Apply cluster server and client assignment for provided app.
*
* @param app app name
* @param clusterMap cluster assign map (server -> clients)
* @param remainingSet unassigned set of machine ID
* @return assign result
*/
ClusterAppAssignResultVO applyAssignToApp(String app, List<ClusterAppAssignMap> clusterMap,
Set<String> remainingSet);
}

+ 235
- 0
sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/service/ClusterAssignServiceImpl.java View File

@@ -0,0 +1,235 @@
/*
* 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.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;

import com.alibaba.csp.sentinel.cluster.ClusterStateManager;
import com.alibaba.csp.sentinel.util.AssertUtil;
import com.alibaba.csp.sentinel.util.function.Tuple2;

import com.taobao.csp.sentinel.dashboard.client.SentinelApiClient;
import com.taobao.csp.sentinel.dashboard.domain.cluster.ClusterAppAssignResultVO;
import com.taobao.csp.sentinel.dashboard.domain.cluster.ClusterGroupEntity;
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.domain.cluster.request.ClusterAppAssignMap;
import com.taobao.csp.sentinel.dashboard.util.MachineUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

/**
* @author Eric Zhao
* @since 1.4.1
*/
@Service
public class ClusterAssignServiceImpl implements ClusterAssignService {

private final Logger LOGGER = LoggerFactory.getLogger(ClusterAssignServiceImpl.class);

@Autowired
private SentinelApiClient sentinelApiClient;
@Autowired
private ClusterConfigService clusterConfigService;

@Override
public ClusterAppAssignResultVO unbindClusterServer(String app, String machineId) {
AssertUtil.assertNotBlank(app, "app cannot be blank");
AssertUtil.assertNotBlank(machineId, "machineId cannot be blank");
Set<String> failedSet = new HashSet<>();

try {
ClusterGroupEntity entity = clusterConfigService.getClusterUniversalStateForAppMachine(app, machineId)
.get(10, TimeUnit.SECONDS);
Set<String> toModifySet = new HashSet<>();
toModifySet.add(machineId);
if (entity.getClientSet() != null) {
toModifySet.addAll(entity.getClientSet());
}
// Modify mode to NOT-STARTED for all chosen token servers and associated token clients.
toModifySet.parallelStream()
.map(MachineUtils::parseCommandIpAndPort)
.filter(Optional::isPresent)
.map(Optional::get)
.map(e -> {
CompletableFuture<Void> f = modifyMode(app, e.r1, e.r2, ClusterStateManager.CLUSTER_NOT_STARTED);
return Tuple2.of(e.r1 + '@' + e.r2, f);
})
.forEach(f -> handleFutureSync(f, failedSet));
} catch (Exception ex) {
Throwable e = ex instanceof ExecutionException ? ex.getCause() : ex;
LOGGER.error("Failed to unbind machine <{}>", machineId, e);
failedSet.add(machineId);
}
return new ClusterAppAssignResultVO()
.setFailedClientSet(failedSet)
.setFailedServerSet(new HashSet<>());
}

@Override
public ClusterAppAssignResultVO unbindClusterServers(String app, Set<String> machineIdSet) {
AssertUtil.assertNotBlank(app, "app cannot be blank");
AssertUtil.isTrue(machineIdSet != null && !machineIdSet.isEmpty(), "machineIdSet cannot be empty");
ClusterAppAssignResultVO result = new ClusterAppAssignResultVO()
.setFailedClientSet(new HashSet<>())
.setFailedServerSet(new HashSet<>());
for (String machineId : machineIdSet) {
ClusterAppAssignResultVO resultVO = unbindClusterServer(app, machineId);
result.getFailedClientSet().addAll(resultVO.getFailedClientSet());
result.getFailedServerSet().addAll(resultVO.getFailedServerSet());
}
return result;
}

@Override
public ClusterAppAssignResultVO applyAssignToApp(String app, List<ClusterAppAssignMap> clusterMap,
Set<String> remainingSet) {
AssertUtil.assertNotBlank(app, "app cannot be blank");
AssertUtil.notNull(clusterMap, "clusterMap cannot be null");
Set<String> failedServerSet = new HashSet<>();
Set<String> failedClientSet = new HashSet<>();

// Assign server and apply config.
clusterMap.stream()
.filter(Objects::nonNull)
.filter(ClusterAppAssignMap::getBelongToApp)
.map(e -> {
String ip = e.getIp();
int commandPort = parsePort(e);
CompletableFuture<Void> f = modifyMode(app, ip, commandPort, ClusterStateManager.CLUSTER_SERVER)
.thenCompose(v -> applyServerConfigChange(app, ip, commandPort, e));
return Tuple2.of(e.getMachineId(), f);
})
.forEach(t -> handleFutureSync(t, failedServerSet));

// Assign client of servers and apply config.
clusterMap.parallelStream()
.filter(Objects::nonNull)
.forEach(e -> applyAllClientConfigChange(app, e, failedClientSet));

// Unbind remaining (unassigned) machines.
applyAllRemainingMachineSet(app, remainingSet, failedClientSet);

return new ClusterAppAssignResultVO()
.setFailedClientSet(failedClientSet)
.setFailedServerSet(failedServerSet);
}

private void applyAllRemainingMachineSet(String app, Set<String> remainingSet, Set<String> failedSet) {
if (remainingSet == null || remainingSet.isEmpty()) {
return;
}
remainingSet.parallelStream()
.filter(Objects::nonNull)
.map(MachineUtils::parseCommandIpAndPort)
.filter(Optional::isPresent)
.map(Optional::get)
.map(ipPort -> {
String ip = ipPort.r1;
int commandPort = ipPort.r2;
CompletableFuture<Void> f = modifyMode(app, ip, commandPort, ClusterStateManager.CLUSTER_NOT_STARTED);
return Tuple2.of(ip + '@' + commandPort, f);
})
.forEach(t -> handleFutureSync(t, failedSet));
}

private void applyAllClientConfigChange(String app, ClusterAppAssignMap assignMap,
Set<String> failedSet) {
Set<String> clientSet = assignMap.getClientSet();
if (clientSet == null || clientSet.isEmpty()) {
return;
}
final String serverIp = assignMap.getIp();
final int serverPort = assignMap.getPort();
clientSet.stream()
.map(MachineUtils::parseCommandIpAndPort)
.filter(Optional::isPresent)
.map(Optional::get)
.map(ipPort -> {
CompletableFuture<Void> f = sentinelApiClient
.modifyClusterMode(app, ipPort.r1, ipPort.r2, ClusterStateManager.CLUSTER_CLIENT)
.thenCompose(v -> sentinelApiClient.modifyClusterClientConfig(app, ipPort.r1, ipPort.r2,
new ClusterClientConfig().setRequestTimeout(20)
.setServerHost(serverIp)
.setServerPort(serverPort)
));
return Tuple2.of(ipPort.r1 + '@' + ipPort.r2, f);
})
.forEach(t -> handleFutureSync(t, failedSet));
}

private void handleFutureSync(Tuple2<String, CompletableFuture<Void>> t, Set<String> failedSet) {
try {
t.r2.get(10, TimeUnit.SECONDS);
} catch (Exception ex) {
if (ex instanceof ExecutionException) {
LOGGER.error("Request for <{}> failed", t.r1, ex.getCause());
} else {
LOGGER.error("Request for <{}> failed", t.r1, ex);
}
failedSet.add(t.r1);
}
}

private CompletableFuture<Void> applyServerConfigChange(String app, String ip, int commandPort,
ClusterAppAssignMap assignMap) {
ServerTransportConfig transportConfig = new ServerTransportConfig()
.setPort(assignMap.getPort())
.setIdleSeconds(600);
return sentinelApiClient.modifyClusterServerTransportConfig(app, ip, commandPort, transportConfig)
.thenCompose(v -> applyServerFlowConfigChange(app, ip, commandPort, assignMap))
.thenCompose(v -> applyServerNamespaceSetConfig(app, ip, commandPort, assignMap));
}

private CompletableFuture<Void> applyServerFlowConfigChange(String app, String ip, int commandPort,
ClusterAppAssignMap assignMap) {
Double maxAllowedQps = assignMap.getMaxAllowedQps();
if (maxAllowedQps == null || maxAllowedQps <= 0 || maxAllowedQps > 20_0000) {
return CompletableFuture.completedFuture(null);
}
return sentinelApiClient.modifyClusterServerFlowConfig(app, ip, commandPort,
new ServerFlowConfig().setMaxAllowedQps(maxAllowedQps));
}

private CompletableFuture<Void> applyServerNamespaceSetConfig(String app, String ip, int commandPort,
ClusterAppAssignMap assignMap) {
Set<String> namespaceSet = assignMap.getNamespaceSet();
if (namespaceSet == null || namespaceSet.isEmpty()) {
return CompletableFuture.completedFuture(null);
}
return sentinelApiClient.modifyClusterServerNamespaceSet(app, ip, commandPort, namespaceSet);
}

private CompletableFuture<Void> modifyMode(String app, String ip, int port, int mode) {
return sentinelApiClient.modifyClusterMode(app, ip, port, mode);
}

private int parsePort(ClusterAppAssignMap assignMap) {
return MachineUtils.parseCommandPort(assignMap.getMachineId())
.orElse(ServerTransportConfig.DEFAULT_PORT);
}
}

+ 72
- 8
sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/service/ClusterConfigService.java View File

@@ -15,21 +15,30 @@
*/
package com.taobao.csp.sentinel.dashboard.service;

import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.stream.Collectors;

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.discovery.AppInfo;
import com.taobao.csp.sentinel.dashboard.discovery.AppManagement;
import com.taobao.csp.sentinel.dashboard.domain.cluster.ClusterGroupEntity;
import com.taobao.csp.sentinel.dashboard.domain.cluster.request.ClusterClientModifyRequest;
import com.taobao.csp.sentinel.dashboard.domain.cluster.state.ClusterClientStateVO;
import com.taobao.csp.sentinel.dashboard.domain.cluster.request.ClusterServerModifyRequest;
import com.taobao.csp.sentinel.dashboard.domain.cluster.state.ClusterUniversalStatePairVO;
import com.taobao.csp.sentinel.dashboard.domain.cluster.state.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 com.taobao.csp.sentinel.dashboard.util.AsyncUtils;
import com.taobao.csp.sentinel.dashboard.util.ClusterEntityUtils;
import com.taobao.csp.sentinel.dashboard.util.MachineUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@@ -42,6 +51,8 @@ public class ClusterConfigService {

@Autowired
private SentinelApiClient sentinelApiClient;
@Autowired
private AppManagement appManagement;

public CompletableFuture<Void> modifyClusterClientConfig(ClusterClientModifyRequest request) {
if (notClientRequestValid(request)) {
@@ -51,7 +62,7 @@ public class ClusterConfigService {
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));
.thenCompose(v -> sentinelApiClient.modifyClusterMode(app, ip, port, ClusterStateManager.CLUSTER_CLIENT));
}

private boolean notClientRequestValid(/*@NonNull */ ClusterClientModifyRequest request) {
@@ -83,12 +94,64 @@ public class ClusterConfigService {
.thenCompose(v -> sentinelApiClient.modifyClusterMode(app, ip, port, ClusterStateManager.CLUSTER_SERVER));
}

/**
* Get cluster state list of all available machines of provided application.
*
* @param app application name
* @return cluster state list of all available machines of the application
* @since 1.4.1
*/
public CompletableFuture<List<ClusterUniversalStatePairVO>> getClusterUniversalState(String app) {
if (StringUtil.isBlank(app)) {
return AsyncUtils.newFailedFuture(new IllegalArgumentException("app cannot be empty"));
}
AppInfo appInfo = appManagement.getDetailApp(app);
if (appInfo == null || appInfo.getMachines() == null) {
return CompletableFuture.completedFuture(new ArrayList<>());
}

List<CompletableFuture<ClusterUniversalStatePairVO>> futures = appInfo.getMachines().stream()
.filter(MachineUtils::isMachineHealth)
.map(machine -> getClusterUniversalState(app, machine.getIp(), machine.getPort())
.thenApply(e -> new ClusterUniversalStatePairVO(machine.getIp(), machine.getPort(), e)))
.collect(Collectors.toList());

return AsyncUtils.sequenceSuccessFuture(futures);
}

public CompletableFuture<ClusterGroupEntity> getClusterUniversalStateForAppMachine(String app, String machineId) {
if (StringUtil.isBlank(app)) {
return AsyncUtils.newFailedFuture(new IllegalArgumentException("app cannot be empty"));
}
AppInfo appInfo = appManagement.getDetailApp(app);
if (appInfo == null || appInfo.getMachines() == null) {
return AsyncUtils.newFailedFuture(new IllegalArgumentException("app does not have machines"));
}

boolean machineOk = appInfo.getMachines().stream()
.filter(MachineUtils::isMachineHealth)
.map(e -> e.getIp() + '@' + e.getPort())
.anyMatch(e -> e.equals(machineId));
if (!machineOk) {
return AsyncUtils.newFailedFuture(new IllegalStateException("machine does not exist or disconnected"));
}

return getClusterUniversalState(app)
.thenApply(ClusterEntityUtils::wrapToClusterGroup)
.thenCompose(e -> e.stream()
.filter(e1 -> e1.getMachineId().equals(machineId))
.findAny()
.map(CompletableFuture::completedFuture)
.orElse(AsyncUtils.newFailedFuture(new IllegalStateException("not a server: " + machineId)))
);
}

public CompletableFuture<ClusterUniversalStateVO> 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)
return sentinelApiClient.fetchClusterClientInfoAndConfig(app, ip, port)
.thenApply(cc -> vo.setClient(new ClusterClientStateVO().setClientConfig(cc)));
} else {
return CompletableFuture.completedFuture(vo);
@@ -111,6 +174,7 @@ public class ClusterConfigService {
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;
|| flowConfig.getIntervalMs() % flowConfig.getSampleCount() != 0
|| flowConfig.getMaxAllowedQps() == null || flowConfig.getMaxAllowedQps() < 0;
}
}

+ 72
- 0
sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/util/AsyncUtils.java View File

@@ -0,0 +1,72 @@
/*
* Copyright 1999-2018 Alibaba Group Holding Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.taobao.csp.sentinel.dashboard.util;

import java.util.List;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* @author Eric Zhao
* @since 1.4.1
*/
public final class AsyncUtils {

private static final Logger LOG = LoggerFactory.getLogger(AsyncUtils.class);

public static <R> CompletableFuture<R> newFailedFuture(Throwable ex) {
CompletableFuture<R> future = new CompletableFuture<>();
future.completeExceptionally(ex);
return future;
}

public static <R> CompletableFuture<List<R>> sequenceFuture(List<CompletableFuture<R>> futures) {
return CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]))
.thenApply(v -> futures.stream()
.map(AsyncUtils::getValue)
.filter(Objects::nonNull)
.collect(Collectors.toList())
);
}

public static <R> CompletableFuture<List<R>> sequenceSuccessFuture(List<CompletableFuture<R>> futures) {
return CompletableFuture.supplyAsync(() -> futures.parallelStream()
.map(AsyncUtils::getValue)
.filter(Objects::nonNull)
.collect(Collectors.toList())
);
}

public static <T> T getValue(CompletableFuture<T> future) {
try {
return future.get(10, TimeUnit.SECONDS);
} catch (Exception ex) {
LOG.error("getValue for async result failed", ex);
}
return null;
}

public static boolean isSuccessFuture(CompletableFuture future) {
return future.isDone() && !future.isCompletedExceptionally() && !future.isCancelled();
}

private AsyncUtils() {}
}

+ 138
- 0
sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/util/ClusterEntityUtils.java View File

@@ -0,0 +1,138 @@
/*
* Copyright 1999-2018 Alibaba Group Holding Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.taobao.csp.sentinel.dashboard.util;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.alibaba.csp.sentinel.cluster.ClusterStateManager;
import com.alibaba.csp.sentinel.util.StringUtil;

import com.taobao.csp.sentinel.dashboard.domain.cluster.ClusterGroupEntity;
import com.taobao.csp.sentinel.dashboard.domain.cluster.ConnectionGroupVO;
import com.taobao.csp.sentinel.dashboard.domain.cluster.state.AppClusterClientStateWrapVO;
import com.taobao.csp.sentinel.dashboard.domain.cluster.state.AppClusterServerStateWrapVO;
import com.taobao.csp.sentinel.dashboard.domain.cluster.state.ClusterClientStateVO;
import com.taobao.csp.sentinel.dashboard.domain.cluster.state.ClusterServerStateVO;
import com.taobao.csp.sentinel.dashboard.domain.cluster.state.ClusterUniversalStatePairVO;

/**
* @author Eric Zhao
* @since 1.4.1
*/
public final class ClusterEntityUtils {

public static List<AppClusterServerStateWrapVO> wrapToAppClusterServerState(
List<ClusterUniversalStatePairVO> list) {
if (list == null || list.isEmpty()) {
return new ArrayList<>();
}
Map<String, AppClusterServerStateWrapVO> map = new HashMap<>();
for (ClusterUniversalStatePairVO stateVO : list) {
int mode = stateVO.getState().getStateInfo().getMode();

if (mode == ClusterStateManager.CLUSTER_SERVER) {
String ip = stateVO.getIp();
String serverId = ip + '@' + stateVO.getCommandPort();
ClusterServerStateVO serverStateVO = stateVO.getState().getServer();
map.computeIfAbsent(serverId, v -> new AppClusterServerStateWrapVO()
.setId(serverId)
.setIp(ip)
.setPort(serverStateVO.getPort())
.setState(serverStateVO)
.setConnectedCount(serverStateVO.getConnection().stream()
.mapToInt(ConnectionGroupVO::getConnectedCount)
.sum()
)
);
}
}
return new ArrayList<>(map.values());
}

public static List<AppClusterClientStateWrapVO> wrapToAppClusterClientState(
List<ClusterUniversalStatePairVO> list) {
if (list == null || list.isEmpty()) {
return new ArrayList<>();
}
Map<String, AppClusterClientStateWrapVO> map = new HashMap<>();
for (ClusterUniversalStatePairVO stateVO : list) {
int mode = stateVO.getState().getStateInfo().getMode();

if (mode == ClusterStateManager.CLUSTER_CLIENT) {
String ip = stateVO.getIp();
String clientId = ip + '@' + stateVO.getCommandPort();
ClusterClientStateVO clientStateVO = stateVO.getState().getClient();
map.computeIfAbsent(clientId, v -> new AppClusterClientStateWrapVO()
.setId(clientId)
.setIp(ip)
.setState(clientStateVO)
.setCommandPort(stateVO.getCommandPort())
);
}
}
return new ArrayList<>(map.values());
}

public static List<ClusterGroupEntity> wrapToClusterGroup(List<ClusterUniversalStatePairVO> list) {
if (list == null || list.isEmpty()) {
return new ArrayList<>();
}
Map<String, ClusterGroupEntity> map = new HashMap<>();
for (ClusterUniversalStatePairVO stateVO : list) {
int mode = stateVO.getState().getStateInfo().getMode();
String ip = stateVO.getIp();
if (mode == ClusterStateManager.CLUSTER_SERVER) {
String serverAddress = getIp(ip);
int port = stateVO.getState().getServer().getPort();
map.computeIfAbsent(serverAddress, v -> new ClusterGroupEntity()
.setBelongToApp(true).setMachineId(ip + '@' + stateVO.getCommandPort())
.setIp(ip).setPort(port)
);
}
}
for (ClusterUniversalStatePairVO stateVO : list) {
int mode = stateVO.getState().getStateInfo().getMode();
String ip = stateVO.getIp();
if (mode == ClusterStateManager.CLUSTER_CLIENT) {
String targetServer = stateVO.getState().getClient().getClientConfig().getServerHost();
Integer targetPort = stateVO.getState().getClient().getClientConfig().getServerPort();
if (StringUtil.isBlank(targetServer) || targetPort == null || targetPort <= 0) {
continue;
}

ClusterGroupEntity group = map.computeIfAbsent(targetServer,
v -> new ClusterGroupEntity()
.setBelongToApp(true).setMachineId(targetServer)
.setIp(targetServer).setPort(targetPort)
);
group.getClientSet().add(ip + '@' + stateVO.getCommandPort());
}
}
return new ArrayList<>(map.values());
}

private static String getIp(String str) {
if (str.contains(":")) {
return str.split(":")[0];
}
return str;
}

private ClusterEntityUtils() {}
}

+ 72
- 0
sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/util/MachineUtils.java View File

@@ -0,0 +1,72 @@
/*
* Copyright 1999-2018 Alibaba Group Holding Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.taobao.csp.sentinel.dashboard.util;

import java.util.Optional;

import com.alibaba.csp.sentinel.util.StringUtil;
import com.alibaba.csp.sentinel.util.function.Tuple2;

import com.taobao.csp.sentinel.dashboard.discovery.MachineInfo;

/**
* @author Eric Zhao
*/
public final class MachineUtils {

public static final long DEFAULT_MAX_CLIENT_PING_TIMEOUT = 60 * 1000;

public static long getMaxClientTimeout() {
return DEFAULT_MAX_CLIENT_PING_TIMEOUT;
}

public static Optional<Integer> parseCommandPort(String machineIp) {
try {
if (!machineIp.contains("@")) {
return Optional.empty();
}
String[] str = machineIp.split("@");
if (str.length <= 1) {
return Optional.empty();
}
return Optional.of(Integer.parseInt(str[1]));
} catch (Exception ex) {
return Optional.empty();
}
}

public static Optional<Tuple2<String, Integer>> parseCommandIpAndPort(String machineIp) {
try {
if (StringUtil.isEmpty(machineIp) || !machineIp.contains("@")) {
return Optional.empty();
}
String[] str = machineIp.split("@");
if (str.length <= 1) {
return Optional.empty();
}
return Optional.of(Tuple2.of(str[0], Integer.parseInt(str[1])));
} catch (Exception ex) {
return Optional.empty();
}
}

public static boolean isMachineHealth(MachineInfo machine) {
if (machine == null) {
return false;
}
return System.currentTimeMillis() - machine.getTimestamp().getTime() < getMaxClientTimeout();
}
}

+ 3
- 1
sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/view/FlowControllerV1.java View File

@@ -134,6 +134,8 @@ public class FlowControllerV1 {
Date date = new Date();
entity.setGmtCreate(date);
entity.setGmtModified(date);
entity.setLimitApp(entity.getLimitApp().trim());
entity.setResource(entity.getResource().trim());
try {
entity = repository.save(entity);
} catch (Throwable throwable) {
@@ -224,7 +226,7 @@ public class FlowControllerV1 {
}

@DeleteMapping("/delete.json")
Result<?> delete(Long id) {
public Result<Long> delete(Long id) {
if (id == null) {
return Result.ofFail(-1, "id can't be null");
}


+ 2
- 0
sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/view/FlowControllerV2.java View File

@@ -137,6 +137,8 @@ public class FlowControllerV2 {
Date date = new Date();
entity.setGmtCreate(date);
entity.setGmtModified(date);
entity.setLimitApp(entity.getLimitApp().trim());
entity.setResource(entity.getResource().trim());
try {
entity = repository.save(entity);
publishRules(entity.getApp());


+ 104
- 0
sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/view/cluster/ClusterAssignController.java View File

@@ -0,0 +1,104 @@
/*
* 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.cluster;

import java.util.Collections;
import java.util.Set;

import com.alibaba.csp.sentinel.util.StringUtil;

import com.taobao.csp.sentinel.dashboard.domain.cluster.ClusterAppFullAssignRequest;
import com.taobao.csp.sentinel.dashboard.domain.cluster.ClusterAppAssignResultVO;
import com.taobao.csp.sentinel.dashboard.domain.cluster.ClusterAppSingleServerAssignRequest;
import com.taobao.csp.sentinel.dashboard.service.ClusterAssignService;
import com.taobao.csp.sentinel.dashboard.view.Result;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
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.RestController;

/**
* @author Eric Zhao
* @since 1.4.1
*/
@RestController
@RequestMapping("/cluster/assign")
public class ClusterAssignController {

private final Logger logger = LoggerFactory.getLogger(ClusterAssignController.class);

@Autowired
private ClusterAssignService clusterAssignService;

@PostMapping("/all_server/{app}")
public Result<ClusterAppAssignResultVO> apiAssignAllClusterServersOfApp(@PathVariable String app,
@RequestBody
ClusterAppFullAssignRequest assignRequest) {
if (StringUtil.isEmpty(app)) {
return Result.ofFail(-1, "app cannot be null or empty");
}
if (assignRequest == null || assignRequest.getClusterMap() == null
|| assignRequest.getRemainingList() == null) {
return Result.ofFail(-1, "bad request body");
}
try {
return Result.ofSuccess(clusterAssignService.applyAssignToApp(app, assignRequest.getClusterMap(),
assignRequest.getRemainingList()));
} catch (Throwable throwable) {
logger.error("Error when assigning full cluster servers for app: " + app, throwable);
return Result.ofFail(-1, throwable.getMessage());
}
}

@PostMapping("/single_server/{app}")
public Result<ClusterAppAssignResultVO> apiAssignSingleClusterServersOfApp(@PathVariable String app,
@RequestBody ClusterAppSingleServerAssignRequest assignRequest) {
if (StringUtil.isEmpty(app)) {
return Result.ofFail(-1, "app cannot be null or empty");
}
if (assignRequest == null || assignRequest.getClusterMap() == null) {
return Result.ofFail(-1, "bad request body");
}
try {
return Result.ofSuccess(clusterAssignService.applyAssignToApp(app, Collections.singletonList(assignRequest.getClusterMap()),
assignRequest.getRemainingList()));
} catch (Throwable throwable) {
logger.error("Error when assigning single cluster servers for app: " + app, throwable);
return Result.ofFail(-1, throwable.getMessage());
}
}

@PostMapping("/unbind_server/{app}")
public Result<ClusterAppAssignResultVO> apiUnbindClusterServersOfApp(@PathVariable String app,
@RequestBody Set<String> machineIds) {
if (StringUtil.isEmpty(app)) {
return Result.ofFail(-1, "app cannot be null or empty");
}
if (machineIds == null || machineIds.isEmpty()) {
return Result.ofFail(-1, "bad request body");
}
try {
return Result.ofSuccess(clusterAssignService.unbindClusterServers(app, machineIds));
} catch (Throwable throwable) {
logger.error("Error when unbinding cluster server {} for app <{}>", machineIds, app, throwable);
return Result.ofFail(-1, throwable.getMessage());
}
}
}

sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/view/ClusterConfigController.java → sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/view/cluster/ClusterConfigController.java View File

@@ -13,8 +13,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.taobao.csp.sentinel.dashboard.view;
package com.taobao.csp.sentinel.dashboard.view.cluster;

import java.util.List;
import java.util.Optional;
import java.util.concurrent.ExecutionException;

@@ -26,16 +27,22 @@ 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.domain.cluster.request.ClusterClientModifyRequest;
import com.taobao.csp.sentinel.dashboard.domain.cluster.request.ClusterModifyRequest;
import com.taobao.csp.sentinel.dashboard.domain.cluster.request.ClusterServerModifyRequest;
import com.taobao.csp.sentinel.dashboard.domain.cluster.state.AppClusterClientStateWrapVO;
import com.taobao.csp.sentinel.dashboard.domain.cluster.state.AppClusterServerStateWrapVO;
import com.taobao.csp.sentinel.dashboard.domain.cluster.state.ClusterUniversalStatePairVO;
import com.taobao.csp.sentinel.dashboard.domain.cluster.state.ClusterUniversalStateVO;
import com.taobao.csp.sentinel.dashboard.service.ClusterConfigService;
import com.taobao.csp.sentinel.dashboard.util.ClusterEntityUtils;
import com.taobao.csp.sentinel.dashboard.util.VersionUtils;
import com.taobao.csp.sentinel.dashboard.view.Result;
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.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
@@ -60,7 +67,7 @@ public class ClusterConfigController {
@Autowired
private ClusterConfigService clusterConfigService;

@PostMapping("/config/modify")
@PostMapping("/config/modify_single")
public Result<Boolean> apiModifyClusterConfig(@RequestBody String payload) {
if (StringUtil.isBlank(payload)) {
return Result.ofFail(-1, "empty request body");
@@ -94,18 +101,22 @@ public class ClusterConfigController {
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());
}
return errorResponse(ex);
} catch (Throwable ex) {
logger.error("Error when modifying cluster config", ex);
return Result.ofFail(-1, ex.getMessage());
}
}

@GetMapping("/state")
private <T> Result<T> errorResponse(ExecutionException ex) {
if (isNotSupported(ex.getCause())) {
return unsupportedVersion();
} else {
return Result.ofThrowable(-1, ex.getCause());
}
}

@GetMapping("/state_single")
public Result<ClusterUniversalStateVO> apiGetClusterState(@RequestParam String app,
@RequestParam String ip,
@RequestParam Integer port) {
@@ -127,17 +138,69 @@ public class ClusterConfigController {
.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());
}
return errorResponse(ex);
} catch (Throwable throwable) {
logger.error("Error when fetching cluster state", throwable);
return Result.ofFail(-1, throwable.getMessage());
}
}

@GetMapping("/server_state/{app}")
public Result<List<AppClusterServerStateWrapVO>> apiGetClusterServerStateOfApp(@PathVariable String app) {
if (StringUtil.isEmpty(app)) {
return Result.ofFail(-1, "app cannot be null or empty");
}
try {
return clusterConfigService.getClusterUniversalState(app)
.thenApply(ClusterEntityUtils::wrapToAppClusterServerState)
.thenApply(Result::ofSuccess)
.get();
} catch (ExecutionException ex) {
logger.error("Error when fetching cluster server state of app: " + app, ex.getCause());
return errorResponse(ex);
} catch (Throwable throwable) {
logger.error("Error when fetching cluster server state of app: " + app, throwable);
return Result.ofFail(-1, throwable.getMessage());
}
}

@GetMapping("/client_state/{app}")
public Result<List<AppClusterClientStateWrapVO>> apiGetClusterClientStateOfApp(@PathVariable String app) {
if (StringUtil.isEmpty(app)) {
return Result.ofFail(-1, "app cannot be null or empty");
}
try {
return clusterConfigService.getClusterUniversalState(app)
.thenApply(ClusterEntityUtils::wrapToAppClusterClientState)
.thenApply(Result::ofSuccess)
.get();
} catch (ExecutionException ex) {
logger.error("Error when fetching cluster token client state of app: " + app, ex.getCause());
return errorResponse(ex);
} catch (Throwable throwable) {
logger.error("Error when fetching cluster token client state of app: " + app, throwable);
return Result.ofFail(-1, throwable.getMessage());
}
}

@GetMapping("/state/{app}")
public Result<List<ClusterUniversalStatePairVO>> apiGetClusterStateOfApp(@PathVariable String app) {
if (StringUtil.isEmpty(app)) {
return Result.ofFail(-1, "app cannot be null or empty");
}
try {
return clusterConfigService.getClusterUniversalState(app)
.thenApply(Result::ofSuccess)
.get();
} catch (ExecutionException ex) {
logger.error("Error when fetching cluster state of app: " + app, ex.getCause());
return errorResponse(ex);
} catch (Throwable throwable) {
logger.error("Error when fetching cluster state of app: " + app, throwable);
return Result.ofFail(-1, throwable.getMessage());
}
}

private boolean isNotSupported(Throwable ex) {
return ex instanceof CommandNotFoundException;
}

+ 1
- 0
sentinel-dashboard/src/test/java/com/taobao/csp/sentinel/dashboard/rule/nacos/NacosConfigUtil.java View File

@@ -25,6 +25,7 @@ public final class NacosConfigUtil {
public static final String FLOW_DATA_ID_POSTFIX = "-flow-rules";
public static final String PARAM_FLOW_DATA_ID_POSTFIX = "-param-rules";
public static final String CLUSTER_MAP_DATA_ID_POSTFIX = "-cluster-map";

/**
* cc for `cluster-client`


+ 62
- 0
sentinel-demo/sentinel-demo-cluster/sentinel-demo-cluster-embedded/src/main/java/com/alibaba/csp/sentinel/demo/cluster/ClusterClientDemo.java View File

@@ -0,0 +1,62 @@
/*
* 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.alibaba.csp.sentinel.demo.cluster;

import com.alibaba.csp.sentinel.Entry;
import com.alibaba.csp.sentinel.EntryType;
import com.alibaba.csp.sentinel.SphU;
import com.alibaba.csp.sentinel.cluster.ClusterStateManager;
import com.alibaba.csp.sentinel.init.InitExecutor;
import com.alibaba.csp.sentinel.slots.block.BlockException;

/**
* <p>Run this demo with the following args: -Dproject.name=appA</p>
* <p>You need a token server running already.</p>
*
* @author Eric Zhao
*/
public class ClusterClientDemo {

public static void main(String[] args) {
InitExecutor.doInit();

// Manually schedule the cluster mode to client.
// In common, we need a scheduling system to modify the cluster mode automatically.
// Command HTTP API: http://<ip>:<port>/setClusterMode?mode=<xxx>
ClusterStateManager.setToClient();

String resourceName = "cluster-demo-entry";

// Assume we have a cluster flow rule for `demo-resource`: QPS = 5 in AVG_LOCAL mode.
for (int i = 0; i < 10; i++) {
tryEntry(resourceName);
}
}

private static void tryEntry(String res) {
Entry entry = null;
try {
entry = SphU.entry(res, EntryType.IN, 1, "abc", "def");
System.out.println("Passed");
} catch (BlockException ex) {
ex.printStackTrace();
} finally {
if (entry != null) {
entry.exit();
}
}
}
}

Loading…
Cancel
Save