Polish Sentinel dashboard backend for cluster flow control enhancement
- Add cluster token server management controller and service for app - Other enhancements and fixes Signed-off-by: Eric Zhao <sczyh16@gmail.com>
This commit is contained in:
parent
f82fcd696f
commit
8d413e1645
|
|
@ -56,12 +56,6 @@
|
|||
<artifactId>spring-boot-starter-logging</artifactId>
|
||||
<version>${spring.boot.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-devtools</artifactId>
|
||||
<version>${spring.boot.version}</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
|
|
@ -105,11 +99,17 @@
|
|||
<groupId>com.alibaba</groupId>
|
||||
<artifactId>fastjson</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.mockito</groupId>
|
||||
<artifactId>mockito-core</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
|
|
|
|||
|
|
@ -15,6 +15,8 @@
|
|||
*/
|
||||
package com.taobao.csp.sentinel.dashboard;
|
||||
|
||||
import com.alibaba.csp.sentinel.init.InitExecutor;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
|
|
@ -27,6 +29,11 @@ import org.springframework.boot.autoconfigure.SpringBootApplication;
|
|||
public class DashboardApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
triggerSentinelInit();
|
||||
SpringApplication.run(DashboardApplication.class, args);
|
||||
}
|
||||
|
||||
private static void triggerSentinelInit() {
|
||||
new Thread(() -> InitExecutor.doInit()).start();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,20 +13,21 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.taobao.csp.sentinel.dashboard.util;
|
||||
|
||||
import com.taobao.csp.sentinel.dashboard.discovery.MachineDiscovery;
|
||||
import com.taobao.csp.sentinel.dashboard.discovery.MachineInfo;
|
||||
package com.taobao.csp.sentinel.dashboard.client;
|
||||
|
||||
/**
|
||||
* @author Eric Zhao
|
||||
*/
|
||||
public final class MachineUtil {
|
||||
public class CommandFailedException extends RuntimeException {
|
||||
|
||||
public static boolean isMachineHealth(MachineInfo machine) {
|
||||
if (machine == null) {
|
||||
return false;
|
||||
}
|
||||
return System.currentTimeMillis() - machine.getTimestamp().getTime() < MachineDiscovery.MAX_CLIENT_LIVE_TIME_MS;
|
||||
public CommandFailedException() {}
|
||||
|
||||
public CommandFailedException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized Throwable fillInStackTrace() {
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
|
@ -20,9 +20,15 @@ package com.taobao.csp.sentinel.dashboard.client;
|
|||
* @since 0.2.1
|
||||
*/
|
||||
public class CommandNotFoundException extends Exception {
|
||||
|
||||
public CommandNotFoundException() { }
|
||||
|
||||
public CommandNotFoundException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized Throwable fillInStackTrace() {
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ import java.util.concurrent.TimeUnit;
|
|||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import com.alibaba.csp.sentinel.command.CommandConstants;
|
||||
import com.alibaba.csp.sentinel.config.SentinelConfig;
|
||||
import com.alibaba.csp.sentinel.command.vo.NodeVo;
|
||||
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRule;
|
||||
|
|
@ -43,8 +44,9 @@ import com.taobao.csp.sentinel.dashboard.datasource.entity.rule.DegradeRuleEntit
|
|||
import com.taobao.csp.sentinel.dashboard.datasource.entity.rule.FlowRuleEntity;
|
||||
import com.taobao.csp.sentinel.dashboard.datasource.entity.rule.ParamFlowRuleEntity;
|
||||
import com.taobao.csp.sentinel.dashboard.datasource.entity.rule.SystemRuleEntity;
|
||||
import com.taobao.csp.sentinel.dashboard.domain.cluster.ClusterServerStateVO;
|
||||
import com.taobao.csp.sentinel.dashboard.domain.cluster.ClusterStateSimpleEntity;
|
||||
import com.taobao.csp.sentinel.dashboard.domain.cluster.ClusterClientInfoVO;
|
||||
import com.taobao.csp.sentinel.dashboard.domain.cluster.state.ClusterServerStateVO;
|
||||
import com.taobao.csp.sentinel.dashboard.domain.cluster.state.ClusterStateSimpleEntity;
|
||||
import com.taobao.csp.sentinel.dashboard.domain.cluster.config.ClusterClientConfig;
|
||||
import com.taobao.csp.sentinel.dashboard.domain.cluster.config.ServerFlowConfig;
|
||||
import com.taobao.csp.sentinel.dashboard.domain.cluster.config.ServerTransportConfig;
|
||||
|
|
@ -63,6 +65,8 @@ import org.slf4j.Logger;
|
|||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import static com.taobao.csp.sentinel.dashboard.util.AsyncUtils.*;
|
||||
|
||||
/**
|
||||
* Communicate with Sentinel client.
|
||||
*
|
||||
|
|
@ -104,7 +108,7 @@ public class SentinelApiClient {
|
|||
private final boolean enableHttps = false;
|
||||
|
||||
public SentinelApiClient() {
|
||||
IOReactorConfig ioConfig = IOReactorConfig.custom().setConnectTimeout(3000).setSoTimeout(3000)
|
||||
IOReactorConfig ioConfig = IOReactorConfig.custom().setConnectTimeout(3000).setSoTimeout(10000)
|
||||
.setIoThreadCount(Runtime.getRuntime().availableProcessors() * 2).build();
|
||||
httpClient = HttpAsyncClients.custom().setRedirectStrategy(new DefaultRedirectStrategy() {
|
||||
@Override
|
||||
|
|
@ -115,6 +119,109 @@ public class SentinelApiClient {
|
|||
httpClient.start();
|
||||
}
|
||||
|
||||
private boolean isSuccess(int statusCode) {
|
||||
return statusCode >= 200 && statusCode < 300;
|
||||
}
|
||||
|
||||
private boolean isCommandNotFound(int statusCode, String body) {
|
||||
return statusCode == 400 && StringUtil.isNotEmpty(body) && body.contains(CommandConstants.MSG_UNKNOWN_COMMAND_PREFIX);
|
||||
}
|
||||
|
||||
private CompletableFuture<String> executeCommand(String command, URI uri) {
|
||||
CompletableFuture<String> future = new CompletableFuture<>();
|
||||
if (StringUtil.isBlank(command) || uri == null) {
|
||||
future.completeExceptionally(new IllegalArgumentException("Bad URL or command name"));
|
||||
return future;
|
||||
}
|
||||
final HttpGet httpGet = new HttpGet(uri);
|
||||
httpClient.execute(httpGet, new FutureCallback<HttpResponse>() {
|
||||
@Override
|
||||
public void completed(final HttpResponse response) {
|
||||
int statusCode = response.getStatusLine().getStatusCode();
|
||||
try {
|
||||
String value = getBody(response);
|
||||
if (isSuccess(statusCode)) {
|
||||
future.complete(value);
|
||||
} else {
|
||||
if (isCommandNotFound(statusCode, value)) {
|
||||
future.completeExceptionally(new CommandNotFoundException(command));
|
||||
} else {
|
||||
future.completeExceptionally(new CommandFailedException(value));
|
||||
}
|
||||
}
|
||||
|
||||
} catch (Exception ex) {
|
||||
future.completeExceptionally(ex);
|
||||
logger.error("HTTP request failed: " + uri.toString(), ex);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void failed(final Exception ex) {
|
||||
future.completeExceptionally(ex);
|
||||
logger.error("HTTP request failed: " + uri.toString(), ex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cancelled() {
|
||||
future.complete(null);
|
||||
}
|
||||
});
|
||||
return future;
|
||||
}
|
||||
|
||||
private String httpGetContent(String url) {
|
||||
final HttpGet httpGet = new HttpGet(url);
|
||||
final CountDownLatch latch = new CountDownLatch(1);
|
||||
final AtomicReference<String> reference = new AtomicReference<>();
|
||||
httpClient.execute(httpGet, new FutureCallback<HttpResponse>() {
|
||||
@Override
|
||||
public void completed(final HttpResponse response) {
|
||||
try {
|
||||
reference.set(getBody(response));
|
||||
} catch (Exception e) {
|
||||
logger.info("httpGetContent " + url + " error:", e);
|
||||
} finally {
|
||||
latch.countDown();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void failed(final Exception ex) {
|
||||
latch.countDown();
|
||||
logger.info("httpGetContent " + url + " failed:", ex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cancelled() {
|
||||
latch.countDown();
|
||||
}
|
||||
});
|
||||
try {
|
||||
latch.await(5, TimeUnit.SECONDS);
|
||||
} catch (Exception e) {
|
||||
logger.info("wait http client error:", e);
|
||||
}
|
||||
return reference.get();
|
||||
}
|
||||
|
||||
private String getBody(HttpResponse response) throws Exception {
|
||||
Charset charset = null;
|
||||
try {
|
||||
String contentTypeStr = response.getFirstHeader("Content-type").getValue();
|
||||
if (StringUtil.isNotEmpty(contentTypeStr)) {
|
||||
ContentType contentType = ContentType.parse(contentTypeStr);
|
||||
charset = contentType.getCharset();
|
||||
}
|
||||
} catch (Exception ignore) {
|
||||
}
|
||||
return EntityUtils.toString(response.getEntity(), charset != null ? charset : DEFAULT_CHARSET);
|
||||
}
|
||||
|
||||
public void close() throws Exception {
|
||||
httpClient.close();
|
||||
}
|
||||
|
||||
public List<NodeVo> fetchResourceOfMachine(String ip, int port, String type) {
|
||||
String url = "http://" + ip + ":" + port + "/" + RESOURCE_URL_PATH + "?type=" + type;
|
||||
String body = httpGetContent(url);
|
||||
|
|
@ -388,7 +495,7 @@ public class SentinelApiClient {
|
|||
.setParameter("data", data);
|
||||
return executeCommand(SET_PARAM_RULE_PATH, uriBuilder.build())
|
||||
.thenCompose(e -> {
|
||||
if ("success".equals(e)) {
|
||||
if (CommandConstants.MSG_SUCCESS.equals(e)) {
|
||||
return CompletableFuture.completedFuture(null);
|
||||
} else {
|
||||
logger.warn("Push parameter flow rules to client failed: " + e);
|
||||
|
|
@ -401,109 +508,6 @@ public class SentinelApiClient {
|
|||
}
|
||||
}
|
||||
|
||||
private boolean isSuccess(int statusCode) {
|
||||
return statusCode >= 200 && statusCode < 300;
|
||||
}
|
||||
|
||||
private CompletableFuture<String> executeCommand(String command, URI uri) {
|
||||
CompletableFuture<String> future = new CompletableFuture<>();
|
||||
if (StringUtil.isBlank(command) || uri == null) {
|
||||
future.completeExceptionally(new IllegalArgumentException("Bad URL or command name"));
|
||||
return future;
|
||||
}
|
||||
final HttpGet httpGet = new HttpGet(uri);
|
||||
httpClient.execute(httpGet, new FutureCallback<HttpResponse>() {
|
||||
@Override
|
||||
public void completed(final HttpResponse response) {
|
||||
int statusCode = response.getStatusLine().getStatusCode();
|
||||
try {
|
||||
String value = getBody(response);
|
||||
if (isSuccess(statusCode)) {
|
||||
future.complete(value);
|
||||
} else {
|
||||
if (statusCode == 400) {
|
||||
future.completeExceptionally(new CommandNotFoundException(command));
|
||||
} else {
|
||||
future.completeExceptionally(new IllegalStateException(value));
|
||||
}
|
||||
}
|
||||
|
||||
} catch (Exception ex) {
|
||||
future.completeExceptionally(ex);
|
||||
logger.error("HTTP request failed: " + uri.toString(), ex);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void failed(final Exception ex) {
|
||||
future.completeExceptionally(ex);
|
||||
logger.error("HTTP request failed: " + uri.toString(), ex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cancelled() {
|
||||
future.complete(null);
|
||||
}
|
||||
});
|
||||
return future;
|
||||
}
|
||||
|
||||
private String httpGetContent(String url) {
|
||||
final HttpGet httpGet = new HttpGet(url);
|
||||
final CountDownLatch latch = new CountDownLatch(1);
|
||||
final AtomicReference<String> reference = new AtomicReference<>();
|
||||
httpClient.execute(httpGet, new FutureCallback<HttpResponse>() {
|
||||
@Override
|
||||
public void completed(final HttpResponse response) {
|
||||
try {
|
||||
reference.set(getBody(response));
|
||||
} catch (Exception e) {
|
||||
logger.info("httpGetContent " + url + " error:", e);
|
||||
} finally {
|
||||
latch.countDown();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void failed(final Exception ex) {
|
||||
latch.countDown();
|
||||
logger.info("httpGetContent " + url + " failed:", ex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cancelled() {
|
||||
latch.countDown();
|
||||
}
|
||||
});
|
||||
try {
|
||||
latch.await(5, TimeUnit.SECONDS);
|
||||
} catch (Exception e) {
|
||||
logger.info("wait http client error:", e);
|
||||
}
|
||||
return reference.get();
|
||||
}
|
||||
|
||||
private String getBody(HttpResponse response) throws Exception {
|
||||
Charset charset = null;
|
||||
try {
|
||||
String contentTypeStr = response.getFirstHeader("Content-type").getValue();
|
||||
ContentType contentType = ContentType.parse(contentTypeStr);
|
||||
charset = contentType.getCharset();
|
||||
} catch (Exception ignore) {
|
||||
}
|
||||
return EntityUtils.toString(response.getEntity(), charset != null ? charset : DEFAULT_CHARSET);
|
||||
}
|
||||
|
||||
public void close() throws Exception {
|
||||
httpClient.close();
|
||||
}
|
||||
|
||||
private <R> CompletableFuture<R> newFailedFuture(Throwable ex) {
|
||||
CompletableFuture<R> future = new CompletableFuture<>();
|
||||
future.completeExceptionally(ex);
|
||||
return future;
|
||||
}
|
||||
|
||||
// Cluster related
|
||||
|
||||
public CompletableFuture<ClusterStateSimpleEntity> fetchClusterMode(String app, String ip, int port) {
|
||||
|
|
@ -533,7 +537,7 @@ public class SentinelApiClient {
|
|||
.setParameter("mode", String.valueOf(mode));
|
||||
return executeCommand(MODIFY_CLUSTER_MODE_PATH, uriBuilder.build())
|
||||
.thenCompose(e -> {
|
||||
if ("success".equals(e)) {
|
||||
if (CommandConstants.MSG_SUCCESS.equals(e)) {
|
||||
return CompletableFuture.completedFuture(null);
|
||||
} else {
|
||||
logger.warn("Error when modifying cluster mode: " + e);
|
||||
|
|
@ -546,7 +550,7 @@ public class SentinelApiClient {
|
|||
}
|
||||
}
|
||||
|
||||
public CompletableFuture<ClusterClientConfig> fetchClusterClientConfig(String app, String ip, int port) {
|
||||
public CompletableFuture<ClusterClientInfoVO> fetchClusterClientInfoAndConfig(String app, String ip, int port) {
|
||||
if (StringUtil.isBlank(ip) || port <= 0) {
|
||||
return newFailedFuture(new IllegalArgumentException("Invalid parameter"));
|
||||
}
|
||||
|
|
@ -555,7 +559,7 @@ public class SentinelApiClient {
|
|||
uriBuilder.setScheme("http").setHost(ip).setPort(port)
|
||||
.setPath(FETCH_CLUSTER_CLIENT_CONFIG_PATH);
|
||||
return executeCommand(FETCH_CLUSTER_CLIENT_CONFIG_PATH, uriBuilder.build())
|
||||
.thenApply(r -> JSON.parseObject(r, ClusterClientConfig.class));
|
||||
.thenApply(r -> JSON.parseObject(r, ClusterClientInfoVO.class));
|
||||
} catch (Exception ex) {
|
||||
logger.warn("Error when fetching cluster client config", ex);
|
||||
return newFailedFuture(ex);
|
||||
|
|
@ -573,7 +577,7 @@ public class SentinelApiClient {
|
|||
.setParameter("data", JSON.toJSONString(config));
|
||||
return executeCommand(MODIFY_CLUSTER_MODE_PATH, uriBuilder.build())
|
||||
.thenCompose(e -> {
|
||||
if ("success".equals(e)) {
|
||||
if (CommandConstants.MSG_SUCCESS.equals(e)) {
|
||||
return CompletableFuture.completedFuture(null);
|
||||
} else {
|
||||
logger.warn("Error when modifying cluster client config: " + e);
|
||||
|
|
@ -597,7 +601,7 @@ public class SentinelApiClient {
|
|||
.setParameter("data", JSON.toJSONString(config));
|
||||
return executeCommand(MODIFY_CLUSTER_SERVER_FLOW_CONFIG_PATH, uriBuilder.build())
|
||||
.thenCompose(e -> {
|
||||
if ("success".equals(e)) {
|
||||
if (CommandConstants.MSG_SUCCESS.equals(e)) {
|
||||
return CompletableFuture.completedFuture(null);
|
||||
} else {
|
||||
logger.warn("Error when modifying cluster server flow config: " + e);
|
||||
|
|
@ -622,7 +626,7 @@ public class SentinelApiClient {
|
|||
.setParameter("idleSeconds", config.getIdleSeconds().toString());
|
||||
return executeCommand(MODIFY_CLUSTER_SERVER_TRANSPORT_CONFIG_PATH, uriBuilder.build())
|
||||
.thenCompose(e -> {
|
||||
if ("success".equals(e)) {
|
||||
if (CommandConstants.MSG_SUCCESS.equals(e)) {
|
||||
return CompletableFuture.completedFuture(null);
|
||||
} else {
|
||||
logger.warn("Error when modifying cluster server transport config: " + e);
|
||||
|
|
@ -646,7 +650,7 @@ public class SentinelApiClient {
|
|||
.setParameter("data", JSON.toJSONString(set));
|
||||
return executeCommand(MODIFY_CLUSTER_SERVER_NAMESPACE_SET_PATH, uriBuilder.build())
|
||||
.thenCompose(e -> {
|
||||
if ("success".equals(e)) {
|
||||
if (CommandConstants.MSG_SUCCESS.equals(e)) {
|
||||
return CompletableFuture.completedFuture(null);
|
||||
} else {
|
||||
logger.warn("Error when modifying cluster server NamespaceSet: " + e);
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ import java.util.Set;
|
|||
public interface MachineDiscovery {
|
||||
|
||||
long MAX_CLIENT_LIVE_TIME_MS = 1000 * 60 * 5;
|
||||
String UNKNOWN_APP_NAME = "UNKNOWN";
|
||||
String UNKNOWN_APP_NAME = "CLUSTER_NOT_STARTED";
|
||||
|
||||
List<String> getAppNames();
|
||||
|
||||
|
|
|
|||
|
|
@ -41,6 +41,10 @@ public class MachineInfo implements Comparable<MachineInfo> {
|
|||
return machineInfo;
|
||||
}
|
||||
|
||||
public String toHostPort() {
|
||||
return ip + ":" + port;
|
||||
}
|
||||
|
||||
public Integer getPort() {
|
||||
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_SAMPLE_COUNT= 10;
|
||||
public static final double DEFAULT_MAX_ALLOWED_QPS= 30000;
|
||||
|
||||
private final String namespace;
|
||||
|
||||
|
|
@ -34,6 +35,8 @@ public class ServerFlowConfig {
|
|||
private Integer intervalMs = DEFAULT_INTERVAL_MS;
|
||||
private Integer sampleCount = DEFAULT_SAMPLE_COUNT;
|
||||
|
||||
private Double maxAllowedQps = DEFAULT_MAX_ALLOWED_QPS;
|
||||
|
||||
public ServerFlowConfig() {
|
||||
this("default");
|
||||
}
|
||||
|
|
@ -82,6 +85,15 @@ public class ServerFlowConfig {
|
|||
return this;
|
||||
}
|
||||
|
||||
public Double getMaxAllowedQps() {
|
||||
return maxAllowedQps;
|
||||
}
|
||||
|
||||
public ServerFlowConfig setMaxAllowedQps(Double maxAllowedQps) {
|
||||
this.maxAllowedQps = maxAllowedQps;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ServerFlowConfig{" +
|
||||
|
|
@ -90,6 +102,7 @@ public class ServerFlowConfig {
|
|||
", maxOccupyRatio=" + maxOccupyRatio +
|
||||
", intervalMs=" + intervalMs +
|
||||
", sampleCount=" + sampleCount +
|
||||
", maxAllowedQps=" + maxAllowedQps +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ package com.taobao.csp.sentinel.dashboard.domain.cluster.config;
|
|||
*/
|
||||
public class ServerTransportConfig {
|
||||
|
||||
public static final int DEFAULT_PORT = 8730;
|
||||
public static final int DEFAULT_PORT = 18730;
|
||||
public static final int DEFAULT_IDLE_SECONDS = 600;
|
||||
|
||||
private Integer port;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
* 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;
|
||||
|
||||
|
|
@ -13,7 +13,7 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.taobao.csp.sentinel.dashboard.domain.cluster;
|
||||
package com.taobao.csp.sentinel.dashboard.domain.cluster.request;
|
||||
|
||||
/**
|
||||
* @author Eric Zhao
|
||||
|
|
@ -13,7 +13,7 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.taobao.csp.sentinel.dashboard.domain.cluster;
|
||||
package com.taobao.csp.sentinel.dashboard.domain.cluster.request;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
|
|
@ -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
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.taobao.csp.sentinel.dashboard.domain.cluster;
|
||||
package com.taobao.csp.sentinel.dashboard.domain.cluster.state;
|
||||
|
||||
import com.taobao.csp.sentinel.dashboard.domain.cluster.config.ClusterClientConfig;
|
||||
import com.taobao.csp.sentinel.dashboard.domain.cluster.ClusterClientInfoVO;
|
||||
|
||||
/**
|
||||
* @author Eric Zhao
|
||||
|
|
@ -23,14 +23,16 @@ import com.taobao.csp.sentinel.dashboard.domain.cluster.config.ClusterClientConf
|
|||
*/
|
||||
public class ClusterClientStateVO {
|
||||
|
||||
private ClusterClientConfig clientConfig;
|
||||
/**
|
||||
* Cluster token client state.
|
||||
*/
|
||||
private ClusterClientInfoVO clientConfig;
|
||||
|
||||
public ClusterClientConfig getClientConfig() {
|
||||
public ClusterClientInfoVO getClientConfig() {
|
||||
return clientConfig;
|
||||
}
|
||||
|
||||
public ClusterClientStateVO setClientConfig(
|
||||
ClusterClientConfig clientConfig) {
|
||||
public ClusterClientStateVO setClientConfig(ClusterClientInfoVO clientConfig) {
|
||||
this.clientConfig = clientConfig;
|
||||
return this;
|
||||
}
|
||||
|
|
@ -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
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.taobao.csp.sentinel.dashboard.domain.cluster;
|
||||
package com.taobao.csp.sentinel.dashboard.domain.cluster.state;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import com.taobao.csp.sentinel.dashboard.domain.cluster.ConnectionGroupVO;
|
||||
import com.taobao.csp.sentinel.dashboard.domain.cluster.config.ServerFlowConfig;
|
||||
import com.taobao.csp.sentinel.dashboard.domain.cluster.config.ServerTransportConfig;
|
||||
|
||||
|
|
@ -32,14 +33,17 @@ public class ClusterServerStateVO {
|
|||
private Set<String> namespaceSet;
|
||||
|
||||
private Integer port;
|
||||
|
||||
private List<ConnectionGroupVO> connection;
|
||||
private List<ClusterRequestLimitVO> requestLimitData;
|
||||
|
||||
private Boolean embedded;
|
||||
|
||||
public ServerTransportConfig getTransport() {
|
||||
return transport;
|
||||
}
|
||||
|
||||
public ClusterServerStateVO setTransport(
|
||||
ServerTransportConfig transport) {
|
||||
public ClusterServerStateVO setTransport(ServerTransportConfig transport) {
|
||||
this.transport = transport;
|
||||
return this;
|
||||
}
|
||||
|
|
@ -75,12 +79,29 @@ public class ClusterServerStateVO {
|
|||
return connection;
|
||||
}
|
||||
|
||||
public ClusterServerStateVO setConnection(
|
||||
List<ConnectionGroupVO> connection) {
|
||||
public ClusterServerStateVO setConnection(List<ConnectionGroupVO> connection) {
|
||||
this.connection = connection;
|
||||
return this;
|
||||
}
|
||||
|
||||
public List<ClusterRequestLimitVO> getRequestLimitData() {
|
||||
return requestLimitData;
|
||||
}
|
||||
|
||||
public ClusterServerStateVO setRequestLimitData(List<ClusterRequestLimitVO> requestLimitData) {
|
||||
this.requestLimitData = requestLimitData;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Boolean getEmbedded() {
|
||||
return embedded;
|
||||
}
|
||||
|
||||
public ClusterServerStateVO setEmbedded(Boolean embedded) {
|
||||
this.embedded = embedded;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ClusterServerStateVO{" +
|
||||
|
|
@ -89,6 +110,8 @@ public class ClusterServerStateVO {
|
|||
", namespaceSet=" + namespaceSet +
|
||||
", port=" + port +
|
||||
", connection=" + connection +
|
||||
", requestLimitData=" + requestLimitData +
|
||||
", embedded=" + embedded +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
|
|
@ -13,7 +13,7 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.taobao.csp.sentinel.dashboard.domain.cluster;
|
||||
package com.taobao.csp.sentinel.dashboard.domain.cluster.state;
|
||||
|
||||
/**
|
||||
* @author Eric Zhao
|
||||
|
|
@ -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
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.taobao.csp.sentinel.dashboard.domain.cluster;
|
||||
package com.taobao.csp.sentinel.dashboard.domain.cluster.state;
|
||||
|
||||
/**
|
||||
* @author Eric Zhao
|
||||
|
|
@ -15,6 +15,8 @@
|
|||
*/
|
||||
package com.taobao.csp.sentinel.dashboard.metric;
|
||||
|
||||
import java.net.ConnectException;
|
||||
import java.net.SocketTimeoutException;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
|
@ -41,6 +43,7 @@ import com.taobao.csp.sentinel.dashboard.datasource.entity.MetricEntity;
|
|||
import com.taobao.csp.sentinel.dashboard.discovery.AppManagement;
|
||||
import com.taobao.csp.sentinel.dashboard.discovery.MachineInfo;
|
||||
import com.taobao.csp.sentinel.dashboard.repository.metric.MetricsRepository;
|
||||
import com.taobao.csp.sentinel.dashboard.util.MachineUtils;
|
||||
import org.apache.http.HttpResponse;
|
||||
import org.apache.http.client.methods.HttpGet;
|
||||
import org.apache.http.concurrent.FutureCallback;
|
||||
|
|
@ -64,7 +67,6 @@ import org.springframework.stereotype.Component;
|
|||
@Component
|
||||
public class MetricFetcher {
|
||||
|
||||
public static final long MAX_CLIENT_LIVE_TIME_MS = 1000 * 60 * 5;
|
||||
public static final String NO_METRICS = "No metrics";
|
||||
private static final int HTTP_OK = 200;
|
||||
private static final long MAX_LAST_FETCH_INTERVAL_MS = 1000 * 15;
|
||||
|
|
@ -183,7 +185,7 @@ public class MetricFetcher {
|
|||
final CountDownLatch latch = new CountDownLatch(machines.size());
|
||||
for (final MachineInfo machine : machines) {
|
||||
// dead
|
||||
if (System.currentTimeMillis() - machine.getTimestamp().getTime() > MAX_CLIENT_LIVE_TIME_MS) {
|
||||
if (System.currentTimeMillis() - machine.getTimestamp().getTime() > MachineUtils.getMaxClientTimeout()) {
|
||||
latch.countDown();
|
||||
dead.incrementAndGet();
|
||||
continue;
|
||||
|
|
@ -210,7 +212,13 @@ public class MetricFetcher {
|
|||
latch.countDown();
|
||||
fail.incrementAndGet();
|
||||
httpGet.abort();
|
||||
logger.error(msg + " metric " + url + " failed:", ex);
|
||||
if (ex instanceof SocketTimeoutException) {
|
||||
logger.error("Failed to fetch metric from <{}>: socket timeout", url);
|
||||
} else if (ex instanceof ConnectException) {
|
||||
logger.error("Failed to fetch metric from <{}> (ConnectionException: {})", url, ex.getMessage());
|
||||
} else {
|
||||
logger.error(msg + " metric " + url + " error", ex);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -273,8 +281,10 @@ public class MetricFetcher {
|
|||
Charset charset = null;
|
||||
try {
|
||||
String contentTypeStr = response.getFirstHeader("Content-type").getValue();
|
||||
ContentType contentType = ContentType.parse(contentTypeStr);
|
||||
charset = contentType.getCharset();
|
||||
if (StringUtil.isNotEmpty(contentTypeStr)) {
|
||||
ContentType contentType = ContentType.parse(contentTypeStr);
|
||||
charset = contentType.getCharset();
|
||||
}
|
||||
} catch (Exception ignore) {
|
||||
}
|
||||
String body = EntityUtils.toString(response.getEntity(), charset != null ? charset : DEFAULT_CHARSET);
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ import com.taobao.csp.sentinel.dashboard.client.SentinelApiClient;
|
|||
import com.taobao.csp.sentinel.dashboard.datasource.entity.rule.FlowRuleEntity;
|
||||
import com.taobao.csp.sentinel.dashboard.discovery.AppManagement;
|
||||
import com.taobao.csp.sentinel.dashboard.discovery.MachineInfo;
|
||||
import com.taobao.csp.sentinel.dashboard.util.MachineUtil;
|
||||
import com.taobao.csp.sentinel.dashboard.util.MachineUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
|
|
@ -47,7 +47,7 @@ public class FlowRuleApiProvider implements DynamicRuleProvider<List<FlowRuleEnt
|
|||
}
|
||||
List<MachineInfo> list = appManagement.getDetailApp(appName).getMachines()
|
||||
.stream()
|
||||
.filter(MachineUtil::isMachineHealth)
|
||||
.filter(MachineUtils::isMachineHealth)
|
||||
.sorted((e1, e2) -> {
|
||||
if (e1.getTimestamp().before(e2.getTimestamp())) {
|
||||
return 1;
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ import com.taobao.csp.sentinel.dashboard.client.SentinelApiClient;
|
|||
import com.taobao.csp.sentinel.dashboard.datasource.entity.rule.FlowRuleEntity;
|
||||
import com.taobao.csp.sentinel.dashboard.discovery.AppManagement;
|
||||
import com.taobao.csp.sentinel.dashboard.discovery.MachineInfo;
|
||||
import com.taobao.csp.sentinel.dashboard.util.MachineUtil;
|
||||
import com.taobao.csp.sentinel.dashboard.util.MachineUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
|
|
@ -51,7 +51,7 @@ public class FlowRuleApiPublisher implements DynamicRulePublisher<List<FlowRuleE
|
|||
Set<MachineInfo> set = appManagement.getDetailApp(app).getMachines();
|
||||
|
||||
for (MachineInfo machine : set) {
|
||||
if (!MachineUtil.isMachineHealth(machine)) {
|
||||
if (!MachineUtils.isMachineHealth(machine)) {
|
||||
continue;
|
||||
}
|
||||
// TODO: parse the results
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import com.alibaba.csp.sentinel.cluster.ClusterStateManager;
|
||||
import com.alibaba.csp.sentinel.util.StringUtil;
|
||||
|
||||
import com.taobao.csp.sentinel.dashboard.client.SentinelApiClient;
|
||||
import com.taobao.csp.sentinel.dashboard.domain.cluster.ClusterClientModifyRequest;
|
||||
import com.taobao.csp.sentinel.dashboard.domain.cluster.ClusterClientStateVO;
|
||||
import com.taobao.csp.sentinel.dashboard.domain.cluster.ClusterServerModifyRequest;
|
||||
import com.taobao.csp.sentinel.dashboard.domain.cluster.ClusterUniversalStateVO;
|
||||
import com.taobao.csp.sentinel.dashboard.discovery.AppInfo;
|
||||
import com.taobao.csp.sentinel.dashboard.discovery.AppManagement;
|
||||
import com.taobao.csp.sentinel.dashboard.domain.cluster.ClusterGroupEntity;
|
||||
import com.taobao.csp.sentinel.dashboard.domain.cluster.request.ClusterClientModifyRequest;
|
||||
import com.taobao.csp.sentinel.dashboard.domain.cluster.state.ClusterClientStateVO;
|
||||
import com.taobao.csp.sentinel.dashboard.domain.cluster.request.ClusterServerModifyRequest;
|
||||
import com.taobao.csp.sentinel.dashboard.domain.cluster.state.ClusterUniversalStatePairVO;
|
||||
import com.taobao.csp.sentinel.dashboard.domain.cluster.state.ClusterUniversalStateVO;
|
||||
import com.taobao.csp.sentinel.dashboard.domain.cluster.config.ClusterClientConfig;
|
||||
import com.taobao.csp.sentinel.dashboard.domain.cluster.config.ServerFlowConfig;
|
||||
import com.taobao.csp.sentinel.dashboard.domain.cluster.config.ServerTransportConfig;
|
||||
import com.taobao.csp.sentinel.dashboard.util.AsyncUtils;
|
||||
import com.taobao.csp.sentinel.dashboard.util.ClusterEntityUtils;
|
||||
import com.taobao.csp.sentinel.dashboard.util.MachineUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
|
|
@ -42,6 +51,8 @@ public class ClusterConfigService {
|
|||
|
||||
@Autowired
|
||||
private SentinelApiClient sentinelApiClient;
|
||||
@Autowired
|
||||
private AppManagement appManagement;
|
||||
|
||||
public CompletableFuture<Void> modifyClusterClientConfig(ClusterClientModifyRequest request) {
|
||||
if (notClientRequestValid(request)) {
|
||||
|
|
@ -51,7 +62,7 @@ public class ClusterConfigService {
|
|||
String ip = request.getIp();
|
||||
int port = request.getPort();
|
||||
return sentinelApiClient.modifyClusterClientConfig(app, ip, port, request.getClientConfig())
|
||||
.thenCompose(v -> sentinelApiClient.modifyClusterMode(app, ip, port, ClusterStateManager.CLUSTER_CLIENT));
|
||||
.thenCompose(v -> sentinelApiClient.modifyClusterMode(app, ip, port, ClusterStateManager.CLUSTER_CLIENT));
|
||||
}
|
||||
|
||||
private boolean notClientRequestValid(/*@NonNull */ ClusterClientModifyRequest request) {
|
||||
|
|
@ -83,12 +94,64 @@ public class ClusterConfigService {
|
|||
.thenCompose(v -> sentinelApiClient.modifyClusterMode(app, ip, port, ClusterStateManager.CLUSTER_SERVER));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get cluster state list of all available machines of provided application.
|
||||
*
|
||||
* @param app application name
|
||||
* @return cluster state list of all available machines of the application
|
||||
* @since 1.4.1
|
||||
*/
|
||||
public CompletableFuture<List<ClusterUniversalStatePairVO>> getClusterUniversalState(String app) {
|
||||
if (StringUtil.isBlank(app)) {
|
||||
return AsyncUtils.newFailedFuture(new IllegalArgumentException("app cannot be empty"));
|
||||
}
|
||||
AppInfo appInfo = appManagement.getDetailApp(app);
|
||||
if (appInfo == null || appInfo.getMachines() == null) {
|
||||
return CompletableFuture.completedFuture(new ArrayList<>());
|
||||
}
|
||||
|
||||
List<CompletableFuture<ClusterUniversalStatePairVO>> futures = appInfo.getMachines().stream()
|
||||
.filter(MachineUtils::isMachineHealth)
|
||||
.map(machine -> getClusterUniversalState(app, machine.getIp(), machine.getPort())
|
||||
.thenApply(e -> new ClusterUniversalStatePairVO(machine.getIp(), machine.getPort(), e)))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
return AsyncUtils.sequenceSuccessFuture(futures);
|
||||
}
|
||||
|
||||
public CompletableFuture<ClusterGroupEntity> getClusterUniversalStateForAppMachine(String app, String machineId) {
|
||||
if (StringUtil.isBlank(app)) {
|
||||
return AsyncUtils.newFailedFuture(new IllegalArgumentException("app cannot be empty"));
|
||||
}
|
||||
AppInfo appInfo = appManagement.getDetailApp(app);
|
||||
if (appInfo == null || appInfo.getMachines() == null) {
|
||||
return AsyncUtils.newFailedFuture(new IllegalArgumentException("app does not have machines"));
|
||||
}
|
||||
|
||||
boolean machineOk = appInfo.getMachines().stream()
|
||||
.filter(MachineUtils::isMachineHealth)
|
||||
.map(e -> e.getIp() + '@' + e.getPort())
|
||||
.anyMatch(e -> e.equals(machineId));
|
||||
if (!machineOk) {
|
||||
return AsyncUtils.newFailedFuture(new IllegalStateException("machine does not exist or disconnected"));
|
||||
}
|
||||
|
||||
return getClusterUniversalState(app)
|
||||
.thenApply(ClusterEntityUtils::wrapToClusterGroup)
|
||||
.thenCompose(e -> e.stream()
|
||||
.filter(e1 -> e1.getMachineId().equals(machineId))
|
||||
.findAny()
|
||||
.map(CompletableFuture::completedFuture)
|
||||
.orElse(AsyncUtils.newFailedFuture(new IllegalStateException("not a server: " + machineId)))
|
||||
);
|
||||
}
|
||||
|
||||
public CompletableFuture<ClusterUniversalStateVO> getClusterUniversalState(String app, String ip, int port) {
|
||||
return sentinelApiClient.fetchClusterMode(app, ip, port)
|
||||
.thenApply(e -> new ClusterUniversalStateVO().setStateInfo(e))
|
||||
.thenCompose(vo -> {
|
||||
if (vo.getStateInfo().getClientAvailable()) {
|
||||
return sentinelApiClient.fetchClusterClientConfig(app, ip, port)
|
||||
return sentinelApiClient.fetchClusterClientInfoAndConfig(app, ip, port)
|
||||
.thenApply(cc -> vo.setClient(new ClusterClientStateVO().setClientConfig(cc)));
|
||||
} else {
|
||||
return CompletableFuture.completedFuture(vo);
|
||||
|
|
@ -111,6 +174,7 @@ public class ClusterConfigService {
|
|||
private boolean invalidFlowConfig(ServerFlowConfig flowConfig) {
|
||||
return flowConfig == null || flowConfig.getSampleCount() == null || flowConfig.getSampleCount() <= 0
|
||||
|| flowConfig.getIntervalMs() == null || flowConfig.getIntervalMs() <= 0
|
||||
|| flowConfig.getIntervalMs() % flowConfig.getSampleCount() != 0;
|
||||
|| flowConfig.getIntervalMs() % flowConfig.getSampleCount() != 0
|
||||
|| flowConfig.getMaxAllowedQps() == null || flowConfig.getMaxAllowedQps() < 0;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
entity.setGmtCreate(date);
|
||||
entity.setGmtModified(date);
|
||||
entity.setLimitApp(entity.getLimitApp().trim());
|
||||
entity.setResource(entity.getResource().trim());
|
||||
try {
|
||||
entity = repository.save(entity);
|
||||
} catch (Throwable throwable) {
|
||||
|
|
@ -224,7 +226,7 @@ public class FlowControllerV1 {
|
|||
}
|
||||
|
||||
@DeleteMapping("/delete.json")
|
||||
Result<?> delete(Long id) {
|
||||
public Result<Long> delete(Long id) {
|
||||
if (id == null) {
|
||||
return Result.ofFail(-1, "id can't be null");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -137,6 +137,8 @@ public class FlowControllerV2 {
|
|||
Date date = new Date();
|
||||
entity.setGmtCreate(date);
|
||||
entity.setGmtModified(date);
|
||||
entity.setLimitApp(entity.getLimitApp().trim());
|
||||
entity.setResource(entity.getResource().trim());
|
||||
try {
|
||||
entity = repository.save(entity);
|
||||
publishRules(entity.getApp());
|
||||
|
|
|
|||
|
|
@ -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
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.taobao.csp.sentinel.dashboard.view;
|
||||
package com.taobao.csp.sentinel.dashboard.view.cluster;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
|
||||
|
|
@ -26,16 +27,22 @@ import com.alibaba.fastjson.JSONObject;
|
|||
import com.taobao.csp.sentinel.dashboard.client.CommandNotFoundException;
|
||||
import com.taobao.csp.sentinel.dashboard.datasource.entity.SentinelVersion;
|
||||
import com.taobao.csp.sentinel.dashboard.discovery.AppManagement;
|
||||
import com.taobao.csp.sentinel.dashboard.domain.cluster.ClusterClientModifyRequest;
|
||||
import com.taobao.csp.sentinel.dashboard.domain.cluster.ClusterModifyRequest;
|
||||
import com.taobao.csp.sentinel.dashboard.domain.cluster.ClusterServerModifyRequest;
|
||||
import com.taobao.csp.sentinel.dashboard.domain.cluster.ClusterUniversalStateVO;
|
||||
import com.taobao.csp.sentinel.dashboard.domain.cluster.request.ClusterClientModifyRequest;
|
||||
import com.taobao.csp.sentinel.dashboard.domain.cluster.request.ClusterModifyRequest;
|
||||
import com.taobao.csp.sentinel.dashboard.domain.cluster.request.ClusterServerModifyRequest;
|
||||
import com.taobao.csp.sentinel.dashboard.domain.cluster.state.AppClusterClientStateWrapVO;
|
||||
import com.taobao.csp.sentinel.dashboard.domain.cluster.state.AppClusterServerStateWrapVO;
|
||||
import com.taobao.csp.sentinel.dashboard.domain.cluster.state.ClusterUniversalStatePairVO;
|
||||
import com.taobao.csp.sentinel.dashboard.domain.cluster.state.ClusterUniversalStateVO;
|
||||
import com.taobao.csp.sentinel.dashboard.service.ClusterConfigService;
|
||||
import com.taobao.csp.sentinel.dashboard.util.ClusterEntityUtils;
|
||||
import com.taobao.csp.sentinel.dashboard.util.VersionUtils;
|
||||
import com.taobao.csp.sentinel.dashboard.view.Result;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
|
|
@ -60,7 +67,7 @@ public class ClusterConfigController {
|
|||
@Autowired
|
||||
private ClusterConfigService clusterConfigService;
|
||||
|
||||
@PostMapping("/config/modify")
|
||||
@PostMapping("/config/modify_single")
|
||||
public Result<Boolean> apiModifyClusterConfig(@RequestBody String payload) {
|
||||
if (StringUtil.isBlank(payload)) {
|
||||
return Result.ofFail(-1, "empty request body");
|
||||
|
|
@ -94,18 +101,22 @@ public class ClusterConfigController {
|
|||
return Result.ofFail(-1, "invalid parameter");
|
||||
} catch (ExecutionException ex) {
|
||||
logger.error("Error when modifying cluster config", ex.getCause());
|
||||
if (isNotSupported(ex.getCause())) {
|
||||
return unsupportedVersion();
|
||||
} else {
|
||||
return Result.ofThrowable(-1, ex.getCause());
|
||||
}
|
||||
return errorResponse(ex);
|
||||
} catch (Throwable ex) {
|
||||
logger.error("Error when modifying cluster config", ex);
|
||||
return Result.ofFail(-1, ex.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@GetMapping("/state")
|
||||
private <T> Result<T> errorResponse(ExecutionException ex) {
|
||||
if (isNotSupported(ex.getCause())) {
|
||||
return unsupportedVersion();
|
||||
} else {
|
||||
return Result.ofThrowable(-1, ex.getCause());
|
||||
}
|
||||
}
|
||||
|
||||
@GetMapping("/state_single")
|
||||
public Result<ClusterUniversalStateVO> apiGetClusterState(@RequestParam String app,
|
||||
@RequestParam String ip,
|
||||
@RequestParam Integer port) {
|
||||
|
|
@ -127,17 +138,69 @@ public class ClusterConfigController {
|
|||
.get();
|
||||
} catch (ExecutionException ex) {
|
||||
logger.error("Error when fetching cluster state", ex.getCause());
|
||||
if (isNotSupported(ex.getCause())) {
|
||||
return unsupportedVersion();
|
||||
} else {
|
||||
return Result.ofThrowable(-1, ex.getCause());
|
||||
}
|
||||
return errorResponse(ex);
|
||||
} catch (Throwable throwable) {
|
||||
logger.error("Error when fetching cluster state", throwable);
|
||||
return Result.ofFail(-1, throwable.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@GetMapping("/server_state/{app}")
|
||||
public Result<List<AppClusterServerStateWrapVO>> apiGetClusterServerStateOfApp(@PathVariable String app) {
|
||||
if (StringUtil.isEmpty(app)) {
|
||||
return Result.ofFail(-1, "app cannot be null or empty");
|
||||
}
|
||||
try {
|
||||
return clusterConfigService.getClusterUniversalState(app)
|
||||
.thenApply(ClusterEntityUtils::wrapToAppClusterServerState)
|
||||
.thenApply(Result::ofSuccess)
|
||||
.get();
|
||||
} catch (ExecutionException ex) {
|
||||
logger.error("Error when fetching cluster server state of app: " + app, ex.getCause());
|
||||
return errorResponse(ex);
|
||||
} catch (Throwable throwable) {
|
||||
logger.error("Error when fetching cluster server state of app: " + app, throwable);
|
||||
return Result.ofFail(-1, throwable.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@GetMapping("/client_state/{app}")
|
||||
public Result<List<AppClusterClientStateWrapVO>> apiGetClusterClientStateOfApp(@PathVariable String app) {
|
||||
if (StringUtil.isEmpty(app)) {
|
||||
return Result.ofFail(-1, "app cannot be null or empty");
|
||||
}
|
||||
try {
|
||||
return clusterConfigService.getClusterUniversalState(app)
|
||||
.thenApply(ClusterEntityUtils::wrapToAppClusterClientState)
|
||||
.thenApply(Result::ofSuccess)
|
||||
.get();
|
||||
} catch (ExecutionException ex) {
|
||||
logger.error("Error when fetching cluster token client state of app: " + app, ex.getCause());
|
||||
return errorResponse(ex);
|
||||
} catch (Throwable throwable) {
|
||||
logger.error("Error when fetching cluster token client state of app: " + app, throwable);
|
||||
return Result.ofFail(-1, throwable.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@GetMapping("/state/{app}")
|
||||
public Result<List<ClusterUniversalStatePairVO>> apiGetClusterStateOfApp(@PathVariable String app) {
|
||||
if (StringUtil.isEmpty(app)) {
|
||||
return Result.ofFail(-1, "app cannot be null or empty");
|
||||
}
|
||||
try {
|
||||
return clusterConfigService.getClusterUniversalState(app)
|
||||
.thenApply(Result::ofSuccess)
|
||||
.get();
|
||||
} catch (ExecutionException ex) {
|
||||
logger.error("Error when fetching cluster state of app: " + app, ex.getCause());
|
||||
return errorResponse(ex);
|
||||
} catch (Throwable throwable) {
|
||||
logger.error("Error when fetching cluster state of app: " + app, throwable);
|
||||
return Result.ofFail(-1, throwable.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isNotSupported(Throwable ex) {
|
||||
return ex instanceof CommandNotFoundException;
|
||||
}
|
||||
|
|
@ -25,6 +25,7 @@ public final class NacosConfigUtil {
|
|||
|
||||
public static final String FLOW_DATA_ID_POSTFIX = "-flow-rules";
|
||||
public static final String PARAM_FLOW_DATA_ID_POSTFIX = "-param-rules";
|
||||
public static final String CLUSTER_MAP_DATA_ID_POSTFIX = "-cluster-map";
|
||||
|
||||
/**
|
||||
* cc for `cluster-client`
|
||||
|
|
|
|||
|
|
@ -0,0 +1,62 @@
|
|||
/*
|
||||
* Copyright 1999-2018 Alibaba Group Holding Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.alibaba.csp.sentinel.demo.cluster;
|
||||
|
||||
import com.alibaba.csp.sentinel.Entry;
|
||||
import com.alibaba.csp.sentinel.EntryType;
|
||||
import com.alibaba.csp.sentinel.SphU;
|
||||
import com.alibaba.csp.sentinel.cluster.ClusterStateManager;
|
||||
import com.alibaba.csp.sentinel.init.InitExecutor;
|
||||
import com.alibaba.csp.sentinel.slots.block.BlockException;
|
||||
|
||||
/**
|
||||
* <p>Run this demo with the following args: -Dproject.name=appA</p>
|
||||
* <p>You need a token server running already.</p>
|
||||
*
|
||||
* @author Eric Zhao
|
||||
*/
|
||||
public class ClusterClientDemo {
|
||||
|
||||
public static void main(String[] args) {
|
||||
InitExecutor.doInit();
|
||||
|
||||
// Manually schedule the cluster mode to client.
|
||||
// In common, we need a scheduling system to modify the cluster mode automatically.
|
||||
// Command HTTP API: http://<ip>:<port>/setClusterMode?mode=<xxx>
|
||||
ClusterStateManager.setToClient();
|
||||
|
||||
String resourceName = "cluster-demo-entry";
|
||||
|
||||
// Assume we have a cluster flow rule for `demo-resource`: QPS = 5 in AVG_LOCAL mode.
|
||||
for (int i = 0; i < 10; i++) {
|
||||
tryEntry(resourceName);
|
||||
}
|
||||
}
|
||||
|
||||
private static void tryEntry(String res) {
|
||||
Entry entry = null;
|
||||
try {
|
||||
entry = SphU.entry(res, EntryType.IN, 1, "abc", "def");
|
||||
System.out.println("Passed");
|
||||
} catch (BlockException ex) {
|
||||
ex.printStackTrace();
|
||||
} finally {
|
||||
if (entry != null) {
|
||||
entry.exit();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue