This commit is contained in:
shuhongfan
2023-09-04 16:40:17 +08:00
commit cf5ac25c14
8267 changed files with 1305066 additions and 0 deletions

View File

@@ -0,0 +1,35 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-transport</artifactId>
<version>1.8.3</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<packaging>jar</packaging>
<artifactId>sentinel-transport-common</artifactId>
<dependencies>
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-extension</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,58 @@
/*
* Copyright 1999-2019 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.command;
import com.alibaba.csp.sentinel.log.RecordLog;
import com.alibaba.csp.sentinel.transport.CommandCenter;
import com.alibaba.csp.sentinel.spi.SpiLoader;
/**
* Provider for a universal {@link CommandCenter} instance.
*
* @author cdfive
* @since 1.5.0
*/
public final class CommandCenterProvider {
private static CommandCenter commandCenter = null;
static {
resolveInstance();
}
private static void resolveInstance() {
CommandCenter resolveCommandCenter = SpiLoader.of(CommandCenter.class).loadHighestPriorityInstance();
if (resolveCommandCenter == null) {
RecordLog.warn("[CommandCenterProvider] WARN: No existing CommandCenter found");
} else {
commandCenter = resolveCommandCenter;
RecordLog.info("[CommandCenterProvider] CommandCenter resolved: {}", resolveCommandCenter.getClass()
.getCanonicalName());
}
}
/**
* Get resolved {@link CommandCenter} instance.
*
* @return resolved {@code CommandCenter} instance
*/
public static CommandCenter getCommandCenter() {
return commandCenter;
}
private CommandCenterProvider() {}
}

View File

@@ -0,0 +1,33 @@
/*
* 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.command;
/**
* @author Eric Zhao
* @since 1.4.1
*/
public final class CommandConstants {
public static final String VERSION_COMMAND = "version";
public static final String MSG_INVALID_COMMAND = "Invalid command";
public static final String MSG_UNKNOWN_COMMAND_PREFIX = "Unknown command";
public static final String MSG_SUCCESS = "success";
public static final String MSG_FAIL = "failed";
private CommandConstants() {}
}

View File

@@ -0,0 +1,32 @@
/*
* 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.command;
/**
* Represent a handler that handles a {@link CommandRequest}.
*
* @author Eric Zhao
*/
public interface CommandHandler<R> {
/**
* Handle the given Courier command request.
*
* @param request the request to handle
* @return the response
*/
CommandResponse<R> handle(CommandRequest request);
}

View File

@@ -0,0 +1,69 @@
/*
* 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.command;
import java.util.*;
import com.alibaba.csp.sentinel.command.annotation.CommandMapping;
import com.alibaba.csp.sentinel.spi.SpiLoader;
import com.alibaba.csp.sentinel.util.StringUtil;
/**
* Provides and filters command handlers registered via SPI.
*
* @author Eric Zhao
*/
public class CommandHandlerProvider implements Iterable<CommandHandler> {
private final SpiLoader<CommandHandler> spiLoader = SpiLoader.of(CommandHandler.class);
/**
* Get all command handlers annotated with {@link CommandMapping} with command name.
*
* @return list of all named command handlers
*/
public Map<String, CommandHandler> namedHandlers() {
Map<String, CommandHandler> map = new HashMap<String, CommandHandler>();
List<CommandHandler> handlers = spiLoader.loadInstanceList();
for (CommandHandler handler : handlers) {
String name = parseCommandName(handler);
if (!StringUtil.isEmpty(name)) {
map.put(name, handler);
}
}
return map;
}
private String parseCommandName(CommandHandler handler) {
CommandMapping commandMapping = handler.getClass().getAnnotation(CommandMapping.class);
if (commandMapping != null) {
return commandMapping.name();
} else {
return null;
}
}
@Override
public Iterator<CommandHandler> iterator() {
return spiLoader.loadInstanceList().iterator();
}
private static final CommandHandlerProvider INSTANCE = new CommandHandlerProvider();
public static CommandHandlerProvider getInstance() {
return INSTANCE;
}
}

View File

@@ -0,0 +1,75 @@
/*
* 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.command;
import java.util.HashMap;
import java.util.Map;
import com.alibaba.csp.sentinel.util.StringUtil;
/**
* Command request representation of command center.
*
* @author Eric Zhao
*/
public class CommandRequest {
private final Map<String, String> metadata = new HashMap<String, String>();
private final Map<String, String> parameters = new HashMap<String, String>();
private byte[] body;
public byte[] getBody() {
return body;
}
public CommandRequest setBody(byte[] body) {
this.body = body;
return this;
}
public Map<String, String> getParameters() {
return parameters;
}
public String getParam(String key) {
return parameters.get(key);
}
public String getParam(String key, String defaultValue) {
String value = parameters.get(key);
return StringUtil.isBlank(value) ? defaultValue : value;
}
public CommandRequest addParam(String key, String value) {
if (StringUtil.isBlank(key)) {
throw new IllegalArgumentException("Parameter key cannot be empty");
}
parameters.put(key, value);
return this;
}
public Map<String, String> getMetadata() {
return metadata;
}
public CommandRequest addMetadata(String key, String value) {
if (StringUtil.isBlank(key)) {
throw new IllegalArgumentException("Metadata key cannot be empty");
}
metadata.put(key, value);
return this;
}
}

View File

@@ -0,0 +1,83 @@
/*
* 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.command;
/**
* Command response representation of command center.
*
* @param <R> type of the result
* @author Eric Zhao
*/
public class CommandResponse<R> {
private final boolean success;
private final R result;
private final Throwable exception;
private CommandResponse(R result) {
this(result, true, null);
}
private CommandResponse(R result, boolean success, Throwable exception) {
this.success = success;
this.result = result;
this.exception = exception;
}
/**
* Construct a successful response with given object.
*
* @param result result object
* @param <T> type of the result
* @return constructed server response
*/
public static <T> CommandResponse<T> ofSuccess(T result) {
return new CommandResponse<T>(result);
}
/**
* Construct a failed response with given exception.
*
* @param ex cause of the failure
* @return constructed server response
*/
public static <T> CommandResponse<T> ofFailure(Throwable ex) {
return new CommandResponse<T>(null, false, ex);
}
/**
* Construct a failed response with given exception.
*
* @param ex cause of the failure
* @param result additional message of the failure
* @return constructed server response
*/
public static <T> CommandResponse<T> ofFailure(Throwable ex, T result) {
return new CommandResponse<T>(result, false, ex);
}
public boolean isSuccess() {
return success;
}
public R getResult() {
return result;
}
public Throwable getException() {
return exception;
}
}

View File

@@ -0,0 +1,37 @@
/*
* 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.command.annotation;
import java.lang.annotation.*;
/**
* @author Eric Zhao
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Documented
public @interface CommandMapping {
String name();
/**
* Get brief description of the command.
*
* @return brief description of the command
* @since 1.5.0
*/
String desc();
}

View File

@@ -0,0 +1,62 @@
/*
* Copyright 1999-2019 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.command.handler;
import com.alibaba.csp.sentinel.command.CommandHandler;
import com.alibaba.csp.sentinel.command.CommandHandlerProvider;
import com.alibaba.csp.sentinel.command.CommandRequest;
import com.alibaba.csp.sentinel.command.CommandResponse;
import com.alibaba.csp.sentinel.command.annotation.CommandMapping;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import java.util.Map;
/**
* <p>
* List all available command handlers by request: </br>
* {@code curl http://ip:commandPort/api}
* </p>
*
* @author houyi
* @since 1.5.0
*/
@CommandMapping(name = "api", desc = "get all available command handlers")
public class ApiCommandHandler implements CommandHandler<String> {
@Override
public CommandResponse<String> handle(CommandRequest request) {
Map<String, CommandHandler> handlers = CommandHandlerProvider.getInstance().namedHandlers();
JSONArray array = new JSONArray();
if (handlers.isEmpty()) {
return CommandResponse.ofSuccess(array.toJSONString());
}
for (CommandHandler handler : handlers.values()) {
CommandMapping commandMapping = handler.getClass().getAnnotation(CommandMapping.class);
if (commandMapping == null) {
continue;
}
String api = commandMapping.name();
String desc = commandMapping.desc();
JSONObject obj = new JSONObject();
obj.put("url", "/" + api);
obj.put("desc", desc);
array.add(obj);
}
return CommandResponse.ofSuccess(array.toJSONString());
}
}

View File

@@ -0,0 +1,36 @@
/*
* 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.command.handler;
import com.alibaba.csp.sentinel.command.CommandHandler;
import com.alibaba.csp.sentinel.command.CommandRequest;
import com.alibaba.csp.sentinel.command.CommandResponse;
import com.alibaba.csp.sentinel.command.annotation.CommandMapping;
import com.alibaba.csp.sentinel.util.HostNameUtil;
/**
* The basic info command returns the runtime properties.
*
* @author Eric Zhao
*/
@CommandMapping(name = "basicInfo", desc = "get sentinel config info")
public class BasicInfoCommandHandler implements CommandHandler<String> {
@Override
public CommandResponse<String> handle(CommandRequest request) {
return CommandResponse.ofSuccess(HostNameUtil.getConfigString());
}
}

View File

@@ -0,0 +1,50 @@
/*
* 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.command.handler;
import com.alibaba.csp.sentinel.command.CommandHandler;
import com.alibaba.csp.sentinel.command.CommandRequest;
import com.alibaba.csp.sentinel.command.CommandResponse;
import com.alibaba.csp.sentinel.command.annotation.CommandMapping;
import com.alibaba.csp.sentinel.slots.block.authority.AuthorityRuleManager;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRuleManager;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager;
import com.alibaba.csp.sentinel.slots.system.SystemRuleManager;
import com.alibaba.fastjson.JSON;
/**
* @author jialiang.linjl
*/
@CommandMapping(name = "getRules", desc = "get all active rules by type, request param: type={ruleType}")
public class FetchActiveRuleCommandHandler implements CommandHandler<String> {
@Override
public CommandResponse<String> handle(CommandRequest request) {
String type = request.getParam("type");
if ("flow".equalsIgnoreCase(type)) {
return CommandResponse.ofSuccess(JSON.toJSONString(FlowRuleManager.getRules()));
} else if ("degrade".equalsIgnoreCase(type)) {
return CommandResponse.ofSuccess(JSON.toJSONString(DegradeRuleManager.getRules()));
} else if ("authority".equalsIgnoreCase(type)) {
return CommandResponse.ofSuccess(JSON.toJSONString(AuthorityRuleManager.getRules()));
} else if ("system".equalsIgnoreCase(type)) {
return CommandResponse.ofSuccess(JSON.toJSONString(SystemRuleManager.getRules()));
} else {
return CommandResponse.ofFailure(new IllegalArgumentException("invalid type"));
}
}
}

View File

@@ -0,0 +1,47 @@
/*
* 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.command.handler;
import com.alibaba.csp.sentinel.command.CommandHandler;
import com.alibaba.csp.sentinel.command.CommandRequest;
import com.alibaba.csp.sentinel.command.CommandResponse;
import com.alibaba.csp.sentinel.command.annotation.CommandMapping;
import com.alibaba.csp.sentinel.node.ClusterNode;
import com.alibaba.csp.sentinel.command.vo.NodeVo;
import com.alibaba.csp.sentinel.slots.clusterbuilder.ClusterBuilderSlot;
import com.alibaba.csp.sentinel.util.StringUtil;
import com.alibaba.fastjson.JSON;
/**
* @author qinan.qn
*/
@CommandMapping(name = "clusterNodeById", desc = "get clusterNode VO by id, request param: id={resourceName}")
public class FetchClusterNodeByIdCommandHandler implements CommandHandler<String> {
@Override
public CommandResponse<String> handle(CommandRequest request) {
String id = request.getParam("id");
if (StringUtil.isEmpty(id)) {
return CommandResponse.ofFailure(new IllegalArgumentException("Invalid parameter: empty clusterNode name"));
}
ClusterNode node = ClusterBuilderSlot.getClusterNode(id);
if (node != null) {
return CommandResponse.ofSuccess(JSON.toJSONString(NodeVo.fromClusterNode(id, node)));
} else {
return CommandResponse.ofSuccess("{}");
}
}
}

View File

@@ -0,0 +1,92 @@
/*
* 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.command.handler;
import java.util.Map.Entry;
import com.alibaba.csp.sentinel.command.CommandHandler;
import com.alibaba.csp.sentinel.command.CommandRequest;
import com.alibaba.csp.sentinel.command.CommandResponse;
import com.alibaba.csp.sentinel.command.annotation.CommandMapping;
import com.alibaba.csp.sentinel.util.StringUtil;
import com.alibaba.csp.sentinel.node.ClusterNode;
import com.alibaba.csp.sentinel.slotchain.ResourceWrapper;
import com.alibaba.csp.sentinel.slots.clusterbuilder.ClusterBuilderSlot;
/**
* @author qinan.qn
*/
@CommandMapping(name = "cnode", desc = "get clusterNode metrics by id, request param: id={resourceName}")
public class FetchClusterNodeHumanCommandHandler implements CommandHandler<String> {
private final static String FORMAT = "%-4s%-80s%-10s%-10s%-10s%-11s%-9s%-6s%-10s%-11s%-9s%-11s";
private final static int MAX_LEN = 79;
@Override
public CommandResponse<String> handle(CommandRequest request) {
String name = request.getParam("id");
if (StringUtil.isEmpty(name)) {
return CommandResponse.ofFailure(new IllegalArgumentException("Invalid parameter: empty clusterNode name"));
}
StringBuilder sb = new StringBuilder();
int i = 0;
int nameLength = 0;
for (Entry<ResourceWrapper, ClusterNode> e : ClusterBuilderSlot.getClusterNodeMap().entrySet()) {
if (e.getKey().getName().contains(name)) {
int l = e.getKey().getShowName().length();
if (l > nameLength) {
nameLength = l;
}
if (++i == 30) {
break;
}
}
}
nameLength = nameLength > MAX_LEN ? MAX_LEN : nameLength;
String format = FORMAT.replaceAll("80", String.valueOf(nameLength + 1));
sb.append(String.format(format, "idx", "id", "thread", "pass", "blocked", "success", "total", "aRt",
"1m-pass", "1m-block", "1m-all", "exception")).append("\n");
for (Entry<ResourceWrapper, ClusterNode> e : ClusterBuilderSlot.getClusterNodeMap().entrySet()) {
if (e.getKey().getName().contains(name)) {
ClusterNode node = e.getValue();
String id = e.getKey().getShowName();
int lenNum = (int)Math.ceil((double)id.length() / nameLength) - 1;
sb.append(String.format(format, i + 1, lenNum == 0 ? id : id.substring(0, nameLength),
node.curThreadNum(), node.passQps(), node.blockQps(), node.successQps(), node.totalQps(),
node.avgRt(), node.totalRequest() - node.blockRequest(), node.blockRequest(),
node.totalRequest(), node.exceptionQps())).append("\n");
for (int j = 1; j <= lenNum; ++j) {
int start = nameLength * j;
int end = j == lenNum ? id.length() : nameLength * (j + 1);
sb.append(String.format(format, "", id.substring(start, end), "", "", "", "", "", "", "", "", "",
"", "", "")).append("\n");
}
if (++i == 30) {
break;
}
}
}
return CommandResponse.ofSuccess(sb.toString());
}
}

View File

@@ -0,0 +1,56 @@
/*
* Copyright 1999-2018 Alibaba Group Holding Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.csp.sentinel.command.handler;
import java.util.ArrayList;
import java.util.List;
import com.alibaba.csp.sentinel.Constants;
import com.alibaba.csp.sentinel.command.CommandHandler;
import com.alibaba.csp.sentinel.command.CommandRequest;
import com.alibaba.csp.sentinel.command.CommandResponse;
import com.alibaba.csp.sentinel.command.annotation.CommandMapping;
import com.alibaba.csp.sentinel.node.DefaultNode;
import com.alibaba.csp.sentinel.node.Node;
import com.alibaba.csp.sentinel.command.vo.NodeVo;
import com.alibaba.fastjson.JSON;
/**
* @author leyou
*/
@CommandMapping(name = "jsonTree", desc = "get tree node VO start from root node")
public class FetchJsonTreeCommandHandler implements CommandHandler<String> {
@Override
public CommandResponse<String> handle(CommandRequest request) {
List<NodeVo> results = new ArrayList<NodeVo>();
visit(Constants.ROOT, results, null);
return CommandResponse.ofSuccess(JSON.toJSONString(results));
}
/**
* Preorder traversal.
*/
private void visit(DefaultNode node, List<NodeVo> results, String parentId) {
NodeVo vo = NodeVo.fromDefaultNode(node, parentId);
results.add(vo);
String id = vo.getId();
for (Node n : node.getChildList()) {
visit((DefaultNode)n, results, id);
}
}
}

View File

@@ -0,0 +1,112 @@
/*
* Copyright 1999-2018 Alibaba Group Holding Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.csp.sentinel.command.handler;
import java.util.Map.Entry;
import com.alibaba.csp.sentinel.command.CommandHandler;
import com.alibaba.csp.sentinel.command.CommandRequest;
import com.alibaba.csp.sentinel.command.CommandResponse;
import com.alibaba.csp.sentinel.command.annotation.CommandMapping;
import com.alibaba.csp.sentinel.node.ClusterNode;
import com.alibaba.csp.sentinel.node.StatisticNode;
import com.alibaba.csp.sentinel.slotchain.ResourceWrapper;
import com.alibaba.csp.sentinel.slots.clusterbuilder.ClusterBuilderSlot;
/**
* @author qinan.qn
*/
@CommandMapping(name = "origin", desc = "get origin clusterNode by id, request param: id={resourceName}")
public class FetchOriginCommandHandler implements CommandHandler<String> {
private final static String FORMAT = "%-4s%-80s%-10s%-10s%-11s%-9s%-6s%-10s%-11s%-9s";
private final static int MAX_LEN = 79;
@Override
public CommandResponse<String> handle(CommandRequest request) {
StringBuilder sb = new StringBuilder();
String name = request.getParam("id");
ClusterNode cNode = null;
boolean exactly = false;
for (Entry<ResourceWrapper, ClusterNode> e : ClusterBuilderSlot.getClusterNodeMap().entrySet()) {
if (e.getKey().getName().equals(name)) {
cNode = e.getValue();
sb.append("id: ").append(e.getKey().getShowName()).append("\n");
sb.append("\n");
exactly = true;
break;
}
}
if (!exactly) {
for (Entry<ResourceWrapper, ClusterNode> e : ClusterBuilderSlot.getClusterNodeMap().entrySet()) {
if (e.getKey().getName().indexOf(name) > 0) {
cNode = e.getValue();
sb.append("id: ").append(e.getKey().getShowName()).append("\n");
sb.append("\n");
break;
}
}
}
if (cNode == null) {
return CommandResponse.ofSuccess("Not find cNode with id " + name);
}
int i = 0;
int nameLength = 0;
for (Entry<String, StatisticNode> e : cNode.getOriginCountMap().entrySet()) {
int l = e.getKey().length();
if (l > nameLength) {
nameLength = l;
}
if (++i == 120) {
break;
}
}
nameLength = nameLength > MAX_LEN ? MAX_LEN : nameLength;
String format = FORMAT.replaceAll("80", String.valueOf(nameLength + 1));
i = 0;
sb.append(String
.format(format, "idx", "origin", "threadNum", "passQps", "blockQps", "totalQps", "aRt", "1m-pass",
"1m-block", "1m-total")).append("\n");
for (Entry<String, StatisticNode> e : cNode.getOriginCountMap().entrySet()) {
StatisticNode node = e.getValue();
String id = e.getKey();
int lenNum = (int)Math.ceil((double)id.length() / nameLength) - 1;
sb.append(String
.format(format, i + 1, lenNum == 0 ? id : id.substring(0, nameLength), node.curThreadNum(),
node.passQps(), node.blockQps(), node.totalQps(), node.avgRt(),
node.totalRequest() - node.blockRequest(), node.blockRequest(), node.totalRequest()))
.append("\n");
for (int j = 1; j <= lenNum; ++j) {
int start = nameLength * j;
int end = j == lenNum ? id.length() : nameLength * (j + 1);
sb.append(String
.format(format, "", id.substring(start, end), "", "", "", "", "", "", "", "", "", "", "", ""))
.append("\n");
}
if (++i == 30) {
break;
}
}
return CommandResponse.ofSuccess(sb.toString());
}
}

View File

@@ -0,0 +1,61 @@
/*
* 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.command.handler;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import com.alibaba.csp.sentinel.command.CommandHandler;
import com.alibaba.csp.sentinel.command.CommandRequest;
import com.alibaba.csp.sentinel.command.CommandResponse;
import com.alibaba.csp.sentinel.command.annotation.CommandMapping;
import com.alibaba.csp.sentinel.node.ClusterNode;
import com.alibaba.csp.sentinel.command.vo.NodeVo;
import com.alibaba.csp.sentinel.slotchain.ResourceWrapper;
import com.alibaba.csp.sentinel.slots.clusterbuilder.ClusterBuilderSlot;
import com.alibaba.fastjson.JSONArray;
/**
* @author jialiang.linjl
*/
@CommandMapping(name = "clusterNode", desc = "get all clusterNode VO, use type=notZero to ignore those nodes with totalRequest <=0")
public class FetchSimpleClusterNodeCommandHandler implements CommandHandler<String> {
@Override
public CommandResponse<String> handle(CommandRequest request) {
/*
* type==notZero means nodes whose totalRequest <= 0 will be ignored.
*/
String type = request.getParam("type");
List<NodeVo> list = new ArrayList<NodeVo>();
Map<ResourceWrapper, ClusterNode> map = ClusterBuilderSlot.getClusterNodeMap();
if (map == null) {
return CommandResponse.ofSuccess(JSONArray.toJSONString(list));
}
for (Map.Entry<ResourceWrapper, ClusterNode> entry : map.entrySet()) {
if ("notZero".equalsIgnoreCase(type)) {
if (entry.getValue().totalRequest() > 0) {
list.add(NodeVo.fromClusterNode(entry.getKey(), entry.getValue()));
}
} else {
list.add(NodeVo.fromClusterNode(entry.getKey(), entry.getValue()));
}
}
return CommandResponse.ofSuccess(JSONArray.toJSONString(list));
}
}

View File

@@ -0,0 +1,47 @@
/*
* 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.command.handler;
import java.util.HashMap;
import java.util.Map;
import com.alibaba.csp.sentinel.Constants;
import com.alibaba.csp.sentinel.command.CommandHandler;
import com.alibaba.csp.sentinel.command.CommandRequest;
import com.alibaba.csp.sentinel.command.CommandResponse;
import com.alibaba.csp.sentinel.command.annotation.CommandMapping;
import com.alibaba.fastjson.JSONObject;
/**
* @author jialiang.linjl
*/
@CommandMapping(name = "systemStatus", desc = "get system status")
public class FetchSystemStatusCommandHandler implements CommandHandler<String> {
@Override
public CommandResponse<String> handle(CommandRequest request) {
Map<String, Object> systemStatus = new HashMap<String, Object>();
systemStatus.put("rqps", Constants.ENTRY_NODE.successQps());
systemStatus.put("qps", Constants.ENTRY_NODE.passQps());
systemStatus.put("b", Constants.ENTRY_NODE.blockQps());
systemStatus.put("r", Constants.ENTRY_NODE.avgRt());
systemStatus.put("t", Constants.ENTRY_NODE.curThreadNum());
return CommandResponse.ofSuccess(JSONObject.toJSONString(systemStatus));
}
}

View File

@@ -0,0 +1,92 @@
/*
* 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.command.handler;
import com.alibaba.csp.sentinel.Constants;
import com.alibaba.csp.sentinel.command.CommandHandler;
import com.alibaba.csp.sentinel.command.CommandRequest;
import com.alibaba.csp.sentinel.command.CommandResponse;
import com.alibaba.csp.sentinel.command.annotation.CommandMapping;
import com.alibaba.csp.sentinel.node.DefaultNode;
import com.alibaba.csp.sentinel.node.EntranceNode;
import com.alibaba.csp.sentinel.node.Node;
/**
* @author qinan.qn
*/
@CommandMapping(name = "tree", desc = "get metrics in tree mode, use id to specify detailed tree root")
public class FetchTreeCommandHandler implements CommandHandler<String> {
@Override
public CommandResponse<String> handle(CommandRequest request) {
String id = request.getParam("id");
StringBuilder sb = new StringBuilder();
DefaultNode start = Constants.ROOT;
if (id == null) {
visitTree(0, start, sb);
} else {
boolean exactly = false;
for (Node n : start.getChildList()) {
DefaultNode dn = (DefaultNode)n;
if (dn.getId().getName().equals(id)) {
visitTree(0, dn, sb);
exactly = true;
break;
}
}
if (!exactly) {
for (Node n : start.getChildList()) {
DefaultNode dn = (DefaultNode)n;
if (dn.getId().getName().contains(id)) {
visitTree(0, dn, sb);
}
}
}
}
sb.append("\r\n\r\n");
sb.append(
"t:threadNum pq:passQps bq:blockQps tq:totalQps rt:averageRt prq: passRequestQps 1mp:1m-pass "
+ "1mb:1m-block 1mt:1m-total").append("\r\n");
return CommandResponse.ofSuccess(sb.toString());
}
private void visitTree(int level, DefaultNode node, /*@NonNull*/ StringBuilder sb) {
for (int i = 0; i < level; ++i) {
sb.append("-");
}
if (!(node instanceof EntranceNode)) {
sb.append(String.format("%s(t:%s pq:%s bq:%s tq:%s rt:%s prq:%s 1mp:%s 1mb:%s 1mt:%s)",
node.getId().getShowName(), node.curThreadNum(), node.passQps(),
node.blockQps(), node.totalQps(), node.avgRt(), node.successQps(),
node.totalRequest() - node.blockRequest(), node.blockRequest(),
node.totalRequest())).append("\n");
} else {
sb.append(String.format("EntranceNode: %s(t:%s pq:%s bq:%s tq:%s rt:%s prq:%s 1mp:%s 1mb:%s 1mt:%s)",
node.getId().getShowName(), node.curThreadNum(), node.passQps(),
node.blockQps(), node.totalQps(), node.avgRt(), node.successQps(),
node.totalRequest() - node.blockRequest(), node.blockRequest(),
node.totalRequest())).append("\n");
}
for (Node n : node.getChildList()) {
DefaultNode dn = (DefaultNode)n;
visitTree(level + 1, dn, sb);
}
}
}

View File

@@ -0,0 +1,132 @@
/*
* 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.command.handler;
import java.net.URLDecoder;
import java.util.List;
import com.alibaba.csp.sentinel.command.CommandHandler;
import com.alibaba.csp.sentinel.command.CommandRequest;
import com.alibaba.csp.sentinel.command.CommandResponse;
import com.alibaba.csp.sentinel.command.annotation.CommandMapping;
import com.alibaba.csp.sentinel.datasource.WritableDataSource;
import com.alibaba.csp.sentinel.log.RecordLog;
import com.alibaba.csp.sentinel.util.StringUtil;
import com.alibaba.csp.sentinel.util.VersionUtil;
import com.alibaba.csp.sentinel.slots.block.authority.AuthorityRule;
import com.alibaba.csp.sentinel.slots.block.authority.AuthorityRuleManager;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRule;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRuleManager;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager;
import com.alibaba.csp.sentinel.slots.system.SystemRuleManager;
import com.alibaba.csp.sentinel.slots.system.SystemRule;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import static com.alibaba.csp.sentinel.transport.util.WritableDataSourceRegistry.*;
/**
* @author jialiang.linjl
* @author Eric Zhao
*/
@CommandMapping(name = "setRules", desc = "modify the rules, accept param: type={ruleType}&data={ruleJson}")
public class ModifyRulesCommandHandler implements CommandHandler<String> {
private static final int FASTJSON_MINIMAL_VER = 0x01020C00;
@Override
public CommandResponse<String> handle(CommandRequest request) {
// XXX from 1.7.2, force to fail when fastjson is older than 1.2.12
// We may need a better solution on this.
if (VersionUtil.fromVersionString(JSON.VERSION) < FASTJSON_MINIMAL_VER) {
// fastjson too old
return CommandResponse.ofFailure(new RuntimeException("The \"fastjson-" + JSON.VERSION
+ "\" introduced in application is too old, you need fastjson-1.2.12 at least."));
}
String type = request.getParam("type");
// rule data in get parameter
String data = request.getParam("data");
if (StringUtil.isNotEmpty(data)) {
try {
data = URLDecoder.decode(data, "utf-8");
} catch (Exception e) {
RecordLog.info("Decode rule data error", e);
return CommandResponse.ofFailure(e, "decode rule data error");
}
}
RecordLog.info("Receiving rule change (type: {}): {}", type, data);
String result = "success";
if (FLOW_RULE_TYPE.equalsIgnoreCase(type)) {
List<FlowRule> flowRules = JSONArray.parseArray(data, FlowRule.class);
FlowRuleManager.loadRules(flowRules);
if (!writeToDataSource(getFlowDataSource(), flowRules)) {
result = WRITE_DS_FAILURE_MSG;
}
return CommandResponse.ofSuccess(result);
} else if (AUTHORITY_RULE_TYPE.equalsIgnoreCase(type)) {
List<AuthorityRule> rules = JSONArray.parseArray(data, AuthorityRule.class);
AuthorityRuleManager.loadRules(rules);
if (!writeToDataSource(getAuthorityDataSource(), rules)) {
result = WRITE_DS_FAILURE_MSG;
}
return CommandResponse.ofSuccess(result);
} else if (DEGRADE_RULE_TYPE.equalsIgnoreCase(type)) {
List<DegradeRule> rules = JSONArray.parseArray(data, DegradeRule.class);
DegradeRuleManager.loadRules(rules);
if (!writeToDataSource(getDegradeDataSource(), rules)) {
result = WRITE_DS_FAILURE_MSG;
}
return CommandResponse.ofSuccess(result);
} else if (SYSTEM_RULE_TYPE.equalsIgnoreCase(type)) {
List<SystemRule> rules = JSONArray.parseArray(data, SystemRule.class);
SystemRuleManager.loadRules(rules);
if (!writeToDataSource(getSystemSource(), rules)) {
result = WRITE_DS_FAILURE_MSG;
}
return CommandResponse.ofSuccess(result);
}
return CommandResponse.ofFailure(new IllegalArgumentException("invalid type"));
}
/**
* Write target value to given data source.
*
* @param dataSource writable data source
* @param value target value to save
* @param <T> value type
* @return true if write successful or data source is empty; false if error occurs
*/
private <T> boolean writeToDataSource(WritableDataSource<T> dataSource, T value) {
if (dataSource != null) {
try {
dataSource.write(value);
} catch (Exception e) {
RecordLog.warn("Write data source failed", e);
return false;
}
}
return true;
}
private static final String WRITE_DS_FAILURE_MSG = "partial success (write data source failed)";
private static final String FLOW_RULE_TYPE = "flow";
private static final String DEGRADE_RULE_TYPE = "degrade";
private static final String SYSTEM_RULE_TYPE = "system";
private static final String AUTHORITY_RULE_TYPE = "authority";
}

View File

@@ -0,0 +1,34 @@
/*
* 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.command.handler;
import com.alibaba.csp.sentinel.Constants;
import com.alibaba.csp.sentinel.command.CommandHandler;
import com.alibaba.csp.sentinel.command.CommandRequest;
import com.alibaba.csp.sentinel.command.CommandResponse;
import com.alibaba.csp.sentinel.command.annotation.CommandMapping;
/**
* @author youji.zj
*/
@CommandMapping(name = "getSwitch", desc = "get sentinel switch status")
public class OnOffGetCommandHandler implements CommandHandler<String> {
@Override
public CommandResponse<String> handle(CommandRequest request) {
return CommandResponse.ofSuccess("Sentinel switch value: " + Constants.ON);
}
}

View File

@@ -0,0 +1,46 @@
/*
* 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.command.handler;
import com.alibaba.csp.sentinel.command.CommandHandler;
import com.alibaba.csp.sentinel.command.CommandRequest;
import com.alibaba.csp.sentinel.command.CommandResponse;
import com.alibaba.csp.sentinel.command.annotation.CommandMapping;
import com.alibaba.csp.sentinel.log.RecordLog;
import com.alibaba.csp.sentinel.Constants;
/**
* @author youji.zj
*/
@CommandMapping(name = "setSwitch", desc = "set sentinel switch, accept param: value={true|false}")
public class OnOffSetCommandHandler implements CommandHandler<String> {
@Override
public CommandResponse<String> handle(CommandRequest request) {
String value = request.getParam("value");
try {
Constants.ON = Boolean.valueOf(value);
} catch (Exception e) {
RecordLog.info("Bad value when setting global switch", e);
}
String info = "Sentinel set switch value: " + value;
RecordLog.info(info);
return CommandResponse.ofSuccess(info);
}
}

View File

@@ -0,0 +1,142 @@
/*
* 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.command.handler;
import java.util.ArrayList;
import java.util.List;
import com.alibaba.csp.sentinel.Constants;
import com.alibaba.csp.sentinel.command.CommandHandler;
import com.alibaba.csp.sentinel.command.CommandRequest;
import com.alibaba.csp.sentinel.command.CommandResponse;
import com.alibaba.csp.sentinel.command.annotation.CommandMapping;
import com.alibaba.csp.sentinel.config.SentinelConfig;
import com.alibaba.csp.sentinel.node.metric.MetricNode;
import com.alibaba.csp.sentinel.node.metric.MetricSearcher;
import com.alibaba.csp.sentinel.node.metric.MetricWriter;
import com.alibaba.csp.sentinel.slots.system.SystemRuleManager;
import com.alibaba.csp.sentinel.util.PidUtil;
import com.alibaba.csp.sentinel.util.StringUtil;
import com.alibaba.csp.sentinel.util.TimeUtil;
/**
* Retrieve and aggregate {@link MetricNode} metrics.
*
* @author leyou
* @author Eric Zhao
*/
@CommandMapping(name = "metric", desc = "get and aggregate metrics, accept param: "
+ "startTime={startTime}&endTime={endTime}&maxLines={maxLines}&identify={resourceName}")
public class SendMetricCommandHandler implements CommandHandler<String> {
private volatile MetricSearcher searcher;
private final Object lock = new Object();
@Override
public CommandResponse<String> handle(CommandRequest request) {
// Note: not thread-safe.
if (searcher == null) {
synchronized (lock) {
String appName = SentinelConfig.getAppName();
if (appName == null) {
appName = "";
}
if (searcher == null) {
searcher = new MetricSearcher(MetricWriter.METRIC_BASE_DIR,
MetricWriter.formMetricFileName(appName, PidUtil.getPid()));
}
}
}
String startTimeStr = request.getParam("startTime");
String endTimeStr = request.getParam("endTime");
String maxLinesStr = request.getParam("maxLines");
String identity = request.getParam("identity");
long startTime = -1;
int maxLines = 6000;
if (StringUtil.isNotBlank(startTimeStr)) {
startTime = Long.parseLong(startTimeStr);
} else {
return CommandResponse.ofSuccess("");
}
List<MetricNode> list;
try {
// Find by end time if set.
if (StringUtil.isNotBlank(endTimeStr)) {
long endTime = Long.parseLong(endTimeStr);
list = searcher.findByTimeAndResource(startTime, endTime, identity);
} else {
if (StringUtil.isNotBlank(maxLinesStr)) {
maxLines = Integer.parseInt(maxLinesStr);
}
maxLines = Math.min(maxLines, 12000);
list = searcher.find(startTime, maxLines);
}
} catch (Exception ex) {
return CommandResponse.ofFailure(new RuntimeException("Error when retrieving metrics", ex));
}
if (list == null) {
list = new ArrayList<>();
}
if (StringUtil.isBlank(identity)) {
addCpuUsageAndLoad(list);
}
StringBuilder sb = new StringBuilder();
for (MetricNode node : list) {
sb.append(node.toThinString()).append("\n");
}
return CommandResponse.ofSuccess(sb.toString());
}
/**
* add current cpu usage and load to the metric list.
*
* @param list metric list, should not be null
*/
private void addCpuUsageAndLoad(List<MetricNode> list) {
long time = TimeUtil.currentTimeMillis() / 1000 * 1000;
double load = SystemRuleManager.getCurrentSystemAvgLoad();
double usage = SystemRuleManager.getCurrentCpuUsage();
if (load > 0) {
MetricNode loadNode = toNode(load, time, Constants.SYSTEM_LOAD_RESOURCE_NAME);
list.add(loadNode);
}
if (usage > 0) {
MetricNode usageNode = toNode(usage, time, Constants.CPU_USAGE_RESOURCE_NAME);
list.add(usageNode);
}
}
/**
* transfer the value to a MetricNode, the value will multiply 10000 then truncate
* to long value, and as the {@link MetricNode#passQps}.
* <p>
* This is an eclectic scheme before we have a standard metric format.
* </p>
*
* @param value value to save.
* @param ts timestamp
* @param resource resource name.
* @return a MetricNode represents the value.
*/
private MetricNode toNode(double value, long ts, String resource) {
MetricNode node = new MetricNode();
node.setPassQps((long)(value * 10000));
node.setTimestamp(ts);
node.setResource(resource);
return node;
}
}

View File

@@ -0,0 +1,35 @@
/*
* 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.command.handler;
import com.alibaba.csp.sentinel.Constants;
import com.alibaba.csp.sentinel.command.CommandHandler;
import com.alibaba.csp.sentinel.command.CommandRequest;
import com.alibaba.csp.sentinel.command.CommandResponse;
import com.alibaba.csp.sentinel.command.annotation.CommandMapping;
/**
* @author jialiang.linjl
* @author Eric Zhao
*/
@CommandMapping(name = "version", desc = "get sentinel version")
public class VersionCommandHandler implements CommandHandler<String> {
@Override
public CommandResponse<String> handle(CommandRequest request) {
return CommandResponse.ofSuccess(Constants.SENTINEL_VERSION);
}
}

View File

@@ -0,0 +1,51 @@
/*
* 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.command.handler.cluster;
import com.alibaba.csp.sentinel.cluster.ClusterStateManager;
import com.alibaba.csp.sentinel.cluster.client.TokenClientProvider;
import com.alibaba.csp.sentinel.cluster.server.EmbeddedClusterTokenServerProvider;
import com.alibaba.csp.sentinel.command.CommandHandler;
import com.alibaba.csp.sentinel.command.CommandRequest;
import com.alibaba.csp.sentinel.command.CommandResponse;
import com.alibaba.csp.sentinel.command.annotation.CommandMapping;
import com.alibaba.fastjson.JSONObject;
/**
* @author Eric Zhao
* @since 1.4.0
*/
@CommandMapping(name = "getClusterMode", desc = "get cluster mode status")
public class FetchClusterModeCommandHandler implements CommandHandler<String> {
@Override
public CommandResponse<String> handle(CommandRequest request) {
JSONObject res = new JSONObject()
.fluentPut("mode", ClusterStateManager.getMode())
.fluentPut("lastModified", ClusterStateManager.getLastModified())
.fluentPut("clientAvailable", isClusterClientSpiAvailable())
.fluentPut("serverAvailable", isClusterServerSpiAvailable());
return CommandResponse.ofSuccess(res.toJSONString());
}
private boolean isClusterClientSpiAvailable() {
return TokenClientProvider.getClient() != null;
}
private boolean isClusterServerSpiAvailable() {
return EmbeddedClusterTokenServerProvider.getServer() != null;
}
}

View File

@@ -0,0 +1,58 @@
/*
* Copyright 1999-2018 Alibaba Group Holding Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.csp.sentinel.command.handler.cluster;
import com.alibaba.csp.sentinel.cluster.ClusterStateManager;
import com.alibaba.csp.sentinel.cluster.client.TokenClientProvider;
import com.alibaba.csp.sentinel.cluster.server.EmbeddedClusterTokenServerProvider;
import com.alibaba.csp.sentinel.command.CommandHandler;
import com.alibaba.csp.sentinel.command.CommandRequest;
import com.alibaba.csp.sentinel.command.CommandResponse;
import com.alibaba.csp.sentinel.command.annotation.CommandMapping;
import com.alibaba.csp.sentinel.log.RecordLog;
/**
* @author Eric Zhao
* @since 1.4.0
*/
@CommandMapping(name = "setClusterMode", desc = "set cluster mode, accept param: mode={0|1} 0:client mode 1:server mode")
public class ModifyClusterModeCommandHandler implements CommandHandler<String> {
@Override
public CommandResponse<String> handle(CommandRequest request) {
try {
int mode = Integer.valueOf(request.getParam("mode"));
if (mode == ClusterStateManager.CLUSTER_CLIENT && !TokenClientProvider.isClientSpiAvailable()) {
return CommandResponse.ofFailure(new IllegalStateException("token client mode not available: no SPI found"));
}
if (mode == ClusterStateManager.CLUSTER_SERVER && !isClusterServerSpiAvailable()) {
return CommandResponse.ofFailure(new IllegalStateException("token server mode not available: no SPI found"));
}
RecordLog.info("[ModifyClusterModeCommandHandler] Modifying cluster mode to: {}", mode);
ClusterStateManager.applyState(mode);
return CommandResponse.ofSuccess("success");
} catch (NumberFormatException ex) {
return CommandResponse.ofFailure(new IllegalArgumentException("invalid parameter"));
} catch (Exception ex) {
return CommandResponse.ofFailure(ex);
}
}
private boolean isClusterServerSpiAvailable() {
return EmbeddedClusterTokenServerProvider.isServerSpiAvailable();
}
}

View File

@@ -0,0 +1,238 @@
/*
* 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.command.vo;
import java.util.UUID;
import com.alibaba.csp.sentinel.node.ClusterNode;
import com.alibaba.csp.sentinel.node.DefaultNode;
import com.alibaba.csp.sentinel.slotchain.ResourceWrapper;
/**
* This class is view object of {@link DefaultNode} or {@link ClusterNode}.
*
* @author leyou
*/
public class NodeVo {
private String id;
private String parentId;
private String resource;
private Integer threadNum;
private Long passQps;
private Long blockQps;
private Long totalQps;
private Long averageRt;
private Long successQps;
private Long exceptionQps;
private Long oneMinutePass;
private Long oneMinuteBlock;
private Long oneMinuteException;
private Long oneMinuteTotal;
private Long timestamp;
/**
* {@link DefaultNode} holds statistics of every node in the invoke tree.
* We use parentId to hold the tree structure.
*
* @param node the DefaultNode to be presented.
* @param parentId random generated parent node id, may be a random UUID
* @return node view object.
*/
public static NodeVo fromDefaultNode(DefaultNode node, String parentId) {
if (node == null) {
return null;
}
NodeVo vo = new NodeVo();
vo.id = UUID.randomUUID().toString();
vo.parentId = parentId;
vo.resource = node.getId().getShowName();
vo.threadNum = node.curThreadNum();
vo.passQps = (long) node.passQps();
vo.blockQps = (long) node.blockQps();
vo.totalQps = (long) node.totalQps();
vo.averageRt = (long) node.avgRt();
vo.successQps = (long) node.successQps();
vo.exceptionQps = (long) node.exceptionQps();
vo.oneMinuteException = node.totalException();
vo.oneMinutePass = node.totalRequest() - node.blockRequest();
vo.oneMinuteBlock = node.blockRequest();
vo.oneMinuteTotal = node.totalRequest();
vo.timestamp = System.currentTimeMillis();
return vo;
}
/**
* {@link ClusterNode} holds total statistics of the same resource name.
*
* @param name resource name.
* @param node the ClusterNode to be presented.
* @return node view object.
*/
public static NodeVo fromClusterNode(ResourceWrapper name, ClusterNode node) {
return fromClusterNode(name.getShowName(), node);
}
/**
* {@link ClusterNode} holds total statistics of the same resource name.
*
* @param name resource name.
* @param node the ClusterNode to be presented.
* @return node view object.
*/
public static NodeVo fromClusterNode(String name, ClusterNode node) {
if (node == null) {
return null;
}
NodeVo vo = new NodeVo();
vo.resource = name;
vo.threadNum = node.curThreadNum();
vo.passQps = (long) node.passQps();
vo.blockQps = (long) node.blockQps();
vo.totalQps = (long) node.totalQps();
vo.averageRt = (long) node.avgRt();
vo.successQps = (long) node.successQps();
vo.exceptionQps = (long) node.exceptionQps();
vo.oneMinuteException = node.totalException();
vo.oneMinutePass = node.totalRequest() - node.blockRequest();
vo.oneMinuteBlock = node.blockRequest();
vo.oneMinuteTotal = node.totalRequest();
vo.timestamp = System.currentTimeMillis();
return vo;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getParentId() {
return parentId;
}
public void setParentId(String parentId) {
this.parentId = parentId;
}
public String getResource() {
return resource;
}
public void setResource(String resource) {
this.resource = resource;
}
public Integer getThreadNum() {
return threadNum;
}
public void setThreadNum(Integer threadNum) {
this.threadNum = threadNum;
}
public Long getPassQps() {
return passQps;
}
public void setPassQps(Long passQps) {
this.passQps = passQps;
}
public Long getBlockQps() {
return blockQps;
}
public void setBlockQps(Long blockQps) {
this.blockQps = blockQps;
}
public Long getTotalQps() {
return totalQps;
}
public void setTotalQps(Long totalQps) {
this.totalQps = totalQps;
}
public Long getAverageRt() {
return averageRt;
}
public void setAverageRt(Long averageRt) {
this.averageRt = averageRt;
}
public Long getSuccessQps() {
return successQps;
}
public void setSuccessQps(Long successQps) {
this.successQps = successQps;
}
public Long getExceptionQps() {
return exceptionQps;
}
public void setExceptionQps(Long exceptionQps) {
this.exceptionQps = exceptionQps;
}
public Long getOneMinuteException() {
return oneMinuteException;
}
public void setOneMinuteException(Long oneMinuteException) {
this.oneMinuteException = oneMinuteException;
}
public Long getOneMinutePass() {
return oneMinutePass;
}
public void setOneMinutePass(Long oneMinutePass) {
this.oneMinutePass = oneMinutePass;
}
public Long getOneMinuteBlock() {
return oneMinuteBlock;
}
public void setOneMinuteBlock(Long oneMinuteBlock) {
this.oneMinuteBlock = oneMinuteBlock;
}
public Long getOneMinuteTotal() {
return oneMinuteTotal;
}
public void setOneMinuteTotal(Long oneMinuteTotal) {
this.oneMinuteTotal = oneMinuteTotal;
}
public Long getTimestamp() {
return timestamp;
}
public void setTimestamp(Long timestamp) {
this.timestamp = timestamp;
}
}

View File

@@ -0,0 +1,55 @@
/*
* Copyright 1999-2019 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
*
* https://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.heartbeat;
import com.alibaba.csp.sentinel.log.RecordLog;
import com.alibaba.csp.sentinel.transport.HeartbeatSender;
import com.alibaba.csp.sentinel.spi.SpiLoader;
/**
* @author Eric Zhao
* @since 1.6.0
*/
public final class HeartbeatSenderProvider {
private static HeartbeatSender heartbeatSender = null;
static {
resolveInstance();
}
private static void resolveInstance() {
HeartbeatSender resolved = SpiLoader.of(HeartbeatSender.class).loadHighestPriorityInstance();
if (resolved == null) {
RecordLog.warn("[HeartbeatSenderProvider] WARN: No existing HeartbeatSender found");
} else {
heartbeatSender = resolved;
RecordLog.info("[HeartbeatSenderProvider] HeartbeatSender activated: {}", resolved.getClass()
.getCanonicalName());
}
}
/**
* Get resolved {@link HeartbeatSender} instance.
*
* @return resolved {@code HeartbeatSender} instance
*/
public static HeartbeatSender getHeartbeatSender() {
return heartbeatSender;
}
private HeartbeatSenderProvider() {}
}

View File

@@ -0,0 +1,45 @@
/*
* 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.transport;
/**
* @author Eric Zhao
*/
public interface CommandCenter {
/**
* Prepare and init for the command center (e.g. register commands).
* This will be executed before starting.
*
* @throws Exception if error occurs
*/
void beforeStart() throws Exception;
/**
* Start the command center in the background.
* This method should NOT block.
*
* @throws Exception if error occurs
*/
void start() throws Exception;
/**
* Stop the command center and do cleanup.
*
* @throws Exception if error occurs
*/
void stop() throws Exception;
}

View File

@@ -0,0 +1,44 @@
/*
* 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.transport;
/**
* The heartbeat sender which is responsible for sending heartbeat to remote dashboard
* periodically per {@code interval}.
*
* @author leyou
* @author Eric Zhao
*/
public interface HeartbeatSender {
/**
* Send heartbeat to Sentinel Dashboard. Each invocation of this method will send
* heartbeat once. Sentinel core is responsible for invoking this method
* at every {@link #intervalMs()} interval.
*
* @return whether heartbeat is successfully send.
* @throws Exception if error occurs
*/
boolean sendHeartbeat() throws Exception;
/**
* Default interval in milliseconds of the sender. It would take effect only when
* the heartbeat interval is not configured in Sentinel config property.
*
* @return default interval of the sender in milliseconds
*/
long intervalMs();
}

View File

@@ -0,0 +1,38 @@
/*
* 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.transport.client;
import com.alibaba.csp.sentinel.command.CommandRequest;
import com.alibaba.csp.sentinel.command.CommandResponse;
/**
* Basic interface for clients that sending commands.
*
* @author Eric Zhao
*/
public interface CommandClient {
/**
* Send a command to target destination.
*
* @param host target host
* @param port target port
* @param request command request
* @return the response from target command server
* @throws Exception when unexpected error occurs
*/
CommandResponse sendCommand(String host, int port, CommandRequest request) throws Exception;
}

View File

@@ -0,0 +1,186 @@
/*
* 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.transport.config;
import com.alibaba.csp.sentinel.config.SentinelConfig;
import com.alibaba.csp.sentinel.log.RecordLog;
import com.alibaba.csp.sentinel.util.HostNameUtil;
import com.alibaba.csp.sentinel.util.StringUtil;
import com.alibaba.csp.sentinel.transport.endpoint.Endpoint;
import com.alibaba.csp.sentinel.transport.endpoint.Protocol;
import java.util.ArrayList;
import java.util.List;
/**
* @author Carpenter Lee
* @author Jason Joo
* @author Leo Li
*/
public class TransportConfig {
public static final String CONSOLE_SERVER = "csp.sentinel.dashboard.server";
public static final String SERVER_PORT = "csp.sentinel.api.port";
public static final String HEARTBEAT_INTERVAL_MS = "csp.sentinel.heartbeat.interval.ms";
public static final String HEARTBEAT_CLIENT_IP = "csp.sentinel.heartbeat.client.ip";
public static final String HEARTBEAT_API_PATH = "csp.sentinel.heartbeat.api.path";
public static final String HEARTBEAT_DEFAULT_PATH = "/registry/machine";
private static int runtimePort = -1;
/**
* Get heartbeat interval in milliseconds.
*
* @return heartbeat interval in milliseconds if exists, or null if not configured or invalid config
*/
public static Long getHeartbeatIntervalMs() {
String interval = SentinelConfig.getConfig(HEARTBEAT_INTERVAL_MS);
try {
return interval == null ? null : Long.parseLong(interval);
} catch (Exception ex) {
RecordLog.warn("[TransportConfig] Failed to parse heartbeat interval: " + interval);
return null;
}
}
/**
* Get a list of Endpoint(protocol, ip/domain, port) indicating Sentinel Dashboard's address.<br>
* NOTE: only support <b>HTTP</b> and <b>HTTPS</b> protocol
*
* @return list of Endpoint(protocol, ip/domain, port). <br>
* <b>May not be null</b>. <br>
* An empty list returned when not configured.
*/
public static List<Endpoint> getConsoleServerList() {
String config = SentinelConfig.getConfig(CONSOLE_SERVER);
List<Endpoint> list = new ArrayList<Endpoint>();
if (StringUtil.isBlank(config)) {
return list;
}
int pos = -1;
int cur = 0;
while (true) {
pos = config.indexOf(',', cur);
if (cur < config.length() - 1 && pos < 0) {
// for single segment, pos move to the end
pos = config.length();
}
if (pos < 0) {
break;
}
if (pos <= cur) {
cur ++;
continue;
}
// parsing
String ipPortStr = config.substring(cur, pos);
cur = pos + 1;
if (StringUtil.isBlank(ipPortStr)) {
continue;
}
ipPortStr = ipPortStr.trim();
int port = 80;
Protocol protocol = Protocol.HTTP;
if (ipPortStr.startsWith("http://")) {
ipPortStr = ipPortStr.substring(7);
} else if (ipPortStr.startsWith("https://")) {
ipPortStr = ipPortStr.substring(8);
port = 443;
protocol = Protocol.HTTPS;
}
int index = ipPortStr.indexOf(":");
if (index == 0) {
// skip
continue;
}
String host = ipPortStr;
if (index >= 0) {
try {
port = Integer.parseInt(ipPortStr.substring(index + 1));
if (port <= 1 || port >= 65535) {
throw new RuntimeException("Port number [" + port + "] over range");
}
} catch (Exception e) {
RecordLog.warn("Parse port of dashboard server failed: " + ipPortStr, e);
// skip
continue;
}
host = ipPortStr.substring(0, index);
}
list.add(new Endpoint(protocol, host, port));
}
return list;
}
public static int getRuntimePort() {
return runtimePort;
}
/**
* Get Server port of this HTTP server.
*
* @return the port, maybe null if not configured.
*/
public static String getPort() {
if (runtimePort > 0) {
return String.valueOf(runtimePort);
}
return SentinelConfig.getConfig(SERVER_PORT, true);
}
/**
* Set real port this HTTP server uses.
*
* @param port real port.
*/
public static void setRuntimePort(int port) {
runtimePort = port;
}
/**
* Get heartbeat client local ip.
* If the client ip not configured,it will be the address of local host
*
* @return the local ip.
*/
public static String getHeartbeatClientIp() {
String ip = SentinelConfig.getConfig(HEARTBEAT_CLIENT_IP, true);
if (StringUtil.isBlank(ip)) {
ip = HostNameUtil.getIp();
}
return ip;
}
/**
* Get the heartbeat api path. If the machine registry path of the dashboard
* is modified, then the API path should also be consistent with the API path of the dashboard.
*
* @return the heartbeat api path
* @since 1.7.1
*/
public static String getHeartbeatApiPath() {
String apiPath = SentinelConfig.getConfig(HEARTBEAT_API_PATH);
if (StringUtil.isBlank(apiPath)) {
return HEARTBEAT_DEFAULT_PATH;
}
if (!apiPath.startsWith("/")) {
apiPath = "/" + apiPath;
}
return apiPath;
}
}

View File

@@ -0,0 +1,49 @@
package com.alibaba.csp.sentinel.transport.endpoint;
import java.net.InetSocketAddress;
/**
* @author Leo Li
*/
public class Endpoint {
private Protocol protocol;
private String host;
private int port;
public Endpoint(Protocol protocol, String host, int port) {
this.protocol = protocol;
this.host = host;
this.port = port;
}
public Protocol getProtocol() {
return protocol;
}
public void setProtocol(Protocol protocol) {
this.protocol = protocol;
}
public String getHost() {
return host;
}
public void setHost(String host) {
this.host = host;
}
public int getPort() {
return port;
}
public void setPort(int port) {
this.port = port;
}
@Override
public String toString() {
return "Endpoint{" + "protocol=" + protocol + ", host='" + host + ", port=" + port + '}';
}
}

View File

@@ -0,0 +1,14 @@
package com.alibaba.csp.sentinel.transport.endpoint;
/**
* @author Leo Li
* @author Yanming Zhou
*/
public enum Protocol {
HTTP,
HTTPS;
public String getProtocol() {
return name().toLowerCase();
}
}

View File

@@ -0,0 +1,44 @@
/*
* 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.transport.init;
import com.alibaba.csp.sentinel.command.CommandCenterProvider;
import com.alibaba.csp.sentinel.init.InitFunc;
import com.alibaba.csp.sentinel.init.InitOrder;
import com.alibaba.csp.sentinel.log.RecordLog;
import com.alibaba.csp.sentinel.transport.CommandCenter;
/**
* @author Eric Zhao
*/
@InitOrder(-1)
public class CommandCenterInitFunc implements InitFunc {
@Override
public void init() throws Exception {
CommandCenter commandCenter = CommandCenterProvider.getCommandCenter();
if (commandCenter == null) {
RecordLog.warn("[CommandCenterInitFunc] Cannot resolve CommandCenter");
return;
}
commandCenter.beforeStart();
commandCenter.start();
RecordLog.info("[CommandCenterInit] Starting command center: "
+ commandCenter.getClass().getCanonicalName());
}
}

View File

@@ -0,0 +1,100 @@
/*
* 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.transport.init;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.ThreadPoolExecutor.DiscardOldestPolicy;
import java.util.concurrent.TimeUnit;
import com.alibaba.csp.sentinel.concurrent.NamedThreadFactory;
import com.alibaba.csp.sentinel.config.SentinelConfig;
import com.alibaba.csp.sentinel.heartbeat.HeartbeatSenderProvider;
import com.alibaba.csp.sentinel.init.InitFunc;
import com.alibaba.csp.sentinel.init.InitOrder;
import com.alibaba.csp.sentinel.log.RecordLog;
import com.alibaba.csp.sentinel.transport.HeartbeatSender;
import com.alibaba.csp.sentinel.transport.config.TransportConfig;
/**
* Global init function for heartbeat sender.
*
* @author Eric Zhao
*/
@InitOrder(-1)
public class HeartbeatSenderInitFunc implements InitFunc {
private ScheduledExecutorService pool = null;
private void initSchedulerIfNeeded() {
if (pool == null) {
pool = new ScheduledThreadPoolExecutor(2,
new NamedThreadFactory("sentinel-heartbeat-send-task", true),
new DiscardOldestPolicy());
}
}
@Override
public void init() {
HeartbeatSender sender = HeartbeatSenderProvider.getHeartbeatSender();
if (sender == null) {
RecordLog.warn("[HeartbeatSenderInitFunc] WARN: No HeartbeatSender loaded");
return;
}
initSchedulerIfNeeded();
long interval = retrieveInterval(sender);
setIntervalIfNotExists(interval);
scheduleHeartbeatTask(sender, interval);
}
private boolean isValidHeartbeatInterval(Long interval) {
return interval != null && interval > 0;
}
private void setIntervalIfNotExists(long interval) {
SentinelConfig.setConfig(TransportConfig.HEARTBEAT_INTERVAL_MS, String.valueOf(interval));
}
long retrieveInterval(/*@NonNull*/ HeartbeatSender sender) {
Long intervalInConfig = TransportConfig.getHeartbeatIntervalMs();
if (isValidHeartbeatInterval(intervalInConfig)) {
RecordLog.info("[HeartbeatSenderInitFunc] Using heartbeat interval "
+ "in Sentinel config property: " + intervalInConfig);
return intervalInConfig;
} else {
long senderInterval = sender.intervalMs();
RecordLog.info("[HeartbeatSenderInit] Heartbeat interval not configured in "
+ "config property or invalid, using sender default: " + senderInterval);
return senderInterval;
}
}
private void scheduleHeartbeatTask(/*@NonNull*/ final HeartbeatSender sender, /*@Valid*/ long interval) {
pool.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
try {
sender.sendHeartbeat();
} catch (Throwable e) {
RecordLog.warn("[HeartbeatSender] Send heartbeat error", e);
}
}
}, 5000, interval, TimeUnit.MILLISECONDS);
RecordLog.info("[HeartbeatSenderInit] HeartbeatSender started: "
+ sender.getClass().getCanonicalName());
}
}

View File

@@ -0,0 +1,88 @@
/*
* 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.transport.log;
import com.alibaba.csp.sentinel.log.LoggerSpiProvider;
import com.alibaba.csp.sentinel.log.jul.JavaLoggingAdapter;
/**
* Logger for command center.
*
* @author Eric Zhao
*/
public class CommandCenterLog {
public static final String LOGGER_NAME = "sentinelCommandCenterLogger";
public static final String DEFAULT_LOG_FILENAME = "command-center.log";
private static com.alibaba.csp.sentinel.log.Logger logger = null;
static {
try {
// Load user-defined logger implementation first.
logger = LoggerSpiProvider.getLogger(LOGGER_NAME);
if (logger == null) {
// If no customized loggers are provided, we use the default logger based on JUL.
logger = new JavaLoggingAdapter(LOGGER_NAME, DEFAULT_LOG_FILENAME);
}
} catch (Throwable t) {
System.err.println("Error: failed to initialize Sentinel CommandCenterLog");
t.printStackTrace();
}
}
public static void info(String format, Object... arguments) {
logger.info(format, arguments);
}
public static void info(String msg, Throwable e) {
logger.info(msg, e);
}
public static void warn(String format, Object... arguments) {
logger.warn(format, arguments);
}
public static void warn(String msg, Throwable e) {
logger.warn(msg, e);
}
public static void trace(String format, Object... arguments) {
logger.trace(format, arguments);
}
public static void trace(String msg, Throwable e) {
logger.trace(msg, e);
}
public static void debug(String format, Object... arguments) {
logger.debug(format, arguments);
}
public static void debug(String msg, Throwable e) {
logger.debug(msg, e);
}
public static void error(String format, Object... arguments) {
logger.error(format, arguments);
}
public static void error(String msg, Throwable e) {
logger.error(msg, e);
}
private CommandCenterLog() {}
}

View File

@@ -0,0 +1,58 @@
package com.alibaba.csp.sentinel.transport.ssl;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import com.alibaba.csp.sentinel.log.RecordLog;
/**
* @author Leo Li
*/
public class SslFactory {
private static class SslContextInstance {
private static final SSLContext SSL_CONTEXT = initSslContext();
}
private static SSLContext initSslContext() {
SSLContext sslContext = null;
try {
sslContext = SSLContext.getInstance("TLS");
X509TrustManager x509TrustManager = new X509TrustManager() {
public boolean isServerTrusted(X509Certificate[] certs) {
return true;
}
public boolean isClientTrusted(X509Certificate[] certs) {
return true;
}
@Override
public void checkServerTrusted(X509Certificate[] certs, String authType) throws CertificateException {
}
@Override
public void checkClientTrusted(X509Certificate[] certs, String authType) throws CertificateException {
}
@Override
public X509Certificate[] getAcceptedIssuers() {
return null;
}
};
sslContext.init(null, new TrustManager[] { x509TrustManager }, null);
} catch (Exception e) {
RecordLog.error("get ssl socket factory error", e);
}
return sslContext;
}
public static SSLContext getSslConnectionSocketFactory() {
return SslContextInstance.SSL_CONTEXT;
}
}

View File

@@ -0,0 +1,37 @@
/*
* 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.transport.util;
import com.alibaba.csp.sentinel.command.CommandRequest;
/**
* Util class for HTTP command center.
*
* @author Eric Zhao
*/
public final class HttpCommandUtils {
public static final String REQUEST_TARGET = "command-target";
public static String getTarget(CommandRequest request) {
if (request == null) {
throw new IllegalArgumentException("Request cannot be null");
}
return request.getMetadata().get(REQUEST_TARGET);
}
private HttpCommandUtils() {}
}

View File

@@ -0,0 +1,56 @@
package com.alibaba.csp.sentinel.transport.util;
import java.util.List;
import com.alibaba.csp.sentinel.datasource.WritableDataSource;
import com.alibaba.csp.sentinel.slots.block.authority.AuthorityRule;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRule;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;
import com.alibaba.csp.sentinel.slots.system.SystemRule;
/**
* Writable data source registry for modifying rules via HTTP API.
*
* @author Eric Zhao
*/
public final class WritableDataSourceRegistry {
private static WritableDataSource<List<FlowRule>> flowDataSource = null;
private static WritableDataSource<List<AuthorityRule>> authorityDataSource = null;
private static WritableDataSource<List<DegradeRule>> degradeDataSource = null;
private static WritableDataSource<List<SystemRule>> systemSource = null;
public static synchronized void registerFlowDataSource(WritableDataSource<List<FlowRule>> datasource) {
flowDataSource = datasource;
}
public static synchronized void registerAuthorityDataSource(WritableDataSource<List<AuthorityRule>> dataSource) {
authorityDataSource = dataSource;
}
public static synchronized void registerDegradeDataSource(WritableDataSource<List<DegradeRule>> dataSource) {
degradeDataSource = dataSource;
}
public static synchronized void registerSystemDataSource(WritableDataSource<List<SystemRule>> dataSource) {
systemSource = dataSource;
}
public static WritableDataSource<List<FlowRule>> getFlowDataSource() {
return flowDataSource;
}
public static WritableDataSource<List<AuthorityRule>> getAuthorityDataSource() {
return authorityDataSource;
}
public static WritableDataSource<List<DegradeRule>> getDegradeDataSource() {
return degradeDataSource;
}
public static WritableDataSource<List<SystemRule>> getSystemSource() {
return systemSource;
}
private WritableDataSourceRegistry() {}
}

View File

@@ -0,0 +1,17 @@
com.alibaba.csp.sentinel.command.handler.BasicInfoCommandHandler
com.alibaba.csp.sentinel.command.handler.FetchActiveRuleCommandHandler
com.alibaba.csp.sentinel.command.handler.FetchClusterNodeByIdCommandHandler
com.alibaba.csp.sentinel.command.handler.FetchClusterNodeHumanCommandHandler
com.alibaba.csp.sentinel.command.handler.FetchJsonTreeCommandHandler
com.alibaba.csp.sentinel.command.handler.FetchOriginCommandHandler
com.alibaba.csp.sentinel.command.handler.FetchSimpleClusterNodeCommandHandler
com.alibaba.csp.sentinel.command.handler.FetchSystemStatusCommandHandler
com.alibaba.csp.sentinel.command.handler.FetchTreeCommandHandler
com.alibaba.csp.sentinel.command.handler.ModifyRulesCommandHandler
com.alibaba.csp.sentinel.command.handler.OnOffGetCommandHandler
com.alibaba.csp.sentinel.command.handler.OnOffSetCommandHandler
com.alibaba.csp.sentinel.command.handler.SendMetricCommandHandler
com.alibaba.csp.sentinel.command.handler.VersionCommandHandler
com.alibaba.csp.sentinel.command.handler.cluster.FetchClusterModeCommandHandler
com.alibaba.csp.sentinel.command.handler.cluster.ModifyClusterModeCommandHandler
com.alibaba.csp.sentinel.command.handler.ApiCommandHandler

View File

@@ -0,0 +1,2 @@
com.alibaba.csp.sentinel.transport.init.CommandCenterInitFunc
com.alibaba.csp.sentinel.transport.init.HeartbeatSenderInitFunc

View File

@@ -0,0 +1,180 @@
/*
* 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.transport.config;
import com.alibaba.csp.sentinel.config.SentinelConfig;
import com.alibaba.csp.sentinel.util.StringUtil;
import com.alibaba.csp.sentinel.transport.endpoint.Endpoint;
import com.alibaba.csp.sentinel.transport.endpoint.Protocol;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import static org.junit.Assert.*;
import java.util.List;
public class TransportConfigTest {
@Before
public void setUp() throws Exception {
SentinelConfig.removeConfig(TransportConfig.HEARTBEAT_INTERVAL_MS);
SentinelConfig.removeConfig(TransportConfig.HEARTBEAT_CLIENT_IP);
}
@After
public void tearDown() throws Exception {
SentinelConfig.removeConfig(TransportConfig.HEARTBEAT_INTERVAL_MS);
SentinelConfig.removeConfig(TransportConfig.HEARTBEAT_CLIENT_IP);
}
@Test
public void testGetHeartbeatInterval() {
long interval = 20000;
assertNull(TransportConfig.getHeartbeatIntervalMs());
// Set valid interval.
SentinelConfig.setConfig(TransportConfig.HEARTBEAT_INTERVAL_MS, String.valueOf(interval));
assertEquals(new Long(interval), TransportConfig.getHeartbeatIntervalMs());
// Set invalid interval.
SentinelConfig.setConfig(TransportConfig.HEARTBEAT_INTERVAL_MS, "Sentinel");
assertNull(TransportConfig.getHeartbeatIntervalMs());
}
@Test
public void testGetHeartbeatClientIp() {
String clientIp = "10.10.10.10";
SentinelConfig.setConfig(TransportConfig.HEARTBEAT_CLIENT_IP, clientIp);
// Set heartbeat client ip to system property.
String ip = TransportConfig.getHeartbeatClientIp();
assertNotNull(ip);
assertEquals(clientIp, ip);
// Set no heartbeat client ip.
SentinelConfig.setConfig(TransportConfig.HEARTBEAT_CLIENT_IP, "");
assertTrue(StringUtil.isNotEmpty(TransportConfig.getHeartbeatClientIp()));
}
@Test
public void testGetHeartbeatApiPath() {
// use default heartbeat api path
assertTrue(StringUtil.isNotEmpty(TransportConfig.getHeartbeatApiPath()));
assertEquals(TransportConfig.HEARTBEAT_DEFAULT_PATH, TransportConfig.getHeartbeatApiPath());
// config heartbeat api path
SentinelConfig.setConfig(TransportConfig.HEARTBEAT_API_PATH, "/demo");
assertTrue(StringUtil.isNotEmpty(TransportConfig.getHeartbeatApiPath()));
assertEquals("/demo", TransportConfig.getHeartbeatApiPath());
SentinelConfig.setConfig(TransportConfig.HEARTBEAT_API_PATH, "demo/registry");
assertEquals("/demo/registry", TransportConfig.getHeartbeatApiPath());
SentinelConfig.removeConfig(TransportConfig.HEARTBEAT_API_PATH);
assertEquals(TransportConfig.HEARTBEAT_DEFAULT_PATH, TransportConfig.getHeartbeatApiPath());
}
@Test
public void testGetConsoleServerList() {
// empty
SentinelConfig.setConfig(TransportConfig.CONSOLE_SERVER, "");
List<Endpoint> list = TransportConfig.getConsoleServerList();
assertNotNull(list);
assertEquals(0, list.size());
// single ip
SentinelConfig.setConfig(TransportConfig.CONSOLE_SERVER, "112.13.223.3");
list = TransportConfig.getConsoleServerList();
assertNotNull(list);
assertEquals(1, list.size());
assertEquals("112.13.223.3", list.get(0).getHost());
assertEquals(80, list.get(0).getPort());
// single domain
SentinelConfig.setConfig(TransportConfig.CONSOLE_SERVER, "www.dashboard.org");
list = TransportConfig.getConsoleServerList();
assertNotNull(list);
assertEquals(1, list.size());
assertEquals("www.dashboard.org", list.get(0).getHost());
assertEquals(80, list.get(0).getPort());
// single ip including port
SentinelConfig.setConfig(TransportConfig.CONSOLE_SERVER, "www.dashboard.org:81");
list = TransportConfig.getConsoleServerList();
assertNotNull(list);
assertEquals(1, list.size());
assertEquals("www.dashboard.org", list.get(0).getHost());
assertEquals(81, list.get(0).getPort());
// mixed
SentinelConfig.setConfig(TransportConfig.CONSOLE_SERVER, "www.dashboard.org:81,112.13.223.3,112.13.223.4:8080,www.dashboard.org");
list = TransportConfig.getConsoleServerList();
assertNotNull(list);
assertEquals(4, list.size());
assertEquals("www.dashboard.org", list.get(0).getHost());
assertEquals(81, list.get(0).getPort());
assertEquals("112.13.223.3", list.get(1).getHost());
assertEquals(80, list.get(1).getPort());
assertEquals("112.13.223.4", list.get(2).getHost());
assertEquals(8080, list.get(2).getPort());
assertEquals("www.dashboard.org", list.get(3).getHost());
assertEquals(80, list.get(3).getPort());
// malformed
SentinelConfig.setConfig(TransportConfig.CONSOLE_SERVER, "www.dashboard.org:0");
list = TransportConfig.getConsoleServerList();
assertNotNull(list);
assertEquals(0, list.size());
SentinelConfig.setConfig(TransportConfig.CONSOLE_SERVER, "www.dashboard.org:-1");
list = TransportConfig.getConsoleServerList();
assertNotNull(list);
assertEquals(0, list.size());
SentinelConfig.setConfig(TransportConfig.CONSOLE_SERVER, ":80");
list = TransportConfig.getConsoleServerList();
assertNotNull(list);
assertEquals(0, list.size());
SentinelConfig.setConfig(TransportConfig.CONSOLE_SERVER, "www.dashboard.org:");
list = TransportConfig.getConsoleServerList();
assertNotNull(list);
assertEquals(0, list.size());
SentinelConfig.setConfig(TransportConfig.CONSOLE_SERVER, "www.dashboard.org:80000");
list = TransportConfig.getConsoleServerList();
assertNotNull(list);
assertEquals(0, list.size());
SentinelConfig.setConfig(TransportConfig.CONSOLE_SERVER, "www.dashboard.org:80000,www.dashboard.org:81,:80");
list = TransportConfig.getConsoleServerList();
assertNotNull(list);
assertEquals(1, list.size());
assertEquals("www.dashboard.org", list.get(0).getHost());
assertEquals(81, list.get(0).getPort());
SentinelConfig.setConfig(TransportConfig.CONSOLE_SERVER, "https://www.dashboard.org,http://www.dashboard.org:8080,www.dashboard.org,www.dashboard.org:8080");
list = TransportConfig.getConsoleServerList();
assertNotNull(list);
assertEquals(4, list.size());
assertEquals(Protocol.HTTPS, list.get(0).getProtocol());
assertEquals(Protocol.HTTP, list.get(1).getProtocol());
assertEquals(Protocol.HTTP, list.get(2).getProtocol());
assertEquals(Protocol.HTTP, list.get(3).getProtocol());
assertEquals(443, list.get(0).getPort());
assertEquals(80, list.get(2).getPort());
}
}

View File

@@ -0,0 +1,63 @@
/*
* Copyright 1999-2018 Alibaba Group Holding Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.csp.sentinel.transport.init;
import com.alibaba.csp.sentinel.config.SentinelConfig;
import com.alibaba.csp.sentinel.transport.HeartbeatSender;
import com.alibaba.csp.sentinel.transport.config.TransportConfig;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import static org.junit.Assert.*;
import static org.mockito.Mockito.*;
/**
* @author Eric Zhao
*/
public class HeartbeatSenderInitFuncTest {
@Before
public void setUp() throws Exception {
SentinelConfig.removeConfig(TransportConfig.HEARTBEAT_INTERVAL_MS);
}
@After
public void tearDown() throws Exception {
SentinelConfig.removeConfig(TransportConfig.HEARTBEAT_INTERVAL_MS);
}
@Test
public void testRetrieveInterval() {
HeartbeatSender sender = mock(HeartbeatSender.class);
long senderInterval = 5666;
long configInterval = 6777;
when(sender.intervalMs()).thenReturn(senderInterval);
HeartbeatSenderInitFunc func = new HeartbeatSenderInitFunc();
assertEquals(senderInterval, func.retrieveInterval(sender));
// Invalid interval.
SentinelConfig.setConfig(TransportConfig.HEARTBEAT_INTERVAL_MS, "-1");
assertEquals(senderInterval, func.retrieveInterval(sender));
SentinelConfig.setConfig(TransportConfig.HEARTBEAT_INTERVAL_MS, String.valueOf(configInterval));
assertEquals(configInterval, func.retrieveInterval(sender));
}
}