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>
|
<artifactId>spring-boot-starter-logging</artifactId>
|
||||||
<version>${spring.boot.version}</version>
|
<version>${spring.boot.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
|
||||||
<groupId>org.springframework.boot</groupId>
|
|
||||||
<artifactId>spring-boot-devtools</artifactId>
|
|
||||||
<version>${spring.boot.version}</version>
|
|
||||||
<optional>true</optional>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springframework.boot</groupId>
|
<groupId>org.springframework.boot</groupId>
|
||||||
<artifactId>spring-boot-starter-test</artifactId>
|
<artifactId>spring-boot-starter-test</artifactId>
|
||||||
|
|
@ -105,11 +99,17 @@
|
||||||
<groupId>com.alibaba</groupId>
|
<groupId>com.alibaba</groupId>
|
||||||
<artifactId>fastjson</artifactId>
|
<artifactId>fastjson</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>junit</groupId>
|
<groupId>junit</groupId>
|
||||||
<artifactId>junit</artifactId>
|
<artifactId>junit</artifactId>
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.mockito</groupId>
|
||||||
|
<artifactId>mockito-core</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
<build>
|
<build>
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,8 @@
|
||||||
*/
|
*/
|
||||||
package com.taobao.csp.sentinel.dashboard;
|
package com.taobao.csp.sentinel.dashboard;
|
||||||
|
|
||||||
|
import com.alibaba.csp.sentinel.init.InitExecutor;
|
||||||
|
|
||||||
import org.springframework.boot.SpringApplication;
|
import org.springframework.boot.SpringApplication;
|
||||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
|
|
||||||
|
|
@ -27,6 +29,11 @@ import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
public class DashboardApplication {
|
public class DashboardApplication {
|
||||||
|
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
|
triggerSentinelInit();
|
||||||
SpringApplication.run(DashboardApplication.class, args);
|
SpringApplication.run(DashboardApplication.class, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void triggerSentinelInit() {
|
||||||
|
new Thread(() -> InitExecutor.doInit()).start();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -13,20 +13,21 @@
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package com.taobao.csp.sentinel.dashboard.util;
|
package com.taobao.csp.sentinel.dashboard.client;
|
||||||
|
|
||||||
import com.taobao.csp.sentinel.dashboard.discovery.MachineDiscovery;
|
|
||||||
import com.taobao.csp.sentinel.dashboard.discovery.MachineInfo;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Eric Zhao
|
* @author Eric Zhao
|
||||||
*/
|
*/
|
||||||
public final class MachineUtil {
|
public class CommandFailedException extends RuntimeException {
|
||||||
|
|
||||||
public static boolean isMachineHealth(MachineInfo machine) {
|
public CommandFailedException() {}
|
||||||
if (machine == null) {
|
|
||||||
return false;
|
public CommandFailedException(String message) {
|
||||||
}
|
super(message);
|
||||||
return System.currentTimeMillis() - machine.getTimestamp().getTime() < MachineDiscovery.MAX_CLIENT_LIVE_TIME_MS;
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized Throwable fillInStackTrace() {
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -20,9 +20,15 @@ package com.taobao.csp.sentinel.dashboard.client;
|
||||||
* @since 0.2.1
|
* @since 0.2.1
|
||||||
*/
|
*/
|
||||||
public class CommandNotFoundException extends Exception {
|
public class CommandNotFoundException extends Exception {
|
||||||
|
|
||||||
public CommandNotFoundException() { }
|
public CommandNotFoundException() { }
|
||||||
|
|
||||||
public CommandNotFoundException(String message) {
|
public CommandNotFoundException(String message) {
|
||||||
super(message);
|
super(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized Throwable fillInStackTrace() {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,7 @@ import java.util.concurrent.TimeUnit;
|
||||||
import java.util.concurrent.atomic.AtomicReference;
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import com.alibaba.csp.sentinel.command.CommandConstants;
|
||||||
import com.alibaba.csp.sentinel.config.SentinelConfig;
|
import com.alibaba.csp.sentinel.config.SentinelConfig;
|
||||||
import com.alibaba.csp.sentinel.command.vo.NodeVo;
|
import com.alibaba.csp.sentinel.command.vo.NodeVo;
|
||||||
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRule;
|
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRule;
|
||||||
|
|
@ -43,8 +44,9 @@ import com.taobao.csp.sentinel.dashboard.datasource.entity.rule.DegradeRuleEntit
|
||||||
import com.taobao.csp.sentinel.dashboard.datasource.entity.rule.FlowRuleEntity;
|
import com.taobao.csp.sentinel.dashboard.datasource.entity.rule.FlowRuleEntity;
|
||||||
import com.taobao.csp.sentinel.dashboard.datasource.entity.rule.ParamFlowRuleEntity;
|
import com.taobao.csp.sentinel.dashboard.datasource.entity.rule.ParamFlowRuleEntity;
|
||||||
import com.taobao.csp.sentinel.dashboard.datasource.entity.rule.SystemRuleEntity;
|
import com.taobao.csp.sentinel.dashboard.datasource.entity.rule.SystemRuleEntity;
|
||||||
import com.taobao.csp.sentinel.dashboard.domain.cluster.ClusterServerStateVO;
|
import com.taobao.csp.sentinel.dashboard.domain.cluster.ClusterClientInfoVO;
|
||||||
import com.taobao.csp.sentinel.dashboard.domain.cluster.ClusterStateSimpleEntity;
|
import com.taobao.csp.sentinel.dashboard.domain.cluster.state.ClusterServerStateVO;
|
||||||
|
import com.taobao.csp.sentinel.dashboard.domain.cluster.state.ClusterStateSimpleEntity;
|
||||||
import com.taobao.csp.sentinel.dashboard.domain.cluster.config.ClusterClientConfig;
|
import com.taobao.csp.sentinel.dashboard.domain.cluster.config.ClusterClientConfig;
|
||||||
import com.taobao.csp.sentinel.dashboard.domain.cluster.config.ServerFlowConfig;
|
import com.taobao.csp.sentinel.dashboard.domain.cluster.config.ServerFlowConfig;
|
||||||
import com.taobao.csp.sentinel.dashboard.domain.cluster.config.ServerTransportConfig;
|
import com.taobao.csp.sentinel.dashboard.domain.cluster.config.ServerTransportConfig;
|
||||||
|
|
@ -63,6 +65,8 @@ import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import static com.taobao.csp.sentinel.dashboard.util.AsyncUtils.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Communicate with Sentinel client.
|
* Communicate with Sentinel client.
|
||||||
*
|
*
|
||||||
|
|
@ -104,7 +108,7 @@ public class SentinelApiClient {
|
||||||
private final boolean enableHttps = false;
|
private final boolean enableHttps = false;
|
||||||
|
|
||||||
public SentinelApiClient() {
|
public SentinelApiClient() {
|
||||||
IOReactorConfig ioConfig = IOReactorConfig.custom().setConnectTimeout(3000).setSoTimeout(3000)
|
IOReactorConfig ioConfig = IOReactorConfig.custom().setConnectTimeout(3000).setSoTimeout(10000)
|
||||||
.setIoThreadCount(Runtime.getRuntime().availableProcessors() * 2).build();
|
.setIoThreadCount(Runtime.getRuntime().availableProcessors() * 2).build();
|
||||||
httpClient = HttpAsyncClients.custom().setRedirectStrategy(new DefaultRedirectStrategy() {
|
httpClient = HttpAsyncClients.custom().setRedirectStrategy(new DefaultRedirectStrategy() {
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -115,6 +119,109 @@ public class SentinelApiClient {
|
||||||
httpClient.start();
|
httpClient.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean isSuccess(int statusCode) {
|
||||||
|
return statusCode >= 200 && statusCode < 300;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isCommandNotFound(int statusCode, String body) {
|
||||||
|
return statusCode == 400 && StringUtil.isNotEmpty(body) && body.contains(CommandConstants.MSG_UNKNOWN_COMMAND_PREFIX);
|
||||||
|
}
|
||||||
|
|
||||||
|
private CompletableFuture<String> executeCommand(String command, URI uri) {
|
||||||
|
CompletableFuture<String> future = new CompletableFuture<>();
|
||||||
|
if (StringUtil.isBlank(command) || uri == null) {
|
||||||
|
future.completeExceptionally(new IllegalArgumentException("Bad URL or command name"));
|
||||||
|
return future;
|
||||||
|
}
|
||||||
|
final HttpGet httpGet = new HttpGet(uri);
|
||||||
|
httpClient.execute(httpGet, new FutureCallback<HttpResponse>() {
|
||||||
|
@Override
|
||||||
|
public void completed(final HttpResponse response) {
|
||||||
|
int statusCode = response.getStatusLine().getStatusCode();
|
||||||
|
try {
|
||||||
|
String value = getBody(response);
|
||||||
|
if (isSuccess(statusCode)) {
|
||||||
|
future.complete(value);
|
||||||
|
} else {
|
||||||
|
if (isCommandNotFound(statusCode, value)) {
|
||||||
|
future.completeExceptionally(new CommandNotFoundException(command));
|
||||||
|
} else {
|
||||||
|
future.completeExceptionally(new CommandFailedException(value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (Exception ex) {
|
||||||
|
future.completeExceptionally(ex);
|
||||||
|
logger.error("HTTP request failed: " + uri.toString(), ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void failed(final Exception ex) {
|
||||||
|
future.completeExceptionally(ex);
|
||||||
|
logger.error("HTTP request failed: " + uri.toString(), ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void cancelled() {
|
||||||
|
future.complete(null);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return future;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String httpGetContent(String url) {
|
||||||
|
final HttpGet httpGet = new HttpGet(url);
|
||||||
|
final CountDownLatch latch = new CountDownLatch(1);
|
||||||
|
final AtomicReference<String> reference = new AtomicReference<>();
|
||||||
|
httpClient.execute(httpGet, new FutureCallback<HttpResponse>() {
|
||||||
|
@Override
|
||||||
|
public void completed(final HttpResponse response) {
|
||||||
|
try {
|
||||||
|
reference.set(getBody(response));
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.info("httpGetContent " + url + " error:", e);
|
||||||
|
} finally {
|
||||||
|
latch.countDown();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void failed(final Exception ex) {
|
||||||
|
latch.countDown();
|
||||||
|
logger.info("httpGetContent " + url + " failed:", ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void cancelled() {
|
||||||
|
latch.countDown();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
try {
|
||||||
|
latch.await(5, TimeUnit.SECONDS);
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.info("wait http client error:", e);
|
||||||
|
}
|
||||||
|
return reference.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getBody(HttpResponse response) throws Exception {
|
||||||
|
Charset charset = null;
|
||||||
|
try {
|
||||||
|
String contentTypeStr = response.getFirstHeader("Content-type").getValue();
|
||||||
|
if (StringUtil.isNotEmpty(contentTypeStr)) {
|
||||||
|
ContentType contentType = ContentType.parse(contentTypeStr);
|
||||||
|
charset = contentType.getCharset();
|
||||||
|
}
|
||||||
|
} catch (Exception ignore) {
|
||||||
|
}
|
||||||
|
return EntityUtils.toString(response.getEntity(), charset != null ? charset : DEFAULT_CHARSET);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void close() throws Exception {
|
||||||
|
httpClient.close();
|
||||||
|
}
|
||||||
|
|
||||||
public List<NodeVo> fetchResourceOfMachine(String ip, int port, String type) {
|
public List<NodeVo> fetchResourceOfMachine(String ip, int port, String type) {
|
||||||
String url = "http://" + ip + ":" + port + "/" + RESOURCE_URL_PATH + "?type=" + type;
|
String url = "http://" + ip + ":" + port + "/" + RESOURCE_URL_PATH + "?type=" + type;
|
||||||
String body = httpGetContent(url);
|
String body = httpGetContent(url);
|
||||||
|
|
@ -388,7 +495,7 @@ public class SentinelApiClient {
|
||||||
.setParameter("data", data);
|
.setParameter("data", data);
|
||||||
return executeCommand(SET_PARAM_RULE_PATH, uriBuilder.build())
|
return executeCommand(SET_PARAM_RULE_PATH, uriBuilder.build())
|
||||||
.thenCompose(e -> {
|
.thenCompose(e -> {
|
||||||
if ("success".equals(e)) {
|
if (CommandConstants.MSG_SUCCESS.equals(e)) {
|
||||||
return CompletableFuture.completedFuture(null);
|
return CompletableFuture.completedFuture(null);
|
||||||
} else {
|
} else {
|
||||||
logger.warn("Push parameter flow rules to client failed: " + e);
|
logger.warn("Push parameter flow rules to client failed: " + e);
|
||||||
|
|
@ -401,109 +508,6 @@ public class SentinelApiClient {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isSuccess(int statusCode) {
|
|
||||||
return statusCode >= 200 && statusCode < 300;
|
|
||||||
}
|
|
||||||
|
|
||||||
private CompletableFuture<String> executeCommand(String command, URI uri) {
|
|
||||||
CompletableFuture<String> future = new CompletableFuture<>();
|
|
||||||
if (StringUtil.isBlank(command) || uri == null) {
|
|
||||||
future.completeExceptionally(new IllegalArgumentException("Bad URL or command name"));
|
|
||||||
return future;
|
|
||||||
}
|
|
||||||
final HttpGet httpGet = new HttpGet(uri);
|
|
||||||
httpClient.execute(httpGet, new FutureCallback<HttpResponse>() {
|
|
||||||
@Override
|
|
||||||
public void completed(final HttpResponse response) {
|
|
||||||
int statusCode = response.getStatusLine().getStatusCode();
|
|
||||||
try {
|
|
||||||
String value = getBody(response);
|
|
||||||
if (isSuccess(statusCode)) {
|
|
||||||
future.complete(value);
|
|
||||||
} else {
|
|
||||||
if (statusCode == 400) {
|
|
||||||
future.completeExceptionally(new CommandNotFoundException(command));
|
|
||||||
} else {
|
|
||||||
future.completeExceptionally(new IllegalStateException(value));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} catch (Exception ex) {
|
|
||||||
future.completeExceptionally(ex);
|
|
||||||
logger.error("HTTP request failed: " + uri.toString(), ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void failed(final Exception ex) {
|
|
||||||
future.completeExceptionally(ex);
|
|
||||||
logger.error("HTTP request failed: " + uri.toString(), ex);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void cancelled() {
|
|
||||||
future.complete(null);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return future;
|
|
||||||
}
|
|
||||||
|
|
||||||
private String httpGetContent(String url) {
|
|
||||||
final HttpGet httpGet = new HttpGet(url);
|
|
||||||
final CountDownLatch latch = new CountDownLatch(1);
|
|
||||||
final AtomicReference<String> reference = new AtomicReference<>();
|
|
||||||
httpClient.execute(httpGet, new FutureCallback<HttpResponse>() {
|
|
||||||
@Override
|
|
||||||
public void completed(final HttpResponse response) {
|
|
||||||
try {
|
|
||||||
reference.set(getBody(response));
|
|
||||||
} catch (Exception e) {
|
|
||||||
logger.info("httpGetContent " + url + " error:", e);
|
|
||||||
} finally {
|
|
||||||
latch.countDown();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void failed(final Exception ex) {
|
|
||||||
latch.countDown();
|
|
||||||
logger.info("httpGetContent " + url + " failed:", ex);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void cancelled() {
|
|
||||||
latch.countDown();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
try {
|
|
||||||
latch.await(5, TimeUnit.SECONDS);
|
|
||||||
} catch (Exception e) {
|
|
||||||
logger.info("wait http client error:", e);
|
|
||||||
}
|
|
||||||
return reference.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
private String getBody(HttpResponse response) throws Exception {
|
|
||||||
Charset charset = null;
|
|
||||||
try {
|
|
||||||
String contentTypeStr = response.getFirstHeader("Content-type").getValue();
|
|
||||||
ContentType contentType = ContentType.parse(contentTypeStr);
|
|
||||||
charset = contentType.getCharset();
|
|
||||||
} catch (Exception ignore) {
|
|
||||||
}
|
|
||||||
return EntityUtils.toString(response.getEntity(), charset != null ? charset : DEFAULT_CHARSET);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void close() throws Exception {
|
|
||||||
httpClient.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
private <R> CompletableFuture<R> newFailedFuture(Throwable ex) {
|
|
||||||
CompletableFuture<R> future = new CompletableFuture<>();
|
|
||||||
future.completeExceptionally(ex);
|
|
||||||
return future;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Cluster related
|
// Cluster related
|
||||||
|
|
||||||
public CompletableFuture<ClusterStateSimpleEntity> fetchClusterMode(String app, String ip, int port) {
|
public CompletableFuture<ClusterStateSimpleEntity> fetchClusterMode(String app, String ip, int port) {
|
||||||
|
|
@ -533,7 +537,7 @@ public class SentinelApiClient {
|
||||||
.setParameter("mode", String.valueOf(mode));
|
.setParameter("mode", String.valueOf(mode));
|
||||||
return executeCommand(MODIFY_CLUSTER_MODE_PATH, uriBuilder.build())
|
return executeCommand(MODIFY_CLUSTER_MODE_PATH, uriBuilder.build())
|
||||||
.thenCompose(e -> {
|
.thenCompose(e -> {
|
||||||
if ("success".equals(e)) {
|
if (CommandConstants.MSG_SUCCESS.equals(e)) {
|
||||||
return CompletableFuture.completedFuture(null);
|
return CompletableFuture.completedFuture(null);
|
||||||
} else {
|
} else {
|
||||||
logger.warn("Error when modifying cluster mode: " + e);
|
logger.warn("Error when modifying cluster mode: " + e);
|
||||||
|
|
@ -546,7 +550,7 @@ public class SentinelApiClient {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public CompletableFuture<ClusterClientConfig> fetchClusterClientConfig(String app, String ip, int port) {
|
public CompletableFuture<ClusterClientInfoVO> fetchClusterClientInfoAndConfig(String app, String ip, int port) {
|
||||||
if (StringUtil.isBlank(ip) || port <= 0) {
|
if (StringUtil.isBlank(ip) || port <= 0) {
|
||||||
return newFailedFuture(new IllegalArgumentException("Invalid parameter"));
|
return newFailedFuture(new IllegalArgumentException("Invalid parameter"));
|
||||||
}
|
}
|
||||||
|
|
@ -555,7 +559,7 @@ public class SentinelApiClient {
|
||||||
uriBuilder.setScheme("http").setHost(ip).setPort(port)
|
uriBuilder.setScheme("http").setHost(ip).setPort(port)
|
||||||
.setPath(FETCH_CLUSTER_CLIENT_CONFIG_PATH);
|
.setPath(FETCH_CLUSTER_CLIENT_CONFIG_PATH);
|
||||||
return executeCommand(FETCH_CLUSTER_CLIENT_CONFIG_PATH, uriBuilder.build())
|
return executeCommand(FETCH_CLUSTER_CLIENT_CONFIG_PATH, uriBuilder.build())
|
||||||
.thenApply(r -> JSON.parseObject(r, ClusterClientConfig.class));
|
.thenApply(r -> JSON.parseObject(r, ClusterClientInfoVO.class));
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
logger.warn("Error when fetching cluster client config", ex);
|
logger.warn("Error when fetching cluster client config", ex);
|
||||||
return newFailedFuture(ex);
|
return newFailedFuture(ex);
|
||||||
|
|
@ -573,7 +577,7 @@ public class SentinelApiClient {
|
||||||
.setParameter("data", JSON.toJSONString(config));
|
.setParameter("data", JSON.toJSONString(config));
|
||||||
return executeCommand(MODIFY_CLUSTER_MODE_PATH, uriBuilder.build())
|
return executeCommand(MODIFY_CLUSTER_MODE_PATH, uriBuilder.build())
|
||||||
.thenCompose(e -> {
|
.thenCompose(e -> {
|
||||||
if ("success".equals(e)) {
|
if (CommandConstants.MSG_SUCCESS.equals(e)) {
|
||||||
return CompletableFuture.completedFuture(null);
|
return CompletableFuture.completedFuture(null);
|
||||||
} else {
|
} else {
|
||||||
logger.warn("Error when modifying cluster client config: " + e);
|
logger.warn("Error when modifying cluster client config: " + e);
|
||||||
|
|
@ -597,7 +601,7 @@ public class SentinelApiClient {
|
||||||
.setParameter("data", JSON.toJSONString(config));
|
.setParameter("data", JSON.toJSONString(config));
|
||||||
return executeCommand(MODIFY_CLUSTER_SERVER_FLOW_CONFIG_PATH, uriBuilder.build())
|
return executeCommand(MODIFY_CLUSTER_SERVER_FLOW_CONFIG_PATH, uriBuilder.build())
|
||||||
.thenCompose(e -> {
|
.thenCompose(e -> {
|
||||||
if ("success".equals(e)) {
|
if (CommandConstants.MSG_SUCCESS.equals(e)) {
|
||||||
return CompletableFuture.completedFuture(null);
|
return CompletableFuture.completedFuture(null);
|
||||||
} else {
|
} else {
|
||||||
logger.warn("Error when modifying cluster server flow config: " + e);
|
logger.warn("Error when modifying cluster server flow config: " + e);
|
||||||
|
|
@ -622,7 +626,7 @@ public class SentinelApiClient {
|
||||||
.setParameter("idleSeconds", config.getIdleSeconds().toString());
|
.setParameter("idleSeconds", config.getIdleSeconds().toString());
|
||||||
return executeCommand(MODIFY_CLUSTER_SERVER_TRANSPORT_CONFIG_PATH, uriBuilder.build())
|
return executeCommand(MODIFY_CLUSTER_SERVER_TRANSPORT_CONFIG_PATH, uriBuilder.build())
|
||||||
.thenCompose(e -> {
|
.thenCompose(e -> {
|
||||||
if ("success".equals(e)) {
|
if (CommandConstants.MSG_SUCCESS.equals(e)) {
|
||||||
return CompletableFuture.completedFuture(null);
|
return CompletableFuture.completedFuture(null);
|
||||||
} else {
|
} else {
|
||||||
logger.warn("Error when modifying cluster server transport config: " + e);
|
logger.warn("Error when modifying cluster server transport config: " + e);
|
||||||
|
|
@ -646,7 +650,7 @@ public class SentinelApiClient {
|
||||||
.setParameter("data", JSON.toJSONString(set));
|
.setParameter("data", JSON.toJSONString(set));
|
||||||
return executeCommand(MODIFY_CLUSTER_SERVER_NAMESPACE_SET_PATH, uriBuilder.build())
|
return executeCommand(MODIFY_CLUSTER_SERVER_NAMESPACE_SET_PATH, uriBuilder.build())
|
||||||
.thenCompose(e -> {
|
.thenCompose(e -> {
|
||||||
if ("success".equals(e)) {
|
if (CommandConstants.MSG_SUCCESS.equals(e)) {
|
||||||
return CompletableFuture.completedFuture(null);
|
return CompletableFuture.completedFuture(null);
|
||||||
} else {
|
} else {
|
||||||
logger.warn("Error when modifying cluster server NamespaceSet: " + e);
|
logger.warn("Error when modifying cluster server NamespaceSet: " + e);
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@ import java.util.Set;
|
||||||
public interface MachineDiscovery {
|
public interface MachineDiscovery {
|
||||||
|
|
||||||
long MAX_CLIENT_LIVE_TIME_MS = 1000 * 60 * 5;
|
long MAX_CLIENT_LIVE_TIME_MS = 1000 * 60 * 5;
|
||||||
String UNKNOWN_APP_NAME = "UNKNOWN";
|
String UNKNOWN_APP_NAME = "CLUSTER_NOT_STARTED";
|
||||||
|
|
||||||
List<String> getAppNames();
|
List<String> getAppNames();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -41,6 +41,10 @@ public class MachineInfo implements Comparable<MachineInfo> {
|
||||||
return machineInfo;
|
return machineInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String toHostPort() {
|
||||||
|
return ip + ":" + port;
|
||||||
|
}
|
||||||
|
|
||||||
public Integer getPort() {
|
public Integer getPort() {
|
||||||
return port;
|
return port;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,66 @@
|
||||||
|
/*
|
||||||
|
* Copyright 1999-2018 Alibaba Group Holding Ltd.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package com.taobao.csp.sentinel.dashboard.domain.cluster;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Eric Zhao
|
||||||
|
* @since 1.4.1
|
||||||
|
*/
|
||||||
|
public class ClusterAppAssignResultVO {
|
||||||
|
|
||||||
|
private Set<String> failedServerSet;
|
||||||
|
private Set<String> failedClientSet;
|
||||||
|
|
||||||
|
private Integer totalCount;
|
||||||
|
|
||||||
|
public Set<String> getFailedServerSet() {
|
||||||
|
return failedServerSet;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ClusterAppAssignResultVO setFailedServerSet(Set<String> failedServerSet) {
|
||||||
|
this.failedServerSet = failedServerSet;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<String> getFailedClientSet() {
|
||||||
|
return failedClientSet;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ClusterAppAssignResultVO setFailedClientSet(Set<String> failedClientSet) {
|
||||||
|
this.failedClientSet = failedClientSet;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getTotalCount() {
|
||||||
|
return totalCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ClusterAppAssignResultVO setTotalCount(Integer totalCount) {
|
||||||
|
this.totalCount = totalCount;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "ClusterAppAssignResultVO{" +
|
||||||
|
"failedServerSet=" + failedServerSet +
|
||||||
|
", failedClientSet=" + failedClientSet +
|
||||||
|
", totalCount=" + totalCount +
|
||||||
|
'}';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,58 @@
|
||||||
|
/*
|
||||||
|
* Copyright 1999-2018 Alibaba Group Holding Ltd.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package com.taobao.csp.sentinel.dashboard.domain.cluster;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import com.taobao.csp.sentinel.dashboard.domain.cluster.request.ClusterAppAssignMap;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Eric Zhao
|
||||||
|
* @since 1.4.1
|
||||||
|
*/
|
||||||
|
public class ClusterAppFullAssignRequest {
|
||||||
|
|
||||||
|
private List<ClusterAppAssignMap> clusterMap;
|
||||||
|
private Set<String> remainingList;
|
||||||
|
|
||||||
|
public List<ClusterAppAssignMap> getClusterMap() {
|
||||||
|
return clusterMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ClusterAppFullAssignRequest setClusterMap(
|
||||||
|
List<ClusterAppAssignMap> clusterMap) {
|
||||||
|
this.clusterMap = clusterMap;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<String> getRemainingList() {
|
||||||
|
return remainingList;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ClusterAppFullAssignRequest setRemainingList(Set<String> remainingList) {
|
||||||
|
this.remainingList = remainingList;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "ClusterAppFullAssignRequest{" +
|
||||||
|
"clusterMap=" + clusterMap +
|
||||||
|
", remainingList=" + remainingList +
|
||||||
|
'}';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,56 @@
|
||||||
|
/*
|
||||||
|
* Copyright 1999-2018 Alibaba Group Holding Ltd.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package com.taobao.csp.sentinel.dashboard.domain.cluster;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import com.taobao.csp.sentinel.dashboard.domain.cluster.request.ClusterAppAssignMap;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Eric Zhao
|
||||||
|
* @since 1.4.1
|
||||||
|
*/
|
||||||
|
public class ClusterAppSingleServerAssignRequest {
|
||||||
|
|
||||||
|
private ClusterAppAssignMap clusterMap;
|
||||||
|
private Set<String> remainingList;
|
||||||
|
|
||||||
|
public ClusterAppAssignMap getClusterMap() {
|
||||||
|
return clusterMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ClusterAppSingleServerAssignRequest setClusterMap(ClusterAppAssignMap clusterMap) {
|
||||||
|
this.clusterMap = clusterMap;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<String> getRemainingList() {
|
||||||
|
return remainingList;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ClusterAppSingleServerAssignRequest setRemainingList(Set<String> remainingList) {
|
||||||
|
this.remainingList = remainingList;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "ClusterAppSingleServerAssignRequest{" +
|
||||||
|
"clusterMap=" + clusterMap +
|
||||||
|
", remainingList=" + remainingList +
|
||||||
|
'}';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,76 @@
|
||||||
|
/*
|
||||||
|
* Copyright 1999-2018 Alibaba Group Holding Ltd.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package com.taobao.csp.sentinel.dashboard.domain.cluster;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Eric Zhao
|
||||||
|
* @since 1.4.1
|
||||||
|
*/
|
||||||
|
public class ClusterClientInfoVO {
|
||||||
|
|
||||||
|
private String serverHost;
|
||||||
|
private Integer serverPort;
|
||||||
|
|
||||||
|
private Integer clientState;
|
||||||
|
|
||||||
|
private Integer requestTimeout;
|
||||||
|
|
||||||
|
public String getServerHost() {
|
||||||
|
return serverHost;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ClusterClientInfoVO setServerHost(String serverHost) {
|
||||||
|
this.serverHost = serverHost;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getServerPort() {
|
||||||
|
return serverPort;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ClusterClientInfoVO setServerPort(Integer serverPort) {
|
||||||
|
this.serverPort = serverPort;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getClientState() {
|
||||||
|
return clientState;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ClusterClientInfoVO setClientState(Integer clientState) {
|
||||||
|
this.clientState = clientState;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getRequestTimeout() {
|
||||||
|
return requestTimeout;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ClusterClientInfoVO setRequestTimeout(Integer requestTimeout) {
|
||||||
|
this.requestTimeout = requestTimeout;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "ClusterClientInfoVO{" +
|
||||||
|
"serverHost='" + serverHost + '\'' +
|
||||||
|
", serverPort=" + serverPort +
|
||||||
|
", clientState=" + clientState +
|
||||||
|
", requestTimeout=" + requestTimeout +
|
||||||
|
'}';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,91 @@
|
||||||
|
/*
|
||||||
|
* Copyright 1999-2018 Alibaba Group Holding Ltd.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package com.taobao.csp.sentinel.dashboard.domain.cluster;
|
||||||
|
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Eric Zhao
|
||||||
|
* @since 1.4.1
|
||||||
|
*/
|
||||||
|
public class ClusterGroupEntity {
|
||||||
|
|
||||||
|
private String machineId;
|
||||||
|
|
||||||
|
private String ip;
|
||||||
|
private Integer port;
|
||||||
|
|
||||||
|
private Set<String> clientSet = new HashSet<>();
|
||||||
|
|
||||||
|
private Boolean belongToApp;
|
||||||
|
|
||||||
|
public String getMachineId() {
|
||||||
|
return machineId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ClusterGroupEntity setMachineId(String machineId) {
|
||||||
|
this.machineId = machineId;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getIp() {
|
||||||
|
return ip;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ClusterGroupEntity setIp(String ip) {
|
||||||
|
this.ip = ip;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getPort() {
|
||||||
|
return port;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ClusterGroupEntity setPort(Integer port) {
|
||||||
|
this.port = port;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<String> getClientSet() {
|
||||||
|
return clientSet;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ClusterGroupEntity setClientSet(Set<String> clientSet) {
|
||||||
|
this.clientSet = clientSet;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Boolean getBelongToApp() {
|
||||||
|
return belongToApp;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ClusterGroupEntity setBelongToApp(Boolean belongToApp) {
|
||||||
|
this.belongToApp = belongToApp;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "ClusterGroupEntity{" +
|
||||||
|
"machineId='" + machineId + '\'' +
|
||||||
|
", ip='" + ip + '\'' +
|
||||||
|
", port=" + port +
|
||||||
|
", clientSet=" + clientSet +
|
||||||
|
", belongToApp=" + belongToApp +
|
||||||
|
'}';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,63 @@
|
||||||
|
/*
|
||||||
|
* Copyright 1999-2018 Alibaba Group Holding Ltd.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package com.taobao.csp.sentinel.dashboard.domain.cluster;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Eric Zhao
|
||||||
|
* @since 1.4.1
|
||||||
|
*/
|
||||||
|
public class ClusterStateSingleVO {
|
||||||
|
|
||||||
|
private String address;
|
||||||
|
private Integer mode;
|
||||||
|
private String target;
|
||||||
|
|
||||||
|
public String getAddress() {
|
||||||
|
return address;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ClusterStateSingleVO setAddress(String address) {
|
||||||
|
this.address = address;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getMode() {
|
||||||
|
return mode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ClusterStateSingleVO setMode(Integer mode) {
|
||||||
|
this.mode = mode;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getTarget() {
|
||||||
|
return target;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ClusterStateSingleVO setTarget(String target) {
|
||||||
|
this.target = target;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "ClusterStateSingleVO{" +
|
||||||
|
"address='" + address + '\'' +
|
||||||
|
", mode=" + mode +
|
||||||
|
", target='" + target + '\'' +
|
||||||
|
'}';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -26,6 +26,7 @@ public class ServerFlowConfig {
|
||||||
|
|
||||||
public static final int DEFAULT_INTERVAL_MS = 1000;
|
public static final int DEFAULT_INTERVAL_MS = 1000;
|
||||||
public static final int DEFAULT_SAMPLE_COUNT= 10;
|
public static final int DEFAULT_SAMPLE_COUNT= 10;
|
||||||
|
public static final double DEFAULT_MAX_ALLOWED_QPS= 30000;
|
||||||
|
|
||||||
private final String namespace;
|
private final String namespace;
|
||||||
|
|
||||||
|
|
@ -34,6 +35,8 @@ public class ServerFlowConfig {
|
||||||
private Integer intervalMs = DEFAULT_INTERVAL_MS;
|
private Integer intervalMs = DEFAULT_INTERVAL_MS;
|
||||||
private Integer sampleCount = DEFAULT_SAMPLE_COUNT;
|
private Integer sampleCount = DEFAULT_SAMPLE_COUNT;
|
||||||
|
|
||||||
|
private Double maxAllowedQps = DEFAULT_MAX_ALLOWED_QPS;
|
||||||
|
|
||||||
public ServerFlowConfig() {
|
public ServerFlowConfig() {
|
||||||
this("default");
|
this("default");
|
||||||
}
|
}
|
||||||
|
|
@ -82,6 +85,15 @@ public class ServerFlowConfig {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Double getMaxAllowedQps() {
|
||||||
|
return maxAllowedQps;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ServerFlowConfig setMaxAllowedQps(Double maxAllowedQps) {
|
||||||
|
this.maxAllowedQps = maxAllowedQps;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "ServerFlowConfig{" +
|
return "ServerFlowConfig{" +
|
||||||
|
|
@ -90,6 +102,7 @@ public class ServerFlowConfig {
|
||||||
", maxOccupyRatio=" + maxOccupyRatio +
|
", maxOccupyRatio=" + maxOccupyRatio +
|
||||||
", intervalMs=" + intervalMs +
|
", intervalMs=" + intervalMs +
|
||||||
", sampleCount=" + sampleCount +
|
", sampleCount=" + sampleCount +
|
||||||
|
", maxAllowedQps=" + maxAllowedQps +
|
||||||
'}';
|
'}';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@ package com.taobao.csp.sentinel.dashboard.domain.cluster.config;
|
||||||
*/
|
*/
|
||||||
public class ServerTransportConfig {
|
public class ServerTransportConfig {
|
||||||
|
|
||||||
public static final int DEFAULT_PORT = 8730;
|
public static final int DEFAULT_PORT = 18730;
|
||||||
public static final int DEFAULT_IDLE_SECONDS = 600;
|
public static final int DEFAULT_IDLE_SECONDS = 600;
|
||||||
|
|
||||||
private Integer port;
|
private Integer port;
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,112 @@
|
||||||
|
/*
|
||||||
|
* Copyright 1999-2018 Alibaba Group Holding Ltd.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package com.taobao.csp.sentinel.dashboard.domain.cluster.request;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Eric Zhao
|
||||||
|
* @since 1.4.1
|
||||||
|
*/
|
||||||
|
public class ClusterAppAssignMap {
|
||||||
|
|
||||||
|
private String machineId;
|
||||||
|
private String ip;
|
||||||
|
private Integer port;
|
||||||
|
|
||||||
|
private Boolean belongToApp;
|
||||||
|
|
||||||
|
private Set<String> clientSet;
|
||||||
|
|
||||||
|
private Set<String> namespaceSet;
|
||||||
|
private Double maxAllowedQps;
|
||||||
|
|
||||||
|
public String getMachineId() {
|
||||||
|
return machineId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ClusterAppAssignMap setMachineId(String machineId) {
|
||||||
|
this.machineId = machineId;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getIp() {
|
||||||
|
return ip;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ClusterAppAssignMap setIp(String ip) {
|
||||||
|
this.ip = ip;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getPort() {
|
||||||
|
return port;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ClusterAppAssignMap setPort(Integer port) {
|
||||||
|
this.port = port;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<String> getClientSet() {
|
||||||
|
return clientSet;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ClusterAppAssignMap setClientSet(Set<String> clientSet) {
|
||||||
|
this.clientSet = clientSet;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<String> getNamespaceSet() {
|
||||||
|
return namespaceSet;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ClusterAppAssignMap setNamespaceSet(Set<String> namespaceSet) {
|
||||||
|
this.namespaceSet = namespaceSet;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Boolean getBelongToApp() {
|
||||||
|
return belongToApp;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ClusterAppAssignMap setBelongToApp(Boolean belongToApp) {
|
||||||
|
this.belongToApp = belongToApp;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Double getMaxAllowedQps() {
|
||||||
|
return maxAllowedQps;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ClusterAppAssignMap setMaxAllowedQps(Double maxAllowedQps) {
|
||||||
|
this.maxAllowedQps = maxAllowedQps;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "ClusterAppAssignMap{" +
|
||||||
|
"machineId='" + machineId + '\'' +
|
||||||
|
", ip='" + ip + '\'' +
|
||||||
|
", port=" + port +
|
||||||
|
", belongToApp=" + belongToApp +
|
||||||
|
", clientSet=" + clientSet +
|
||||||
|
", namespaceSet=" + namespaceSet +
|
||||||
|
", maxAllowedQps=" + maxAllowedQps +
|
||||||
|
'}';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -13,7 +13,7 @@
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package com.taobao.csp.sentinel.dashboard.domain.cluster;
|
package com.taobao.csp.sentinel.dashboard.domain.cluster.request;
|
||||||
|
|
||||||
import com.taobao.csp.sentinel.dashboard.domain.cluster.config.ClusterClientConfig;
|
import com.taobao.csp.sentinel.dashboard.domain.cluster.config.ClusterClientConfig;
|
||||||
|
|
||||||
|
|
@ -13,7 +13,7 @@
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package com.taobao.csp.sentinel.dashboard.domain.cluster;
|
package com.taobao.csp.sentinel.dashboard.domain.cluster.request;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Eric Zhao
|
* @author Eric Zhao
|
||||||
|
|
@ -13,7 +13,7 @@
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package com.taobao.csp.sentinel.dashboard.domain.cluster;
|
package com.taobao.csp.sentinel.dashboard.domain.cluster.request;
|
||||||
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
|
|
@ -0,0 +1,79 @@
|
||||||
|
/*
|
||||||
|
* Copyright 1999-2018 Alibaba Group Holding Ltd.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package com.taobao.csp.sentinel.dashboard.domain.cluster.state;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Eric Zhao
|
||||||
|
* @since 1.4.1
|
||||||
|
*/
|
||||||
|
public class AppClusterClientStateWrapVO {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {ip}@{transport_command_port}.
|
||||||
|
*/
|
||||||
|
private String id;
|
||||||
|
|
||||||
|
private Integer commandPort;
|
||||||
|
private String ip;
|
||||||
|
|
||||||
|
private ClusterClientStateVO state;
|
||||||
|
|
||||||
|
public String getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public AppClusterClientStateWrapVO setId(String id) {
|
||||||
|
this.id = id;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getIp() {
|
||||||
|
return ip;
|
||||||
|
}
|
||||||
|
|
||||||
|
public AppClusterClientStateWrapVO setIp(String ip) {
|
||||||
|
this.ip = ip;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ClusterClientStateVO getState() {
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
|
public AppClusterClientStateWrapVO setState(ClusterClientStateVO state) {
|
||||||
|
this.state = state;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getCommandPort() {
|
||||||
|
return commandPort;
|
||||||
|
}
|
||||||
|
|
||||||
|
public AppClusterClientStateWrapVO setCommandPort(Integer commandPort) {
|
||||||
|
this.commandPort = commandPort;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "AppClusterClientStateWrapVO{" +
|
||||||
|
"id='" + id + '\'' +
|
||||||
|
", commandPort=" + commandPort +
|
||||||
|
", ip='" + ip + '\'' +
|
||||||
|
", state=" + state +
|
||||||
|
'}';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,102 @@
|
||||||
|
/*
|
||||||
|
* Copyright 1999-2018 Alibaba Group Holding Ltd.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package com.taobao.csp.sentinel.dashboard.domain.cluster.state;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Eric Zhao
|
||||||
|
* @since 1.4.1
|
||||||
|
*/
|
||||||
|
public class AppClusterServerStateWrapVO {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {ip}@{transport_command_port}.
|
||||||
|
*/
|
||||||
|
private String id;
|
||||||
|
|
||||||
|
private String ip;
|
||||||
|
private Integer port;
|
||||||
|
|
||||||
|
private Integer connectedCount;
|
||||||
|
|
||||||
|
private Boolean belongToApp;
|
||||||
|
|
||||||
|
private ClusterServerStateVO state;
|
||||||
|
|
||||||
|
public String getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public AppClusterServerStateWrapVO setId(String id) {
|
||||||
|
this.id = id;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getIp() {
|
||||||
|
return ip;
|
||||||
|
}
|
||||||
|
|
||||||
|
public AppClusterServerStateWrapVO setIp(String ip) {
|
||||||
|
this.ip = ip;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getPort() {
|
||||||
|
return port;
|
||||||
|
}
|
||||||
|
|
||||||
|
public AppClusterServerStateWrapVO setPort(Integer port) {
|
||||||
|
this.port = port;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Boolean getBelongToApp() {
|
||||||
|
return belongToApp;
|
||||||
|
}
|
||||||
|
|
||||||
|
public AppClusterServerStateWrapVO setBelongToApp(Boolean belongToApp) {
|
||||||
|
this.belongToApp = belongToApp;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getConnectedCount() {
|
||||||
|
return connectedCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
public AppClusterServerStateWrapVO setConnectedCount(Integer connectedCount) {
|
||||||
|
this.connectedCount = connectedCount;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ClusterServerStateVO getState() {
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
|
public AppClusterServerStateWrapVO setState(ClusterServerStateVO state) {
|
||||||
|
this.state = state;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "AppClusterServerStateWrapVO{" +
|
||||||
|
"id='" + id + '\'' +
|
||||||
|
", ip='" + ip + '\'' +
|
||||||
|
", port='" + port + '\'' +
|
||||||
|
", belongToApp=" + belongToApp +
|
||||||
|
", state=" + state +
|
||||||
|
'}';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -13,9 +13,9 @@
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package com.taobao.csp.sentinel.dashboard.domain.cluster;
|
package com.taobao.csp.sentinel.dashboard.domain.cluster.state;
|
||||||
|
|
||||||
import com.taobao.csp.sentinel.dashboard.domain.cluster.config.ClusterClientConfig;
|
import com.taobao.csp.sentinel.dashboard.domain.cluster.ClusterClientInfoVO;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Eric Zhao
|
* @author Eric Zhao
|
||||||
|
|
@ -23,14 +23,16 @@ import com.taobao.csp.sentinel.dashboard.domain.cluster.config.ClusterClientConf
|
||||||
*/
|
*/
|
||||||
public class ClusterClientStateVO {
|
public class ClusterClientStateVO {
|
||||||
|
|
||||||
private ClusterClientConfig clientConfig;
|
/**
|
||||||
|
* Cluster token client state.
|
||||||
|
*/
|
||||||
|
private ClusterClientInfoVO clientConfig;
|
||||||
|
|
||||||
public ClusterClientConfig getClientConfig() {
|
public ClusterClientInfoVO getClientConfig() {
|
||||||
return clientConfig;
|
return clientConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ClusterClientStateVO setClientConfig(
|
public ClusterClientStateVO setClientConfig(ClusterClientInfoVO clientConfig) {
|
||||||
ClusterClientConfig clientConfig) {
|
|
||||||
this.clientConfig = clientConfig;
|
this.clientConfig = clientConfig;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,63 @@
|
||||||
|
/*
|
||||||
|
* Copyright 1999-2018 Alibaba Group Holding Ltd.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package com.taobao.csp.sentinel.dashboard.domain.cluster.state;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Eric Zhao
|
||||||
|
* @since 1.4.1
|
||||||
|
*/
|
||||||
|
public class ClusterRequestLimitVO {
|
||||||
|
|
||||||
|
private String namespace;
|
||||||
|
private Double currentQps;
|
||||||
|
private Double maxAllowedQps;
|
||||||
|
|
||||||
|
public String getNamespace() {
|
||||||
|
return namespace;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ClusterRequestLimitVO setNamespace(String namespace) {
|
||||||
|
this.namespace = namespace;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Double getCurrentQps() {
|
||||||
|
return currentQps;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ClusterRequestLimitVO setCurrentQps(Double currentQps) {
|
||||||
|
this.currentQps = currentQps;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Double getMaxAllowedQps() {
|
||||||
|
return maxAllowedQps;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ClusterRequestLimitVO setMaxAllowedQps(Double maxAllowedQps) {
|
||||||
|
this.maxAllowedQps = maxAllowedQps;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "ClusterRequestLimitVO{" +
|
||||||
|
"namespace='" + namespace + '\'' +
|
||||||
|
", currentQps=" + currentQps +
|
||||||
|
", maxAllowedQps=" + maxAllowedQps +
|
||||||
|
'}';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -13,11 +13,12 @@
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package com.taobao.csp.sentinel.dashboard.domain.cluster;
|
package com.taobao.csp.sentinel.dashboard.domain.cluster.state;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
|
import com.taobao.csp.sentinel.dashboard.domain.cluster.ConnectionGroupVO;
|
||||||
import com.taobao.csp.sentinel.dashboard.domain.cluster.config.ServerFlowConfig;
|
import com.taobao.csp.sentinel.dashboard.domain.cluster.config.ServerFlowConfig;
|
||||||
import com.taobao.csp.sentinel.dashboard.domain.cluster.config.ServerTransportConfig;
|
import com.taobao.csp.sentinel.dashboard.domain.cluster.config.ServerTransportConfig;
|
||||||
|
|
||||||
|
|
@ -32,14 +33,17 @@ public class ClusterServerStateVO {
|
||||||
private Set<String> namespaceSet;
|
private Set<String> namespaceSet;
|
||||||
|
|
||||||
private Integer port;
|
private Integer port;
|
||||||
|
|
||||||
private List<ConnectionGroupVO> connection;
|
private List<ConnectionGroupVO> connection;
|
||||||
|
private List<ClusterRequestLimitVO> requestLimitData;
|
||||||
|
|
||||||
|
private Boolean embedded;
|
||||||
|
|
||||||
public ServerTransportConfig getTransport() {
|
public ServerTransportConfig getTransport() {
|
||||||
return transport;
|
return transport;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ClusterServerStateVO setTransport(
|
public ClusterServerStateVO setTransport(ServerTransportConfig transport) {
|
||||||
ServerTransportConfig transport) {
|
|
||||||
this.transport = transport;
|
this.transport = transport;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
@ -75,12 +79,29 @@ public class ClusterServerStateVO {
|
||||||
return connection;
|
return connection;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ClusterServerStateVO setConnection(
|
public ClusterServerStateVO setConnection(List<ConnectionGroupVO> connection) {
|
||||||
List<ConnectionGroupVO> connection) {
|
|
||||||
this.connection = connection;
|
this.connection = connection;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<ClusterRequestLimitVO> getRequestLimitData() {
|
||||||
|
return requestLimitData;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ClusterServerStateVO setRequestLimitData(List<ClusterRequestLimitVO> requestLimitData) {
|
||||||
|
this.requestLimitData = requestLimitData;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Boolean getEmbedded() {
|
||||||
|
return embedded;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ClusterServerStateVO setEmbedded(Boolean embedded) {
|
||||||
|
this.embedded = embedded;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "ClusterServerStateVO{" +
|
return "ClusterServerStateVO{" +
|
||||||
|
|
@ -89,6 +110,8 @@ public class ClusterServerStateVO {
|
||||||
", namespaceSet=" + namespaceSet +
|
", namespaceSet=" + namespaceSet +
|
||||||
", port=" + port +
|
", port=" + port +
|
||||||
", connection=" + connection +
|
", connection=" + connection +
|
||||||
|
", requestLimitData=" + requestLimitData +
|
||||||
|
", embedded=" + embedded +
|
||||||
'}';
|
'}';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -13,7 +13,7 @@
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package com.taobao.csp.sentinel.dashboard.domain.cluster;
|
package com.taobao.csp.sentinel.dashboard.domain.cluster.state;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Eric Zhao
|
* @author Eric Zhao
|
||||||
|
|
@ -0,0 +1,72 @@
|
||||||
|
/*
|
||||||
|
* Copyright 1999-2018 Alibaba Group Holding Ltd.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package com.taobao.csp.sentinel.dashboard.domain.cluster.state;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Eric Zhao
|
||||||
|
* @since 1.4.1
|
||||||
|
*/
|
||||||
|
public class ClusterUniversalStatePairVO {
|
||||||
|
|
||||||
|
private String ip;
|
||||||
|
private Integer commandPort;
|
||||||
|
|
||||||
|
private ClusterUniversalStateVO state;
|
||||||
|
|
||||||
|
public ClusterUniversalStatePairVO() {}
|
||||||
|
|
||||||
|
public ClusterUniversalStatePairVO(String ip, Integer commandPort, ClusterUniversalStateVO state) {
|
||||||
|
this.ip = ip;
|
||||||
|
this.commandPort = commandPort;
|
||||||
|
this.state = state;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getIp() {
|
||||||
|
return ip;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ClusterUniversalStatePairVO setIp(String ip) {
|
||||||
|
this.ip = ip;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getCommandPort() {
|
||||||
|
return commandPort;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ClusterUniversalStatePairVO setCommandPort(Integer commandPort) {
|
||||||
|
this.commandPort = commandPort;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ClusterUniversalStateVO getState() {
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ClusterUniversalStatePairVO setState(ClusterUniversalStateVO state) {
|
||||||
|
this.state = state;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "ClusterUniversalStatePairVO{" +
|
||||||
|
"ip='" + ip + '\'' +
|
||||||
|
", commandPort=" + commandPort +
|
||||||
|
", state=" + state +
|
||||||
|
'}';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -13,7 +13,7 @@
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package com.taobao.csp.sentinel.dashboard.domain.cluster;
|
package com.taobao.csp.sentinel.dashboard.domain.cluster.state;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Eric Zhao
|
* @author Eric Zhao
|
||||||
|
|
@ -15,6 +15,8 @@
|
||||||
*/
|
*/
|
||||||
package com.taobao.csp.sentinel.dashboard.metric;
|
package com.taobao.csp.sentinel.dashboard.metric;
|
||||||
|
|
||||||
|
import java.net.ConnectException;
|
||||||
|
import java.net.SocketTimeoutException;
|
||||||
import java.nio.charset.Charset;
|
import java.nio.charset.Charset;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
@ -41,6 +43,7 @@ import com.taobao.csp.sentinel.dashboard.datasource.entity.MetricEntity;
|
||||||
import com.taobao.csp.sentinel.dashboard.discovery.AppManagement;
|
import com.taobao.csp.sentinel.dashboard.discovery.AppManagement;
|
||||||
import com.taobao.csp.sentinel.dashboard.discovery.MachineInfo;
|
import com.taobao.csp.sentinel.dashboard.discovery.MachineInfo;
|
||||||
import com.taobao.csp.sentinel.dashboard.repository.metric.MetricsRepository;
|
import com.taobao.csp.sentinel.dashboard.repository.metric.MetricsRepository;
|
||||||
|
import com.taobao.csp.sentinel.dashboard.util.MachineUtils;
|
||||||
import org.apache.http.HttpResponse;
|
import org.apache.http.HttpResponse;
|
||||||
import org.apache.http.client.methods.HttpGet;
|
import org.apache.http.client.methods.HttpGet;
|
||||||
import org.apache.http.concurrent.FutureCallback;
|
import org.apache.http.concurrent.FutureCallback;
|
||||||
|
|
@ -64,7 +67,6 @@ import org.springframework.stereotype.Component;
|
||||||
@Component
|
@Component
|
||||||
public class MetricFetcher {
|
public class MetricFetcher {
|
||||||
|
|
||||||
public static final long MAX_CLIENT_LIVE_TIME_MS = 1000 * 60 * 5;
|
|
||||||
public static final String NO_METRICS = "No metrics";
|
public static final String NO_METRICS = "No metrics";
|
||||||
private static final int HTTP_OK = 200;
|
private static final int HTTP_OK = 200;
|
||||||
private static final long MAX_LAST_FETCH_INTERVAL_MS = 1000 * 15;
|
private static final long MAX_LAST_FETCH_INTERVAL_MS = 1000 * 15;
|
||||||
|
|
@ -183,7 +185,7 @@ public class MetricFetcher {
|
||||||
final CountDownLatch latch = new CountDownLatch(machines.size());
|
final CountDownLatch latch = new CountDownLatch(machines.size());
|
||||||
for (final MachineInfo machine : machines) {
|
for (final MachineInfo machine : machines) {
|
||||||
// dead
|
// dead
|
||||||
if (System.currentTimeMillis() - machine.getTimestamp().getTime() > MAX_CLIENT_LIVE_TIME_MS) {
|
if (System.currentTimeMillis() - machine.getTimestamp().getTime() > MachineUtils.getMaxClientTimeout()) {
|
||||||
latch.countDown();
|
latch.countDown();
|
||||||
dead.incrementAndGet();
|
dead.incrementAndGet();
|
||||||
continue;
|
continue;
|
||||||
|
|
@ -210,7 +212,13 @@ public class MetricFetcher {
|
||||||
latch.countDown();
|
latch.countDown();
|
||||||
fail.incrementAndGet();
|
fail.incrementAndGet();
|
||||||
httpGet.abort();
|
httpGet.abort();
|
||||||
logger.error(msg + " metric " + url + " failed:", ex);
|
if (ex instanceof SocketTimeoutException) {
|
||||||
|
logger.error("Failed to fetch metric from <{}>: socket timeout", url);
|
||||||
|
} else if (ex instanceof ConnectException) {
|
||||||
|
logger.error("Failed to fetch metric from <{}> (ConnectionException: {})", url, ex.getMessage());
|
||||||
|
} else {
|
||||||
|
logger.error(msg + " metric " + url + " error", ex);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -273,8 +281,10 @@ public class MetricFetcher {
|
||||||
Charset charset = null;
|
Charset charset = null;
|
||||||
try {
|
try {
|
||||||
String contentTypeStr = response.getFirstHeader("Content-type").getValue();
|
String contentTypeStr = response.getFirstHeader("Content-type").getValue();
|
||||||
ContentType contentType = ContentType.parse(contentTypeStr);
|
if (StringUtil.isNotEmpty(contentTypeStr)) {
|
||||||
charset = contentType.getCharset();
|
ContentType contentType = ContentType.parse(contentTypeStr);
|
||||||
|
charset = contentType.getCharset();
|
||||||
|
}
|
||||||
} catch (Exception ignore) {
|
} catch (Exception ignore) {
|
||||||
}
|
}
|
||||||
String body = EntityUtils.toString(response.getEntity(), charset != null ? charset : DEFAULT_CHARSET);
|
String body = EntityUtils.toString(response.getEntity(), charset != null ? charset : DEFAULT_CHARSET);
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ import com.taobao.csp.sentinel.dashboard.client.SentinelApiClient;
|
||||||
import com.taobao.csp.sentinel.dashboard.datasource.entity.rule.FlowRuleEntity;
|
import com.taobao.csp.sentinel.dashboard.datasource.entity.rule.FlowRuleEntity;
|
||||||
import com.taobao.csp.sentinel.dashboard.discovery.AppManagement;
|
import com.taobao.csp.sentinel.dashboard.discovery.AppManagement;
|
||||||
import com.taobao.csp.sentinel.dashboard.discovery.MachineInfo;
|
import com.taobao.csp.sentinel.dashboard.discovery.MachineInfo;
|
||||||
import com.taobao.csp.sentinel.dashboard.util.MachineUtil;
|
import com.taobao.csp.sentinel.dashboard.util.MachineUtils;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
|
@ -47,7 +47,7 @@ public class FlowRuleApiProvider implements DynamicRuleProvider<List<FlowRuleEnt
|
||||||
}
|
}
|
||||||
List<MachineInfo> list = appManagement.getDetailApp(appName).getMachines()
|
List<MachineInfo> list = appManagement.getDetailApp(appName).getMachines()
|
||||||
.stream()
|
.stream()
|
||||||
.filter(MachineUtil::isMachineHealth)
|
.filter(MachineUtils::isMachineHealth)
|
||||||
.sorted((e1, e2) -> {
|
.sorted((e1, e2) -> {
|
||||||
if (e1.getTimestamp().before(e2.getTimestamp())) {
|
if (e1.getTimestamp().before(e2.getTimestamp())) {
|
||||||
return 1;
|
return 1;
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@ import com.taobao.csp.sentinel.dashboard.client.SentinelApiClient;
|
||||||
import com.taobao.csp.sentinel.dashboard.datasource.entity.rule.FlowRuleEntity;
|
import com.taobao.csp.sentinel.dashboard.datasource.entity.rule.FlowRuleEntity;
|
||||||
import com.taobao.csp.sentinel.dashboard.discovery.AppManagement;
|
import com.taobao.csp.sentinel.dashboard.discovery.AppManagement;
|
||||||
import com.taobao.csp.sentinel.dashboard.discovery.MachineInfo;
|
import com.taobao.csp.sentinel.dashboard.discovery.MachineInfo;
|
||||||
import com.taobao.csp.sentinel.dashboard.util.MachineUtil;
|
import com.taobao.csp.sentinel.dashboard.util.MachineUtils;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
|
@ -51,7 +51,7 @@ public class FlowRuleApiPublisher implements DynamicRulePublisher<List<FlowRuleE
|
||||||
Set<MachineInfo> set = appManagement.getDetailApp(app).getMachines();
|
Set<MachineInfo> set = appManagement.getDetailApp(app).getMachines();
|
||||||
|
|
||||||
for (MachineInfo machine : set) {
|
for (MachineInfo machine : set) {
|
||||||
if (!MachineUtil.isMachineHealth(machine)) {
|
if (!MachineUtils.isMachineHealth(machine)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
// TODO: parse the results
|
// TODO: parse the results
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,58 @@
|
||||||
|
/*
|
||||||
|
* Copyright 1999-2018 Alibaba Group Holding Ltd.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package com.taobao.csp.sentinel.dashboard.service;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import com.taobao.csp.sentinel.dashboard.domain.cluster.ClusterAppAssignResultVO;
|
||||||
|
import com.taobao.csp.sentinel.dashboard.domain.cluster.request.ClusterAppAssignMap;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Eric Zhao
|
||||||
|
* @since 1.4.1
|
||||||
|
*/
|
||||||
|
public interface ClusterAssignService {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unbind a specific cluster server and its clients.
|
||||||
|
*
|
||||||
|
* @param app app name
|
||||||
|
* @param machineId valid machine ID ({@code host@commandPort})
|
||||||
|
* @return assign result
|
||||||
|
*/
|
||||||
|
ClusterAppAssignResultVO unbindClusterServer(String app, String machineId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unbind a set of cluster servers and its clients.
|
||||||
|
*
|
||||||
|
* @param app app name
|
||||||
|
* @param machineIdSet set of valid machine ID ({@code host@commandPort})
|
||||||
|
* @return assign result
|
||||||
|
*/
|
||||||
|
ClusterAppAssignResultVO unbindClusterServers(String app, Set<String> machineIdSet);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Apply cluster server and client assignment for provided app.
|
||||||
|
*
|
||||||
|
* @param app app name
|
||||||
|
* @param clusterMap cluster assign map (server -> clients)
|
||||||
|
* @param remainingSet unassigned set of machine ID
|
||||||
|
* @return assign result
|
||||||
|
*/
|
||||||
|
ClusterAppAssignResultVO applyAssignToApp(String app, List<ClusterAppAssignMap> clusterMap,
|
||||||
|
Set<String> remainingSet);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,235 @@
|
||||||
|
/*
|
||||||
|
* Copyright 1999-2018 Alibaba Group Holding Ltd.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package com.taobao.csp.sentinel.dashboard.service;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
import java.util.concurrent.ExecutionException;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
import com.alibaba.csp.sentinel.cluster.ClusterStateManager;
|
||||||
|
import com.alibaba.csp.sentinel.util.AssertUtil;
|
||||||
|
import com.alibaba.csp.sentinel.util.function.Tuple2;
|
||||||
|
|
||||||
|
import com.taobao.csp.sentinel.dashboard.client.SentinelApiClient;
|
||||||
|
import com.taobao.csp.sentinel.dashboard.domain.cluster.ClusterAppAssignResultVO;
|
||||||
|
import com.taobao.csp.sentinel.dashboard.domain.cluster.ClusterGroupEntity;
|
||||||
|
import com.taobao.csp.sentinel.dashboard.domain.cluster.config.ClusterClientConfig;
|
||||||
|
import com.taobao.csp.sentinel.dashboard.domain.cluster.config.ServerFlowConfig;
|
||||||
|
import com.taobao.csp.sentinel.dashboard.domain.cluster.config.ServerTransportConfig;
|
||||||
|
import com.taobao.csp.sentinel.dashboard.domain.cluster.request.ClusterAppAssignMap;
|
||||||
|
import com.taobao.csp.sentinel.dashboard.util.MachineUtils;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Eric Zhao
|
||||||
|
* @since 1.4.1
|
||||||
|
*/
|
||||||
|
@Service
|
||||||
|
public class ClusterAssignServiceImpl implements ClusterAssignService {
|
||||||
|
|
||||||
|
private final Logger LOGGER = LoggerFactory.getLogger(ClusterAssignServiceImpl.class);
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private SentinelApiClient sentinelApiClient;
|
||||||
|
@Autowired
|
||||||
|
private ClusterConfigService clusterConfigService;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ClusterAppAssignResultVO unbindClusterServer(String app, String machineId) {
|
||||||
|
AssertUtil.assertNotBlank(app, "app cannot be blank");
|
||||||
|
AssertUtil.assertNotBlank(machineId, "machineId cannot be blank");
|
||||||
|
Set<String> failedSet = new HashSet<>();
|
||||||
|
|
||||||
|
try {
|
||||||
|
ClusterGroupEntity entity = clusterConfigService.getClusterUniversalStateForAppMachine(app, machineId)
|
||||||
|
.get(10, TimeUnit.SECONDS);
|
||||||
|
Set<String> toModifySet = new HashSet<>();
|
||||||
|
toModifySet.add(machineId);
|
||||||
|
if (entity.getClientSet() != null) {
|
||||||
|
toModifySet.addAll(entity.getClientSet());
|
||||||
|
}
|
||||||
|
// Modify mode to NOT-STARTED for all chosen token servers and associated token clients.
|
||||||
|
toModifySet.parallelStream()
|
||||||
|
.map(MachineUtils::parseCommandIpAndPort)
|
||||||
|
.filter(Optional::isPresent)
|
||||||
|
.map(Optional::get)
|
||||||
|
.map(e -> {
|
||||||
|
CompletableFuture<Void> f = modifyMode(app, e.r1, e.r2, ClusterStateManager.CLUSTER_NOT_STARTED);
|
||||||
|
return Tuple2.of(e.r1 + '@' + e.r2, f);
|
||||||
|
})
|
||||||
|
.forEach(f -> handleFutureSync(f, failedSet));
|
||||||
|
} catch (Exception ex) {
|
||||||
|
Throwable e = ex instanceof ExecutionException ? ex.getCause() : ex;
|
||||||
|
LOGGER.error("Failed to unbind machine <{}>", machineId, e);
|
||||||
|
failedSet.add(machineId);
|
||||||
|
}
|
||||||
|
return new ClusterAppAssignResultVO()
|
||||||
|
.setFailedClientSet(failedSet)
|
||||||
|
.setFailedServerSet(new HashSet<>());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ClusterAppAssignResultVO unbindClusterServers(String app, Set<String> machineIdSet) {
|
||||||
|
AssertUtil.assertNotBlank(app, "app cannot be blank");
|
||||||
|
AssertUtil.isTrue(machineIdSet != null && !machineIdSet.isEmpty(), "machineIdSet cannot be empty");
|
||||||
|
ClusterAppAssignResultVO result = new ClusterAppAssignResultVO()
|
||||||
|
.setFailedClientSet(new HashSet<>())
|
||||||
|
.setFailedServerSet(new HashSet<>());
|
||||||
|
for (String machineId : machineIdSet) {
|
||||||
|
ClusterAppAssignResultVO resultVO = unbindClusterServer(app, machineId);
|
||||||
|
result.getFailedClientSet().addAll(resultVO.getFailedClientSet());
|
||||||
|
result.getFailedServerSet().addAll(resultVO.getFailedServerSet());
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ClusterAppAssignResultVO applyAssignToApp(String app, List<ClusterAppAssignMap> clusterMap,
|
||||||
|
Set<String> remainingSet) {
|
||||||
|
AssertUtil.assertNotBlank(app, "app cannot be blank");
|
||||||
|
AssertUtil.notNull(clusterMap, "clusterMap cannot be null");
|
||||||
|
Set<String> failedServerSet = new HashSet<>();
|
||||||
|
Set<String> failedClientSet = new HashSet<>();
|
||||||
|
|
||||||
|
// Assign server and apply config.
|
||||||
|
clusterMap.stream()
|
||||||
|
.filter(Objects::nonNull)
|
||||||
|
.filter(ClusterAppAssignMap::getBelongToApp)
|
||||||
|
.map(e -> {
|
||||||
|
String ip = e.getIp();
|
||||||
|
int commandPort = parsePort(e);
|
||||||
|
CompletableFuture<Void> f = modifyMode(app, ip, commandPort, ClusterStateManager.CLUSTER_SERVER)
|
||||||
|
.thenCompose(v -> applyServerConfigChange(app, ip, commandPort, e));
|
||||||
|
return Tuple2.of(e.getMachineId(), f);
|
||||||
|
})
|
||||||
|
.forEach(t -> handleFutureSync(t, failedServerSet));
|
||||||
|
|
||||||
|
// Assign client of servers and apply config.
|
||||||
|
clusterMap.parallelStream()
|
||||||
|
.filter(Objects::nonNull)
|
||||||
|
.forEach(e -> applyAllClientConfigChange(app, e, failedClientSet));
|
||||||
|
|
||||||
|
// Unbind remaining (unassigned) machines.
|
||||||
|
applyAllRemainingMachineSet(app, remainingSet, failedClientSet);
|
||||||
|
|
||||||
|
return new ClusterAppAssignResultVO()
|
||||||
|
.setFailedClientSet(failedClientSet)
|
||||||
|
.setFailedServerSet(failedServerSet);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void applyAllRemainingMachineSet(String app, Set<String> remainingSet, Set<String> failedSet) {
|
||||||
|
if (remainingSet == null || remainingSet.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
remainingSet.parallelStream()
|
||||||
|
.filter(Objects::nonNull)
|
||||||
|
.map(MachineUtils::parseCommandIpAndPort)
|
||||||
|
.filter(Optional::isPresent)
|
||||||
|
.map(Optional::get)
|
||||||
|
.map(ipPort -> {
|
||||||
|
String ip = ipPort.r1;
|
||||||
|
int commandPort = ipPort.r2;
|
||||||
|
CompletableFuture<Void> f = modifyMode(app, ip, commandPort, ClusterStateManager.CLUSTER_NOT_STARTED);
|
||||||
|
return Tuple2.of(ip + '@' + commandPort, f);
|
||||||
|
})
|
||||||
|
.forEach(t -> handleFutureSync(t, failedSet));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void applyAllClientConfigChange(String app, ClusterAppAssignMap assignMap,
|
||||||
|
Set<String> failedSet) {
|
||||||
|
Set<String> clientSet = assignMap.getClientSet();
|
||||||
|
if (clientSet == null || clientSet.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
final String serverIp = assignMap.getIp();
|
||||||
|
final int serverPort = assignMap.getPort();
|
||||||
|
clientSet.stream()
|
||||||
|
.map(MachineUtils::parseCommandIpAndPort)
|
||||||
|
.filter(Optional::isPresent)
|
||||||
|
.map(Optional::get)
|
||||||
|
.map(ipPort -> {
|
||||||
|
CompletableFuture<Void> f = sentinelApiClient
|
||||||
|
.modifyClusterMode(app, ipPort.r1, ipPort.r2, ClusterStateManager.CLUSTER_CLIENT)
|
||||||
|
.thenCompose(v -> sentinelApiClient.modifyClusterClientConfig(app, ipPort.r1, ipPort.r2,
|
||||||
|
new ClusterClientConfig().setRequestTimeout(20)
|
||||||
|
.setServerHost(serverIp)
|
||||||
|
.setServerPort(serverPort)
|
||||||
|
));
|
||||||
|
return Tuple2.of(ipPort.r1 + '@' + ipPort.r2, f);
|
||||||
|
})
|
||||||
|
.forEach(t -> handleFutureSync(t, failedSet));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleFutureSync(Tuple2<String, CompletableFuture<Void>> t, Set<String> failedSet) {
|
||||||
|
try {
|
||||||
|
t.r2.get(10, TimeUnit.SECONDS);
|
||||||
|
} catch (Exception ex) {
|
||||||
|
if (ex instanceof ExecutionException) {
|
||||||
|
LOGGER.error("Request for <{}> failed", t.r1, ex.getCause());
|
||||||
|
} else {
|
||||||
|
LOGGER.error("Request for <{}> failed", t.r1, ex);
|
||||||
|
}
|
||||||
|
failedSet.add(t.r1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private CompletableFuture<Void> applyServerConfigChange(String app, String ip, int commandPort,
|
||||||
|
ClusterAppAssignMap assignMap) {
|
||||||
|
ServerTransportConfig transportConfig = new ServerTransportConfig()
|
||||||
|
.setPort(assignMap.getPort())
|
||||||
|
.setIdleSeconds(600);
|
||||||
|
return sentinelApiClient.modifyClusterServerTransportConfig(app, ip, commandPort, transportConfig)
|
||||||
|
.thenCompose(v -> applyServerFlowConfigChange(app, ip, commandPort, assignMap))
|
||||||
|
.thenCompose(v -> applyServerNamespaceSetConfig(app, ip, commandPort, assignMap));
|
||||||
|
}
|
||||||
|
|
||||||
|
private CompletableFuture<Void> applyServerFlowConfigChange(String app, String ip, int commandPort,
|
||||||
|
ClusterAppAssignMap assignMap) {
|
||||||
|
Double maxAllowedQps = assignMap.getMaxAllowedQps();
|
||||||
|
if (maxAllowedQps == null || maxAllowedQps <= 0 || maxAllowedQps > 20_0000) {
|
||||||
|
return CompletableFuture.completedFuture(null);
|
||||||
|
}
|
||||||
|
return sentinelApiClient.modifyClusterServerFlowConfig(app, ip, commandPort,
|
||||||
|
new ServerFlowConfig().setMaxAllowedQps(maxAllowedQps));
|
||||||
|
}
|
||||||
|
|
||||||
|
private CompletableFuture<Void> applyServerNamespaceSetConfig(String app, String ip, int commandPort,
|
||||||
|
ClusterAppAssignMap assignMap) {
|
||||||
|
Set<String> namespaceSet = assignMap.getNamespaceSet();
|
||||||
|
if (namespaceSet == null || namespaceSet.isEmpty()) {
|
||||||
|
return CompletableFuture.completedFuture(null);
|
||||||
|
}
|
||||||
|
return sentinelApiClient.modifyClusterServerNamespaceSet(app, ip, commandPort, namespaceSet);
|
||||||
|
}
|
||||||
|
|
||||||
|
private CompletableFuture<Void> modifyMode(String app, String ip, int port, int mode) {
|
||||||
|
return sentinelApiClient.modifyClusterMode(app, ip, port, mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
private int parsePort(ClusterAppAssignMap assignMap) {
|
||||||
|
return MachineUtils.parseCommandPort(assignMap.getMachineId())
|
||||||
|
.orElse(ServerTransportConfig.DEFAULT_PORT);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -15,21 +15,30 @@
|
||||||
*/
|
*/
|
||||||
package com.taobao.csp.sentinel.dashboard.service;
|
package com.taobao.csp.sentinel.dashboard.service;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
import java.util.concurrent.ExecutionException;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import com.alibaba.csp.sentinel.cluster.ClusterStateManager;
|
import com.alibaba.csp.sentinel.cluster.ClusterStateManager;
|
||||||
import com.alibaba.csp.sentinel.util.StringUtil;
|
import com.alibaba.csp.sentinel.util.StringUtil;
|
||||||
|
|
||||||
import com.taobao.csp.sentinel.dashboard.client.SentinelApiClient;
|
import com.taobao.csp.sentinel.dashboard.client.SentinelApiClient;
|
||||||
import com.taobao.csp.sentinel.dashboard.domain.cluster.ClusterClientModifyRequest;
|
import com.taobao.csp.sentinel.dashboard.discovery.AppInfo;
|
||||||
import com.taobao.csp.sentinel.dashboard.domain.cluster.ClusterClientStateVO;
|
import com.taobao.csp.sentinel.dashboard.discovery.AppManagement;
|
||||||
import com.taobao.csp.sentinel.dashboard.domain.cluster.ClusterServerModifyRequest;
|
import com.taobao.csp.sentinel.dashboard.domain.cluster.ClusterGroupEntity;
|
||||||
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.state.ClusterClientStateVO;
|
||||||
|
import com.taobao.csp.sentinel.dashboard.domain.cluster.request.ClusterServerModifyRequest;
|
||||||
|
import com.taobao.csp.sentinel.dashboard.domain.cluster.state.ClusterUniversalStatePairVO;
|
||||||
|
import com.taobao.csp.sentinel.dashboard.domain.cluster.state.ClusterUniversalStateVO;
|
||||||
import com.taobao.csp.sentinel.dashboard.domain.cluster.config.ClusterClientConfig;
|
import com.taobao.csp.sentinel.dashboard.domain.cluster.config.ClusterClientConfig;
|
||||||
import com.taobao.csp.sentinel.dashboard.domain.cluster.config.ServerFlowConfig;
|
import com.taobao.csp.sentinel.dashboard.domain.cluster.config.ServerFlowConfig;
|
||||||
import com.taobao.csp.sentinel.dashboard.domain.cluster.config.ServerTransportConfig;
|
import com.taobao.csp.sentinel.dashboard.domain.cluster.config.ServerTransportConfig;
|
||||||
|
import com.taobao.csp.sentinel.dashboard.util.AsyncUtils;
|
||||||
|
import com.taobao.csp.sentinel.dashboard.util.ClusterEntityUtils;
|
||||||
|
import com.taobao.csp.sentinel.dashboard.util.MachineUtils;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
|
@ -42,6 +51,8 @@ public class ClusterConfigService {
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private SentinelApiClient sentinelApiClient;
|
private SentinelApiClient sentinelApiClient;
|
||||||
|
@Autowired
|
||||||
|
private AppManagement appManagement;
|
||||||
|
|
||||||
public CompletableFuture<Void> modifyClusterClientConfig(ClusterClientModifyRequest request) {
|
public CompletableFuture<Void> modifyClusterClientConfig(ClusterClientModifyRequest request) {
|
||||||
if (notClientRequestValid(request)) {
|
if (notClientRequestValid(request)) {
|
||||||
|
|
@ -51,7 +62,7 @@ public class ClusterConfigService {
|
||||||
String ip = request.getIp();
|
String ip = request.getIp();
|
||||||
int port = request.getPort();
|
int port = request.getPort();
|
||||||
return sentinelApiClient.modifyClusterClientConfig(app, ip, port, request.getClientConfig())
|
return sentinelApiClient.modifyClusterClientConfig(app, ip, port, request.getClientConfig())
|
||||||
.thenCompose(v -> sentinelApiClient.modifyClusterMode(app, ip, port, ClusterStateManager.CLUSTER_CLIENT));
|
.thenCompose(v -> sentinelApiClient.modifyClusterMode(app, ip, port, ClusterStateManager.CLUSTER_CLIENT));
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean notClientRequestValid(/*@NonNull */ ClusterClientModifyRequest request) {
|
private boolean notClientRequestValid(/*@NonNull */ ClusterClientModifyRequest request) {
|
||||||
|
|
@ -83,12 +94,64 @@ public class ClusterConfigService {
|
||||||
.thenCompose(v -> sentinelApiClient.modifyClusterMode(app, ip, port, ClusterStateManager.CLUSTER_SERVER));
|
.thenCompose(v -> sentinelApiClient.modifyClusterMode(app, ip, port, ClusterStateManager.CLUSTER_SERVER));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get cluster state list of all available machines of provided application.
|
||||||
|
*
|
||||||
|
* @param app application name
|
||||||
|
* @return cluster state list of all available machines of the application
|
||||||
|
* @since 1.4.1
|
||||||
|
*/
|
||||||
|
public CompletableFuture<List<ClusterUniversalStatePairVO>> getClusterUniversalState(String app) {
|
||||||
|
if (StringUtil.isBlank(app)) {
|
||||||
|
return AsyncUtils.newFailedFuture(new IllegalArgumentException("app cannot be empty"));
|
||||||
|
}
|
||||||
|
AppInfo appInfo = appManagement.getDetailApp(app);
|
||||||
|
if (appInfo == null || appInfo.getMachines() == null) {
|
||||||
|
return CompletableFuture.completedFuture(new ArrayList<>());
|
||||||
|
}
|
||||||
|
|
||||||
|
List<CompletableFuture<ClusterUniversalStatePairVO>> futures = appInfo.getMachines().stream()
|
||||||
|
.filter(MachineUtils::isMachineHealth)
|
||||||
|
.map(machine -> getClusterUniversalState(app, machine.getIp(), machine.getPort())
|
||||||
|
.thenApply(e -> new ClusterUniversalStatePairVO(machine.getIp(), machine.getPort(), e)))
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
return AsyncUtils.sequenceSuccessFuture(futures);
|
||||||
|
}
|
||||||
|
|
||||||
|
public CompletableFuture<ClusterGroupEntity> getClusterUniversalStateForAppMachine(String app, String machineId) {
|
||||||
|
if (StringUtil.isBlank(app)) {
|
||||||
|
return AsyncUtils.newFailedFuture(new IllegalArgumentException("app cannot be empty"));
|
||||||
|
}
|
||||||
|
AppInfo appInfo = appManagement.getDetailApp(app);
|
||||||
|
if (appInfo == null || appInfo.getMachines() == null) {
|
||||||
|
return AsyncUtils.newFailedFuture(new IllegalArgumentException("app does not have machines"));
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean machineOk = appInfo.getMachines().stream()
|
||||||
|
.filter(MachineUtils::isMachineHealth)
|
||||||
|
.map(e -> e.getIp() + '@' + e.getPort())
|
||||||
|
.anyMatch(e -> e.equals(machineId));
|
||||||
|
if (!machineOk) {
|
||||||
|
return AsyncUtils.newFailedFuture(new IllegalStateException("machine does not exist or disconnected"));
|
||||||
|
}
|
||||||
|
|
||||||
|
return getClusterUniversalState(app)
|
||||||
|
.thenApply(ClusterEntityUtils::wrapToClusterGroup)
|
||||||
|
.thenCompose(e -> e.stream()
|
||||||
|
.filter(e1 -> e1.getMachineId().equals(machineId))
|
||||||
|
.findAny()
|
||||||
|
.map(CompletableFuture::completedFuture)
|
||||||
|
.orElse(AsyncUtils.newFailedFuture(new IllegalStateException("not a server: " + machineId)))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
public CompletableFuture<ClusterUniversalStateVO> getClusterUniversalState(String app, String ip, int port) {
|
public CompletableFuture<ClusterUniversalStateVO> getClusterUniversalState(String app, String ip, int port) {
|
||||||
return sentinelApiClient.fetchClusterMode(app, ip, port)
|
return sentinelApiClient.fetchClusterMode(app, ip, port)
|
||||||
.thenApply(e -> new ClusterUniversalStateVO().setStateInfo(e))
|
.thenApply(e -> new ClusterUniversalStateVO().setStateInfo(e))
|
||||||
.thenCompose(vo -> {
|
.thenCompose(vo -> {
|
||||||
if (vo.getStateInfo().getClientAvailable()) {
|
if (vo.getStateInfo().getClientAvailable()) {
|
||||||
return sentinelApiClient.fetchClusterClientConfig(app, ip, port)
|
return sentinelApiClient.fetchClusterClientInfoAndConfig(app, ip, port)
|
||||||
.thenApply(cc -> vo.setClient(new ClusterClientStateVO().setClientConfig(cc)));
|
.thenApply(cc -> vo.setClient(new ClusterClientStateVO().setClientConfig(cc)));
|
||||||
} else {
|
} else {
|
||||||
return CompletableFuture.completedFuture(vo);
|
return CompletableFuture.completedFuture(vo);
|
||||||
|
|
@ -111,6 +174,7 @@ public class ClusterConfigService {
|
||||||
private boolean invalidFlowConfig(ServerFlowConfig flowConfig) {
|
private boolean invalidFlowConfig(ServerFlowConfig flowConfig) {
|
||||||
return flowConfig == null || flowConfig.getSampleCount() == null || flowConfig.getSampleCount() <= 0
|
return flowConfig == null || flowConfig.getSampleCount() == null || flowConfig.getSampleCount() <= 0
|
||||||
|| flowConfig.getIntervalMs() == null || flowConfig.getIntervalMs() <= 0
|
|| flowConfig.getIntervalMs() == null || flowConfig.getIntervalMs() <= 0
|
||||||
|| flowConfig.getIntervalMs() % flowConfig.getSampleCount() != 0;
|
|| flowConfig.getIntervalMs() % flowConfig.getSampleCount() != 0
|
||||||
|
|| flowConfig.getMaxAllowedQps() == null || flowConfig.getMaxAllowedQps() < 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,72 @@
|
||||||
|
/*
|
||||||
|
* Copyright 1999-2018 Alibaba Group Holding Ltd.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package com.taobao.csp.sentinel.dashboard.util;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Eric Zhao
|
||||||
|
* @since 1.4.1
|
||||||
|
*/
|
||||||
|
public final class AsyncUtils {
|
||||||
|
|
||||||
|
private static final Logger LOG = LoggerFactory.getLogger(AsyncUtils.class);
|
||||||
|
|
||||||
|
public static <R> CompletableFuture<R> newFailedFuture(Throwable ex) {
|
||||||
|
CompletableFuture<R> future = new CompletableFuture<>();
|
||||||
|
future.completeExceptionally(ex);
|
||||||
|
return future;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <R> CompletableFuture<List<R>> sequenceFuture(List<CompletableFuture<R>> futures) {
|
||||||
|
return CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]))
|
||||||
|
.thenApply(v -> futures.stream()
|
||||||
|
.map(AsyncUtils::getValue)
|
||||||
|
.filter(Objects::nonNull)
|
||||||
|
.collect(Collectors.toList())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <R> CompletableFuture<List<R>> sequenceSuccessFuture(List<CompletableFuture<R>> futures) {
|
||||||
|
return CompletableFuture.supplyAsync(() -> futures.parallelStream()
|
||||||
|
.map(AsyncUtils::getValue)
|
||||||
|
.filter(Objects::nonNull)
|
||||||
|
.collect(Collectors.toList())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> T getValue(CompletableFuture<T> future) {
|
||||||
|
try {
|
||||||
|
return future.get(10, TimeUnit.SECONDS);
|
||||||
|
} catch (Exception ex) {
|
||||||
|
LOG.error("getValue for async result failed", ex);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isSuccessFuture(CompletableFuture future) {
|
||||||
|
return future.isDone() && !future.isCompletedExceptionally() && !future.isCancelled();
|
||||||
|
}
|
||||||
|
|
||||||
|
private AsyncUtils() {}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,138 @@
|
||||||
|
/*
|
||||||
|
* Copyright 1999-2018 Alibaba Group Holding Ltd.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package com.taobao.csp.sentinel.dashboard.util;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import com.alibaba.csp.sentinel.cluster.ClusterStateManager;
|
||||||
|
import com.alibaba.csp.sentinel.util.StringUtil;
|
||||||
|
|
||||||
|
import com.taobao.csp.sentinel.dashboard.domain.cluster.ClusterGroupEntity;
|
||||||
|
import com.taobao.csp.sentinel.dashboard.domain.cluster.ConnectionGroupVO;
|
||||||
|
import com.taobao.csp.sentinel.dashboard.domain.cluster.state.AppClusterClientStateWrapVO;
|
||||||
|
import com.taobao.csp.sentinel.dashboard.domain.cluster.state.AppClusterServerStateWrapVO;
|
||||||
|
import com.taobao.csp.sentinel.dashboard.domain.cluster.state.ClusterClientStateVO;
|
||||||
|
import com.taobao.csp.sentinel.dashboard.domain.cluster.state.ClusterServerStateVO;
|
||||||
|
import com.taobao.csp.sentinel.dashboard.domain.cluster.state.ClusterUniversalStatePairVO;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Eric Zhao
|
||||||
|
* @since 1.4.1
|
||||||
|
*/
|
||||||
|
public final class ClusterEntityUtils {
|
||||||
|
|
||||||
|
public static List<AppClusterServerStateWrapVO> wrapToAppClusterServerState(
|
||||||
|
List<ClusterUniversalStatePairVO> list) {
|
||||||
|
if (list == null || list.isEmpty()) {
|
||||||
|
return new ArrayList<>();
|
||||||
|
}
|
||||||
|
Map<String, AppClusterServerStateWrapVO> map = new HashMap<>();
|
||||||
|
for (ClusterUniversalStatePairVO stateVO : list) {
|
||||||
|
int mode = stateVO.getState().getStateInfo().getMode();
|
||||||
|
|
||||||
|
if (mode == ClusterStateManager.CLUSTER_SERVER) {
|
||||||
|
String ip = stateVO.getIp();
|
||||||
|
String serverId = ip + '@' + stateVO.getCommandPort();
|
||||||
|
ClusterServerStateVO serverStateVO = stateVO.getState().getServer();
|
||||||
|
map.computeIfAbsent(serverId, v -> new AppClusterServerStateWrapVO()
|
||||||
|
.setId(serverId)
|
||||||
|
.setIp(ip)
|
||||||
|
.setPort(serverStateVO.getPort())
|
||||||
|
.setState(serverStateVO)
|
||||||
|
.setConnectedCount(serverStateVO.getConnection().stream()
|
||||||
|
.mapToInt(ConnectionGroupVO::getConnectedCount)
|
||||||
|
.sum()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return new ArrayList<>(map.values());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<AppClusterClientStateWrapVO> wrapToAppClusterClientState(
|
||||||
|
List<ClusterUniversalStatePairVO> list) {
|
||||||
|
if (list == null || list.isEmpty()) {
|
||||||
|
return new ArrayList<>();
|
||||||
|
}
|
||||||
|
Map<String, AppClusterClientStateWrapVO> map = new HashMap<>();
|
||||||
|
for (ClusterUniversalStatePairVO stateVO : list) {
|
||||||
|
int mode = stateVO.getState().getStateInfo().getMode();
|
||||||
|
|
||||||
|
if (mode == ClusterStateManager.CLUSTER_CLIENT) {
|
||||||
|
String ip = stateVO.getIp();
|
||||||
|
String clientId = ip + '@' + stateVO.getCommandPort();
|
||||||
|
ClusterClientStateVO clientStateVO = stateVO.getState().getClient();
|
||||||
|
map.computeIfAbsent(clientId, v -> new AppClusterClientStateWrapVO()
|
||||||
|
.setId(clientId)
|
||||||
|
.setIp(ip)
|
||||||
|
.setState(clientStateVO)
|
||||||
|
.setCommandPort(stateVO.getCommandPort())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return new ArrayList<>(map.values());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<ClusterGroupEntity> wrapToClusterGroup(List<ClusterUniversalStatePairVO> list) {
|
||||||
|
if (list == null || list.isEmpty()) {
|
||||||
|
return new ArrayList<>();
|
||||||
|
}
|
||||||
|
Map<String, ClusterGroupEntity> map = new HashMap<>();
|
||||||
|
for (ClusterUniversalStatePairVO stateVO : list) {
|
||||||
|
int mode = stateVO.getState().getStateInfo().getMode();
|
||||||
|
String ip = stateVO.getIp();
|
||||||
|
if (mode == ClusterStateManager.CLUSTER_SERVER) {
|
||||||
|
String serverAddress = getIp(ip);
|
||||||
|
int port = stateVO.getState().getServer().getPort();
|
||||||
|
map.computeIfAbsent(serverAddress, v -> new ClusterGroupEntity()
|
||||||
|
.setBelongToApp(true).setMachineId(ip + '@' + stateVO.getCommandPort())
|
||||||
|
.setIp(ip).setPort(port)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (ClusterUniversalStatePairVO stateVO : list) {
|
||||||
|
int mode = stateVO.getState().getStateInfo().getMode();
|
||||||
|
String ip = stateVO.getIp();
|
||||||
|
if (mode == ClusterStateManager.CLUSTER_CLIENT) {
|
||||||
|
String targetServer = stateVO.getState().getClient().getClientConfig().getServerHost();
|
||||||
|
Integer targetPort = stateVO.getState().getClient().getClientConfig().getServerPort();
|
||||||
|
if (StringUtil.isBlank(targetServer) || targetPort == null || targetPort <= 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
ClusterGroupEntity group = map.computeIfAbsent(targetServer,
|
||||||
|
v -> new ClusterGroupEntity()
|
||||||
|
.setBelongToApp(true).setMachineId(targetServer)
|
||||||
|
.setIp(targetServer).setPort(targetPort)
|
||||||
|
);
|
||||||
|
group.getClientSet().add(ip + '@' + stateVO.getCommandPort());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return new ArrayList<>(map.values());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String getIp(String str) {
|
||||||
|
if (str.contains(":")) {
|
||||||
|
return str.split(":")[0];
|
||||||
|
}
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
private ClusterEntityUtils() {}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,72 @@
|
||||||
|
/*
|
||||||
|
* Copyright 1999-2018 Alibaba Group Holding Ltd.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package com.taobao.csp.sentinel.dashboard.util;
|
||||||
|
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
import com.alibaba.csp.sentinel.util.StringUtil;
|
||||||
|
import com.alibaba.csp.sentinel.util.function.Tuple2;
|
||||||
|
|
||||||
|
import com.taobao.csp.sentinel.dashboard.discovery.MachineInfo;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Eric Zhao
|
||||||
|
*/
|
||||||
|
public final class MachineUtils {
|
||||||
|
|
||||||
|
public static final long DEFAULT_MAX_CLIENT_PING_TIMEOUT = 60 * 1000;
|
||||||
|
|
||||||
|
public static long getMaxClientTimeout() {
|
||||||
|
return DEFAULT_MAX_CLIENT_PING_TIMEOUT;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Optional<Integer> parseCommandPort(String machineIp) {
|
||||||
|
try {
|
||||||
|
if (!machineIp.contains("@")) {
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
String[] str = machineIp.split("@");
|
||||||
|
if (str.length <= 1) {
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
return Optional.of(Integer.parseInt(str[1]));
|
||||||
|
} catch (Exception ex) {
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Optional<Tuple2<String, Integer>> parseCommandIpAndPort(String machineIp) {
|
||||||
|
try {
|
||||||
|
if (StringUtil.isEmpty(machineIp) || !machineIp.contains("@")) {
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
String[] str = machineIp.split("@");
|
||||||
|
if (str.length <= 1) {
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
return Optional.of(Tuple2.of(str[0], Integer.parseInt(str[1])));
|
||||||
|
} catch (Exception ex) {
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isMachineHealth(MachineInfo machine) {
|
||||||
|
if (machine == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return System.currentTimeMillis() - machine.getTimestamp().getTime() < getMaxClientTimeout();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -134,6 +134,8 @@ public class FlowControllerV1 {
|
||||||
Date date = new Date();
|
Date date = new Date();
|
||||||
entity.setGmtCreate(date);
|
entity.setGmtCreate(date);
|
||||||
entity.setGmtModified(date);
|
entity.setGmtModified(date);
|
||||||
|
entity.setLimitApp(entity.getLimitApp().trim());
|
||||||
|
entity.setResource(entity.getResource().trim());
|
||||||
try {
|
try {
|
||||||
entity = repository.save(entity);
|
entity = repository.save(entity);
|
||||||
} catch (Throwable throwable) {
|
} catch (Throwable throwable) {
|
||||||
|
|
@ -224,7 +226,7 @@ public class FlowControllerV1 {
|
||||||
}
|
}
|
||||||
|
|
||||||
@DeleteMapping("/delete.json")
|
@DeleteMapping("/delete.json")
|
||||||
Result<?> delete(Long id) {
|
public Result<Long> delete(Long id) {
|
||||||
if (id == null) {
|
if (id == null) {
|
||||||
return Result.ofFail(-1, "id can't be null");
|
return Result.ofFail(-1, "id can't be null");
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -137,6 +137,8 @@ public class FlowControllerV2 {
|
||||||
Date date = new Date();
|
Date date = new Date();
|
||||||
entity.setGmtCreate(date);
|
entity.setGmtCreate(date);
|
||||||
entity.setGmtModified(date);
|
entity.setGmtModified(date);
|
||||||
|
entity.setLimitApp(entity.getLimitApp().trim());
|
||||||
|
entity.setResource(entity.getResource().trim());
|
||||||
try {
|
try {
|
||||||
entity = repository.save(entity);
|
entity = repository.save(entity);
|
||||||
publishRules(entity.getApp());
|
publishRules(entity.getApp());
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,104 @@
|
||||||
|
/*
|
||||||
|
* Copyright 1999-2018 Alibaba Group Holding Ltd.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package com.taobao.csp.sentinel.dashboard.view.cluster;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import com.alibaba.csp.sentinel.util.StringUtil;
|
||||||
|
|
||||||
|
import com.taobao.csp.sentinel.dashboard.domain.cluster.ClusterAppFullAssignRequest;
|
||||||
|
import com.taobao.csp.sentinel.dashboard.domain.cluster.ClusterAppAssignResultVO;
|
||||||
|
import com.taobao.csp.sentinel.dashboard.domain.cluster.ClusterAppSingleServerAssignRequest;
|
||||||
|
import com.taobao.csp.sentinel.dashboard.service.ClusterAssignService;
|
||||||
|
import com.taobao.csp.sentinel.dashboard.view.Result;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.web.bind.annotation.PathVariable;
|
||||||
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestBody;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Eric Zhao
|
||||||
|
* @since 1.4.1
|
||||||
|
*/
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/cluster/assign")
|
||||||
|
public class ClusterAssignController {
|
||||||
|
|
||||||
|
private final Logger logger = LoggerFactory.getLogger(ClusterAssignController.class);
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private ClusterAssignService clusterAssignService;
|
||||||
|
|
||||||
|
@PostMapping("/all_server/{app}")
|
||||||
|
public Result<ClusterAppAssignResultVO> apiAssignAllClusterServersOfApp(@PathVariable String app,
|
||||||
|
@RequestBody
|
||||||
|
ClusterAppFullAssignRequest assignRequest) {
|
||||||
|
if (StringUtil.isEmpty(app)) {
|
||||||
|
return Result.ofFail(-1, "app cannot be null or empty");
|
||||||
|
}
|
||||||
|
if (assignRequest == null || assignRequest.getClusterMap() == null
|
||||||
|
|| assignRequest.getRemainingList() == null) {
|
||||||
|
return Result.ofFail(-1, "bad request body");
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return Result.ofSuccess(clusterAssignService.applyAssignToApp(app, assignRequest.getClusterMap(),
|
||||||
|
assignRequest.getRemainingList()));
|
||||||
|
} catch (Throwable throwable) {
|
||||||
|
logger.error("Error when assigning full cluster servers for app: " + app, throwable);
|
||||||
|
return Result.ofFail(-1, throwable.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("/single_server/{app}")
|
||||||
|
public Result<ClusterAppAssignResultVO> apiAssignSingleClusterServersOfApp(@PathVariable String app,
|
||||||
|
@RequestBody ClusterAppSingleServerAssignRequest assignRequest) {
|
||||||
|
if (StringUtil.isEmpty(app)) {
|
||||||
|
return Result.ofFail(-1, "app cannot be null or empty");
|
||||||
|
}
|
||||||
|
if (assignRequest == null || assignRequest.getClusterMap() == null) {
|
||||||
|
return Result.ofFail(-1, "bad request body");
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return Result.ofSuccess(clusterAssignService.applyAssignToApp(app, Collections.singletonList(assignRequest.getClusterMap()),
|
||||||
|
assignRequest.getRemainingList()));
|
||||||
|
} catch (Throwable throwable) {
|
||||||
|
logger.error("Error when assigning single cluster servers for app: " + app, throwable);
|
||||||
|
return Result.ofFail(-1, throwable.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("/unbind_server/{app}")
|
||||||
|
public Result<ClusterAppAssignResultVO> apiUnbindClusterServersOfApp(@PathVariable String app,
|
||||||
|
@RequestBody Set<String> machineIds) {
|
||||||
|
if (StringUtil.isEmpty(app)) {
|
||||||
|
return Result.ofFail(-1, "app cannot be null or empty");
|
||||||
|
}
|
||||||
|
if (machineIds == null || machineIds.isEmpty()) {
|
||||||
|
return Result.ofFail(-1, "bad request body");
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return Result.ofSuccess(clusterAssignService.unbindClusterServers(app, machineIds));
|
||||||
|
} catch (Throwable throwable) {
|
||||||
|
logger.error("Error when unbinding cluster server {} for app <{}>", machineIds, app, throwable);
|
||||||
|
return Result.ofFail(-1, throwable.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -13,8 +13,9 @@
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package com.taobao.csp.sentinel.dashboard.view;
|
package com.taobao.csp.sentinel.dashboard.view.cluster;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.concurrent.ExecutionException;
|
import java.util.concurrent.ExecutionException;
|
||||||
|
|
||||||
|
|
@ -26,16 +27,22 @@ import com.alibaba.fastjson.JSONObject;
|
||||||
import com.taobao.csp.sentinel.dashboard.client.CommandNotFoundException;
|
import com.taobao.csp.sentinel.dashboard.client.CommandNotFoundException;
|
||||||
import com.taobao.csp.sentinel.dashboard.datasource.entity.SentinelVersion;
|
import com.taobao.csp.sentinel.dashboard.datasource.entity.SentinelVersion;
|
||||||
import com.taobao.csp.sentinel.dashboard.discovery.AppManagement;
|
import com.taobao.csp.sentinel.dashboard.discovery.AppManagement;
|
||||||
import com.taobao.csp.sentinel.dashboard.domain.cluster.ClusterClientModifyRequest;
|
import com.taobao.csp.sentinel.dashboard.domain.cluster.request.ClusterClientModifyRequest;
|
||||||
import com.taobao.csp.sentinel.dashboard.domain.cluster.ClusterModifyRequest;
|
import com.taobao.csp.sentinel.dashboard.domain.cluster.request.ClusterModifyRequest;
|
||||||
import com.taobao.csp.sentinel.dashboard.domain.cluster.ClusterServerModifyRequest;
|
import com.taobao.csp.sentinel.dashboard.domain.cluster.request.ClusterServerModifyRequest;
|
||||||
import com.taobao.csp.sentinel.dashboard.domain.cluster.ClusterUniversalStateVO;
|
import com.taobao.csp.sentinel.dashboard.domain.cluster.state.AppClusterClientStateWrapVO;
|
||||||
|
import com.taobao.csp.sentinel.dashboard.domain.cluster.state.AppClusterServerStateWrapVO;
|
||||||
|
import com.taobao.csp.sentinel.dashboard.domain.cluster.state.ClusterUniversalStatePairVO;
|
||||||
|
import com.taobao.csp.sentinel.dashboard.domain.cluster.state.ClusterUniversalStateVO;
|
||||||
import com.taobao.csp.sentinel.dashboard.service.ClusterConfigService;
|
import com.taobao.csp.sentinel.dashboard.service.ClusterConfigService;
|
||||||
|
import com.taobao.csp.sentinel.dashboard.util.ClusterEntityUtils;
|
||||||
import com.taobao.csp.sentinel.dashboard.util.VersionUtils;
|
import com.taobao.csp.sentinel.dashboard.util.VersionUtils;
|
||||||
|
import com.taobao.csp.sentinel.dashboard.view.Result;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
import org.springframework.web.bind.annotation.PathVariable;
|
||||||
import org.springframework.web.bind.annotation.PostMapping;
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
import org.springframework.web.bind.annotation.RequestBody;
|
import org.springframework.web.bind.annotation.RequestBody;
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
|
@ -60,7 +67,7 @@ public class ClusterConfigController {
|
||||||
@Autowired
|
@Autowired
|
||||||
private ClusterConfigService clusterConfigService;
|
private ClusterConfigService clusterConfigService;
|
||||||
|
|
||||||
@PostMapping("/config/modify")
|
@PostMapping("/config/modify_single")
|
||||||
public Result<Boolean> apiModifyClusterConfig(@RequestBody String payload) {
|
public Result<Boolean> apiModifyClusterConfig(@RequestBody String payload) {
|
||||||
if (StringUtil.isBlank(payload)) {
|
if (StringUtil.isBlank(payload)) {
|
||||||
return Result.ofFail(-1, "empty request body");
|
return Result.ofFail(-1, "empty request body");
|
||||||
|
|
@ -94,18 +101,22 @@ public class ClusterConfigController {
|
||||||
return Result.ofFail(-1, "invalid parameter");
|
return Result.ofFail(-1, "invalid parameter");
|
||||||
} catch (ExecutionException ex) {
|
} catch (ExecutionException ex) {
|
||||||
logger.error("Error when modifying cluster config", ex.getCause());
|
logger.error("Error when modifying cluster config", ex.getCause());
|
||||||
if (isNotSupported(ex.getCause())) {
|
return errorResponse(ex);
|
||||||
return unsupportedVersion();
|
|
||||||
} else {
|
|
||||||
return Result.ofThrowable(-1, ex.getCause());
|
|
||||||
}
|
|
||||||
} catch (Throwable ex) {
|
} catch (Throwable ex) {
|
||||||
logger.error("Error when modifying cluster config", ex);
|
logger.error("Error when modifying cluster config", ex);
|
||||||
return Result.ofFail(-1, ex.getMessage());
|
return Result.ofFail(-1, ex.getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/state")
|
private <T> Result<T> errorResponse(ExecutionException ex) {
|
||||||
|
if (isNotSupported(ex.getCause())) {
|
||||||
|
return unsupportedVersion();
|
||||||
|
} else {
|
||||||
|
return Result.ofThrowable(-1, ex.getCause());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/state_single")
|
||||||
public Result<ClusterUniversalStateVO> apiGetClusterState(@RequestParam String app,
|
public Result<ClusterUniversalStateVO> apiGetClusterState(@RequestParam String app,
|
||||||
@RequestParam String ip,
|
@RequestParam String ip,
|
||||||
@RequestParam Integer port) {
|
@RequestParam Integer port) {
|
||||||
|
|
@ -127,17 +138,69 @@ public class ClusterConfigController {
|
||||||
.get();
|
.get();
|
||||||
} catch (ExecutionException ex) {
|
} catch (ExecutionException ex) {
|
||||||
logger.error("Error when fetching cluster state", ex.getCause());
|
logger.error("Error when fetching cluster state", ex.getCause());
|
||||||
if (isNotSupported(ex.getCause())) {
|
return errorResponse(ex);
|
||||||
return unsupportedVersion();
|
|
||||||
} else {
|
|
||||||
return Result.ofThrowable(-1, ex.getCause());
|
|
||||||
}
|
|
||||||
} catch (Throwable throwable) {
|
} catch (Throwable throwable) {
|
||||||
logger.error("Error when fetching cluster state", throwable);
|
logger.error("Error when fetching cluster state", throwable);
|
||||||
return Result.ofFail(-1, throwable.getMessage());
|
return Result.ofFail(-1, throwable.getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@GetMapping("/server_state/{app}")
|
||||||
|
public Result<List<AppClusterServerStateWrapVO>> apiGetClusterServerStateOfApp(@PathVariable String app) {
|
||||||
|
if (StringUtil.isEmpty(app)) {
|
||||||
|
return Result.ofFail(-1, "app cannot be null or empty");
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return clusterConfigService.getClusterUniversalState(app)
|
||||||
|
.thenApply(ClusterEntityUtils::wrapToAppClusterServerState)
|
||||||
|
.thenApply(Result::ofSuccess)
|
||||||
|
.get();
|
||||||
|
} catch (ExecutionException ex) {
|
||||||
|
logger.error("Error when fetching cluster server state of app: " + app, ex.getCause());
|
||||||
|
return errorResponse(ex);
|
||||||
|
} catch (Throwable throwable) {
|
||||||
|
logger.error("Error when fetching cluster server state of app: " + app, throwable);
|
||||||
|
return Result.ofFail(-1, throwable.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/client_state/{app}")
|
||||||
|
public Result<List<AppClusterClientStateWrapVO>> apiGetClusterClientStateOfApp(@PathVariable String app) {
|
||||||
|
if (StringUtil.isEmpty(app)) {
|
||||||
|
return Result.ofFail(-1, "app cannot be null or empty");
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return clusterConfigService.getClusterUniversalState(app)
|
||||||
|
.thenApply(ClusterEntityUtils::wrapToAppClusterClientState)
|
||||||
|
.thenApply(Result::ofSuccess)
|
||||||
|
.get();
|
||||||
|
} catch (ExecutionException ex) {
|
||||||
|
logger.error("Error when fetching cluster token client state of app: " + app, ex.getCause());
|
||||||
|
return errorResponse(ex);
|
||||||
|
} catch (Throwable throwable) {
|
||||||
|
logger.error("Error when fetching cluster token client state of app: " + app, throwable);
|
||||||
|
return Result.ofFail(-1, throwable.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/state/{app}")
|
||||||
|
public Result<List<ClusterUniversalStatePairVO>> apiGetClusterStateOfApp(@PathVariable String app) {
|
||||||
|
if (StringUtil.isEmpty(app)) {
|
||||||
|
return Result.ofFail(-1, "app cannot be null or empty");
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return clusterConfigService.getClusterUniversalState(app)
|
||||||
|
.thenApply(Result::ofSuccess)
|
||||||
|
.get();
|
||||||
|
} catch (ExecutionException ex) {
|
||||||
|
logger.error("Error when fetching cluster state of app: " + app, ex.getCause());
|
||||||
|
return errorResponse(ex);
|
||||||
|
} catch (Throwable throwable) {
|
||||||
|
logger.error("Error when fetching cluster state of app: " + app, throwable);
|
||||||
|
return Result.ofFail(-1, throwable.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private boolean isNotSupported(Throwable ex) {
|
private boolean isNotSupported(Throwable ex) {
|
||||||
return ex instanceof CommandNotFoundException;
|
return ex instanceof CommandNotFoundException;
|
||||||
}
|
}
|
||||||
|
|
@ -25,6 +25,7 @@ public final class NacosConfigUtil {
|
||||||
|
|
||||||
public static final String FLOW_DATA_ID_POSTFIX = "-flow-rules";
|
public static final String FLOW_DATA_ID_POSTFIX = "-flow-rules";
|
||||||
public static final String PARAM_FLOW_DATA_ID_POSTFIX = "-param-rules";
|
public static final String PARAM_FLOW_DATA_ID_POSTFIX = "-param-rules";
|
||||||
|
public static final String CLUSTER_MAP_DATA_ID_POSTFIX = "-cluster-map";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* cc for `cluster-client`
|
* cc for `cluster-client`
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,62 @@
|
||||||
|
/*
|
||||||
|
* Copyright 1999-2018 Alibaba Group Holding Ltd.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package com.alibaba.csp.sentinel.demo.cluster;
|
||||||
|
|
||||||
|
import com.alibaba.csp.sentinel.Entry;
|
||||||
|
import com.alibaba.csp.sentinel.EntryType;
|
||||||
|
import com.alibaba.csp.sentinel.SphU;
|
||||||
|
import com.alibaba.csp.sentinel.cluster.ClusterStateManager;
|
||||||
|
import com.alibaba.csp.sentinel.init.InitExecutor;
|
||||||
|
import com.alibaba.csp.sentinel.slots.block.BlockException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Run this demo with the following args: -Dproject.name=appA</p>
|
||||||
|
* <p>You need a token server running already.</p>
|
||||||
|
*
|
||||||
|
* @author Eric Zhao
|
||||||
|
*/
|
||||||
|
public class ClusterClientDemo {
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
InitExecutor.doInit();
|
||||||
|
|
||||||
|
// Manually schedule the cluster mode to client.
|
||||||
|
// In common, we need a scheduling system to modify the cluster mode automatically.
|
||||||
|
// Command HTTP API: http://<ip>:<port>/setClusterMode?mode=<xxx>
|
||||||
|
ClusterStateManager.setToClient();
|
||||||
|
|
||||||
|
String resourceName = "cluster-demo-entry";
|
||||||
|
|
||||||
|
// Assume we have a cluster flow rule for `demo-resource`: QPS = 5 in AVG_LOCAL mode.
|
||||||
|
for (int i = 0; i < 10; i++) {
|
||||||
|
tryEntry(resourceName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void tryEntry(String res) {
|
||||||
|
Entry entry = null;
|
||||||
|
try {
|
||||||
|
entry = SphU.entry(res, EntryType.IN, 1, "abc", "def");
|
||||||
|
System.out.println("Passed");
|
||||||
|
} catch (BlockException ex) {
|
||||||
|
ex.printStackTrace();
|
||||||
|
} finally {
|
||||||
|
if (entry != null) {
|
||||||
|
entry.exit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue