/*
|
* 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.dashboard.service;
|
|
import java.util.ArrayList;
|
import java.util.List;
|
import java.util.Set;
|
import java.util.concurrent.CompletableFuture;
|
import java.util.stream.Collectors;
|
|
import com.alibaba.csp.sentinel.cluster.ClusterStateManager;
|
import com.alibaba.csp.sentinel.dashboard.client.SentinelApiClient;
|
import com.alibaba.csp.sentinel.dashboard.discovery.AppInfo;
|
import com.alibaba.csp.sentinel.dashboard.discovery.AppManagement;
|
import com.alibaba.csp.sentinel.dashboard.domain.cluster.request.ClusterServerModifyRequest;
|
import com.alibaba.csp.sentinel.dashboard.util.AsyncUtils;
|
import com.alibaba.csp.sentinel.dashboard.util.ClusterEntityUtils;
|
import com.alibaba.csp.sentinel.util.StringUtil;
|
|
import com.alibaba.csp.sentinel.dashboard.domain.cluster.ClusterGroupEntity;
|
import com.alibaba.csp.sentinel.dashboard.domain.cluster.request.ClusterClientModifyRequest;
|
import com.alibaba.csp.sentinel.dashboard.domain.cluster.state.ClusterClientStateVO;
|
import com.alibaba.csp.sentinel.dashboard.domain.cluster.state.ClusterUniversalStatePairVO;
|
import com.alibaba.csp.sentinel.dashboard.domain.cluster.state.ClusterUniversalStateVO;
|
import com.alibaba.csp.sentinel.dashboard.domain.cluster.config.ClusterClientConfig;
|
import com.alibaba.csp.sentinel.dashboard.domain.cluster.config.ServerFlowConfig;
|
import com.alibaba.csp.sentinel.dashboard.domain.cluster.config.ServerTransportConfig;
|
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.stereotype.Service;
|
|
/**
|
* @author Eric Zhao
|
* @since 1.4.0
|
*/
|
@Service
|
public class ClusterConfigService {
|
|
@Autowired
|
private SentinelApiClient sentinelApiClient;
|
@Autowired
|
private AppManagement appManagement;
|
|
public CompletableFuture<Void> modifyClusterClientConfig(ClusterClientModifyRequest request) {
|
if (notClientRequestValid(request)) {
|
throw new IllegalArgumentException("Invalid request");
|
}
|
String app = request.getApp();
|
String ip = request.getIp();
|
int port = request.getPort();
|
return sentinelApiClient.modifyClusterClientConfig(app, ip, port, request.getClientConfig())
|
.thenCompose(v -> sentinelApiClient.modifyClusterMode(ip, port, ClusterStateManager.CLUSTER_CLIENT));
|
}
|
|
private boolean notClientRequestValid(/*@NonNull */ ClusterClientModifyRequest request) {
|
ClusterClientConfig config = request.getClientConfig();
|
return config == null || StringUtil.isEmpty(config.getServerHost())
|
|| config.getServerPort() == null || config.getServerPort() <= 0
|
|| config.getRequestTimeout() == null || config.getRequestTimeout() <= 0;
|
}
|
|
public CompletableFuture<Void> modifyClusterServerConfig(ClusterServerModifyRequest request) {
|
ServerTransportConfig transportConfig = request.getTransportConfig();
|
ServerFlowConfig flowConfig = request.getFlowConfig();
|
Set<String> namespaceSet = request.getNamespaceSet();
|
if (invalidTransportConfig(transportConfig)) {
|
throw new IllegalArgumentException("Invalid transport config in request");
|
}
|
if (invalidFlowConfig(flowConfig)) {
|
throw new IllegalArgumentException("Invalid flow config in request");
|
}
|
if (namespaceSet == null) {
|
throw new IllegalArgumentException("namespace set cannot be null");
|
}
|
String app = request.getApp();
|
String ip = request.getIp();
|
int port = request.getPort();
|
return sentinelApiClient.modifyClusterServerNamespaceSet(app, ip, port, namespaceSet)
|
.thenCompose(v -> sentinelApiClient.modifyClusterServerTransportConfig(app, ip, port, transportConfig))
|
.thenCompose(v -> sentinelApiClient.modifyClusterServerFlowConfig(app, ip, port, flowConfig))
|
.thenCompose(v -> sentinelApiClient.modifyClusterMode(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(e -> e.isHealthy())
|
.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(e -> e.isHealthy())
|
.map(e -> e.getIp() + '@' + e.getPort())
|
.anyMatch(e -> e.equals(machineId));
|
if (!machineOk) {
|
return AsyncUtils.newFailedFuture(new IllegalStateException("machine does not exist or disconnected"));
|
}
|
|
return getClusterUniversalState(app)
|
.thenApply(ClusterEntityUtils::wrapToClusterGroup)
|
.thenCompose(e -> e.stream()
|
.filter(e1 -> e1.getMachineId().equals(machineId))
|
.findAny()
|
.map(CompletableFuture::completedFuture)
|
.orElse(AsyncUtils.newFailedFuture(new IllegalStateException("not a server: " + machineId)))
|
);
|
}
|
|
public CompletableFuture<ClusterUniversalStateVO> getClusterUniversalState(String app, String ip, int port) {
|
return sentinelApiClient.fetchClusterMode(ip, port)
|
.thenApply(e -> new ClusterUniversalStateVO().setStateInfo(e))
|
.thenCompose(vo -> {
|
if (vo.getStateInfo().getClientAvailable()) {
|
return sentinelApiClient.fetchClusterClientInfoAndConfig(ip, port)
|
.thenApply(cc -> vo.setClient(new ClusterClientStateVO().setClientConfig(cc)));
|
} else {
|
return CompletableFuture.completedFuture(vo);
|
}
|
}).thenCompose(vo -> {
|
if (vo.getStateInfo().getServerAvailable()) {
|
return sentinelApiClient.fetchClusterServerBasicInfo(ip, port)
|
.thenApply(vo::setServer);
|
} else {
|
return CompletableFuture.completedFuture(vo);
|
}
|
});
|
}
|
|
private boolean invalidTransportConfig(ServerTransportConfig transportConfig) {
|
return transportConfig == null || transportConfig.getPort() == null || transportConfig.getPort() <= 0
|
|| transportConfig.getIdleSeconds() == null || transportConfig.getIdleSeconds() <= 0;
|
}
|
|
private boolean invalidFlowConfig(ServerFlowConfig flowConfig) {
|
return flowConfig == null || flowConfig.getSampleCount() == null || flowConfig.getSampleCount() <= 0
|
|| flowConfig.getIntervalMs() == null || flowConfig.getIntervalMs() <= 0
|
|| flowConfig.getIntervalMs() % flowConfig.getSampleCount() != 0
|
|| flowConfig.getMaxAllowedQps() == null || flowConfig.getMaxAllowedQps() < 0;
|
}
|
}
|