- Add cluster token server management controller and service for app - Other enhancements and fixes Signed-off-by: Eric Zhao <sczyh16@gmail.com>master
@@ -56,12 +56,6 @@ | |||||
<artifactId>spring-boot-starter-logging</artifactId> | <artifactId>spring-boot-starter-logging</artifactId> | ||||
<version>${spring.boot.version}</version> | <version>${spring.boot.version}</version> | ||||
</dependency> | </dependency> | ||||
<dependency> | |||||
<groupId>org.springframework.boot</groupId> | |||||
<artifactId>spring-boot-devtools</artifactId> | |||||
<version>${spring.boot.version}</version> | |||||
<optional>true</optional> | |||||
</dependency> | |||||
<dependency> | <dependency> | ||||
<groupId>org.springframework.boot</groupId> | <groupId>org.springframework.boot</groupId> | ||||
<artifactId>spring-boot-starter-test</artifactId> | <artifactId>spring-boot-starter-test</artifactId> | ||||
@@ -105,11 +99,17 @@ | |||||
<groupId>com.alibaba</groupId> | <groupId>com.alibaba</groupId> | ||||
<artifactId>fastjson</artifactId> | <artifactId>fastjson</artifactId> | ||||
</dependency> | </dependency> | ||||
<dependency> | <dependency> | ||||
<groupId>junit</groupId> | <groupId>junit</groupId> | ||||
<artifactId>junit</artifactId> | <artifactId>junit</artifactId> | ||||
<scope>test</scope> | <scope>test</scope> | ||||
</dependency> | </dependency> | ||||
<dependency> | |||||
<groupId>org.mockito</groupId> | |||||
<artifactId>mockito-core</artifactId> | |||||
<scope>test</scope> | |||||
</dependency> | |||||
</dependencies> | </dependencies> | ||||
<build> | <build> | ||||
@@ -15,6 +15,8 @@ | |||||
*/ | */ | ||||
package com.taobao.csp.sentinel.dashboard; | package com.taobao.csp.sentinel.dashboard; | ||||
import com.alibaba.csp.sentinel.init.InitExecutor; | |||||
import org.springframework.boot.SpringApplication; | import org.springframework.boot.SpringApplication; | ||||
import org.springframework.boot.autoconfigure.SpringBootApplication; | import org.springframework.boot.autoconfigure.SpringBootApplication; | ||||
@@ -27,6 +29,11 @@ import org.springframework.boot.autoconfigure.SpringBootApplication; | |||||
public class DashboardApplication { | public class DashboardApplication { | ||||
public static void main(String[] args) { | public static void main(String[] args) { | ||||
triggerSentinelInit(); | |||||
SpringApplication.run(DashboardApplication.class, args); | SpringApplication.run(DashboardApplication.class, args); | ||||
} | } | ||||
private static void triggerSentinelInit() { | |||||
new Thread(() -> InitExecutor.doInit()).start(); | |||||
} | |||||
} | } |
@@ -13,20 +13,21 @@ | |||||
* See the License for the specific language governing permissions and | * See the License for the specific language governing permissions and | ||||
* limitations under the License. | * 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 | * @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; | |||||
} | } | ||||
} | } |
@@ -20,9 +20,15 @@ package com.taobao.csp.sentinel.dashboard.client; | |||||
* @since 0.2.1 | * @since 0.2.1 | ||||
*/ | */ | ||||
public class CommandNotFoundException extends Exception { | public class CommandNotFoundException extends Exception { | ||||
public CommandNotFoundException() { } | public CommandNotFoundException() { } | ||||
public CommandNotFoundException(String message) { | public CommandNotFoundException(String message) { | ||||
super(message); | super(message); | ||||
} | } | ||||
@Override | |||||
public synchronized Throwable fillInStackTrace() { | |||||
return this; | |||||
} | |||||
} | } |
@@ -29,6 +29,7 @@ import java.util.concurrent.TimeUnit; | |||||
import java.util.concurrent.atomic.AtomicReference; | import java.util.concurrent.atomic.AtomicReference; | ||||
import java.util.stream.Collectors; | import java.util.stream.Collectors; | ||||
import com.alibaba.csp.sentinel.command.CommandConstants; | |||||
import com.alibaba.csp.sentinel.config.SentinelConfig; | import com.alibaba.csp.sentinel.config.SentinelConfig; | ||||
import com.alibaba.csp.sentinel.command.vo.NodeVo; | import com.alibaba.csp.sentinel.command.vo.NodeVo; | ||||
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRule; | 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.FlowRuleEntity; | ||||
import com.taobao.csp.sentinel.dashboard.datasource.entity.rule.ParamFlowRuleEntity; | 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.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.ClusterClientConfig; | ||||
import com.taobao.csp.sentinel.dashboard.domain.cluster.config.ServerFlowConfig; | 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.config.ServerTransportConfig; | ||||
@@ -63,6 +65,8 @@ import org.slf4j.Logger; | |||||
import org.slf4j.LoggerFactory; | import org.slf4j.LoggerFactory; | ||||
import org.springframework.stereotype.Component; | import org.springframework.stereotype.Component; | ||||
import static com.taobao.csp.sentinel.dashboard.util.AsyncUtils.*; | |||||
/** | /** | ||||
* Communicate with Sentinel client. | * Communicate with Sentinel client. | ||||
* | * | ||||
@@ -104,7 +108,7 @@ public class SentinelApiClient { | |||||
private final boolean enableHttps = false; | private final boolean enableHttps = false; | ||||
public SentinelApiClient() { | public SentinelApiClient() { | ||||
IOReactorConfig ioConfig = IOReactorConfig.custom().setConnectTimeout(3000).setSoTimeout(3000) | |||||
IOReactorConfig ioConfig = IOReactorConfig.custom().setConnectTimeout(3000).setSoTimeout(10000) | |||||
.setIoThreadCount(Runtime.getRuntime().availableProcessors() * 2).build(); | .setIoThreadCount(Runtime.getRuntime().availableProcessors() * 2).build(); | ||||
httpClient = HttpAsyncClients.custom().setRedirectStrategy(new DefaultRedirectStrategy() { | httpClient = HttpAsyncClients.custom().setRedirectStrategy(new DefaultRedirectStrategy() { | ||||
@Override | @Override | ||||
@@ -115,6 +119,109 @@ public class SentinelApiClient { | |||||
httpClient.start(); | 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) { | public List<NodeVo> fetchResourceOfMachine(String ip, int port, String type) { | ||||
String url = "http://" + ip + ":" + port + "/" + RESOURCE_URL_PATH + "?type=" + type; | String url = "http://" + ip + ":" + port + "/" + RESOURCE_URL_PATH + "?type=" + type; | ||||
String body = httpGetContent(url); | String body = httpGetContent(url); | ||||
@@ -388,7 +495,7 @@ public class SentinelApiClient { | |||||
.setParameter("data", data); | .setParameter("data", data); | ||||
return executeCommand(SET_PARAM_RULE_PATH, uriBuilder.build()) | return executeCommand(SET_PARAM_RULE_PATH, uriBuilder.build()) | ||||
.thenCompose(e -> { | .thenCompose(e -> { | ||||
if ("success".equals(e)) { | |||||
if (CommandConstants.MSG_SUCCESS.equals(e)) { | |||||
return CompletableFuture.completedFuture(null); | return CompletableFuture.completedFuture(null); | ||||
} else { | } else { | ||||
logger.warn("Push parameter flow rules to client failed: " + e); | 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 | // Cluster related | ||||
public CompletableFuture<ClusterStateSimpleEntity> fetchClusterMode(String app, String ip, int port) { | public CompletableFuture<ClusterStateSimpleEntity> fetchClusterMode(String app, String ip, int port) { | ||||
@@ -533,7 +537,7 @@ public class SentinelApiClient { | |||||
.setParameter("mode", String.valueOf(mode)); | .setParameter("mode", String.valueOf(mode)); | ||||
return executeCommand(MODIFY_CLUSTER_MODE_PATH, uriBuilder.build()) | return executeCommand(MODIFY_CLUSTER_MODE_PATH, uriBuilder.build()) | ||||
.thenCompose(e -> { | .thenCompose(e -> { | ||||
if ("success".equals(e)) { | |||||
if (CommandConstants.MSG_SUCCESS.equals(e)) { | |||||
return CompletableFuture.completedFuture(null); | return CompletableFuture.completedFuture(null); | ||||
} else { | } else { | ||||
logger.warn("Error when modifying cluster mode: " + e); | 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) { | if (StringUtil.isBlank(ip) || port <= 0) { | ||||
return newFailedFuture(new IllegalArgumentException("Invalid parameter")); | return newFailedFuture(new IllegalArgumentException("Invalid parameter")); | ||||
} | } | ||||
@@ -555,7 +559,7 @@ public class SentinelApiClient { | |||||
uriBuilder.setScheme("http").setHost(ip).setPort(port) | uriBuilder.setScheme("http").setHost(ip).setPort(port) | ||||
.setPath(FETCH_CLUSTER_CLIENT_CONFIG_PATH); | .setPath(FETCH_CLUSTER_CLIENT_CONFIG_PATH); | ||||
return executeCommand(FETCH_CLUSTER_CLIENT_CONFIG_PATH, uriBuilder.build()) | 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) { | } catch (Exception ex) { | ||||
logger.warn("Error when fetching cluster client config", ex); | logger.warn("Error when fetching cluster client config", ex); | ||||
return newFailedFuture(ex); | return newFailedFuture(ex); | ||||
@@ -573,7 +577,7 @@ public class SentinelApiClient { | |||||
.setParameter("data", JSON.toJSONString(config)); | .setParameter("data", JSON.toJSONString(config)); | ||||
return executeCommand(MODIFY_CLUSTER_MODE_PATH, uriBuilder.build()) | return executeCommand(MODIFY_CLUSTER_MODE_PATH, uriBuilder.build()) | ||||
.thenCompose(e -> { | .thenCompose(e -> { | ||||
if ("success".equals(e)) { | |||||
if (CommandConstants.MSG_SUCCESS.equals(e)) { | |||||
return CompletableFuture.completedFuture(null); | return CompletableFuture.completedFuture(null); | ||||
} else { | } else { | ||||
logger.warn("Error when modifying cluster client config: " + e); | logger.warn("Error when modifying cluster client config: " + e); | ||||
@@ -597,7 +601,7 @@ public class SentinelApiClient { | |||||
.setParameter("data", JSON.toJSONString(config)); | .setParameter("data", JSON.toJSONString(config)); | ||||
return executeCommand(MODIFY_CLUSTER_SERVER_FLOW_CONFIG_PATH, uriBuilder.build()) | return executeCommand(MODIFY_CLUSTER_SERVER_FLOW_CONFIG_PATH, uriBuilder.build()) | ||||
.thenCompose(e -> { | .thenCompose(e -> { | ||||
if ("success".equals(e)) { | |||||
if (CommandConstants.MSG_SUCCESS.equals(e)) { | |||||
return CompletableFuture.completedFuture(null); | return CompletableFuture.completedFuture(null); | ||||
} else { | } else { | ||||
logger.warn("Error when modifying cluster server flow config: " + e); | logger.warn("Error when modifying cluster server flow config: " + e); | ||||
@@ -622,7 +626,7 @@ public class SentinelApiClient { | |||||
.setParameter("idleSeconds", config.getIdleSeconds().toString()); | .setParameter("idleSeconds", config.getIdleSeconds().toString()); | ||||
return executeCommand(MODIFY_CLUSTER_SERVER_TRANSPORT_CONFIG_PATH, uriBuilder.build()) | return executeCommand(MODIFY_CLUSTER_SERVER_TRANSPORT_CONFIG_PATH, uriBuilder.build()) | ||||
.thenCompose(e -> { | .thenCompose(e -> { | ||||
if ("success".equals(e)) { | |||||
if (CommandConstants.MSG_SUCCESS.equals(e)) { | |||||
return CompletableFuture.completedFuture(null); | return CompletableFuture.completedFuture(null); | ||||
} else { | } else { | ||||
logger.warn("Error when modifying cluster server transport config: " + e); | logger.warn("Error when modifying cluster server transport config: " + e); | ||||
@@ -646,7 +650,7 @@ public class SentinelApiClient { | |||||
.setParameter("data", JSON.toJSONString(set)); | .setParameter("data", JSON.toJSONString(set)); | ||||
return executeCommand(MODIFY_CLUSTER_SERVER_NAMESPACE_SET_PATH, uriBuilder.build()) | return executeCommand(MODIFY_CLUSTER_SERVER_NAMESPACE_SET_PATH, uriBuilder.build()) | ||||
.thenCompose(e -> { | .thenCompose(e -> { | ||||
if ("success".equals(e)) { | |||||
if (CommandConstants.MSG_SUCCESS.equals(e)) { | |||||
return CompletableFuture.completedFuture(null); | return CompletableFuture.completedFuture(null); | ||||
} else { | } else { | ||||
logger.warn("Error when modifying cluster server NamespaceSet: " + e); | logger.warn("Error when modifying cluster server NamespaceSet: " + e); | ||||
@@ -21,7 +21,7 @@ import java.util.Set; | |||||
public interface MachineDiscovery { | public interface MachineDiscovery { | ||||
long MAX_CLIENT_LIVE_TIME_MS = 1000 * 60 * 5; | long MAX_CLIENT_LIVE_TIME_MS = 1000 * 60 * 5; | ||||
String UNKNOWN_APP_NAME = "UNKNOWN"; | |||||
String UNKNOWN_APP_NAME = "CLUSTER_NOT_STARTED"; | |||||
List<String> getAppNames(); | List<String> getAppNames(); | ||||
@@ -41,6 +41,10 @@ public class MachineInfo implements Comparable<MachineInfo> { | |||||
return machineInfo; | return machineInfo; | ||||
} | } | ||||
public String toHostPort() { | |||||
return ip + ":" + port; | |||||
} | |||||
public Integer getPort() { | public Integer getPort() { | ||||
return port; | return port; | ||||
} | } | ||||
@@ -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 + | |||||
'}'; | |||||
} | |||||
} |
@@ -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 + | |||||
'}'; | |||||
} | |||||
} |
@@ -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 + | |||||
'}'; | |||||
} | |||||
} |
@@ -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 + | |||||
'}'; | |||||
} | |||||
} |
@@ -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 + | |||||
'}'; | |||||
} | |||||
} |
@@ -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 + '\'' + | |||||
'}'; | |||||
} | |||||
} |
@@ -26,6 +26,7 @@ public class ServerFlowConfig { | |||||
public static final int DEFAULT_INTERVAL_MS = 1000; | public static final int DEFAULT_INTERVAL_MS = 1000; | ||||
public static final int DEFAULT_SAMPLE_COUNT= 10; | public static final int DEFAULT_SAMPLE_COUNT= 10; | ||||
public static final double DEFAULT_MAX_ALLOWED_QPS= 30000; | |||||
private final String namespace; | private final String namespace; | ||||
@@ -34,6 +35,8 @@ public class ServerFlowConfig { | |||||
private Integer intervalMs = DEFAULT_INTERVAL_MS; | private Integer intervalMs = DEFAULT_INTERVAL_MS; | ||||
private Integer sampleCount = DEFAULT_SAMPLE_COUNT; | private Integer sampleCount = DEFAULT_SAMPLE_COUNT; | ||||
private Double maxAllowedQps = DEFAULT_MAX_ALLOWED_QPS; | |||||
public ServerFlowConfig() { | public ServerFlowConfig() { | ||||
this("default"); | this("default"); | ||||
} | } | ||||
@@ -82,6 +85,15 @@ public class ServerFlowConfig { | |||||
return this; | return this; | ||||
} | } | ||||
public Double getMaxAllowedQps() { | |||||
return maxAllowedQps; | |||||
} | |||||
public ServerFlowConfig setMaxAllowedQps(Double maxAllowedQps) { | |||||
this.maxAllowedQps = maxAllowedQps; | |||||
return this; | |||||
} | |||||
@Override | @Override | ||||
public String toString() { | public String toString() { | ||||
return "ServerFlowConfig{" + | return "ServerFlowConfig{" + | ||||
@@ -90,6 +102,7 @@ public class ServerFlowConfig { | |||||
", maxOccupyRatio=" + maxOccupyRatio + | ", maxOccupyRatio=" + maxOccupyRatio + | ||||
", intervalMs=" + intervalMs + | ", intervalMs=" + intervalMs + | ||||
", sampleCount=" + sampleCount + | ", sampleCount=" + sampleCount + | ||||
", maxAllowedQps=" + maxAllowedQps + | |||||
'}'; | '}'; | ||||
} | } | ||||
} | } |
@@ -21,7 +21,7 @@ package com.taobao.csp.sentinel.dashboard.domain.cluster.config; | |||||
*/ | */ | ||||
public class ServerTransportConfig { | 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; | public static final int DEFAULT_IDLE_SECONDS = 600; | ||||
private Integer port; | private Integer port; | ||||
@@ -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 + | |||||
'}'; | |||||
} | |||||
} |
@@ -13,7 +13,7 @@ | |||||
* See the License for the specific language governing permissions and | * See the License for the specific language governing permissions and | ||||
* limitations under the License. | * 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; | import com.taobao.csp.sentinel.dashboard.domain.cluster.config.ClusterClientConfig; | ||||
@@ -13,7 +13,7 @@ | |||||
* See the License for the specific language governing permissions and | * See the License for the specific language governing permissions and | ||||
* limitations under the License. | * limitations under the License. | ||||
*/ | */ | ||||
package com.taobao.csp.sentinel.dashboard.domain.cluster; | |||||
package com.taobao.csp.sentinel.dashboard.domain.cluster.request; | |||||
/** | /** | ||||
* @author Eric Zhao | * @author Eric Zhao |
@@ -13,7 +13,7 @@ | |||||
* See the License for the specific language governing permissions and | * See the License for the specific language governing permissions and | ||||
* limitations under the License. | * 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; | import java.util.Set; | ||||
@@ -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 + | |||||
'}'; | |||||
} | |||||
} |
@@ -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 + | |||||
'}'; | |||||
} | |||||
} |
@@ -13,9 +13,9 @@ | |||||
* See the License for the specific language governing permissions and | * See the License for the specific language governing permissions and | ||||
* limitations under the License. | * 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 | * @author Eric Zhao | ||||
@@ -23,14 +23,16 @@ import com.taobao.csp.sentinel.dashboard.domain.cluster.config.ClusterClientConf | |||||
*/ | */ | ||||
public class ClusterClientStateVO { | public class ClusterClientStateVO { | ||||
private ClusterClientConfig clientConfig; | |||||
/** | |||||
* Cluster token client state. | |||||
*/ | |||||
private ClusterClientInfoVO clientConfig; | |||||
public ClusterClientConfig getClientConfig() { | |||||
public ClusterClientInfoVO getClientConfig() { | |||||
return clientConfig; | return clientConfig; | ||||
} | } | ||||
public ClusterClientStateVO setClientConfig( | |||||
ClusterClientConfig clientConfig) { | |||||
public ClusterClientStateVO setClientConfig(ClusterClientInfoVO clientConfig) { | |||||
this.clientConfig = clientConfig; | this.clientConfig = clientConfig; | ||||
return this; | return this; | ||||
} | } |
@@ -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 + | |||||
'}'; | |||||
} | |||||
} |
@@ -13,11 +13,12 @@ | |||||
* See the License for the specific language governing permissions and | * See the License for the specific language governing permissions and | ||||
* limitations under the License. | * 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.List; | ||||
import java.util.Set; | 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.ServerFlowConfig; | ||||
import com.taobao.csp.sentinel.dashboard.domain.cluster.config.ServerTransportConfig; | import com.taobao.csp.sentinel.dashboard.domain.cluster.config.ServerTransportConfig; | ||||
@@ -32,14 +33,17 @@ public class ClusterServerStateVO { | |||||
private Set<String> namespaceSet; | private Set<String> namespaceSet; | ||||
private Integer port; | private Integer port; | ||||
private List<ConnectionGroupVO> connection; | private List<ConnectionGroupVO> connection; | ||||
private List<ClusterRequestLimitVO> requestLimitData; | |||||
private Boolean embedded; | |||||
public ServerTransportConfig getTransport() { | public ServerTransportConfig getTransport() { | ||||
return transport; | return transport; | ||||
} | } | ||||
public ClusterServerStateVO setTransport( | |||||
ServerTransportConfig transport) { | |||||
public ClusterServerStateVO setTransport(ServerTransportConfig transport) { | |||||
this.transport = transport; | this.transport = transport; | ||||
return this; | return this; | ||||
} | } | ||||
@@ -75,12 +79,29 @@ public class ClusterServerStateVO { | |||||
return connection; | return connection; | ||||
} | } | ||||
public ClusterServerStateVO setConnection( | |||||
List<ConnectionGroupVO> connection) { | |||||
public ClusterServerStateVO setConnection(List<ConnectionGroupVO> connection) { | |||||
this.connection = connection; | this.connection = connection; | ||||
return this; | 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 | @Override | ||||
public String toString() { | public String toString() { | ||||
return "ClusterServerStateVO{" + | return "ClusterServerStateVO{" + | ||||
@@ -89,6 +110,8 @@ public class ClusterServerStateVO { | |||||
", namespaceSet=" + namespaceSet + | ", namespaceSet=" + namespaceSet + | ||||
", port=" + port + | ", port=" + port + | ||||
", connection=" + connection + | ", connection=" + connection + | ||||
", requestLimitData=" + requestLimitData + | |||||
", embedded=" + embedded + | |||||
'}'; | '}'; | ||||
} | } | ||||
} | } |
@@ -13,7 +13,7 @@ | |||||
* See the License for the specific language governing permissions and | * See the License for the specific language governing permissions and | ||||
* limitations under the License. | * limitations under the License. | ||||
*/ | */ | ||||
package com.taobao.csp.sentinel.dashboard.domain.cluster; | |||||
package com.taobao.csp.sentinel.dashboard.domain.cluster.state; | |||||
/** | /** | ||||
* @author Eric Zhao | * @author Eric Zhao |
@@ -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 + | |||||
'}'; | |||||
} | |||||
} |
@@ -13,7 +13,7 @@ | |||||
* See the License for the specific language governing permissions and | * See the License for the specific language governing permissions and | ||||
* limitations under the License. | * limitations under the License. | ||||
*/ | */ | ||||
package com.taobao.csp.sentinel.dashboard.domain.cluster; | |||||
package com.taobao.csp.sentinel.dashboard.domain.cluster.state; | |||||
/** | /** | ||||
* @author Eric Zhao | * @author Eric Zhao |
@@ -15,6 +15,8 @@ | |||||
*/ | */ | ||||
package com.taobao.csp.sentinel.dashboard.metric; | package com.taobao.csp.sentinel.dashboard.metric; | ||||
import java.net.ConnectException; | |||||
import java.net.SocketTimeoutException; | |||||
import java.nio.charset.Charset; | import java.nio.charset.Charset; | ||||
import java.util.Date; | import java.util.Date; | ||||
import java.util.List; | 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.AppManagement; | ||||
import com.taobao.csp.sentinel.dashboard.discovery.MachineInfo; | import com.taobao.csp.sentinel.dashboard.discovery.MachineInfo; | ||||
import com.taobao.csp.sentinel.dashboard.repository.metric.MetricsRepository; | 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.HttpResponse; | ||||
import org.apache.http.client.methods.HttpGet; | import org.apache.http.client.methods.HttpGet; | ||||
import org.apache.http.concurrent.FutureCallback; | import org.apache.http.concurrent.FutureCallback; | ||||
@@ -64,7 +67,6 @@ import org.springframework.stereotype.Component; | |||||
@Component | @Component | ||||
public class MetricFetcher { | public class MetricFetcher { | ||||
public static final long MAX_CLIENT_LIVE_TIME_MS = 1000 * 60 * 5; | |||||
public static final String NO_METRICS = "No metrics"; | public static final String NO_METRICS = "No metrics"; | ||||
private static final int HTTP_OK = 200; | private static final int HTTP_OK = 200; | ||||
private static final long MAX_LAST_FETCH_INTERVAL_MS = 1000 * 15; | 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()); | final CountDownLatch latch = new CountDownLatch(machines.size()); | ||||
for (final MachineInfo machine : machines) { | for (final MachineInfo machine : machines) { | ||||
// dead | // dead | ||||
if (System.currentTimeMillis() - machine.getTimestamp().getTime() > MAX_CLIENT_LIVE_TIME_MS) { | |||||
if (System.currentTimeMillis() - machine.getTimestamp().getTime() > MachineUtils.getMaxClientTimeout()) { | |||||
latch.countDown(); | latch.countDown(); | ||||
dead.incrementAndGet(); | dead.incrementAndGet(); | ||||
continue; | continue; | ||||
@@ -210,7 +212,13 @@ public class MetricFetcher { | |||||
latch.countDown(); | latch.countDown(); | ||||
fail.incrementAndGet(); | fail.incrementAndGet(); | ||||
httpGet.abort(); | 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 | @Override | ||||
@@ -273,8 +281,10 @@ public class MetricFetcher { | |||||
Charset charset = null; | Charset charset = null; | ||||
try { | try { | ||||
String contentTypeStr = response.getFirstHeader("Content-type").getValue(); | 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) { | } catch (Exception ignore) { | ||||
} | } | ||||
String body = EntityUtils.toString(response.getEntity(), charset != null ? charset : DEFAULT_CHARSET); | String body = EntityUtils.toString(response.getEntity(), charset != null ? charset : DEFAULT_CHARSET); | ||||
@@ -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.datasource.entity.rule.FlowRuleEntity; | ||||
import com.taobao.csp.sentinel.dashboard.discovery.AppManagement; | import com.taobao.csp.sentinel.dashboard.discovery.AppManagement; | ||||
import com.taobao.csp.sentinel.dashboard.discovery.MachineInfo; | 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.beans.factory.annotation.Autowired; | ||||
import org.springframework.stereotype.Component; | import org.springframework.stereotype.Component; | ||||
@@ -47,7 +47,7 @@ public class FlowRuleApiProvider implements DynamicRuleProvider<List<FlowRuleEnt | |||||
} | } | ||||
List<MachineInfo> list = appManagement.getDetailApp(appName).getMachines() | List<MachineInfo> list = appManagement.getDetailApp(appName).getMachines() | ||||
.stream() | .stream() | ||||
.filter(MachineUtil::isMachineHealth) | |||||
.filter(MachineUtils::isMachineHealth) | |||||
.sorted((e1, e2) -> { | .sorted((e1, e2) -> { | ||||
if (e1.getTimestamp().before(e2.getTimestamp())) { | if (e1.getTimestamp().before(e2.getTimestamp())) { | ||||
return 1; | return 1; | ||||
@@ -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.datasource.entity.rule.FlowRuleEntity; | ||||
import com.taobao.csp.sentinel.dashboard.discovery.AppManagement; | import com.taobao.csp.sentinel.dashboard.discovery.AppManagement; | ||||
import com.taobao.csp.sentinel.dashboard.discovery.MachineInfo; | 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.beans.factory.annotation.Autowired; | ||||
import org.springframework.stereotype.Component; | import org.springframework.stereotype.Component; | ||||
@@ -51,7 +51,7 @@ public class FlowRuleApiPublisher implements DynamicRulePublisher<List<FlowRuleE | |||||
Set<MachineInfo> set = appManagement.getDetailApp(app).getMachines(); | Set<MachineInfo> set = appManagement.getDetailApp(app).getMachines(); | ||||
for (MachineInfo machine : set) { | for (MachineInfo machine : set) { | ||||
if (!MachineUtil.isMachineHealth(machine)) { | |||||
if (!MachineUtils.isMachineHealth(machine)) { | |||||
continue; | continue; | ||||
} | } | ||||
// TODO: parse the results | // TODO: parse the results | ||||
@@ -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); | |||||
} |
@@ -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); | |||||
} | |||||
} |
@@ -15,21 +15,30 @@ | |||||
*/ | */ | ||||
package com.taobao.csp.sentinel.dashboard.service; | package com.taobao.csp.sentinel.dashboard.service; | ||||
import java.util.ArrayList; | |||||
import java.util.List; | |||||
import java.util.Set; | import java.util.Set; | ||||
import java.util.concurrent.CompletableFuture; | 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.cluster.ClusterStateManager; | ||||
import com.alibaba.csp.sentinel.util.StringUtil; | import com.alibaba.csp.sentinel.util.StringUtil; | ||||
import com.taobao.csp.sentinel.dashboard.client.SentinelApiClient; | 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.ClusterClientConfig; | ||||
import com.taobao.csp.sentinel.dashboard.domain.cluster.config.ServerFlowConfig; | 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.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.beans.factory.annotation.Autowired; | ||||
import org.springframework.stereotype.Service; | import org.springframework.stereotype.Service; | ||||
@@ -42,6 +51,8 @@ public class ClusterConfigService { | |||||
@Autowired | @Autowired | ||||
private SentinelApiClient sentinelApiClient; | private SentinelApiClient sentinelApiClient; | ||||
@Autowired | |||||
private AppManagement appManagement; | |||||
public CompletableFuture<Void> modifyClusterClientConfig(ClusterClientModifyRequest request) { | public CompletableFuture<Void> modifyClusterClientConfig(ClusterClientModifyRequest request) { | ||||
if (notClientRequestValid(request)) { | if (notClientRequestValid(request)) { | ||||
@@ -51,7 +62,7 @@ public class ClusterConfigService { | |||||
String ip = request.getIp(); | String ip = request.getIp(); | ||||
int port = request.getPort(); | int port = request.getPort(); | ||||
return sentinelApiClient.modifyClusterClientConfig(app, ip, port, request.getClientConfig()) | 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) { | private boolean notClientRequestValid(/*@NonNull */ ClusterClientModifyRequest request) { | ||||
@@ -83,12 +94,64 @@ public class ClusterConfigService { | |||||
.thenCompose(v -> sentinelApiClient.modifyClusterMode(app, ip, port, ClusterStateManager.CLUSTER_SERVER)); | .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) { | public CompletableFuture<ClusterUniversalStateVO> getClusterUniversalState(String app, String ip, int port) { | ||||
return sentinelApiClient.fetchClusterMode(app, ip, port) | return sentinelApiClient.fetchClusterMode(app, ip, port) | ||||
.thenApply(e -> new ClusterUniversalStateVO().setStateInfo(e)) | .thenApply(e -> new ClusterUniversalStateVO().setStateInfo(e)) | ||||
.thenCompose(vo -> { | .thenCompose(vo -> { | ||||
if (vo.getStateInfo().getClientAvailable()) { | if (vo.getStateInfo().getClientAvailable()) { | ||||
return sentinelApiClient.fetchClusterClientConfig(app, ip, port) | |||||
return sentinelApiClient.fetchClusterClientInfoAndConfig(app, ip, port) | |||||
.thenApply(cc -> vo.setClient(new ClusterClientStateVO().setClientConfig(cc))); | .thenApply(cc -> vo.setClient(new ClusterClientStateVO().setClientConfig(cc))); | ||||
} else { | } else { | ||||
return CompletableFuture.completedFuture(vo); | return CompletableFuture.completedFuture(vo); | ||||
@@ -111,6 +174,7 @@ public class ClusterConfigService { | |||||
private boolean invalidFlowConfig(ServerFlowConfig flowConfig) { | private boolean invalidFlowConfig(ServerFlowConfig flowConfig) { | ||||
return flowConfig == null || flowConfig.getSampleCount() == null || flowConfig.getSampleCount() <= 0 | return flowConfig == null || flowConfig.getSampleCount() == null || flowConfig.getSampleCount() <= 0 | ||||
|| flowConfig.getIntervalMs() == null || flowConfig.getIntervalMs() <= 0 | || flowConfig.getIntervalMs() == null || flowConfig.getIntervalMs() <= 0 | ||||
|| flowConfig.getIntervalMs() % flowConfig.getSampleCount() != 0; | |||||
|| flowConfig.getIntervalMs() % flowConfig.getSampleCount() != 0 | |||||
|| flowConfig.getMaxAllowedQps() == null || flowConfig.getMaxAllowedQps() < 0; | |||||
} | } | ||||
} | } |
@@ -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() {} | |||||
} |
@@ -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() {} | |||||
} |
@@ -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(); | |||||
} | |||||
} |
@@ -134,6 +134,8 @@ public class FlowControllerV1 { | |||||
Date date = new Date(); | Date date = new Date(); | ||||
entity.setGmtCreate(date); | entity.setGmtCreate(date); | ||||
entity.setGmtModified(date); | entity.setGmtModified(date); | ||||
entity.setLimitApp(entity.getLimitApp().trim()); | |||||
entity.setResource(entity.getResource().trim()); | |||||
try { | try { | ||||
entity = repository.save(entity); | entity = repository.save(entity); | ||||
} catch (Throwable throwable) { | } catch (Throwable throwable) { | ||||
@@ -224,7 +226,7 @@ public class FlowControllerV1 { | |||||
} | } | ||||
@DeleteMapping("/delete.json") | @DeleteMapping("/delete.json") | ||||
Result<?> delete(Long id) { | |||||
public Result<Long> delete(Long id) { | |||||
if (id == null) { | if (id == null) { | ||||
return Result.ofFail(-1, "id can't be null"); | return Result.ofFail(-1, "id can't be null"); | ||||
} | } | ||||
@@ -137,6 +137,8 @@ public class FlowControllerV2 { | |||||
Date date = new Date(); | Date date = new Date(); | ||||
entity.setGmtCreate(date); | entity.setGmtCreate(date); | ||||
entity.setGmtModified(date); | entity.setGmtModified(date); | ||||
entity.setLimitApp(entity.getLimitApp().trim()); | |||||
entity.setResource(entity.getResource().trim()); | |||||
try { | try { | ||||
entity = repository.save(entity); | entity = repository.save(entity); | ||||
publishRules(entity.getApp()); | publishRules(entity.getApp()); | ||||
@@ -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()); | |||||
} | |||||
} | |||||
} |
@@ -13,8 +13,9 @@ | |||||
* See the License for the specific language governing permissions and | * See the License for the specific language governing permissions and | ||||
* limitations under the License. | * 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.Optional; | ||||
import java.util.concurrent.ExecutionException; | 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.client.CommandNotFoundException; | ||||
import com.taobao.csp.sentinel.dashboard.datasource.entity.SentinelVersion; | import com.taobao.csp.sentinel.dashboard.datasource.entity.SentinelVersion; | ||||
import com.taobao.csp.sentinel.dashboard.discovery.AppManagement; | 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.service.ClusterConfigService; | ||||
import com.taobao.csp.sentinel.dashboard.util.ClusterEntityUtils; | |||||
import com.taobao.csp.sentinel.dashboard.util.VersionUtils; | import com.taobao.csp.sentinel.dashboard.util.VersionUtils; | ||||
import com.taobao.csp.sentinel.dashboard.view.Result; | |||||
import org.slf4j.Logger; | import org.slf4j.Logger; | ||||
import org.slf4j.LoggerFactory; | import org.slf4j.LoggerFactory; | ||||
import org.springframework.beans.factory.annotation.Autowired; | import org.springframework.beans.factory.annotation.Autowired; | ||||
import org.springframework.web.bind.annotation.GetMapping; | 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.PostMapping; | ||||
import org.springframework.web.bind.annotation.RequestBody; | import org.springframework.web.bind.annotation.RequestBody; | ||||
import org.springframework.web.bind.annotation.RequestMapping; | import org.springframework.web.bind.annotation.RequestMapping; | ||||
@@ -60,7 +67,7 @@ public class ClusterConfigController { | |||||
@Autowired | @Autowired | ||||
private ClusterConfigService clusterConfigService; | private ClusterConfigService clusterConfigService; | ||||
@PostMapping("/config/modify") | |||||
@PostMapping("/config/modify_single") | |||||
public Result<Boolean> apiModifyClusterConfig(@RequestBody String payload) { | public Result<Boolean> apiModifyClusterConfig(@RequestBody String payload) { | ||||
if (StringUtil.isBlank(payload)) { | if (StringUtil.isBlank(payload)) { | ||||
return Result.ofFail(-1, "empty request body"); | return Result.ofFail(-1, "empty request body"); | ||||
@@ -94,18 +101,22 @@ public class ClusterConfigController { | |||||
return Result.ofFail(-1, "invalid parameter"); | return Result.ofFail(-1, "invalid parameter"); | ||||
} catch (ExecutionException ex) { | } catch (ExecutionException ex) { | ||||
logger.error("Error when modifying cluster config", ex.getCause()); | 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) { | } catch (Throwable ex) { | ||||
logger.error("Error when modifying cluster config", ex); | logger.error("Error when modifying cluster config", ex); | ||||
return Result.ofFail(-1, ex.getMessage()); | 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, | public Result<ClusterUniversalStateVO> apiGetClusterState(@RequestParam String app, | ||||
@RequestParam String ip, | @RequestParam String ip, | ||||
@RequestParam Integer port) { | @RequestParam Integer port) { | ||||
@@ -127,17 +138,69 @@ public class ClusterConfigController { | |||||
.get(); | .get(); | ||||
} catch (ExecutionException ex) { | } catch (ExecutionException ex) { | ||||
logger.error("Error when fetching cluster state", ex.getCause()); | 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) { | } catch (Throwable throwable) { | ||||
logger.error("Error when fetching cluster state", throwable); | logger.error("Error when fetching cluster state", throwable); | ||||
return Result.ofFail(-1, throwable.getMessage()); | 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) { | private boolean isNotSupported(Throwable ex) { | ||||
return ex instanceof CommandNotFoundException; | return ex instanceof CommandNotFoundException; | ||||
} | } |
@@ -25,6 +25,7 @@ public final class NacosConfigUtil { | |||||
public static final String FLOW_DATA_ID_POSTFIX = "-flow-rules"; | 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 PARAM_FLOW_DATA_ID_POSTFIX = "-param-rules"; | ||||
public static final String CLUSTER_MAP_DATA_ID_POSTFIX = "-cluster-map"; | |||||
/** | /** | ||||
* cc for `cluster-client` | * cc for `cluster-client` | ||||
@@ -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(); | |||||
} | |||||
} | |||||
} | |||||
} |