Refactor connection limit module to plugin (#9653)

* 限流插件提交

* 限流插件提交

* 限流插件提交

* 限流插件重构提交

* connection control test case submit

* tps control test case submit

* tps control test case submit

* tps control test case submit

* exact model has higher priority

* exact model has higher priority

* tpsreporter

* tpsreporter

* tpsreporter

* check style

* log optimize

* interceptor

* checkstyle

* tps check

* configController query http

* rename component

* 优化参数命名

* 优化参数命名

* 优化参数命名

* 优化参数命名

* optimize connection manager

* optimize connection manager

* optimize connection manager

* optimize connection manager

* interceptor to mse

* Monitor Model

* Monitor Model

* revert opensource tps ability

* revert opensource tps ability

* revert opensource tps ability

* revert opensource tps ability

* revert opensource tps ability

* revert opensource tps ability

* opensource submit

* opensource submit

* check style

* check style

* pmd ,rat

* update db params

* test case fix

* 简化开源反脆弱实现

* test case

* test  case fix

* 修复单测

* 修复单测

* 修复单测
This commit is contained in:
nov.lzf 2022-12-06 16:15:51 +08:00 committed by GitHub
parent d30aeadb63
commit acc8ab46cf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
124 changed files with 4658 additions and 3069 deletions

View File

@ -628,7 +628,7 @@ public abstract class RpcClient implements Closeable {
public Response request(Request request, long timeoutMills) throws NacosException {
int retryTimes = 0;
Response response;
Exception exceptionThrow = null;
Throwable exceptionThrow = null;
long start = System.currentTimeMillis();
while (retryTimes < rpcClientConfig.retryTimes() && System.currentTimeMillis() < timeoutMills + start) {
boolean waitReconnect = false;
@ -661,7 +661,7 @@ public abstract class RpcClient implements Closeable {
lastActiveTimeStamp = System.currentTimeMillis();
return response;
} catch (Exception e) {
} catch (Throwable e) {
if (waitReconnect) {
try {
// wait client to reconnect.
@ -701,8 +701,8 @@ public abstract class RpcClient implements Closeable {
*/
public void asyncRequest(Request request, RequestCallBack callback) throws NacosException {
int retryTimes = 0;
Exception exceptionToThrow = null;
Throwable exceptionToThrow = null;
long start = System.currentTimeMillis();
while (retryTimes < rpcClientConfig.retryTimes() && System.currentTimeMillis() < start + callback
.getTimeout()) {
@ -714,7 +714,7 @@ public abstract class RpcClient implements Closeable {
}
this.currentConnection.asyncRequest(request, callback);
return;
} catch (Exception e) {
} catch (Throwable e) {
if (waitReconnect) {
try {
// wait client to reconnect.

View File

@ -117,6 +117,11 @@
<groupId>org.yaml</groupId>
<artifactId>snakeyaml</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.nacos</groupId>
<artifactId>nacos-contrl-plugin</artifactId>
<version>${revision}</version>
</dependency>
<dependency>
<groupId>com.alibaba.nacos</groupId>
<artifactId>nacos-datasource-plugin</artifactId>

View File

@ -55,6 +55,7 @@ import com.alibaba.nacos.config.server.utils.RequestUtil;
import com.alibaba.nacos.config.server.utils.TimeUtils;
import com.alibaba.nacos.config.server.utils.YamlParserUtil;
import com.alibaba.nacos.config.server.utils.ZipUtils;
import com.alibaba.nacos.core.control.TpsControl;
import com.alibaba.nacos.plugin.auth.constant.ActionTypes;
import com.alibaba.nacos.plugin.auth.constant.SignType;
import com.alibaba.nacos.plugin.encryption.handler.EncryptionHandler;
@ -133,13 +134,14 @@ public class ConfigController {
/**
* Adds or updates non-aggregated data.
* <p>
* request and response will be used in aspect, see
* {@link com.alibaba.nacos.config.server.aspect.CapacityManagementAspect} and
* {@link com.alibaba.nacos.config.server.aspect.RequestLogAspect}.
* request and response will be used in aspect, see {@link com.alibaba.nacos.config.server.aspect.CapacityManagementAspect}
* and {@link com.alibaba.nacos.config.server.aspect.RequestLogAspect}.
* </p>
*
* @throws NacosException NacosException.
*/
@PostMapping
@TpsControl(pointName = "ConfigPublish")
@Secured(action = ActionTypes.WRITE, signType = SignType.CONFIG)
public Boolean publishConfig(HttpServletRequest request, HttpServletResponse response,
@RequestParam(value = "dataId") String dataId,
@ -204,6 +206,7 @@ public class ConfigController {
* @throws NacosException NacosException.
*/
@GetMapping
@TpsControl(pointName = "ConfigQuery")
@Secured(action = ActionTypes.READ, signType = SignType.CONFIG)
public void getConfig(HttpServletRequest request, HttpServletResponse response,
@RequestParam("dataId") String dataId, @RequestParam("group") String group,
@ -237,7 +240,7 @@ public class ConfigController {
// check params
ParamUtils.checkParam(dataId, group, "datumId", "content");
ConfigAllInfo configAllInfo = configInfoPersistService.findConfigAllInfo(dataId, group, tenant);
// decrypted
if (Objects.nonNull(configAllInfo)) {
String encryptedDataKey = configAllInfo.getEncryptedDataKey();
@ -252,9 +255,8 @@ public class ConfigController {
* Synchronously delete all pre-aggregation data under a dataId.
*
* <p>
* request and response will be used in aspect, see
* {@link com.alibaba.nacos.config.server.aspect.CapacityManagementAspect} and
* {@link com.alibaba.nacos.config.server.aspect.RequestLogAspect}.
* request and response will be used in aspect, see {@link com.alibaba.nacos.config.server.aspect.CapacityManagementAspect}
* and {@link com.alibaba.nacos.config.server.aspect.RequestLogAspect}.
* </p>
*
* @throws NacosException NacosException.
@ -298,10 +300,10 @@ public class ConfigController {
ConfigChangePublisher.notifyConfigChange(
new ConfigDataChangeEvent(false, configInfo.getDataId(), configInfo.getGroup(),
configInfo.getTenant(), time.getTime()));
ConfigTraceService.logPersistenceEvent(configInfo.getDataId(), configInfo.getGroup(),
configInfo.getTenant(), null, time.getTime(), clientIp, ConfigTraceService.PERSISTENCE_EVENT_REMOVE,
null);
ConfigTraceService
.logPersistenceEvent(configInfo.getDataId(), configInfo.getGroup(), configInfo.getTenant(), null,
time.getTime(), clientIp, ConfigTraceService.PERSISTENCE_EVENT_REMOVE, null);
}
return RestResultUtils.success(true);
}
@ -434,8 +436,8 @@ public class ConfigController {
LOGGER.error("remove beta data error", e);
return RestResultUtils.failed(500, false, "remove beta data error");
}
ConfigChangePublisher.notifyConfigChange(
new ConfigDataChangeEvent(true, dataId, group, tenant, System.currentTimeMillis()));
ConfigChangePublisher
.notifyConfigChange(new ConfigDataChangeEvent(true, dataId, group, tenant, System.currentTimeMillis()));
return RestResultUtils.success("stop beta ok", true);
}
@ -454,7 +456,7 @@ public class ConfigController {
@RequestParam(value = "tenant", required = false, defaultValue = StringUtils.EMPTY) String tenant) {
try {
ConfigInfo4Beta ci = configInfoBetaPersistService.findConfigInfo4Beta(dataId, group, tenant);
if (Objects.nonNull(ci)) {
String encryptedDataKey = ci.getEncryptedDataKey();
Pair<String, String> pair = EncryptionHandler.decryptHandler(dataId, encryptedDataKey, ci.getContent());
@ -497,15 +499,15 @@ public class ConfigController {
}
String metaDataId = ci.getDataId();
if (metaDataId.contains(".")) {
metaDataId = metaDataId.substring(0, metaDataId.lastIndexOf(".")) + "~" + metaDataId.substring(
metaDataId.lastIndexOf(".") + 1);
metaDataId = metaDataId.substring(0, metaDataId.lastIndexOf(".")) + "~" + metaDataId
.substring(metaDataId.lastIndexOf(".") + 1);
}
metaData.append(ci.getGroup()).append('.').append(metaDataId).append(".app=")
// Fixed use of "\r\n" here
.append(ci.getAppName()).append("\r\n");
}
Pair<String, String> pair = EncryptionHandler.decryptHandler(ci.getDataId(), ci.getEncryptedDataKey(),
ci.getContent());
Pair<String, String> pair = EncryptionHandler
.decryptHandler(ci.getDataId(), ci.getEncryptedDataKey(), ci.getContent());
String itemName = ci.getGroup() + Constants.CONFIG_EXPORT_ITEM_FILE_SEPARATOR + ci.getDataId();
zipItemList.add(new ZipUtils.ZipItem(itemName, pair.getSecond()));
}
@ -551,15 +553,15 @@ public class ConfigController {
configMetadataItem.setGroup(ci.getGroup());
configMetadataItem.setType(ci.getType());
configMetadataItems.add(configMetadataItem);
Pair<String, String> pair = EncryptionHandler.decryptHandler(ci.getDataId(), ci.getEncryptedDataKey(),
ci.getContent());
Pair<String, String> pair = EncryptionHandler
.decryptHandler(ci.getDataId(), ci.getEncryptedDataKey(), ci.getContent());
String itemName = ci.getGroup() + Constants.CONFIG_EXPORT_ITEM_FILE_SEPARATOR + ci.getDataId();
zipItemList.add(new ZipUtils.ZipItem(itemName, pair.getSecond()));
}
ConfigMetadata configMetadata = new ConfigMetadata();
configMetadata.setMetadata(configMetadataItems);
zipItemList.add(
new ZipUtils.ZipItem(Constants.CONFIG_EXPORT_METADATA_NEW, YamlParserUtil.dumpObject(configMetadata)));
zipItemList.add(new ZipUtils.ZipItem(Constants.CONFIG_EXPORT_METADATA_NEW,
YamlParserUtil.dumpObject(configMetadata)));
HttpHeaders headers = new HttpHeaders();
String fileName =
EXPORT_CONFIG_FILE_NAME + DateFormatUtils.format(new Date(), EXPORT_CONFIG_FILE_NAME_DATE_FORMAT)
@ -625,15 +627,16 @@ public class ConfigController {
final String srcIp = RequestUtil.getRemoteIp(request);
String requestIpApp = RequestUtil.getAppName(request);
final Timestamp time = TimeUtils.getCurrentTime();
Map<String, Object> saveResult = configInfoPersistService.batchInsertOrUpdate(configInfoList, srcUser, srcIp, null, time,
false, policy);
Map<String, Object> saveResult = configInfoPersistService
.batchInsertOrUpdate(configInfoList, srcUser, srcIp, null, time, false, policy);
for (ConfigInfo configInfo : configInfoList) {
ConfigChangePublisher.notifyConfigChange(
new ConfigDataChangeEvent(false, configInfo.getDataId(), configInfo.getGroup(),
configInfo.getTenant(), time.getTime()));
ConfigTraceService.logPersistenceEvent(configInfo.getDataId(), configInfo.getGroup(),
configInfo.getTenant(), requestIpApp, time.getTime(), InetUtils.getSelfIP(),
ConfigTraceService.PERSISTENCE_EVENT_PUB, configInfo.getContent());
ConfigTraceService
.logPersistenceEvent(configInfo.getDataId(), configInfo.getGroup(), configInfo.getTenant(),
requestIpApp, time.getTime(), InetUtils.getSelfIP(),
ConfigTraceService.PERSISTENCE_EVENT_PUB, configInfo.getContent());
}
// unrecognizedCount
if (!unrecognizedList.isEmpty()) {
@ -686,11 +689,11 @@ public class ConfigController {
String dataId = groupAdnDataId[1];
String tempDataId = dataId;
if (tempDataId.contains(".")) {
tempDataId = tempDataId.substring(0, tempDataId.lastIndexOf(".")) + "~" + tempDataId.substring(
tempDataId.lastIndexOf(".") + 1);
tempDataId = tempDataId.substring(0, tempDataId.lastIndexOf(".")) + "~" + tempDataId
.substring(tempDataId.lastIndexOf(".") + 1);
}
final String metaDataId = group + "." + tempDataId + ".app";
//encrypted
String content = item.getItemData();
Pair<String, String> pair = EncryptionHandler.encryptHandler(dataId, content);
@ -863,22 +866,24 @@ public class ConfigController {
ci4save.setAppName(ci.getAppName());
}
ci4save.setDesc(ci.getDesc());
ci4save.setEncryptedDataKey(ci.getEncryptedDataKey() == null ? StringUtils.EMPTY : ci.getEncryptedDataKey());
ci4save.setEncryptedDataKey(
ci.getEncryptedDataKey() == null ? StringUtils.EMPTY : ci.getEncryptedDataKey());
configInfoList4Clone.add(ci4save);
}
final String srcIp = RequestUtil.getRemoteIp(request);
String requestIpApp = RequestUtil.getAppName(request);
final Timestamp time = TimeUtils.getCurrentTime();
Map<String, Object> saveResult = configInfoPersistService.batchInsertOrUpdate(configInfoList4Clone, srcUser, srcIp, null,
time, false, policy);
Map<String, Object> saveResult = configInfoPersistService
.batchInsertOrUpdate(configInfoList4Clone, srcUser, srcIp, null, time, false, policy);
for (ConfigInfo configInfo : configInfoList4Clone) {
ConfigChangePublisher.notifyConfigChange(
new ConfigDataChangeEvent(false, configInfo.getDataId(), configInfo.getGroup(),
configInfo.getTenant(), time.getTime()));
ConfigTraceService.logPersistenceEvent(configInfo.getDataId(), configInfo.getGroup(),
configInfo.getTenant(), requestIpApp, time.getTime(), InetUtils.getSelfIP(),
ConfigTraceService.PERSISTENCE_EVENT_PUB, configInfo.getContent());
ConfigTraceService
.logPersistenceEvent(configInfo.getDataId(), configInfo.getGroup(), configInfo.getTenant(),
requestIpApp, time.getTime(), InetUtils.getSelfIP(),
ConfigTraceService.PERSISTENCE_EVENT_PUB, configInfo.getContent());
}
return RestResultUtils.success("Clone Completed Successfully", saveResult);
}

View File

@ -25,7 +25,7 @@ import com.alibaba.nacos.auth.annotation.Secured;
import com.alibaba.nacos.config.server.service.ConfigCacheService;
import com.alibaba.nacos.config.server.utils.GroupKey2;
import com.alibaba.nacos.core.remote.RequestHandler;
import com.alibaba.nacos.core.remote.control.TpsControl;
import com.alibaba.nacos.core.control.TpsControl;
import com.alibaba.nacos.core.utils.StringPool;
import com.alibaba.nacos.plugin.auth.constant.ActionTypes;
import com.alibaba.nacos.plugin.auth.constant.SignType;

View File

@ -22,7 +22,7 @@ import com.alibaba.nacos.api.exception.NacosException;
import com.alibaba.nacos.api.remote.request.RequestMeta;
import com.alibaba.nacos.config.server.service.dump.DumpService;
import com.alibaba.nacos.core.remote.RequestHandler;
import com.alibaba.nacos.core.remote.control.TpsControl;
import com.alibaba.nacos.core.control.TpsControl;
import org.springframework.stereotype.Component;
/**

View File

@ -1,61 +0,0 @@
/*
* Copyright 1999-2020 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.nacos.config.server.remote;
import com.alibaba.nacos.api.config.remote.request.ConfigPublishRequest;
import com.alibaba.nacos.config.server.utils.GroupKey;
import com.alibaba.nacos.core.remote.control.MonitorKey;
import com.alibaba.nacos.core.remote.control.MonitorKeyParser;
/**
* Parser to get group key of config query request.
*
* @author liuzunfei
* @version $Id: ConfigPublishGroupKeyParser.java, v 0.1 2021年01月20日 20:38 PM liuzunfei Exp $
*/
public class ConfigPublishGroupKeyParser extends MonitorKeyParser {
/**
* parse group.
*
* @param args parameters.
* @return MonitorKey.
*/
@Override
public MonitorKey parse(Object... args) {
if (args != null && args.length != 0 && args[0] instanceof ConfigPublishRequest) {
return new ConfigGroupKey(GroupKey.getKeyTenant(((ConfigPublishRequest) args[0]).getDataId(),
((ConfigPublishRequest) args[0]).getGroup(), ((ConfigPublishRequest) args[0]).getTenant()));
} else {
return null;
}
}
class ConfigGroupKey extends MonitorKey {
public ConfigGroupKey(String key) {
this.setKey(key);
}
@Override
public String getType() {
return "groupKey";
}
}
}

View File

@ -1,57 +0,0 @@
/*
* Copyright 1999-2020 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.nacos.config.server.remote;
import com.alibaba.nacos.api.config.remote.request.ConfigPublishRequest;
import com.alibaba.nacos.core.remote.control.MonitorKey;
import com.alibaba.nacos.core.remote.control.MonitorKeyParser;
/**
* parse to get group from config publish parser.
*
* @author liuzunfei
* @version $Id: ConfigPublishGroupParser.java, v 0.1 2021年01月20日 20:38 PM liuzunfei Exp $
*/
public class ConfigPublishGroupParser extends MonitorKeyParser {
/**
* parse group.
*
* @param args parameters.
* @return MonitorKey.
*/
@Override
public MonitorKey parse(Object... args) {
if (args != null && args.length != 0 && args[0] instanceof ConfigPublishRequest) {
return new ConfigGroupKey(((ConfigPublishRequest) args[0]).getGroup());
} else {
return null;
}
}
class ConfigGroupKey extends MonitorKey {
public ConfigGroupKey(String key) {
this.setKey(key);
}
@Override
public String getType() {
return "group";
}
}
}

View File

@ -35,7 +35,7 @@ import com.alibaba.nacos.config.server.service.trace.ConfigTraceService;
import com.alibaba.nacos.config.server.utils.ParamUtils;
import com.alibaba.nacos.config.server.utils.TimeUtils;
import com.alibaba.nacos.core.remote.RequestHandler;
import com.alibaba.nacos.core.remote.control.TpsControl;
import com.alibaba.nacos.core.control.TpsControl;
import com.alibaba.nacos.core.utils.Loggers;
import com.alibaba.nacos.plugin.auth.constant.ActionTypes;
import com.alibaba.nacos.plugin.auth.constant.SignType;
@ -70,8 +70,7 @@ public class ConfigPublishRequestHandler extends RequestHandler<ConfigPublishReq
}
@Override
@TpsControl(pointName = "ConfigPublish", parsers = {ConfigPublishGroupKeyParser.class,
ConfigPublishGroupParser.class})
@TpsControl(pointName = "ConfigPublish")
@Secured(action = ActionTypes.WRITE, signType = SignType.CONFIG)
public ConfigPublishResponse handle(ConfigPublishRequest request, RequestMeta meta) throws NacosException {

View File

@ -1,61 +0,0 @@
/*
* Copyright 1999-2020 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.nacos.config.server.remote;
import com.alibaba.nacos.api.config.remote.request.ConfigQueryRequest;
import com.alibaba.nacos.config.server.utils.GroupKey;
import com.alibaba.nacos.core.remote.control.MonitorKey;
import com.alibaba.nacos.core.remote.control.MonitorKeyParser;
/**
* ConfigQueryGroupKeyParser.
*
* @author liuzunfei
* @version $Id: ConfigQueryGroupKeyParser.java, v 0.1 2021年01月20日 20:38 PM liuzunfei Exp $
*/
public class ConfigQueryGroupKeyParser extends MonitorKeyParser {
/**
* parse group key.
*
* @param args parameters.
* @return MonitorKey.
*/
@Override
public MonitorKey parse(Object... args) {
if (args != null && args.length != 0 && args[0] instanceof ConfigQueryRequest) {
return new ConfigGroupKey(GroupKey.getKeyTenant(((ConfigQueryRequest) args[0]).getDataId(),
((ConfigQueryRequest) args[0]).getGroup(), ((ConfigQueryRequest) args[0]).getTenant()));
} else {
return null;
}
}
class ConfigGroupKey extends MonitorKey {
public ConfigGroupKey(String key) {
this.setKey(key);
}
@Override
public String getType() {
return "groupKey";
}
}
}

View File

@ -1,56 +0,0 @@
package com.alibaba.nacos.config.server.remote;
/*
* Copyright 1999-2020 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.
*/
import com.alibaba.nacos.api.config.remote.request.ConfigQueryRequest;
import com.alibaba.nacos.core.remote.control.MonitorKey;
import com.alibaba.nacos.core.remote.control.MonitorKeyParser;
/**
* parse to get group from config query parser.
*
* @author liuzunfei
* @version $Id: ConfigQueryGroupParser.java, v 0.1 2021年01月20日 20:38 PM liuzunfei Exp $
*/
public class ConfigQueryGroupParser extends MonitorKeyParser {
/**
* parse group.
*
* @param args parameters.
* @return MonitorKey.
*/
@Override
public MonitorKey parse(Object... args) {
if (args != null && args.length != 0 && args[0] instanceof ConfigQueryRequest) {
return new ConfigGroupKey(((ConfigQueryRequest) args[0]).getGroup());
} else {
return null;
}
}
class ConfigGroupKey extends MonitorKey {
public ConfigGroupKey(String key) {
this.setKey(key);
}
@Override
public String getType() {
return "group";
}
}
}

View File

@ -37,7 +37,7 @@ import com.alibaba.nacos.config.server.utils.LogUtil;
import com.alibaba.nacos.config.server.utils.PropertyUtil;
import com.alibaba.nacos.config.server.utils.TimeUtils;
import com.alibaba.nacos.core.remote.RequestHandler;
import com.alibaba.nacos.core.remote.control.TpsControl;
import com.alibaba.nacos.core.control.TpsControl;
import com.alibaba.nacos.plugin.auth.constant.ActionTypes;
import com.alibaba.nacos.plugin.auth.constant.SignType;
import org.apache.commons.io.FileUtils;
@ -79,7 +79,7 @@ public class ConfigQueryRequestHandler extends RequestHandler<ConfigQueryRequest
}
@Override
@TpsControl(pointName = "ConfigQuery", parsers = {ConfigQueryGroupKeyParser.class, ConfigQueryGroupParser.class})
@TpsControl(pointName = "ConfigQuery")
@Secured(action = ActionTypes.READ, signType = SignType.CONFIG)
public ConfigQueryResponse handle(ConfigQueryRequest request, RequestMeta meta) throws NacosException {

View File

@ -30,7 +30,7 @@ import com.alibaba.nacos.config.server.service.trace.ConfigTraceService;
import com.alibaba.nacos.config.server.utils.ParamUtils;
import com.alibaba.nacos.config.server.utils.TimeUtils;
import com.alibaba.nacos.core.remote.RequestHandler;
import com.alibaba.nacos.core.remote.control.TpsControl;
import com.alibaba.nacos.core.control.TpsControl;
import com.alibaba.nacos.core.utils.Loggers;
import com.alibaba.nacos.plugin.auth.constant.ActionTypes;
import com.alibaba.nacos.plugin.auth.constant.SignType;

View File

@ -1,118 +0,0 @@
/*
* Copyright 1999-2020 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.nacos.config.server.remote;
import com.alibaba.nacos.api.config.remote.request.ConfigQueryRequest;
import com.alibaba.nacos.api.config.remote.response.ConfigQueryResponse;
import com.alibaba.nacos.api.exception.NacosException;
import com.alibaba.nacos.api.remote.request.RequestMeta;
import com.alibaba.nacos.api.utils.NetUtils;
import com.alibaba.nacos.common.notify.Event;
import com.alibaba.nacos.common.notify.NotifyCenter;
import com.alibaba.nacos.common.notify.listener.Subscriber;
import com.alibaba.nacos.config.server.model.event.LocalDataChangeEvent;
import com.alibaba.nacos.config.server.utils.GroupKey;
import com.alibaba.nacos.core.remote.control.TpsControlRuleChangeEvent;
import com.alibaba.nacos.core.remote.event.ConnectionLimitRuleChangeEvent;
import com.alibaba.nacos.core.utils.Loggers;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
/**
* ConfigChangeNotifier.
*
* @author liuzunfei
* @version $Id: ConfigChangeNotifier.java, v 0.1 2020年07月20日 3:00 PM liuzunfei Exp $
*/
@Component(value = "internalConfigChangeNotifier")
public class InternalConfigChangeNotifier extends Subscriber<LocalDataChangeEvent> {
@Autowired
private ConfigQueryRequestHandler configQueryRequestHandler;
public InternalConfigChangeNotifier() {
NotifyCenter.registerToPublisher(ConnectionLimitRuleChangeEvent.class, 16384);
NotifyCenter.registerToPublisher(TpsControlRuleChangeEvent.class, 16384);
NotifyCenter.registerSubscriber(this);
}
private static final String DATA_ID_TPS_CONTROL_RULE = "nacos.internal.tps.control_rule_";
private static final String DATA_ID_CONNECTION_LIMIT_RULE = "nacos.internal.connection.limit.rule";
private static final String NACOS_GROUP = "nacos";
@Override
public void onEvent(LocalDataChangeEvent event) {
String groupKey = event.groupKey;
String dataId = GroupKey.parseKey(groupKey)[0];
String group = GroupKey.parseKey(groupKey)[1];
if (DATA_ID_CONNECTION_LIMIT_RULE.equals(dataId) && NACOS_GROUP.equals(group)) {
try {
String content = loadLocalConfigLikeClient(dataId, group);
NotifyCenter.publishEvent(new ConnectionLimitRuleChangeEvent(content));
} catch (NacosException e) {
Loggers.REMOTE.error("connection limit rule load fail.", e);
}
}
if (dataId.startsWith(DATA_ID_TPS_CONTROL_RULE) && NACOS_GROUP.equals(group)) {
try {
String pointName = dataId.replaceFirst(DATA_ID_TPS_CONTROL_RULE, "");
String content = loadLocalConfigLikeClient(dataId, group);
NotifyCenter.publishEvent(new TpsControlRuleChangeEvent(pointName, content));
} catch (NacosException e) {
Loggers.REMOTE.error("connection limit rule load fail.", e);
}
}
}
private String loadLocalConfigLikeClient(String dataId, String group) throws NacosException {
ConfigQueryRequest queryRequest = new ConfigQueryRequest();
queryRequest.setDataId(dataId);
queryRequest.setGroup(group);
RequestMeta meta = new RequestMeta();
meta.setClientIp(NetUtils.localIP());
ConfigQueryResponse handle = configQueryRequestHandler.handle(queryRequest, meta);
if (handle == null) {
throw new NacosException(NacosException.SERVER_ERROR, "load local config fail,response is null");
}
if (handle.isSuccess()) {
return handle.getContent();
} else if (handle.getErrorCode() == ConfigQueryResponse.CONFIG_NOT_FOUND) {
return null;
} else {
Loggers.REMOTE.error("connection limit rule load fail,errorCode={}", handle.getErrorCode());
throw new NacosException(NacosException.SERVER_ERROR,
"load local config fail,error code=" + handle.getErrorCode());
}
}
@Override
public Class<? extends Event> subscribeType() {
return LocalDataChangeEvent.class;
}
}

View File

@ -30,9 +30,10 @@ import com.alibaba.nacos.core.remote.Connection;
import com.alibaba.nacos.core.remote.ConnectionManager;
import com.alibaba.nacos.core.remote.ConnectionMeta;
import com.alibaba.nacos.core.remote.RpcPushService;
import com.alibaba.nacos.core.remote.control.TpsMonitorManager;
import com.alibaba.nacos.core.remote.control.TpsMonitorPoint;
import com.alibaba.nacos.core.utils.Loggers;
import com.alibaba.nacos.plugin.control.ControlManagerCenter;
import com.alibaba.nacos.plugin.control.tps.TpsControlManager;
import com.alibaba.nacos.plugin.control.tps.request.TpsCheckRequest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@ -56,8 +57,7 @@ public class RpcConfigChangeNotifier extends Subscriber<LocalDataChangeEvent> {
private static final String POINT_CONFIG_PUSH_FAIL = "CONFIG_PUSH_FAIL";
@Autowired
private TpsMonitorManager tpsMonitorManager;
TpsControlManager tpsControlManager = ControlManagerCenter.getInstance().getTpsControlManager();
public RpcConfigChangeNotifier() {
NotifyCenter.registerSubscriber(this);
@ -65,10 +65,9 @@ public class RpcConfigChangeNotifier extends Subscriber<LocalDataChangeEvent> {
@PostConstruct
private void registerTpsPoint() {
tpsMonitorManager.registerTpsControlPoint(new TpsMonitorPoint(POINT_CONFIG_PUSH));
tpsMonitorManager.registerTpsControlPoint(new TpsMonitorPoint(POINT_CONFIG_PUSH_SUCCESS));
tpsMonitorManager.registerTpsControlPoint(new TpsMonitorPoint(POINT_CONFIG_PUSH_FAIL));
tpsControlManager.registerTpsPoint(POINT_CONFIG_PUSH);
tpsControlManager.registerTpsPoint(POINT_CONFIG_PUSH_SUCCESS);
tpsControlManager.registerTpsPoint(POINT_CONFIG_PUSH_FAIL);
}
@ -171,18 +170,27 @@ public class RpcConfigChangeNotifier extends Subscriber<LocalDataChangeEvent> {
@Override
public void run() {
tryTimes++;
if (!tpsMonitorManager.applyTpsForClientIp(POINT_CONFIG_PUSH, connectionId, clientIp)) {
TpsCheckRequest tpsCheckRequest = new TpsCheckRequest();
tpsCheckRequest.setPointName(POINT_CONFIG_PUSH);
if (!tpsControlManager.check(tpsCheckRequest).isSuccess()) {
push(this);
} else {
rpcPushService.pushWithCallback(connectionId, notifyRequest, new AbstractPushCallBack(3000L) {
@Override
public void onSuccess() {
tpsMonitorManager.applyTpsForClientIp(POINT_CONFIG_PUSH_SUCCESS, connectionId, clientIp);
TpsCheckRequest tpsCheckRequest = new TpsCheckRequest();
tpsCheckRequest.setPointName(POINT_CONFIG_PUSH_SUCCESS);
tpsControlManager.check(tpsCheckRequest);
}
@Override
public void onFail(Throwable e) {
tpsMonitorManager.applyTpsForClientIp(POINT_CONFIG_PUSH_FAIL, connectionId, clientIp);
TpsCheckRequest tpsCheckRequest = new TpsCheckRequest();
tpsCheckRequest.setPointName(POINT_CONFIG_PUSH_FAIL);
tpsControlManager.check(tpsCheckRequest);
Loggers.REMOTE_PUSH.warn("Push fail", e);
push(RpcPushTask.this);
}
@ -197,10 +205,10 @@ public class RpcConfigChangeNotifier extends Subscriber<LocalDataChangeEvent> {
private void push(RpcPushTask retryTask) {
ConfigChangeNotifyRequest notifyRequest = retryTask.notifyRequest;
if (retryTask.isOverTimes()) {
Loggers.REMOTE_PUSH.warn(
"push callback retry fail over times .dataId={},group={},tenant={},clientId={},will unregister client.",
notifyRequest.getDataId(), notifyRequest.getGroup(), notifyRequest.getTenant(),
retryTask.connectionId);
Loggers.REMOTE_PUSH
.warn("push callback retry fail over times .dataId={},group={},tenant={},clientId={},will unregister client.",
notifyRequest.getDataId(), notifyRequest.getGroup(), notifyRequest.getTenant(),
retryTask.connectionId);
connectionManager.unregister(retryTask.connectionId);
} else if (connectionManager.getConnection(retryTask.connectionId) != null) {
// first time:delay 0s; second time:delay 2s; third time:delay 4s

View File

@ -0,0 +1,48 @@
/*
*
* Copyright 1999-2021 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.nacos.config.server.service;
import com.alibaba.nacos.plugin.control.connection.ConnectionMetricsCollector;
import com.alibaba.nacos.sys.utils.ApplicationUtils;
import java.util.stream.Collectors;
/**
* long polling connection metrics.
*
* @author shiyiyue
*/
public class LongPollingConnectionMetricsCollector implements ConnectionMetricsCollector {
@Override
public String getName() {
return "long_polling";
}
@Override
public int getTotalCount() {
return ApplicationUtils.getBean(LongPollingService.class).allSubs.size();
}
@Override
public int getCountForIp(String ip) {
return ApplicationUtils.getBean(LongPollingService.class).allSubs.stream()
.filter(a -> a.ip.equalsIgnoreCase(ip)).collect(Collectors.toList()).size();
}
}

View File

@ -21,6 +21,7 @@ import com.alibaba.nacos.common.notify.NotifyCenter;
import com.alibaba.nacos.common.notify.listener.Subscriber;
import com.alibaba.nacos.common.utils.CollectionUtils;
import com.alibaba.nacos.common.utils.ExceptionUtil;
import com.alibaba.nacos.common.utils.StringUtils;
import com.alibaba.nacos.config.server.model.SampleResult;
import com.alibaba.nacos.config.server.model.event.LocalDataChangeEvent;
import com.alibaba.nacos.config.server.monitor.MetricsMonitor;
@ -29,7 +30,9 @@ import com.alibaba.nacos.config.server.utils.GroupKey;
import com.alibaba.nacos.config.server.utils.LogUtil;
import com.alibaba.nacos.config.server.utils.MD5Util;
import com.alibaba.nacos.config.server.utils.RequestUtil;
import com.alibaba.nacos.common.utils.StringUtils;
import com.alibaba.nacos.plugin.control.ControlManagerCenter;
import com.alibaba.nacos.plugin.control.connection.request.ConnectionCheckRequest;
import com.alibaba.nacos.plugin.control.connection.response.ConnectionCheckResponse;
import org.springframework.stereotype.Service;
import javax.servlet.AsyncContext;
@ -240,8 +243,6 @@ public class LongPollingService {
String str = req.getHeader(LongPollingService.LONG_POLLING_HEADER);
String noHangUpFlag = req.getHeader(LongPollingService.LONG_POLLING_NO_HANG_UP_HEADER);
final String appName = req.getHeader(RequestUtil.CLIENT_APPNAME_HEADER);
final String tag = req.getHeader("Vipserver-Tag");
int delayTime = SwitchService.getSwitchInteger(SwitchService.FIXED_DELAY_TIME, 500);
// Add delay time for LoadBalance, and one response is returned 500 ms in advance to avoid client timeout.
@ -266,6 +267,11 @@ public class LongPollingService {
}
}
String ip = RequestUtil.getRemoteIp(req);
ConnectionCheckResponse connectionCheckResponse = checkLimit(req);
if (!connectionCheckResponse.isSuccess()) {
generate503Response(req, rsp, connectionCheckResponse.getMessage());
return;
}
// Must be called by http thread, or send response.
final AsyncContext asyncContext = req.startAsync();
@ -273,10 +279,21 @@ public class LongPollingService {
// AsyncContext.setTimeout() is incorrect, Control by oneself
asyncContext.setTimeout(0L);
String appName = req.getHeader(RequestUtil.CLIENT_APPNAME_HEADER);
String tag = req.getHeader("Vipserver-Tag");
ConfigExecutor.executeLongPolling(
new ClientLongPolling(asyncContext, clientMd5Map, ip, probeRequestSize, timeout, appName, tag));
}
private ConnectionCheckResponse checkLimit(HttpServletRequest httpServletRequest) {
String ip = RequestUtil.getRemoteIp(httpServletRequest);
String appName = httpServletRequest.getHeader(RequestUtil.CLIENT_APPNAME_HEADER);
ConnectionCheckRequest connectionCheckRequest = new ConnectionCheckRequest(ip, appName, "LongPolling");
ConnectionCheckResponse checkResponse = ControlManagerCenter.getInstance().getConnectionControlManager()
.check(connectionCheckRequest);
return checkResponse;
}
public static boolean isSupportLongPolling(HttpServletRequest req) {
return null != req.getHeader(LONG_POLLING_HEADER);
}
@ -523,6 +540,21 @@ public class LongPollingService {
}
}
void generate503Response(HttpServletRequest request, HttpServletResponse response, String message) {
try {
// Disable cache.
response.setHeader("Pragma", "no-cache");
response.setDateHeader("Expires", 0);
response.setHeader("Cache-Control", "no-cache,no-store");
response.setStatus(HttpServletResponse.SC_SERVICE_UNAVAILABLE);
response.getWriter().println(message);
} catch (Exception ex) {
PULL_LOG.error(ex.toString(), ex);
}
}
public Map<String, Long> getRetainIps() {
return retainIps;
}

View File

@ -56,7 +56,7 @@ public class ExternalDataSourceServiceImpl implements DataSourceService {
private static final int TRANSACTION_QUERY_TIMEOUT = 5;
private static final int DB_MASTER_SELECT_THRESHOLD = 1;
private static final String DB_LOAD_ERROR_MSG = "[db-load-error]load jdbc.properties error";
private List<HikariDataSource> dataSourceList = new ArrayList<>();
@ -106,17 +106,17 @@ public class ExternalDataSourceServiceImpl implements DataSourceService {
// Transaction timeout needs to be distinguished from ordinary operations.
tjt.setTimeout(TRANSACTION_QUERY_TIMEOUT);
dataSourceType = DatasourcePlatformUtil.getDatasourcePlatform(defaultDataSourceType);
if (PropertyUtil.isUseExternalDB()) {
try {
reload();
} catch (IOException e) {
FATAL_LOG.error("[ExternalDataSourceService] datasource reload error", e);
throw new RuntimeException(DB_LOAD_ERROR_MSG);
throw new RuntimeException(DB_LOAD_ERROR_MSG, e);
}
if (this.dataSourceList.size() > DB_MASTER_SELECT_THRESHOLD) {
ConfigExecutor.scheduleConfigTask(new SelectMasterTask(), 10, 10, TimeUnit.SECONDS);
}
@ -129,7 +129,7 @@ public class ExternalDataSourceServiceImpl implements DataSourceService {
try {
final List<JdbcTemplate> testJtListNew = new ArrayList<JdbcTemplate>();
final List<Boolean> isHealthListNew = new ArrayList<Boolean>();
List<HikariDataSource> dataSourceListNew = new ExternalDataSourceProperties()
.build(EnvUtil.getEnvironment(), (dataSource) -> {
JdbcTemplate jdbcTemplate = new JdbcTemplate();
@ -138,7 +138,6 @@ public class ExternalDataSourceServiceImpl implements DataSourceService {
testJtListNew.add(jdbcTemplate);
isHealthListNew.add(Boolean.TRUE);
});
final List<HikariDataSource> dataSourceListOld = dataSourceList;
final List<JdbcTemplate> testJtListOld = testJtList;
dataSourceList = dataSourceListNew;

View File

@ -0,0 +1,19 @@
#
#
# Copyright 1999-2021 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.
#
#
com.alibaba.nacos.config.server.service.LongPollingConnectionMetricsCollector

View File

@ -1,49 +0,0 @@
/*
* 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.nacos.config.server.remote;
import com.alibaba.nacos.api.config.remote.request.ConfigPublishRequest;
import com.alibaba.nacos.core.remote.control.MonitorKey;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.junit.MockitoJUnitRunner;
@RunWith(MockitoJUnitRunner.class)
public class ConfigPublishGroupKeyParserTest {
private ConfigPublishGroupKeyParser configPublishGroupKeyParser;
@Before
public void setUp() throws Exception {
configPublishGroupKeyParser = new ConfigPublishGroupKeyParser();
}
@Test
public void testParse() {
ConfigPublishRequest configPublishRequest = new ConfigPublishRequest();
configPublishRequest.setContent("content");
configPublishRequest.setRequestId("requestId");
configPublishRequest.setGroup("group");
configPublishRequest.setCasMd5("md5");
configPublishRequest.setDataId("dataId");
configPublishRequest.setTenant("tenant");
MonitorKey parse = configPublishGroupKeyParser.parse(configPublishRequest);
Assert.assertEquals("dataId+group+tenant", parse.getKey());
}
}

View File

@ -1,50 +0,0 @@
/*
* 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.nacos.config.server.remote;
import com.alibaba.nacos.api.config.remote.request.ConfigPublishRequest;
import com.alibaba.nacos.core.remote.control.MonitorKey;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.junit.MockitoJUnitRunner;
@RunWith(MockitoJUnitRunner.class)
public class ConfigPublishGroupParserTest {
private ConfigPublishGroupParser configPublishGroupParser;
@Before
public void setUp() throws Exception {
configPublishGroupParser = new ConfigPublishGroupParser();
}
@Test
public void testParse() {
ConfigPublishRequest configPublishRequest = new ConfigPublishRequest();
configPublishRequest.setContent("content");
configPublishRequest.setRequestId("requestId");
configPublishRequest.setGroup("group");
configPublishRequest.setCasMd5("md5");
configPublishRequest.setDataId("dataId");
configPublishRequest.setTenant("tenant");
MonitorKey parse = configPublishGroupParser.parse(configPublishRequest);
Assert.assertEquals("group", parse.getKey());
}
}

View File

@ -1,45 +0,0 @@
/*
* 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.nacos.config.server.remote;
import com.alibaba.nacos.api.config.remote.request.ConfigQueryRequest;
import com.alibaba.nacos.core.remote.control.MonitorKey;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
public class ConfigQueryGroupKeyParserTest {
private ConfigQueryGroupKeyParser configQueryGroupKeyParser;
@Before
public void setUp() throws Exception {
configQueryGroupKeyParser = new ConfigQueryGroupKeyParser();
}
@Test
public void testParse() {
ConfigQueryRequest configQueryRequest = new ConfigQueryRequest();
configQueryRequest.setRequestId("requestId");
configQueryRequest.setGroup("group");
configQueryRequest.setDataId("dataId");
configQueryRequest.setTenant("tenant");
MonitorKey parse = configQueryGroupKeyParser.parse(configQueryRequest);
Assert.assertEquals("dataId+group+tenant", parse.getKey());
}
}

View File

@ -1,44 +0,0 @@
/*
* 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.nacos.config.server.remote;
import com.alibaba.nacos.api.config.remote.request.ConfigQueryRequest;
import com.alibaba.nacos.core.remote.control.MonitorKey;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
public class ConfigQueryGroupParserTest {
private ConfigQueryGroupParser configQueryGroupParser;
@Before
public void setUp() throws Exception {
configQueryGroupParser = new ConfigQueryGroupParser();
}
@Test
public void testParse() {
ConfigQueryRequest configQueryRequest = new ConfigQueryRequest();
configQueryRequest.setRequestId("requestId");
configQueryRequest.setGroup("group");
configQueryRequest.setDataId("dataId");
configQueryRequest.setTenant("tenant");
MonitorKey parse = configQueryGroupParser.parse(configQueryRequest);
Assert.assertEquals("group", parse.getKey());
}
}

View File

@ -1,100 +0,0 @@
/*
* 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.nacos.config.server.remote;
import com.alibaba.nacos.api.config.remote.response.ConfigQueryResponse;
import com.alibaba.nacos.api.exception.NacosException;
import com.alibaba.nacos.common.notify.Event;
import com.alibaba.nacos.common.notify.NotifyCenter;
import com.alibaba.nacos.common.notify.listener.Subscriber;
import com.alibaba.nacos.config.server.model.event.LocalDataChangeEvent;
import com.alibaba.nacos.config.server.utils.GroupKey2;
import com.alibaba.nacos.core.remote.control.TpsControlRuleChangeEvent;
import com.alibaba.nacos.core.remote.event.ConnectionLimitRuleChangeEvent;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.junit.MockitoJUnitRunner;
import org.springframework.test.util.ReflectionTestUtils;
import java.io.IOException;
import static org.mockito.Mockito.when;
@RunWith(MockitoJUnitRunner.class)
public class InternalConfigChangeNotifierTest {
private InternalConfigChangeNotifier internalConfigChangeNotifier;
@Mock
private ConfigQueryRequestHandler configQueryRequestHandler;
@Before
public void setUp() throws IOException, NacosException {
internalConfigChangeNotifier = new InternalConfigChangeNotifier();
ReflectionTestUtils.setField(internalConfigChangeNotifier, "configQueryRequestHandler", configQueryRequestHandler);
when(configQueryRequestHandler.handle(Mockito.any(), Mockito.any())).thenReturn(ConfigQueryResponse.buildSuccessResponse("content"));
}
@Test
public void testOnEvent() {
final String groupKey = GroupKey2.getKey("nacos.internal.tps.control_rule_1", "nacos", "tenant");
final String limitGroupKey = GroupKey2.getKey("nacos.internal.tps.nacos.internal.connection.limit.rule", "nacos", "tenant");
NotifyCenter.registerSubscriber(new Subscriber() {
@Override
public void onEvent(Event event) {
ConnectionLimitRuleChangeEvent connectionLimitRuleChangeEvent = (ConnectionLimitRuleChangeEvent) event;
Assert.assertEquals("content", connectionLimitRuleChangeEvent.getLimitRule());
}
@Override
public Class<? extends Event> subscribeType() {
return ConnectionLimitRuleChangeEvent.class;
}
});
NotifyCenter.registerSubscriber(new Subscriber() {
@Override
public void onEvent(Event event) {
TpsControlRuleChangeEvent tpsControlRuleChangeEvent = (TpsControlRuleChangeEvent) event;
Assert.assertEquals("content", tpsControlRuleChangeEvent.getRuleContent());
}
@Override
public Class<? extends Event> subscribeType() {
return TpsControlRuleChangeEvent.class;
}
});
internalConfigChangeNotifier.onEvent(new LocalDataChangeEvent(groupKey));
internalConfigChangeNotifier.onEvent(new LocalDataChangeEvent(limitGroupKey));
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}

View File

@ -20,7 +20,6 @@ import com.alibaba.nacos.config.server.model.event.LocalDataChangeEvent;
import com.alibaba.nacos.config.server.utils.GroupKey2;
import com.alibaba.nacos.core.remote.ConnectionManager;
import com.alibaba.nacos.core.remote.RpcPushService;
import com.alibaba.nacos.core.remote.control.TpsMonitorManager;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@ -36,9 +35,6 @@ public class RpcConfigChangeNotifierTest {
private RpcConfigChangeNotifier rpcConfigChangeNotifier;
@Mock
private TpsMonitorManager tpsMonitorManager;
@Mock
private ConfigChangeListenContext configChangeListenContext;
@ -52,7 +48,6 @@ public class RpcConfigChangeNotifierTest {
public void setUp() {
rpcConfigChangeNotifier = new RpcConfigChangeNotifier();
ReflectionTestUtils.setField(rpcConfigChangeNotifier, "tpsMonitorManager", tpsMonitorManager);
ReflectionTestUtils.setField(rpcConfigChangeNotifier, "configChangeListenContext", configChangeListenContext);
ReflectionTestUtils.setField(rpcConfigChangeNotifier, "rpcPushService", rpcPushService);
ReflectionTestUtils.setField(rpcConfigChangeNotifier, "connectionManager", connectionManager);
@ -61,7 +56,8 @@ public class RpcConfigChangeNotifierTest {
@Test
public void testOnEvent() {
final String groupKey = GroupKey2.getKey("nacos.internal.tps.control_rule_1", "nacos", "tenant");
final String limitGroupKey = GroupKey2.getKey("nacos.internal.tps.nacos.internal.connection.limit.rule", "nacos", "tenant");
final String limitGroupKey = GroupKey2
.getKey("nacos.internal.tps.nacos.internal.connection.limit.rule", "nacos", "tenant");
List<String> betaIps = new ArrayList<>();
betaIps.add("1.1.1.1");

View File

@ -34,7 +34,6 @@ import org.springframework.test.util.ReflectionTestUtils;
import org.springframework.transaction.support.TransactionCallback;
import org.springframework.transaction.support.TransactionTemplate;
import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
@ -68,9 +67,9 @@ public class StandaloneDatabaseOperateImplTest {
@Mock
private BiConsumer<Boolean, Throwable> biConsumer;
@Mock
private File file;
/* @Mock
private File file;
*/
@Mock
private TransactionTemplate transactionTemplate;
@ -78,10 +77,7 @@ public class StandaloneDatabaseOperateImplTest {
public void setUp() {
ReflectionTestUtils.setField(operate, "jdbcTemplate", jdbcTemplate);
ReflectionTestUtils.setField(operate, "transactionTemplate", transactionTemplate);
when(file.exists()).thenReturn(true);
when(file.isDirectory()).thenReturn(false);
when(file.canRead()).thenReturn(true);
when(file.getPath()).thenReturn("test");
}
@Test
@ -100,7 +96,7 @@ public class StandaloneDatabaseOperateImplTest {
configInfo.setId(1L);
configInfo.setDataId("test");
configInfo.setGroup("test");
Object[] args = new Object[]{configInfo.getId(), configInfo.getDataId(), configInfo.getGroup()};
Object[] args = new Object[] {configInfo.getId(), configInfo.getDataId(), configInfo.getGroup()};
when(jdbcTemplate.queryForObject(sql, args, ConfigInfo.class)).thenReturn(configInfo);
Assert.assertEquals(operate.queryOne(sql, args, ConfigInfo.class), configInfo);
}
@ -112,7 +108,7 @@ public class StandaloneDatabaseOperateImplTest {
configInfo.setId(1L);
configInfo.setDataId("test");
configInfo.setGroup("test");
Object[] args = new Object[]{configInfo.getId(), configInfo.getDataId(), configInfo.getGroup()};
Object[] args = new Object[] {configInfo.getId(), configInfo.getDataId(), configInfo.getGroup()};
when(jdbcTemplate.queryForObject(eq(sql), eq(args), any(RowMapper.class))).thenReturn(configInfo);
Assert.assertEquals(operate.queryOne(sql, args, rowMapper), configInfo);
}
@ -133,7 +129,7 @@ public class StandaloneDatabaseOperateImplTest {
configInfo.setId(1L);
configInfo.setDataId("test");
configInfo.setGroup("test");
Object[] args = new Object[]{configInfo.getId(), configInfo.getDataId(), configInfo.getGroup()};
Object[] args = new Object[] {configInfo.getId(), configInfo.getDataId(), configInfo.getGroup()};
when(tempJdbcTemplate.queryForObject(sql, args, ConfigInfo.class)).thenReturn(configInfo);
Assert.assertEquals(operate.queryOne(tempJdbcTemplate, sql, args, ConfigInfo.class), configInfo);
}
@ -145,7 +141,7 @@ public class StandaloneDatabaseOperateImplTest {
configInfo.setId(1L);
configInfo.setDataId("test");
configInfo.setGroup("test");
Object[] args = new Object[]{configInfo.getId(), configInfo.getDataId(), configInfo.getGroup()};
Object[] args = new Object[] {configInfo.getId(), configInfo.getDataId(), configInfo.getGroup()};
when(tempJdbcTemplate.queryForObject(eq(sql), eq(args), any(RowMapper.class))).thenReturn(configInfo);
Assert.assertEquals(operate.queryOne(tempJdbcTemplate, sql, args, rowMapper), configInfo);
}
@ -153,7 +149,7 @@ public class StandaloneDatabaseOperateImplTest {
@Test
public void testQueryMany1() {
final String sql = "SELECT * FROM config_info WHERE id >= ? AND id <= ?";
final Object[] args = new Object[]{1, 2};
final Object[] args = new Object[] {1, 2};
ConfigInfo configInfo1 = new ConfigInfo();
configInfo1.setId(1);
ConfigInfo configInfo2 = new ConfigInfo();
@ -168,7 +164,7 @@ public class StandaloneDatabaseOperateImplTest {
@Test
public void testQueryMany2() {
final String sql = "SELECT id, data_id, group_id FROM config_info WHERE id >= ? AND id <= ?";
final Object[] args = new Object[]{1, 2};
final Object[] args = new Object[] {1, 2};
final List<Map<String, Object>> resultList = new ArrayList<>();
Map<String, Object> map1 = new HashMap<>();
@ -191,7 +187,7 @@ public class StandaloneDatabaseOperateImplTest {
@Test
public void testQueryMany3() {
String sql = "SELECT data_id FROM config_info WHERE id >= ? AND id <= ?";
Object[] args = new Object[]{1, 2};
Object[] args = new Object[] {1, 2};
String dataId1 = "test1";
String dataId2 = "test2";
List<String> resultList = new ArrayList<>();
@ -205,18 +201,18 @@ public class StandaloneDatabaseOperateImplTest {
@Test
public void testQueryMany4() {
final String sql = "SELECT data_id FROM config_info WHERE id >= ? AND id <= ?";
final Object[] args = new Object[]{1, 2};
final Object[] args = new Object[] {1, 2};
final List<Map<String, Object>> resultList = new ArrayList<>();
Map<String, Object> map1 = new HashMap<>();
map1.put("id", 1);
map1.put("data_id", "test");
map1.put("group_id", "test");
final Map<String, Object> map2 = new HashMap<>();
map1.put("id", 2);
map1.put("data_id", "test");
map1.put("group_id", "test");
resultList.add(map1);
resultList.add(map2);
@ -227,7 +223,7 @@ public class StandaloneDatabaseOperateImplTest {
@Test
public void testQueryMany5() {
String sql = "SELECT data_id FROM config_info WHERE id >= ? AND id <= ?";
Object[] args = new Object[]{1, 2};
Object[] args = new Object[] {1, 2};
String dataId1 = "test1";
String dataId2 = "test2";
List<String> resultList = new ArrayList<>();
@ -241,7 +237,7 @@ public class StandaloneDatabaseOperateImplTest {
@Test
public void testQueryMany6() {
final String sql = "SELECT * FROM config_info WHERE id >= ? AND id <= ?";
final Object[] args = new Object[]{1, 2};
final Object[] args = new Object[] {1, 2};
ConfigInfo configInfo1 = new ConfigInfo();
configInfo1.setId(1);
ConfigInfo configInfo2 = new ConfigInfo();
@ -269,7 +265,7 @@ public class StandaloneDatabaseOperateImplTest {
ModifyRequest modifyRequest1 = new ModifyRequest();
String sql = "UPDATE config_info SET data_id = 'test' WHERE id = ?;";
modifyRequest1.setSql(sql);
Object[] args = new Object[]{1};
Object[] args = new Object[] {1};
modifyRequest1.setArgs(args);
modifyRequests.add(modifyRequest1);
when(transactionTemplate.execute(any(TransactionCallback.class))).thenReturn(true);
@ -282,7 +278,7 @@ public class StandaloneDatabaseOperateImplTest {
ModifyRequest modifyRequest1 = new ModifyRequest();
String sql = "UPDATE config_info SET data_id = 'test' WHERE id = ?;";
modifyRequest1.setSql(sql);
Object[] args = new Object[]{1};
Object[] args = new Object[] {1};
modifyRequest1.setArgs(args);
modifyRequests.add(modifyRequest1);
when(transactionTemplate.execute(any(TransactionCallback.class))).thenReturn(true);
@ -295,7 +291,7 @@ public class StandaloneDatabaseOperateImplTest {
ModifyRequest modifyRequest1 = new ModifyRequest();
String sql = "UPDATE config_info SET data_id = 'test' WHERE id = ?;";
modifyRequest1.setSql(sql);
Object[] args = new Object[]{1};
Object[] args = new Object[] {1};
modifyRequest1.setArgs(args);
modifyRequests.add(modifyRequest1);
when(transactionTemplate.execute(any(TransactionCallback.class))).thenReturn(true);
@ -308,7 +304,7 @@ public class StandaloneDatabaseOperateImplTest {
ModifyRequest modifyRequest1 = new ModifyRequest();
String sql = "UPDATE config_info SET data_id = 'test' WHERE id = ?;";
modifyRequest1.setSql(sql);
Object[] args = new Object[]{1};
Object[] args = new Object[] {1};
modifyRequest1.setArgs(args);
modifyRequests.add(modifyRequest1);
when(transactionTemplate.execute(any(TransactionCallback.class))).thenReturn(true);
@ -337,10 +333,10 @@ public class StandaloneDatabaseOperateImplTest {
ModifyRequest modifyRequest1 = new ModifyRequest();
String sql = "UPDATE config_info SET data_id = 'test' WHERE id = ?;";
modifyRequest1.setSql(sql);
Object[] args = new Object[]{1};
Object[] args = new Object[] {1};
modifyRequest1.setArgs(args);
modifyRequests.add(modifyRequest1);
when(tempJdbcTemplate.batchUpdate(sql)).thenReturn(new int[]{1});
when(tempJdbcTemplate.batchUpdate(sql)).thenReturn(new int[] {1});
Assert.assertTrue(operate.doDataImport(tempJdbcTemplate, modifyRequests));
}

View File

@ -22,7 +22,8 @@ import com.alibaba.nacos.consistency.entity.WriteRequest;
/**
* Can be discovered through SPI or Spring, This interface is just a function definition interface. Different
* consistency protocols have their own LogDispatcher. It is not recommended to directly implement this interface.
* consistency protocols have their pwd
* LogDispatcher. It is not recommended to directly implement this interface.
*
* @author <a href="mailto:liaochuntao@live.com">liaochuntao</a>
*/

View File

@ -35,6 +35,7 @@
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>nacos-config</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>

View File

@ -34,7 +34,7 @@ server.port=8848
### Deprecated configuration property, it is recommended to use `spring.sql.init.platform` replaced.
# spring.datasource.platform=mysql
# nacos.plugin.datasource.log.enabled=true
spring.sql.init.platform=mysql
### Count of DB:
# db.num=1
@ -104,7 +104,10 @@ server.tomcat.basedir=file:.
#*************** Access Control Related Configurations ***************#
### If enable spring security, this option is deprecated in 1.2.0:
#spring.security.enabled=false
db.num=1
db.url.0=jdbc:mysql://127.0.0.1:3306/nacos?characterEncoding=utf8&connectTimeout=1000&socketTimeout=10000&autoReconnect=true
db.user=root
db.password=11021102
### The ignore urls of auth, is deprecated in 1.2.0:
nacos.security.ignore.urls=/,/error,/**/*.css,/**/*.js,/**/*.html,/**/*.map,/**/*.svg,/**/*.png,/**/*.ico,/console-ui/public/**,/v1/auth/**,/v1/console/health/**,/actuator/**,/v1/console/server/**

View File

@ -170,5 +170,14 @@
<groupId>io.grpc</groupId>
<artifactId>grpc-testing</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.nacos</groupId>
<artifactId>nacos-contrl-plugin</artifactId>
<version>${revision}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
</dependency>
</dependencies>
</project>

View File

@ -154,8 +154,10 @@ public class ClusterRpcClientProxy extends MemberChangeListener {
* Using {@link EnvUtil#getAvailableProcessors(int)} to build cluster clients' grpc thread pool.
*/
private RpcClient buildRpcClient(ConnectionType type, Map<String, String> labels, String memberClientKey) {
return RpcClientFactory.createClusterClient(memberClientKey, type,
EnvUtil.getAvailableProcessors(2), EnvUtil.getAvailableProcessors(8), labels);
RpcClient clusterClient = RpcClientFactory
.createClusterClient(memberClientKey, type, EnvUtil.getAvailableProcessors(2),
EnvUtil.getAvailableProcessors(8), labels);
return clusterClient;
}
/**

View File

@ -1,5 +1,6 @@
/*
* Copyright 1999-2020 Alibaba Group Holding Ltd.
*
* Copyright 1999-2021 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.
@ -12,9 +13,10 @@
* 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.nacos.core.remote.control;
package com.alibaba.nacos.core.control;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@ -28,18 +30,19 @@ import java.lang.annotation.RetentionPolicy;
@Retention(RetentionPolicy.RUNTIME)
public @interface TpsControl {
/**
* The action type of the request.
* alias name for control point.
*
* @return
*/
String name() default "";
/**
* The point name should applied for.
*
* @return action type, default READ
*/
String pointName();
/**
* Resource name parser. Should have lower priority than resource().
*
* @return class type of resource parser
*/
Class[] parsers() default {};
}

View File

@ -1,5 +1,6 @@
/*
* Copyright 1999-2020 Alibaba Group Holding Ltd.
*
* Copyright 1999-2021 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.
@ -12,9 +13,10 @@
* 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.nacos.core.remote.control;
package com.alibaba.nacos.core.control;
/**
* tps control manager.

View File

@ -0,0 +1,63 @@
/*
*
* Copyright 1999-2021 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.nacos.core.control.http;
import com.alibaba.nacos.plugin.control.tps.request.TpsCheckRequest;
import javax.servlet.http.HttpServletRequest;
/**
* http tps check request parser.
*
* @author shiyiyue
*/
@SuppressWarnings("PMD.AbstractClassShouldStartWithAbstractNamingRule")
public abstract class HttpTpsCheckRequestParser {
public HttpTpsCheckRequestParser() {
registerParser();
}
public void registerParser() {
HttpTpsCheckRequestParserRegistry.register(this);
}
/**
* parse tps check request.
*
* @param httpServletRequest http request.
* @return
*/
public abstract TpsCheckRequest parse(HttpServletRequest httpServletRequest);
/**
* get point name.
*
* @return
*/
public abstract String getPointName();
/**
* get name.
*
* @return
*/
public abstract String getName();
}

View File

@ -0,0 +1,56 @@
/*
*
* Copyright 1999-2021 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.nacos.core.control.http;
import com.alibaba.nacos.plugin.control.Loggers;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* http tps check request parse registry.
*
* @author shiyiyue
*/
public class HttpTpsCheckRequestParserRegistry {
static final Map<String, HttpTpsCheckRequestParser> PARSER_MAP = new ConcurrentHashMap<>();
/**
* register httpTpsCheckParser.
*
* @param httpTpsCheckParser httpTpsCheckParser.
*/
public static synchronized void register(HttpTpsCheckRequestParser httpTpsCheckParser) {
HttpTpsCheckRequestParser prevTpsCheckParser = PARSER_MAP.put(httpTpsCheckParser.getName(), httpTpsCheckParser);
if (prevTpsCheckParser != null) {
Loggers.CONTROL.info("HttpTpsCheckRequestParser name {}, point name {} will be replaced with {}",
httpTpsCheckParser.getName(), prevTpsCheckParser.getPointName(),
httpTpsCheckParser.getClass().getSimpleName());
} else {
Loggers.CONTROL.info("HttpTpsCheckRequestParser register parser {} of name {},point name {}",
httpTpsCheckParser.getClass().getSimpleName(), httpTpsCheckParser.getName(),
httpTpsCheckParser.getPointName());
}
}
public static HttpTpsCheckRequestParser getParser(String pointName) {
return PARSER_MAP.get(pointName);
}
}

View File

@ -0,0 +1,57 @@
/*
*
* Copyright 1999-2021 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.nacos.core.control.http;
import com.alibaba.nacos.core.control.TpsControl;
import com.alibaba.nacos.core.control.TpsControlConfig;
import com.alibaba.nacos.plugin.control.ControlManagerCenter;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.stereotype.Service;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
import java.lang.reflect.Method;
import java.util.Map;
/**
* RequestHandlerRegistry.
*
* @author liuzunfei
* @version $Id: RequestHandlerRegistry.java, v 0.1 2020年07月13日 8:24 PM liuzunfei Exp $
*/
@Service
public class HttpTpsPointRegistry implements ApplicationListener<ContextRefreshedEvent> {
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
RequestMappingHandlerMapping requestMapping = event.getApplicationContext()
.getBean(RequestMappingHandlerMapping.class);
Map<RequestMappingInfo, HandlerMethod> handlerMethods = requestMapping.getHandlerMethods();
for (HandlerMethod handlerMethod : handlerMethods.values()) {
Method method = handlerMethod.getMethod();
if (method.isAnnotationPresent(TpsControl.class) && TpsControlConfig.isTpsControlEnabled()) {
TpsControl tpsControl = method.getAnnotation(TpsControl.class);
String pointName = tpsControl.pointName();
ControlManagerCenter.getInstance().getTpsControlManager().registerTpsPoint(pointName);
}
}
}
}

View File

@ -0,0 +1,106 @@
/*
*
* Copyright 1999-2021 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.nacos.core.control.http;
import com.alibaba.nacos.common.utils.StringUtils;
import com.alibaba.nacos.core.control.TpsControl;
import com.alibaba.nacos.core.control.TpsControlConfig;
import com.alibaba.nacos.plugin.control.ControlManagerCenter;
import com.alibaba.nacos.plugin.control.Loggers;
import com.alibaba.nacos.plugin.control.tps.request.TpsCheckRequest;
import com.alibaba.nacos.plugin.control.tps.response.TpsCheckResponse;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
/**
* nacos http control interceptor.
*
* @author shiyiyue
*/
public class NacosHttpTpsControlInterceptor implements HandlerInterceptor {
private static final String X_REAL_IP = "X-Real-IP";
private static final String X_FORWARDED_FOR = "X-Forwarded-For";
private static final String X_FORWARDED_FOR_SPLIT_SYMBOL = ",";
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
try {
if (handler instanceof HandlerMethod) {
Method method = ((HandlerMethod) handler).getMethod();
if (method.isAnnotationPresent(TpsControl.class) && TpsControlConfig.isTpsControlEnabled()) {
TpsControl tpsControl = method.getAnnotation(TpsControl.class);
String pointName = tpsControl.pointName();
HttpTpsCheckRequestParser parser = HttpTpsCheckRequestParserRegistry.getParser(pointName);
TpsCheckRequest httpTpsCheckRequest = null;
if (parser != null) {
httpTpsCheckRequest = parser.parse(request);
}
if (httpTpsCheckRequest == null) {
httpTpsCheckRequest = new TpsCheckRequest();
}
httpTpsCheckRequest.setPointName(pointName);
TpsCheckResponse checkResponse = ControlManagerCenter.getInstance().getTpsControlManager()
.check(httpTpsCheckRequest);
if (!checkResponse.isSuccess()) {
generate503Response(request, response, checkResponse.getMessage());
return false;
}
}
}
} catch (Throwable throwable) {
Loggers.TPS.error("Error to check tps control", throwable);
}
return true;
}
private static String getRemoteIp(HttpServletRequest request) {
String xForwardedFor = request.getHeader(X_FORWARDED_FOR);
if (!StringUtils.isBlank(xForwardedFor)) {
return xForwardedFor.split(X_FORWARDED_FOR_SPLIT_SYMBOL)[0].trim();
}
String nginxHeader = request.getHeader(X_REAL_IP);
return StringUtils.isBlank(nginxHeader) ? request.getRemoteAddr() : nginxHeader;
}
void generate503Response(HttpServletRequest request, HttpServletResponse response, String message) {
try {
// Disable cache.
response.setHeader("Pragma", "no-cache");
response.setDateHeader("Expires", 0);
response.setHeader("Cache-Control", "no-cache,no-store");
response.setStatus(HttpServletResponse.SC_SERVICE_UNAVAILABLE);
response.getWriter().println(message);
} catch (Exception ex) {
Loggers.TPS.error("Error to generate tps 503 response", ex);
}
}
}

View File

@ -0,0 +1,37 @@
/*
*
* Copyright 1999-2021 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.nacos.core.control.http;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/**
* http tps control configurer.
*
* @author shiyiyue
*/
@Component
public class NacosHttpTpsControlWebMvcConfigurer implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new NacosHttpTpsControlInterceptor());
}
}

View File

@ -0,0 +1,59 @@
/*
*
* Copyright 1999-2021 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.nacos.core.control.remote;
import com.alibaba.nacos.api.remote.request.Request;
import com.alibaba.nacos.api.remote.request.RequestMeta;
import com.alibaba.nacos.plugin.control.tps.request.TpsCheckRequest;
/**
* remote tps check request parser.
*
* @author shiyiyue
*/
@SuppressWarnings("PMD.AbstractClassShouldStartWithAbstractNamingRule")
public abstract class RemoteTpsCheckRequestParser {
public RemoteTpsCheckRequestParser() {
RemoteTpsCheckRequestParserRegistry.register(this);
}
/**
* parse tps check request.
*
* @param request request.
* @param meta meta.
* @return
*/
public abstract TpsCheckRequest parse(Request request, RequestMeta meta);
/**
* get point name.
*
* @return
*/
public abstract String getPointName();
/**
* get name.
*
* @return
*/
public abstract String getName();
}

View File

@ -0,0 +1,57 @@
/*
*
* Copyright 1999-2021 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.nacos.core.control.remote;
import com.alibaba.nacos.plugin.control.Loggers;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* remote tps check request parser registry.
*
* @author shiyiyue
*/
public class RemoteTpsCheckRequestParserRegistry {
static final Map<String, RemoteTpsCheckRequestParser> PARSER_MAP = new ConcurrentHashMap<>();
/**
* register remoteTpsCheckParser.
*
* @param remoteTpsCheckParser remoteTpsCheckParser.
*/
public static void register(RemoteTpsCheckRequestParser remoteTpsCheckParser) {
RemoteTpsCheckRequestParser prevRemoteTpsCheckParser = PARSER_MAP
.put(remoteTpsCheckParser.getName(), remoteTpsCheckParser);
if (prevRemoteTpsCheckParser != null) {
Loggers.CONTROL.info("RemoteTpsCheckRequestParser name {},point name {} will be replaced with {}",
remoteTpsCheckParser.getName(), remoteTpsCheckParser.getPointName(),
remoteTpsCheckParser.getClass().getSimpleName());
} else {
Loggers.CONTROL.info("RemoteTpsCheckRequestParser register parser {} of name {},point name {}",
remoteTpsCheckParser.getClass().getSimpleName(), remoteTpsCheckParser.getName(),
remoteTpsCheckParser.getPointName());
}
}
public static RemoteTpsCheckRequestParser getParser(String name) {
return PARSER_MAP.get(name);
}
}

View File

@ -0,0 +1,100 @@
/*
*
* Copyright 1999-2021 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.nacos.core.control.remote;
import com.alibaba.nacos.api.exception.NacosException;
import com.alibaba.nacos.api.remote.request.Request;
import com.alibaba.nacos.api.remote.request.RequestMeta;
import com.alibaba.nacos.api.remote.response.Response;
import com.alibaba.nacos.common.utils.StringUtils;
import com.alibaba.nacos.core.control.TpsControl;
import com.alibaba.nacos.core.control.TpsControlConfig;
import com.alibaba.nacos.core.remote.AbstractRequestFilter;
import com.alibaba.nacos.plugin.control.ControlManagerCenter;
import com.alibaba.nacos.plugin.control.tps.TpsControlManager;
import com.alibaba.nacos.plugin.control.tps.request.TpsCheckRequest;
import com.alibaba.nacos.plugin.control.tps.response.TpsCheckResponse;
import org.springframework.stereotype.Service;
import java.lang.reflect.Method;
/**
* tps control point.
*
* @author liuzunfei
* @version $Id: TpsControlRequestFilter.java, v 0.1 2021年01月09日 12:38 PM liuzunfei Exp $
*/
@Service
public class TpsControlRequestFilter extends AbstractRequestFilter {
TpsControlManager tpsControlManager = ControlManagerCenter.getInstance().getTpsControlManager();
@Override
protected Response filter(Request request, RequestMeta meta, Class handlerClazz) {
Method method = null;
try {
method = getHandleMethod(handlerClazz);
} catch (NacosException e) {
return null;
}
if (method.isAnnotationPresent(TpsControl.class) && TpsControlConfig.isTpsControlEnabled()) {
try {
TpsControl tpsControl = method.getAnnotation(TpsControl.class);
String pointName = tpsControl.pointName();
TpsCheckRequest tpsCheckRequest = null;
String parseName = StringUtils.isBlank(tpsControl.name()) ? pointName : tpsControl.name();
RemoteTpsCheckRequestParser parser = RemoteTpsCheckRequestParserRegistry.getParser(parseName);
if (parser != null) {
tpsCheckRequest = parser.parse(request, meta);
}
if (tpsCheckRequest == null) {
tpsCheckRequest = new TpsCheckRequest();
}
tpsCheckRequest.setPointName(pointName);
TpsCheckResponse check = tpsControlManager.check(tpsCheckRequest);
if (!check.isSuccess()) {
Response response;
try {
response = super.getDefaultResponseInstance(handlerClazz);
response.setErrorInfo(NacosException.OVER_THRESHOLD,
"Tps Flow restricted:" + check.getMessage());
return response;
} catch (Exception e) {
com.alibaba.nacos.plugin.control.Loggers.TPS
.warn("Tps check fail , request: {},exception:{}", request.getClass().getSimpleName(),
e);
return null;
}
}
} catch (Throwable throwable) {
com.alibaba.nacos.plugin.control.Loggers.TPS
.warn("Tps check exception , request: {},exception:{}", request.getClass().getSimpleName(),
throwable);
}
}
return null;
}
}

View File

@ -1,5 +1,6 @@
/*
* Copyright 1999-2020 Alibaba Group Holding Ltd.
*
* Copyright 1999-2021 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.
@ -12,53 +13,35 @@
* 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.nacos.core.remote;
import com.alibaba.nacos.api.common.Constants;
import com.alibaba.nacos.api.exception.NacosException;
import com.alibaba.nacos.api.remote.RemoteConstants;
import com.alibaba.nacos.api.remote.RequestCallBack;
import com.alibaba.nacos.api.remote.RpcScheduledExecutor;
import com.alibaba.nacos.api.remote.request.ClientDetectionRequest;
import com.alibaba.nacos.api.remote.request.ConnectResetRequest;
import com.alibaba.nacos.api.remote.request.RequestMeta;
import com.alibaba.nacos.api.remote.response.Response;
import com.alibaba.nacos.api.utils.NetUtils;
import com.alibaba.nacos.common.notify.Event;
import com.alibaba.nacos.common.notify.NotifyCenter;
import com.alibaba.nacos.common.notify.listener.Subscriber;
import com.alibaba.nacos.common.remote.exception.ConnectionAlreadyClosedException;
import com.alibaba.nacos.common.utils.CollectionUtils;
import com.alibaba.nacos.common.utils.JacksonUtils;
import com.alibaba.nacos.common.spi.NacosServiceLoader;
import com.alibaba.nacos.common.utils.StringUtils;
import com.alibaba.nacos.common.utils.VersionUtils;
import com.alibaba.nacos.core.monitor.MetricsMonitor;
import com.alibaba.nacos.core.remote.event.ConnectionLimitRuleChangeEvent;
import com.alibaba.nacos.core.utils.Loggers;
import com.alibaba.nacos.sys.env.EnvUtil;
import com.alibaba.nacos.sys.file.FileChangeEvent;
import com.alibaba.nacos.sys.file.FileWatcher;
import com.alibaba.nacos.sys.file.WatchFileCenter;
import com.alibaba.nacos.sys.utils.DiskUtils;
import org.springframework.beans.factory.annotation.Autowired;
import com.alibaba.nacos.plugin.control.ControlManagerCenter;
import com.alibaba.nacos.plugin.control.Loggers;
import com.alibaba.nacos.plugin.control.configs.ControlConfigs;
import com.alibaba.nacos.plugin.control.connection.request.ConnectionCheckRequest;
import com.alibaba.nacos.plugin.control.connection.response.ConnectionCheckResponse;
import com.alibaba.nacos.plugin.control.connection.rule.ConnectionControlRule;
import org.slf4j.Logger;
import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;
import java.io.File;
import java.io.IOException;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
@ -69,37 +52,20 @@ import java.util.concurrent.atomic.AtomicInteger;
* @version $Id: ConnectionManager.java, v 0.1 2020年07月13日 7:07 PM liuzunfei Exp $
*/
@Service
public class ConnectionManager extends Subscriber<ConnectionLimitRuleChangeEvent> {
public class ConnectionManager {
public static final String RULE_FILE_NAME = "limitRule";
/**
* 4 times of client keep alive.
*/
private static final long KEEP_ALIVE_TIME = 20000L;
/**
* connection limit rule.
*/
private ConnectionLimitRule connectionLimitRule = new ConnectionLimitRule();
/**
* current loader adjust count,only effective once,use to re balance.
*/
private int loadClient = -1;
String redirectAddress = null;
private static final Logger LOGGER = com.alibaba.nacos.plugin.control.Loggers.CONNECTION;
private Map<String, AtomicInteger> connectionForClientIp = new ConcurrentHashMap<>(16);
Map<String, Connection> connections = new ConcurrentHashMap<>();
@Autowired
private RuntimeConnectionEjector runtimeConnectionEjector;
private ClientConnectionEventListenerRegistry clientConnectionEventListenerRegistry;
public ConnectionManager() {
NotifyCenter.registerToPublisher(ConnectionLimitRuleChangeEvent.class, NotifyCenter.ringBufferSize);
NotifyCenter.registerSubscriber(this);
public ConnectionManager(ClientConnectionEventListenerRegistry clientConnectionEventListenerRegistry) {
this.clientConnectionEventListenerRegistry = clientConnectionEventListenerRegistry;
}
/**
@ -109,18 +75,10 @@ public class ConnectionManager extends Subscriber<ConnectionLimitRuleChangeEvent
* @return
*/
public boolean traced(String clientIp) {
return connectionLimitRule != null && connectionLimitRule.getMonitorIpList() != null && connectionLimitRule
.getMonitorIpList().contains(clientIp);
}
@PostConstruct
protected void initLimitRue() {
try {
loadRuleFromLocal();
registerFileWatch();
} catch (Exception e) {
Loggers.REMOTE.warn("Fail to init limit rue from local ,error= ", e);
}
ConnectionControlRule connectionControlRule = ControlManagerCenter.getInstance().getConnectionControlManager()
.getConnectionLimitRule();
return connectionControlRule != null && connectionControlRule.getMonitorIpList() != null
&& connectionControlRule.getMonitorIpList().contains(clientIp);
}
/**
@ -142,22 +100,26 @@ public class ConnectionManager extends Subscriber<ConnectionLimitRuleChangeEvent
public synchronized boolean register(String connectionId, Connection connection) {
if (connection.isConnected()) {
String clientIp = connection.getMetaInfo().clientIp;
if (connections.containsKey(connectionId)) {
return true;
}
if (!checkLimit(connection)) {
if (checkLimit(connection)) {
return false;
}
if (traced(connection.getMetaInfo().clientIp)) {
if (traced(clientIp)) {
connection.setTraced(true);
}
connections.put(connectionId, connection);
connectionForClientIp.get(connection.getMetaInfo().clientIp).getAndIncrement();
if (!connectionForClientIp.containsKey(clientIp)) {
connectionForClientIp.put(clientIp, new AtomicInteger(0));
}
connectionForClientIp.get(clientIp).getAndIncrement();
clientConnectionEventListenerRegistry.notifyClientConnected(connection);
Loggers.REMOTE_DIGEST
.info("new connection registered successfully, connectionId = {},connection={} ", connectionId,
connection);
LOGGER.info("new connection registered successfully, connectionId = {},connection={} ", connectionId,
connection);
return true;
}
@ -166,49 +128,16 @@ public class ConnectionManager extends Subscriber<ConnectionLimitRuleChangeEvent
}
private boolean checkLimit(Connection connection) {
String clientIp = connection.getMetaInfo().clientIp;
if (connection.getMetaInfo().isClusterSource()) {
if (!connectionForClientIp.containsKey(clientIp)) {
connectionForClientIp.putIfAbsent(clientIp, new AtomicInteger(0));
}
return true;
}
if (isOverLimit()) {
return false;
}
if (!connectionForClientIp.containsKey(clientIp)) {
connectionForClientIp.putIfAbsent(clientIp, new AtomicInteger(0));
}
AtomicInteger currentCount = connectionForClientIp.get(clientIp);
if (connectionLimitRule != null) {
// 1.check rule of specific client ip limit.
if (connectionLimitRule.getCountLimitPerClientIp().containsKey(clientIp)) {
Integer integer = connectionLimitRule.getCountLimitPerClientIp().get(clientIp);
if (integer != null && integer >= 0) {
return currentCount.get() < integer;
}
}
// 2.check rule of specific client app limit.
String appName = connection.getMetaInfo().getAppName();
if (StringUtils.isNotBlank(appName) && connectionLimitRule.getCountLimitPerClientApp()
.containsKey(appName)) {
Integer integerApp = connectionLimitRule.getCountLimitPerClientApp().get(appName);
if (integerApp != null && integerApp >= 0) {
return currentCount.get() < integerApp;
}
}
// 3.check rule of default client ip.
int countLimitPerClientIpDefault = connectionLimitRule.getCountLimitPerClientIpDefault();
return countLimitPerClientIpDefault <= 0 || currentCount.get() < countLimitPerClientIpDefault;
}
return true;
ConnectionMeta metaInfo = connection.getMetaInfo();
ConnectionCheckRequest connectionCheckRequest = new ConnectionCheckRequest(metaInfo.getClientIp(),
metaInfo.getAppName(), metaInfo.getLabel(RemoteConstants.LABEL_SOURCE));
connectionCheckRequest.setLabels(connection.getLabels());
ConnectionCheckResponse checkResponse = ControlManagerCenter.getInstance().getConnectionControlManager()
.check(connectionCheckRequest);
return !checkResponse.isSuccess();
}
/**
@ -228,7 +157,7 @@ public class ConnectionManager extends Subscriber<ConnectionLimitRuleChangeEvent
}
}
remove.close();
Loggers.REMOTE_DIGEST.info("[{}]Connection unregistered successfully. ", connectionId);
LOGGER.info("[{}]Connection unregistered successfully. ", connectionId);
clientConnectionEventListenerRegistry.notifyClientDisConnected(remove);
}
}
@ -261,6 +190,34 @@ public class ConnectionManager extends Subscriber<ConnectionLimitRuleChangeEvent
return connections;
}
/**
* init connection ejector.
*/
public void initConnectionEjector() {
String connectionRuntimeEjector = null;
try {
connectionRuntimeEjector = ControlConfigs.getInstance().getConnectionRuntimeEjector();
Collection<RuntimeConnectionEjector> ejectors = NacosServiceLoader.load(RuntimeConnectionEjector.class);
for (RuntimeConnectionEjector runtimeConnectionEjectorLoad : ejectors) {
if (runtimeConnectionEjectorLoad.getName().equalsIgnoreCase(connectionRuntimeEjector)) {
Loggers.CONNECTION.info("Found connection runtime ejector for name {}", connectionRuntimeEjector);
runtimeConnectionEjectorLoad.setConnectionManager(this);
runtimeConnectionEjector = runtimeConnectionEjectorLoad;
}
}
} catch (Throwable throwable) {
Loggers.CONNECTION.warn("Fail to load runtime ejector ", throwable);
}
if (runtimeConnectionEjector == null) {
Loggers.CONNECTION
.info("Fail to find connection runtime ejector for name {},use default", connectionRuntimeEjector);
NacosRuntimeConnectionEjector nacosRuntimeConnectionEjector = new NacosRuntimeConnectionEjector();
nacosRuntimeConnectionEjector.setConnectionManager(this);
runtimeConnectionEjector = nacosRuntimeConnectionEjector;
}
}
/**
* get current connections count.
*
@ -288,211 +245,17 @@ public class ConnectionManager extends Subscriber<ConnectionLimitRuleChangeEvent
@PostConstruct
public void start() {
initConnectionEjector();
// Start UnHealthy Connection Expel Task.
RpcScheduledExecutor.COMMON_SERVER_EXECUTOR.scheduleWithFixedDelay(() -> {
try {
int totalCount = connections.size();
Loggers.REMOTE_DIGEST.info("Connection check task start");
MetricsMonitor.getLongConnectionMonitor().set(totalCount);
Set<Map.Entry<String, Connection>> entries = connections.entrySet();
int currentSdkClientCount = currentSdkClientCount();
boolean isLoaderClient = loadClient >= 0;
int currentMaxClient = isLoaderClient ? loadClient : connectionLimitRule.countLimit;
int expelCount = currentMaxClient < 0 ? 0 : Math.max(currentSdkClientCount - currentMaxClient, 0);
Loggers.REMOTE_DIGEST
.info("Total count ={}, sdkCount={},clusterCount={}, currentLimit={}, toExpelCount={}",
totalCount, currentSdkClientCount, (totalCount - currentSdkClientCount),
currentMaxClient + (isLoaderClient ? "(loaderCount)" : ""), expelCount);
List<String> expelClient = new LinkedList<>();
Map<String, AtomicInteger> expelForIp = new HashMap<>(16);
//1. calculate expel count of ip.
for (Map.Entry<String, Connection> entry : entries) {
Connection client = entry.getValue();
String appName = client.getMetaInfo().getAppName();
String clientIp = client.getMetaInfo().getClientIp();
if (client.getMetaInfo().isSdkSource() && !expelForIp.containsKey(clientIp)) {
//get limit for current ip.
int countLimitOfIp = connectionLimitRule.getCountLimitOfIp(clientIp);
if (countLimitOfIp < 0) {
int countLimitOfApp = connectionLimitRule.getCountLimitOfApp(appName);
countLimitOfIp = countLimitOfApp < 0 ? countLimitOfIp : countLimitOfApp;
}
if (countLimitOfIp < 0) {
countLimitOfIp = connectionLimitRule.getCountLimitPerClientIpDefault();
}
if (countLimitOfIp >= 0 && connectionForClientIp.containsKey(clientIp)) {
AtomicInteger currentCountIp = connectionForClientIp.get(clientIp);
if (currentCountIp != null && currentCountIp.get() > countLimitOfIp) {
expelForIp.put(clientIp, new AtomicInteger(currentCountIp.get() - countLimitOfIp));
}
}
}
}
Loggers.REMOTE_DIGEST
.info("Check over limit for ip limit rule, over limit ip count={}", expelForIp.size());
if (expelForIp.size() > 0) {
Loggers.REMOTE_DIGEST.info("Over limit ip expel info, {}", expelForIp);
}
Set<String> outDatedConnections = new HashSet<>();
long now = System.currentTimeMillis();
//2.get expel connection for ip limit.
for (Map.Entry<String, Connection> entry : entries) {
Connection client = entry.getValue();
String clientIp = client.getMetaInfo().getClientIp();
AtomicInteger integer = expelForIp.get(clientIp);
if (integer != null && integer.intValue() > 0) {
integer.decrementAndGet();
expelClient.add(client.getMetaInfo().getConnectionId());
expelCount--;
} else if (now - client.getMetaInfo().getLastActiveTime() >= KEEP_ALIVE_TIME) {
outDatedConnections.add(client.getMetaInfo().getConnectionId());
}
}
//3. if total count is still over limit.
if (expelCount > 0) {
for (Map.Entry<String, Connection> entry : entries) {
Connection client = entry.getValue();
if (!expelForIp.containsKey(client.getMetaInfo().clientIp) && client.getMetaInfo()
.isSdkSource() && expelCount > 0) {
expelClient.add(client.getMetaInfo().getConnectionId());
expelCount--;
outDatedConnections.remove(client.getMetaInfo().getConnectionId());
}
}
}
String serverIp = null;
String serverPort = null;
if (StringUtils.isNotBlank(redirectAddress) && redirectAddress.contains(Constants.COLON)) {
String[] split = redirectAddress.split(Constants.COLON);
serverIp = split[0];
serverPort = split[1];
}
for (String expelledClientId : expelClient) {
try {
Connection connection = getConnection(expelledClientId);
if (connection != null) {
ConnectResetRequest connectResetRequest = new ConnectResetRequest();
connectResetRequest.setServerIp(serverIp);
connectResetRequest.setServerPort(serverPort);
connection.asyncRequest(connectResetRequest, null);
Loggers.REMOTE_DIGEST
.info("Send connection reset request , connection id = {},recommendServerIp={}, recommendServerPort={}",
expelledClientId, connectResetRequest.getServerIp(),
connectResetRequest.getServerPort());
}
} catch (ConnectionAlreadyClosedException e) {
unregister(expelledClientId);
} catch (Exception e) {
Loggers.REMOTE_DIGEST.error("Error occurs when expel connection, expelledClientId:{}", expelledClientId, e);
}
}
//4.client active detection.
Loggers.REMOTE_DIGEST.info("Out dated connection ,size={}", outDatedConnections.size());
if (CollectionUtils.isNotEmpty(outDatedConnections)) {
Set<String> successConnections = new HashSet<>();
final CountDownLatch latch = new CountDownLatch(outDatedConnections.size());
for (String outDateConnectionId : outDatedConnections) {
try {
Connection connection = getConnection(outDateConnectionId);
if (connection != null) {
ClientDetectionRequest clientDetectionRequest = new ClientDetectionRequest();
connection.asyncRequest(clientDetectionRequest, new RequestCallBack() {
@Override
public Executor getExecutor() {
return null;
}
@Override
public long getTimeout() {
return 1000L;
}
@Override
public void onResponse(Response response) {
latch.countDown();
if (response != null && response.isSuccess()) {
connection.freshActiveTime();
successConnections.add(outDateConnectionId);
}
}
@Override
public void onException(Throwable e) {
latch.countDown();
}
});
Loggers.REMOTE_DIGEST
.info("[{}]send connection active request ", outDateConnectionId);
} else {
latch.countDown();
}
} catch (ConnectionAlreadyClosedException e) {
latch.countDown();
} catch (Exception e) {
Loggers.REMOTE_DIGEST
.error("[{}]Error occurs when check client active detection ,error={}",
outDateConnectionId, e);
latch.countDown();
}
}
latch.await(3000L, TimeUnit.MILLISECONDS);
Loggers.REMOTE_DIGEST
.info("Out dated connection check successCount={}", successConnections.size());
for (String outDateConnectionId : outDatedConnections) {
if (!successConnections.contains(outDateConnectionId)) {
Loggers.REMOTE_DIGEST
.info("[{}]Unregister Out dated connection....", outDateConnectionId);
unregister(outDateConnectionId);
}
}
}
//reset loader client
if (isLoaderClient) {
loadClient = -1;
redirectAddress = null;
}
Loggers.REMOTE_DIGEST.info("Connection check task end");
} catch (Throwable e) {
Loggers.REMOTE.error("Error occurs during connection check... ", e);
}
runtimeConnectionEjector.doEject();
}, 1000L, 3000L, TimeUnit.MILLISECONDS);
}
private RequestMeta buildMeta() {
RequestMeta meta = new RequestMeta();
meta.setClientVersion(VersionUtils.getFullClientVersion());
meta.setClientIp(NetUtils.localIP());
return meta;
}
public void loadCount(int loadClient, String redirectAddress) {
this.loadClient = loadClient;
this.redirectAddress = redirectAddress;
runtimeConnectionEjector.setLoadClient(loadClient);
runtimeConnectionEjector.setRedirectAddress(redirectAddress);
}
/**
@ -517,7 +280,7 @@ public class ConnectionManager extends Subscriber<ConnectionLimitRuleChangeEvent
} catch (ConnectionAlreadyClosedException e) {
unregister(connectionId);
} catch (Exception e) {
Loggers.REMOTE.error("error occurs when expel connection, connectionId: {} ", connectionId, e);
LOGGER.error("error occurs when expel connection, connectionId: {} ", connectionId, e);
}
}
}
@ -572,188 +335,7 @@ public class ConnectionManager extends Subscriber<ConnectionLimitRuleChangeEvent
return connections;
}
/**
* check if over limit.
*
* @return over limit or not.
*/
private boolean isOverLimit() {
return connectionLimitRule.countLimit > 0 && currentSdkClientCount() >= connectionLimitRule.getCountLimit();
}
@Override
public void onEvent(ConnectionLimitRuleChangeEvent event) {
String limitRule = event.getLimitRule();
Loggers.REMOTE.info("connection limit rule change event receive :{}", limitRule);
try {
ConnectionLimitRule connectionLimitRule = JacksonUtils.toObj(limitRule, ConnectionLimitRule.class);
if (connectionLimitRule != null) {
this.connectionLimitRule = connectionLimitRule;
try {
saveRuleToLocal(this.connectionLimitRule);
} catch (Exception e) {
Loggers.REMOTE.warn("Fail to save rule to local error is ", e);
}
} else {
Loggers.REMOTE.info("Parse rule is null,Ignore illegal rule :{}", limitRule);
}
} catch (Exception e) {
Loggers.REMOTE.error("Fail to parse connection limit rule :{}", limitRule, e);
}
}
@Override
public Class<? extends Event> subscribeType() {
return ConnectionLimitRuleChangeEvent.class;
}
static class ConnectionLimitRule {
private Set<String> monitorIpList = new HashSet<>();
private int countLimit = -1;
private int countLimitPerClientIpDefault = -1;
private Map<String, Integer> countLimitPerClientIp = new HashMap<>();
private Map<String, Integer> countLimitPerClientApp = new HashMap<>();
public int getCountLimit() {
return countLimit;
}
public void setCountLimit(int countLimit) {
this.countLimit = countLimit;
}
public int getCountLimitPerClientIpDefault() {
return countLimitPerClientIpDefault;
}
public void setCountLimitPerClientIpDefault(int countLimitPerClientIpDefault) {
this.countLimitPerClientIpDefault = countLimitPerClientIpDefault;
}
public int getCountLimitOfIp(String clientIp) {
if (countLimitPerClientIp.containsKey(clientIp)) {
Integer integer = countLimitPerClientIp.get(clientIp);
if (integer != null && integer >= 0) {
return integer;
}
}
return -1;
}
public int getCountLimitOfApp(String appName) {
if (countLimitPerClientApp.containsKey(appName)) {
Integer integer = countLimitPerClientApp.get(appName);
if (integer != null && integer >= 0) {
return integer;
}
}
return -1;
}
public Map<String, Integer> getCountLimitPerClientIp() {
return countLimitPerClientIp;
}
public void setCountLimitPerClientIp(Map<String, Integer> countLimitPerClientIp) {
this.countLimitPerClientIp = countLimitPerClientIp;
}
public Map<String, Integer> getCountLimitPerClientApp() {
return countLimitPerClientApp;
}
public void setCountLimitPerClientApp(Map<String, Integer> countLimitPerClientApp) {
this.countLimitPerClientApp = countLimitPerClientApp;
}
public Set<String> getMonitorIpList() {
return monitorIpList;
}
public void setMonitorIpList(Set<String> monitorIpList) {
this.monitorIpList = monitorIpList;
}
}
public ConnectionLimitRule getConnectionLimitRule() {
return connectionLimitRule;
}
private synchronized void loadRuleFromLocal() throws Exception {
File limitFile = getRuleFile();
if (!limitFile.exists()) {
limitFile.createNewFile();
}
String ruleContent = DiskUtils.readFile(limitFile);
ConnectionLimitRule connectionLimitRule = StringUtils.isBlank(ruleContent) ? new ConnectionLimitRule()
: JacksonUtils.toObj(ruleContent, ConnectionLimitRule.class);
// apply rule.
if (connectionLimitRule != null) {
this.connectionLimitRule = connectionLimitRule;
Set<String> monitorIpList = connectionLimitRule.monitorIpList;
for (Connection connection : this.connections.values()) {
String clientIp = connection.getMetaInfo().getClientIp();
if (!CollectionUtils.isEmpty(monitorIpList) && monitorIpList.contains(clientIp)) {
connection.setTraced(true);
} else {
connection.setTraced(false);
}
}
}
Loggers.REMOTE.info("Init loader limit rule from local,rule={}", ruleContent);
}
private synchronized void saveRuleToLocal(ConnectionLimitRule limitRule) throws IOException {
File limitFile = getRuleFile();
if (!limitFile.exists()) {
limitFile.createNewFile();
}
DiskUtils.writeFile(limitFile, JacksonUtils.toJson(limitRule).getBytes(Constants.ENCODE), false);
}
private File getRuleFile() {
File baseDir = new File(EnvUtil.getNacosHome(), "data" + File.separator + "loader" + File.separator);
if (!baseDir.exists()) {
baseDir.mkdir();
}
return new File(baseDir, RULE_FILE_NAME);
}
private void registerFileWatch() {
try {
String tpsPath = Paths.get(EnvUtil.getNacosHome(), "data", "loader").toString();
WatchFileCenter.registerWatcher(tpsPath, new FileWatcher() {
@Override
public void onChange(FileChangeEvent event) {
try {
String fileName = event.getContext().toString();
if (RULE_FILE_NAME.equals(fileName)) {
loadRuleFromLocal();
}
} catch (Throwable throwable) {
Loggers.REMOTE.warn("Fail to load rule from local", throwable);
}
}
@Override
public boolean interest(String context) {
return RULE_FILE_NAME.equals(context);
}
});
} catch (NacosException e) {
Loggers.REMOTE.warn("Register connection rule fail ", e);
}
public Map<String, AtomicInteger> getConnectionForClientIp() {
return connectionForClientIp;
}
}

View File

@ -19,7 +19,7 @@ package com.alibaba.nacos.core.remote;
import com.alibaba.nacos.api.remote.request.RequestMeta;
import com.alibaba.nacos.api.remote.request.HealthCheckRequest;
import com.alibaba.nacos.api.remote.response.HealthCheckResponse;
import com.alibaba.nacos.core.remote.control.TpsControl;
import com.alibaba.nacos.core.control.TpsControl;
import org.springframework.stereotype.Component;
/**

View File

@ -0,0 +1,50 @@
/*
*
* Copyright 1999-2021 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.nacos.core.remote;
import com.alibaba.nacos.plugin.control.connection.ConnectionMetricsCollector;
import com.alibaba.nacos.sys.utils.ApplicationUtils;
/**
* long connection metrics collector.
*
* @author shiyiyue
*/
public class LongConnectionMetricsCollector implements ConnectionMetricsCollector {
@Override
public String getName() {
return "long_connection";
}
@Override
public int getTotalCount() {
return ApplicationUtils.getBean(ConnectionManager.class).currentClientsCount();
}
@Override
public int getCountForIp(String ip) {
ConnectionManager connectionManager = ApplicationUtils.getBean(ConnectionManager.class);
if (connectionManager.getConnectionForClientIp().containsKey(ip)) {
return connectionManager.getConnectionForClientIp().get(ip).intValue();
} else {
return 0;
}
}
}

View File

@ -0,0 +1,64 @@
/*
*
* Copyright 1999-2021 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.nacos.core.remote;
import com.alibaba.nacos.core.monitor.MetricsMonitor;
import com.alibaba.nacos.plugin.control.Loggers;
import java.util.Map;
/**
* nacos runtime connection ejector.
*
* @author shiyiyue
*/
public class NacosRuntimeConnectionEjector extends RuntimeConnectionEjector {
public NacosRuntimeConnectionEjector() {
}
/**
* eject connections on runtime.
*/
public void doEject() {
try {
Map<String, Connection> connections = connectionManager.connections;
int totalCount = connections.size();
Loggers.CONNECTION.info("Connection check task start");
MetricsMonitor.getLongConnectionMonitor().set(totalCount);
int currentSdkClientCount = connectionManager.currentSdkClientCount();
Loggers.CONNECTION
.info("Long connection metrics detail ,Total count ={}, sdkCount={},clusterCount={}", totalCount,
currentSdkClientCount, (totalCount - currentSdkClientCount));
Loggers.CONNECTION.info("Connection check task end");
} catch (Throwable e) {
Loggers.CONNECTION.error("Error occurs during connection check... ", e);
}
}
@Override
public String getName() {
return "nacos";
}
}

View File

@ -18,11 +18,9 @@ package com.alibaba.nacos.core.remote;
import com.alibaba.nacos.api.remote.request.Request;
import com.alibaba.nacos.api.remote.request.RequestMeta;
import com.alibaba.nacos.core.remote.control.TpsControl;
import com.alibaba.nacos.core.remote.control.TpsControlConfig;
import com.alibaba.nacos.core.remote.control.TpsMonitorManager;
import com.alibaba.nacos.core.remote.control.TpsMonitorPoint;
import org.springframework.beans.factory.annotation.Autowired;
import com.alibaba.nacos.core.control.TpsControl;
import com.alibaba.nacos.core.control.TpsControlConfig;
import com.alibaba.nacos.plugin.control.ControlManagerCenter;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.stereotype.Service;
@ -45,9 +43,6 @@ public class RequestHandlerRegistry implements ApplicationListener<ContextRefres
Map<String, RequestHandler> registryHandlers = new HashMap<>();
@Autowired
private TpsMonitorManager tpsMonitorManager;
/**
* Get Request Handler By request Type.
*
@ -82,8 +77,7 @@ public class RequestHandlerRegistry implements ApplicationListener<ContextRefres
if (method.isAnnotationPresent(TpsControl.class) && TpsControlConfig.isTpsControlEnabled()) {
TpsControl tpsControl = method.getAnnotation(TpsControl.class);
String pointName = tpsControl.pointName();
TpsMonitorPoint tpsMonitorPoint = new TpsMonitorPoint(pointName);
tpsMonitorManager.registerTpsControlPoint(tpsMonitorPoint);
ControlManagerCenter.getInstance().getTpsControlManager().registerTpsPoint(pointName);
}
} catch (Exception e) {
//ignore.

View File

@ -0,0 +1,81 @@
/*
*
* Copyright 1999-2021 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.nacos.core.remote;
/**
* runtime connection ejector.
*
* @author shiyiyue
*/
@SuppressWarnings("PMD.AbstractClassShouldStartWithAbstractNamingRule")
public abstract class RuntimeConnectionEjector {
/**
* 4 times of client keep alive.
*/
public static final long KEEP_ALIVE_TIME = 20000L;
/**
* current loader adjust count,only effective once,use to re balance.
*/
private int loadClient = -1;
String redirectAddress = null;
protected ConnectionManager connectionManager;
public RuntimeConnectionEjector() {
}
public ConnectionManager getConnectionManager() {
return connectionManager;
}
public void setConnectionManager(ConnectionManager connectionManager) {
this.connectionManager = connectionManager;
}
/**
* eject runtime connection.
*/
public abstract void doEject();
public int getLoadClient() {
return loadClient;
}
public void setLoadClient(int loadClient) {
this.loadClient = loadClient;
}
public String getRedirectAddress() {
return redirectAddress;
}
public void setRedirectAddress(String redirectAddress) {
this.redirectAddress = redirectAddress;
}
/**
* get name.
*
* @return
*/
public abstract String getName();
}

View File

@ -1,58 +0,0 @@
/*
* Copyright 1999-2020 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.nacos.core.remote.control;
import com.alibaba.nacos.api.common.Constants;
/**
* MonitorType.
*
* @author liuzunfei
* @version $Id: MonitorKey.java, v 0.1 2021年01月20日 20:38 PM liuzunfei Exp $
*/
@SuppressWarnings("PMD.AbstractClassShouldStartWithAbstractNamingRule")
public abstract class MonitorKey {
String key;
public MonitorKey() {
}
public MonitorKey(String key) {
this.key = key;
}
/**
* get monitor key type.
*
* @return type.
*/
public abstract String getType();
public String getKey() {
return this.key;
}
public void setKey(String key) {
this.key = key;
}
public String build() {
return this.getType() + Constants.COLON + this.getKey();
}
}

View File

@ -1,82 +0,0 @@
/*
* Copyright 1999-2020 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.nacos.core.remote.control;
import com.alibaba.nacos.api.common.Constants;
import com.alibaba.nacos.common.utils.StringUtils;
import java.util.Objects;
/**
* MatchMode.
*
* @author liuzunfei
* @version $Id: MatchMode.java, v 0.1 2021年01月22日 12:38 PM liuzunfei Exp $
*/
@SuppressWarnings({"PMD.AbstractClassShouldStartWithAbstractNamingRule", "PMD.UndefineMagicConstantRule"})
public class MonitorKeyMatcher {
/**
* if provided monitor key match this monitor ,with monitor type.
*
* @param monitorKey monitorKey.
* @return type matched result.
*/
public static boolean matchWithType(String pattern, String monitorKey) {
String[] typeInPattern = pattern.split(Constants.COLON);
String[] typeInMonitorKey = monitorKey.split(Constants.COLON);
if (!Objects.equals(typeInPattern[0], typeInMonitorKey[0])) {
return false;
}
return match(pattern.substring(pattern.indexOf(Constants.COLON)),
monitorKey.substring(monitorKey.indexOf(Constants.COLON)));
}
/**
* if provided monitor key match this monitor.
*
* @param monitorKey monitorKey.
* @return matched result.
*/
public static boolean match(String pattern, String monitorKey) {
pattern = pattern.trim();
monitorKey = monitorKey.trim();
//"AB",equals.
if (!pattern.contains(Constants.ALL_PATTERN)) {
return pattern.equals(monitorKey.trim());
}
//"*",match all.
if (pattern.equals(Constants.ALL_PATTERN)) {
return true;
}
String[] split = pattern.split("\\" + Constants.ALL_PATTERN);
if (split.length == 1) {
//"A*",prefix match.
return monitorKey.startsWith(split[0]);
} else if (split.length == 2) {
//"*A",postfix match.
if (StringUtils.isBlank(split[0])) {
return monitorKey.endsWith(split[1]);
}
return monitorKey.startsWith(split[0]) && monitorKey.endsWith(split[1]);
}
return false;
}
}

View File

@ -1,96 +0,0 @@
/*
* Copyright 1999-2020 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.nacos.core.remote.control;
import com.alibaba.nacos.api.exception.NacosException;
import com.alibaba.nacos.api.remote.request.Request;
import com.alibaba.nacos.api.remote.request.RequestMeta;
import com.alibaba.nacos.api.remote.response.Response;
import com.alibaba.nacos.core.remote.AbstractRequestFilter;
import com.alibaba.nacos.core.utils.Loggers;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
/**
* tps control point.
*
* @author liuzunfei
* @version $Id: TpsControlRequestFilter.java, v 0.1 2021年01月09日 12:38 PM liuzunfei Exp $
*/
@Service
public class TpsControlRequestFilter extends AbstractRequestFilter {
@Autowired
private TpsMonitorManager tpsMonitorManager;
@Override
protected Response filter(Request request, RequestMeta meta, Class handlerClazz) {
Method method = null;
try {
method = getHandleMethod(handlerClazz);
} catch (NacosException e) {
return null;
}
if (method.isAnnotationPresent(TpsControl.class) && TpsControlConfig.isTpsControlEnabled()) {
TpsControl tpsControl = method.getAnnotation(TpsControl.class);
String pointName = tpsControl.pointName();
Class[] parsers = tpsControl.parsers();
List<MonitorKey> monitorKeys = new ArrayList<>();
monitorKeys.add(new ClientIpMonitorKey(meta.getClientIp()));
if (parsers != null) {
for (Class clazz : parsers) {
try {
if (MonitorKeyParser.class.isAssignableFrom(clazz)) {
MonitorKey parseKey = ((MonitorKeyParser) (clazz.newInstance())).parse(request, meta);
if (parseKey != null) {
monitorKeys.add(parseKey);
}
}
} catch (Throwable throwable) {
//ignore
}
}
}
boolean pass = tpsMonitorManager.applyTps(pointName, meta.getConnectionId(), monitorKeys);
if (!pass) {
Response response;
try {
response = super.getDefaultResponseInstance(handlerClazz);
response.setErrorInfo(NacosException.OVER_THRESHOLD, "Tps Flow restricted");
return response;
} catch (Exception e) {
Loggers.TPS_CONTROL_DETAIL
.warn("Tps monitor fail , request: {},exception:{}", request.getClass().getSimpleName(), e);
return null;
}
}
}
return null;
}
}

View File

@ -1,143 +0,0 @@
/*
* Copyright 1999-2020 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.nacos.core.remote.control;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
/**
* tps control point.
*
* @author liuzunfei
* @version $Id: TpsControlPoint.java, v 0.1 2021年01月09日 12:38 PM liuzunfei Exp $
*/
public class TpsControlRule {
private String pointName;
private Rule pointRule;
/**
* Pattern,Rule map.
*/
private Map<String, Rule> monitorKeyRule = new HashMap<>();
public String getPointName() {
return pointName;
}
public void setPointName(String pointName) {
this.pointName = pointName;
}
public Rule getPointRule() {
return pointRule;
}
public void setPointRule(Rule pointRule) {
this.pointRule = pointRule;
}
public Map<String, Rule> getMonitorKeyRule() {
return monitorKeyRule;
}
public void setMonitorKeyRule(Map<String, Rule> monitorKeyRule) {
this.monitorKeyRule = monitorKeyRule;
}
public static class Rule {
long maxCount = -1;
TimeUnit period = TimeUnit.SECONDS;
public static final String MODEL_FUZZY = "FUZZY";
public static final String MODEL_PROTO = "PROTO";
String model = MODEL_FUZZY;
/**
* monitor/intercept.
*/
String monitorType = "";
public Rule() {
}
public boolean isFuzzyModel() {
return MODEL_FUZZY.equalsIgnoreCase(model);
}
public boolean isProtoModel() {
return MODEL_PROTO.equalsIgnoreCase(model);
}
public Rule(long maxCount, TimeUnit period, String model, String monitorType) {
this.maxCount = maxCount;
this.period = period;
this.model = model;
this.monitorType = monitorType;
}
public String getModel() {
return model;
}
public void setModel(String model) {
this.model = model;
}
public TimeUnit getPeriod() {
return period;
}
public void setPeriod(TimeUnit period) {
this.period = period;
}
public long getMaxCount() {
return maxCount;
}
public void setMaxCount(long maxCount) {
this.maxCount = maxCount;
}
public String getMonitorType() {
return monitorType;
}
public void setMonitorType(String monitorType) {
this.monitorType = monitorType;
}
@Override
public String toString() {
return "Rule{" + "maxTps=" + maxCount + ", monitorType='" + monitorType + '\'' + '}';
}
}
@Override
public String toString() {
return "TpsControlRule{" + "pointName='" + pointName + '\'' + ", pointRule=" + pointRule + ", monitorKeyRule="
+ monitorKeyRule + '}';
}
}

View File

@ -1,328 +0,0 @@
/*
* Copyright 1999-2020 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.nacos.core.remote.control;
import com.alibaba.nacos.api.exception.NacosException;
import com.alibaba.nacos.common.executor.ExecutorFactory;
import com.alibaba.nacos.common.notify.Event;
import com.alibaba.nacos.common.notify.NotifyCenter;
import com.alibaba.nacos.common.notify.listener.Subscriber;
import com.alibaba.nacos.common.utils.JacksonUtils;
import com.alibaba.nacos.common.utils.StringUtils;
import com.alibaba.nacos.common.utils.ThreadUtils;
import com.alibaba.nacos.core.utils.Loggers;
import com.alibaba.nacos.sys.env.EnvUtil;
import com.alibaba.nacos.sys.file.FileChangeEvent;
import com.alibaba.nacos.sys.file.FileWatcher;
import com.alibaba.nacos.sys.file.WatchFileCenter;
import com.alibaba.nacos.sys.utils.DiskUtils;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.stereotype.Service;
import java.io.File;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
/**
* tps control manager.
*
* @author liuzunfei
* @version $Id: TpsControlManager.java, v 0.1 2021年01月09日 12:38 PM liuzunfei Exp $
*/
@Service
public class TpsMonitorManager extends Subscriber<TpsControlRuleChangeEvent> implements DisposableBean {
public final Map<String, TpsMonitorPoint> points = new ConcurrentHashMap<>(16);
private static ScheduledExecutorService executorService = ExecutorFactory.newSingleScheduledExecutorService(r -> {
Thread thread = new Thread(r, "nacos.core.remote.tps.control.reporter");
thread.setDaemon(true);
return thread;
});
public TpsMonitorManager() {
NotifyCenter.registerToPublisher(TpsControlRuleChangeEvent.class, NotifyCenter.ringBufferSize);
NotifyCenter.registerSubscriber(this);
executorService.scheduleWithFixedDelay(new TpsMonitorReporter(), 0, 900, TimeUnit.MILLISECONDS);
registerFileWatch();
}
/**
* register point.
*
* @param tpsMonitorPoint tps point.
*/
public void registerTpsControlPoint(TpsMonitorPoint tpsMonitorPoint) {
Loggers.TPS_CONTROL
.info("Register tps control,pointName={}, point={} ", tpsMonitorPoint.getPointName(), tpsMonitorPoint);
try {
loadRuleFromLocal(tpsMonitorPoint);
} catch (IOException e) {
Loggers.TPS_CONTROL
.error("Fail to init rule from local,pointName={},error={}", tpsMonitorPoint.getPointName(), e);
}
points.putIfAbsent(tpsMonitorPoint.getPointName(), tpsMonitorPoint);
}
private void registerFileWatch() {
try {
String tpsPath = Paths.get(EnvUtil.getNacosHome(), "data" + File.separator + "tps" + File.separator)
.toString();
checkBaseDir();
WatchFileCenter.registerWatcher(tpsPath, new FileWatcher() {
@Override
public void onChange(FileChangeEvent event) {
String fileName = event.getContext().toString();
try {
if (points.get(fileName) != null) {
loadRuleFromLocal(points.get(fileName));
}
} catch (Throwable throwable) {
Loggers.TPS_CONTROL
.warn("Fail to load rule from local,pointName={},error={}", fileName, throwable);
}
}
@Override
public boolean interest(String context) {
for (String pointName : points.keySet()) {
if (context.equals(pointName)) {
return true;
}
}
return false;
}
});
} catch (NacosException e) {
Loggers.TPS_CONTROL.warn("Register fire watch fail.", e);
}
}
/**
* apply tps.
*
* @param clientIp clientIp.
* @param pointName pointName.
* @return pass or not.
*/
public boolean applyTpsForClientIp(String pointName, String connectionId, String clientIp) {
if (points.containsKey(pointName)) {
return points.get(pointName).applyTps(connectionId, Arrays.asList(new ClientIpMonitorKey(clientIp)));
}
return true;
}
/**
* apply tps.
*
* @param pointName pointName.
* @param monitorKeyList monitorKeyList.
* @return pass or not.
*/
public boolean applyTps(String pointName, String connectionId, List<MonitorKey> monitorKeyList) {
if (points.containsKey(pointName)) {
return points.get(pointName).applyTps(connectionId, monitorKeyList);
}
return true;
}
@Override
public void onEvent(TpsControlRuleChangeEvent event) {
if (event == null) {
Loggers.TPS_CONTROL.info("Tps control rule change event receive, but event is null");
return;
}
Loggers.TPS_CONTROL
.info("Tps control rule change event receive, pointName={}, ruleContent={} ", event.getPointName(),
event.ruleContent);
if (event.getPointName() == null) {
return;
}
try {
TpsControlRule tpsControlRule = StringUtils.isBlank(event.ruleContent) ? new TpsControlRule()
: JacksonUtils.toObj(event.ruleContent, TpsControlRule.class);
if (!points.containsKey(event.getPointName())) {
Loggers.TPS_CONTROL.info("Tps control rule change event ignore, pointName={} ", event.getPointName());
return;
}
saveRuleToLocal(event.getPointName(), tpsControlRule);
} catch (Exception e) {
Loggers.TPS_CONTROL.warn("Tps control rule apply error, error= ", e);
}
}
@Override
public Class<? extends Event> subscribeType() {
return TpsControlRuleChangeEvent.class;
}
@Override
public void destroy() throws Exception {
if (executorService == null) {
return;
}
ThreadUtils.shutdownThreadPool(executorService);
}
class TpsMonitorReporter implements Runnable {
long lastReportSecond = 0L;
long lastReportMinutes = 0L;
@Override
public void run() {
try {
long now = System.currentTimeMillis();
StringBuilder stringBuilder = new StringBuilder();
Set<Map.Entry<String, TpsMonitorPoint>> entries = points.entrySet();
long tempSecond = 0L;
long tempMinutes = 0L;
String formatString = TpsMonitorPoint.getTimeFormatOfSecond(now - 1000L);
for (Map.Entry<String, TpsMonitorPoint> entry : entries) {
TpsMonitorPoint value = entry.getValue();
//get last second
TpsRecorder.TpsSlot pointSlot = value.getTpsRecorder().getPoint(now - 1000L);
if (pointSlot == null) {
continue;
}
//already reported.
if (lastReportSecond != 0L && lastReportSecond == pointSlot.time) {
continue;
}
String point = entry.getKey();
tempSecond = pointSlot.time;
stringBuilder.append(point).append('|').append("point|").append(value.getTpsRecorder().period)
.append('|').append(formatString).append('|')
.append(pointSlot.getCountHolder(point).count.get()).append('|')
.append(pointSlot.getCountHolder(point).interceptedCount.get()).append('\n');
for (Map.Entry<String, TpsRecorder> monitorKeyEntry : value.monitorKeysRecorder.entrySet()) {
String monitorPattern = monitorKeyEntry.getKey();
TpsRecorder ipRecord = monitorKeyEntry.getValue();
TpsRecorder.TpsSlot keySlot = ipRecord.getPoint(now - ipRecord.period.toMillis(1));
if (keySlot == null) {
continue;
}
//already reported.
if (ipRecord.period == TimeUnit.SECONDS) {
if (lastReportSecond != 0L && lastReportSecond == keySlot.time) {
continue;
}
}
if (ipRecord.period == TimeUnit.MINUTES) {
if (lastReportMinutes != 0L && lastReportMinutes == keySlot.time) {
continue;
}
}
String timeFormatOfSecond = TpsMonitorPoint.getTimeFormatOfSecond(keySlot.time);
tempMinutes = keySlot.time;
if (ipRecord.isProtoModel()) {
Map<String, TpsRecorder.SlotCountHolder> keySlots = ((TpsRecorder.MultiKeyTpsSlot) keySlot).keySlots;
for (Map.Entry<String, TpsRecorder.SlotCountHolder> slotCountHolder : keySlots.entrySet()) {
stringBuilder.append(point).append('|').append(monitorPattern).append('|')
.append(ipRecord.period).append('|').append(timeFormatOfSecond).append('|')
.append(slotCountHolder.getKey()).append('|')
.append(slotCountHolder.getValue().count).append('|')
.append(slotCountHolder.getValue().interceptedCount).append('\n');
}
} else {
stringBuilder.append(point).append('|').append(monitorPattern).append('|')
.append(ipRecord.period).append('|').append(timeFormatOfSecond).append('|')
.append(keySlot.getCountHolder(point).count.get()).append('|')
.append(keySlot.getCountHolder(point).interceptedCount.get()).append('\n');
}
}
}
if (tempSecond > 0) {
lastReportSecond = tempSecond;
}
if (tempMinutes > 0) {
lastReportMinutes = tempMinutes;
}
if (stringBuilder.length() > 0) {
Loggers.TPS_CONTROL_DIGEST.info("Tps reporting...\n" + stringBuilder.toString());
}
} catch (Throwable throwable) {
Loggers.TPS_CONTROL_DIGEST.error("Tps reporting error", throwable);
}
}
}
private synchronized void loadRuleFromLocal(TpsMonitorPoint tpsMonitorPoint) throws IOException {
File pointFile = getRuleFile(tpsMonitorPoint.getPointName());
if (!pointFile.exists()) {
pointFile.createNewFile();
}
String ruleContent = DiskUtils.readFile(pointFile);
TpsControlRule tpsControlRule = StringUtils.isBlank(ruleContent) ? new TpsControlRule()
: JacksonUtils.toObj(ruleContent, TpsControlRule.class);
Loggers.TPS_CONTROL.info("Load rule from local,pointName={}, ruleContent={} ", tpsMonitorPoint.getPointName(),
ruleContent);
tpsMonitorPoint.applyRule(tpsControlRule);
}
private synchronized void saveRuleToLocal(String pointName, TpsControlRule tpsControlRule) throws IOException {
try {
File pointFile = getRuleFile(pointName);
if (!pointFile.exists()) {
pointFile.createNewFile();
}
String content = JacksonUtils.toJson(tpsControlRule);
DiskUtils.writeFile(pointFile, content.getBytes(StandardCharsets.UTF_8), false);
Loggers.TPS_CONTROL.info("Save rule to local,pointName={}, ruleContent ={} ", pointName, content);
} catch (IOException e) {
Loggers.TPS_CONTROL
.warn("Tps control rule persist fail, pointName={},error={} ", pointName, e);
}
}
private File getRuleFile(String pointName) {
File baseDir = checkBaseDir();
return new File(baseDir, pointName);
}
private File checkBaseDir() {
File baseDir = new File(EnvUtil.getNacosHome(), "data" + File.separator + "tps" + File.separator);
if (!baseDir.exists()) {
baseDir.mkdirs();
}
return baseDir;
}
}

View File

@ -1,297 +0,0 @@
/*
* Copyright 1999-2020 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.nacos.core.remote.control;
import com.alibaba.nacos.core.utils.Loggers;
import java.time.Instant;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
/**
* tps control point.
*
* @author liuzunfei
* @version $Id: TpsControlPoint.java, v 0.1 2021年01月09日 12:38 PM liuzunfei Exp $
*/
public class TpsMonitorPoint {
public static final int DEFAULT_RECORD_SIZE = 10;
private static final String DATETIME_PATTERN = "yyyy-MM-dd HH:mm:ss";
private static final DateTimeFormatter DATETIME_FORMATTER = DateTimeFormatter.ofPattern(DATETIME_PATTERN)
.withZone(ZoneId.systemDefault()).withLocale(Locale.getDefault());
private long startTime;
private String pointName;
private TpsRecorder tpsRecorder;
public Map<String, TpsRecorder> monitorKeysRecorder = new HashMap<>();
public TpsMonitorPoint(String pointName) {
this(pointName, -1, "monitor");
}
public TpsMonitorPoint(String pointName, int maxTps, String monitorType) {
// trim to second,uniform all tps control.
this.startTime = getTrimMillsOfSecond(System.currentTimeMillis());
this.pointName = pointName;
this.tpsRecorder = new TpsRecorder(startTime, TimeUnit.SECONDS, TpsControlRule.Rule.MODEL_FUZZY,
DEFAULT_RECORD_SIZE);
this.tpsRecorder.setMaxCount(maxTps);
this.tpsRecorder.setMonitorType(monitorType);
}
/**
* get trim mills of second.
*
* @param timeStamp timestamp milliseconds.
* @return mills of second.
*/
public static long getTrimMillsOfSecond(long timeStamp) {
return timeStamp - timeStamp % 1_000L;
}
/**
* get trim mills of second.
*
* @param timeStamp timestamp milliseconds.
* @return minis of minute.
*/
public static long getTrimMillsOfMinute(long timeStamp) {
return timeStamp - timeStamp % 60_000L;
}
/**
* get trim mills of second.
*
* @param timeStamp timestamp milliseconds.
* @return mills of hour.
*/
public static long getTrimMillsOfHour(long timeStamp) {
return timeStamp - timeStamp % 3_600_000L;
}
/**
* get format string for "yyyy-MM-dd HH:mm:ss".
*
* @param timeStamp timestamp milliseconds.
* @return datetime string.
*/
public static String getTimeFormatOfSecond(long timeStamp) {
return DATETIME_FORMATTER.format(Instant.ofEpochMilli(timeStamp));
}
private void stopAllMonitorClient() {
monitorKeysRecorder.clear();
}
/**
* increase tps.
*
* @param monitorKeys monitorKeys.
* @return check current tps is allowed.
*/
public boolean applyTps(String connectionId, List<MonitorKey> monitorKeys) {
long now = System.currentTimeMillis();
TpsRecorder.TpsSlot currentTps = tpsRecorder.createSlotIfAbsent(now);
//1.check monitor keys.
List<TpsRecorder.SlotCountHolder> passedSlots = new ArrayList<>();
for (MonitorKey monitorKey : monitorKeys) {
for (Map.Entry<String, TpsRecorder> entry : monitorKeysRecorder.entrySet()) {
if (MonitorKeyMatcher.matchWithType(entry.getKey(), monitorKey.build())) {
TpsRecorder tpsRecorderKey = entry.getValue();
TpsRecorder.TpsSlot currentKeySlot = tpsRecorderKey.createSlotIfAbsent(now);
long maxTpsCount = tpsRecorderKey.getMaxCount();
TpsRecorder.SlotCountHolder countHolder = currentKeySlot.getCountHolder(monitorKey.build());
boolean overLimit = maxTpsCount >= 0 && countHolder.count.longValue() >= maxTpsCount;
if (overLimit) {
Loggers.TPS_CONTROL_DETAIL
.info("[{}]Tps over limit ,pointName=[{}],barrier=[{}]monitorModel={},maxTps={}",
connectionId, this.getPointName(), entry.getKey(),
tpsRecorderKey.getMonitorType(), maxTpsCount + "/" + tpsRecorderKey.period);
if (tpsRecorderKey.isInterceptMode()) {
currentKeySlot.getCountHolder(monitorKey.build()).interceptedCount.incrementAndGet();
currentTps.getCountHolder(monitorKey.build()).interceptedCount.incrementAndGet();
return false;
}
} else {
passedSlots.add(countHolder);
}
}
}
}
//2.check total tps.
long maxTps = tpsRecorder.getMaxCount();
boolean overLimit = maxTps >= 0 && currentTps.getCountHolder(pointName).count.longValue() >= maxTps;
if (overLimit) {
Loggers.TPS_CONTROL_DETAIL
.info("[{}]Tps over limit ,pointName=[{}],barrier=[{}]monitorType={}", connectionId,
this.getPointName(), "pointRule", tpsRecorder.getMonitorType());
if (tpsRecorder.isInterceptMode()) {
currentTps.getCountHolder(pointName).interceptedCount.incrementAndGet();
return false;
}
}
currentTps.getCountHolder(pointName).count.incrementAndGet();
for (TpsRecorder.SlotCountHolder passedTpsSlot : passedSlots) {
passedTpsSlot.count.incrementAndGet();
}
//3.check pass.
return true;
}
public TpsRecorder getTpsRecorder() {
return tpsRecorder;
}
public String getPointName() {
return pointName;
}
public void setPointName(String pointName) {
this.pointName = pointName;
}
/**
* apply tps control rule to this point.
*
* @param newControlRule controlRule.
*/
public synchronized void applyRule(TpsControlRule newControlRule) {
Loggers.TPS_CONTROL.info("Apply tps control rule parse start,pointName=[{}] ", this.getPointName());
//1.reset all monitor point for null.
if (newControlRule == null) {
Loggers.TPS_CONTROL.info("Clear all tps control rule ,pointName=[{}] ", this.getPointName());
this.tpsRecorder.clearLimitRule();
this.stopAllMonitorClient();
return;
}
//2.check point rule.
TpsControlRule.Rule newPointRule = newControlRule.getPointRule();
if (newPointRule == null) {
Loggers.TPS_CONTROL.info("Clear point control rule ,pointName=[{}] ", this.getPointName());
this.tpsRecorder.clearLimitRule();
} else {
Loggers.TPS_CONTROL.info("Update point control rule ,pointName=[{}],original maxTps={}, new maxTps={}"
+ ",original monitorType={}, original monitorType={}, ", this.getPointName(),
this.tpsRecorder.getMaxCount(), newPointRule.maxCount, this.tpsRecorder.getMonitorType(),
newPointRule.monitorType);
this.tpsRecorder.setMaxCount(newPointRule.maxCount);
this.tpsRecorder.setMonitorType(newPointRule.monitorType);
}
//3.check monitor key rules.
Map<String, TpsControlRule.Rule> newMonitorKeyRules = newControlRule.getMonitorKeyRule();
//3.1 clear all monitor keys.
if (newMonitorKeyRules == null || newMonitorKeyRules.isEmpty()) {
Loggers.TPS_CONTROL
.info("Clear point control rule for monitorKeys, pointName=[{}] ", this.getPointName());
this.stopAllMonitorClient();
} else {
Map<String, TpsRecorder> monitorKeysRecorderCurrent = this.monitorKeysRecorder;
for (Map.Entry<String, TpsControlRule.Rule> newMonitorRule : newMonitorKeyRules.entrySet()) {
if (newMonitorRule.getValue() == null) {
continue;
}
boolean checkPattern = newMonitorRule.getKey() != null;
if (!checkPattern) {
Loggers.TPS_CONTROL.info("Invalid monitor rule, pointName=[{}] ,monitorRule={} ,Ignore this.",
this.getPointName(), newMonitorRule.getKey());
continue;
}
TpsControlRule.Rule newRule = newMonitorRule.getValue();
if (newRule.period == null) {
newRule.period = TimeUnit.SECONDS;
}
if (newRule.model == null) {
newRule.model = TpsControlRule.Rule.MODEL_FUZZY;
}
//update rule.
if (monitorKeysRecorderCurrent.containsKey(newMonitorRule.getKey())) {
TpsRecorder tpsRecorder = monitorKeysRecorderCurrent.get(newMonitorRule.getKey());
Loggers.TPS_CONTROL
.info("Update point control rule for client ip ,pointName=[{}],monitorKey=[{}],original maxTps={}"
+ ", new maxTps={},original monitorType={}, new monitorType={}, ",
this.getPointName(), newMonitorRule.getKey(), tpsRecorder.getMaxCount(),
newRule.maxCount, tpsRecorder.getMonitorType(), newRule.monitorType);
if (!Objects.equals(tpsRecorder.period, newRule.period) || !Objects
.equals(tpsRecorder.getModel(), newRule.model)) {
TpsRecorder tpsRecorderNew = new TpsRecorder(startTime, newRule.period, newRule.model,
DEFAULT_RECORD_SIZE);
tpsRecorderNew.setMaxCount(newRule.maxCount);
tpsRecorderNew.setMonitorType(newRule.monitorType);
monitorKeysRecorderCurrent.put(newMonitorRule.getKey(), tpsRecorderNew);
} else {
tpsRecorder.setMaxCount(newRule.maxCount);
tpsRecorder.setMonitorType(newRule.monitorType);
}
} else {
Loggers.TPS_CONTROL
.info("Add point control rule for client ip ,pointName=[{}],monitorKey=[{}], new maxTps={}, new monitorType={}, ",
this.getPointName(), newMonitorRule.getKey(), newMonitorRule.getValue().maxCount,
newMonitorRule.getValue().monitorType);
// add rule
TpsRecorder tpsRecorderAdd = new TpsRecorder(startTime, newRule.period, newRule.model,
DEFAULT_RECORD_SIZE);
tpsRecorderAdd.setMaxCount(newRule.maxCount);
tpsRecorderAdd.setMonitorType(newRule.monitorType);
monitorKeysRecorderCurrent.put(newMonitorRule.getKey(), tpsRecorderAdd);
}
}
//delete rule.
Iterator<Map.Entry<String, TpsRecorder>> iteratorCurrent = monitorKeysRecorderCurrent.entrySet().iterator();
while (iteratorCurrent.hasNext()) {
Map.Entry<String, TpsRecorder> next1 = iteratorCurrent.next();
if (!newMonitorKeyRules.containsKey(next1.getKey())) {
Loggers.TPS_CONTROL.info("Delete point control rule for pointName=[{}] ,monitorKey=[{}]",
this.getPointName(), next1.getKey());
iteratorCurrent.remove();
}
}
}
}
}

View File

@ -1,220 +0,0 @@
/*
* Copyright 1999-2020 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.nacos.core.remote.control;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;
/**
* tps record.
*
* @author liuzunfei
* @version $Id: TpsRecorder.java, v 0.1 2021年01月09日 12:38 PM liuzunfei Exp $
*/
public class TpsRecorder {
private long startTime;
TimeUnit period;
private int slotSize;
private List<TpsSlot> slotList;
private long maxCount = -1;
private String model;
/**
* monitor/intercept.
*/
private String monitorType = MonitorType.MONITOR.type;
public TpsRecorder(long startTime, TimeUnit period, String model, int recordSize) {
this.startTime = startTime;
if (period.equals(TimeUnit.MINUTES)) {
this.startTime = TpsMonitorPoint.getTrimMillsOfMinute(startTime);
}
if (period.equals(TimeUnit.HOURS)) {
this.startTime = TpsMonitorPoint.getTrimMillsOfHour(startTime);
}
this.period = period;
this.model = model;
this.slotSize = recordSize + 1;
slotList = new ArrayList<>(slotSize);
for (int i = 0; i < slotSize; i++) {
slotList.add(isProtoModel() ? new MultiKeyTpsSlot() : new TpsSlot());
}
}
public boolean isProtoModel() {
return TpsControlRule.Rule.MODEL_PROTO.equalsIgnoreCase(this.model);
}
public String getModel() {
return model;
}
public void setModel(String model) {
this.model = model;
}
/**
* get slot of the timestamp second,create if not exist.
*
* @param timeStamp the timestamp second.
* @return tps slot.
*/
public TpsSlot createSlotIfAbsent(long timeStamp) {
long distance = timeStamp - startTime;
long diff = (distance < 0 ? distance + period.toMillis(1) * slotSize : distance) / period.toMillis(1);
long currentWindowTime = startTime + diff * period.toMillis(1);
int index = (int) diff % slotSize;
if (slotList.get(index).time != currentWindowTime) {
slotList.get(index).reset(currentWindowTime);
}
return slotList.get(index);
}
/**
* get slot of the timestamp second,read only ,return nul if not exist.
*
* @param timeStamp the timestamp second.
* @return tps slot.
*/
public TpsSlot getPoint(long timeStamp) {
long distance = timeStamp - startTime;
long diff = (distance < 0 ? distance + period.toMillis(1) * slotSize : distance) / period.toMillis(1);
long currentWindowTime = startTime + diff * period.toMillis(1);
int index = (int) diff % slotSize;
TpsSlot tpsSlot = slotList.get(index);
if (tpsSlot.time != currentWindowTime) {
return null;
}
return tpsSlot;
}
public long getMaxCount() {
return maxCount;
}
public void setMaxCount(long maxCount) {
this.maxCount = maxCount;
}
public boolean isInterceptMode() {
return MonitorType.INTERCEPT.type.equals(this.monitorType);
}
/**
* clearLimitRule.
*/
public void clearLimitRule() {
this.setMonitorType(MonitorType.MONITOR.type);
this.setMaxCount(-1);
}
public String getMonitorType() {
return monitorType;
}
public void setMonitorType(String monitorType) {
this.monitorType = monitorType;
}
static class TpsSlot {
long time = 0L;
private SlotCountHolder countHolder = new SlotCountHolder();
public SlotCountHolder getCountHolder(String key) {
return countHolder;
}
public void reset(long second) {
synchronized (this) {
if (this.time != second) {
this.time = second;
countHolder.count.set(0L);
countHolder.interceptedCount.set(0);
}
}
}
@Override
public String toString() {
return "TpsSlot{" + "time=" + time + ", countHolder=" + countHolder + '}';
}
}
static class MultiKeyTpsSlot extends TpsSlot {
Map<String, SlotCountHolder> keySlots = new HashMap<>(16);
@Override
public SlotCountHolder getCountHolder(String key) {
if (!keySlots.containsKey(key)) {
keySlots.putIfAbsent(key, new SlotCountHolder());
}
return keySlots.get(key);
}
public Map<String, SlotCountHolder> getKeySlots() {
return keySlots;
}
@Override
public void reset(long second) {
synchronized (this) {
if (this.time != second) {
this.time = second;
keySlots.clear();
}
}
}
@Override
public String toString() {
return "MultiKeyTpsSlot{" + "time=" + time + "}'";
}
}
static class SlotCountHolder {
AtomicLong count = new AtomicLong();
AtomicLong interceptedCount = new AtomicLong();
@Override
public String toString() {
return "{" + count + "|" + interceptedCount + '}';
}
}
public List<TpsSlot> getSlotList() {
return slotList;
}
}

View File

@ -21,7 +21,6 @@ import com.alibaba.nacos.api.remote.RemoteConstants;
import com.alibaba.nacos.api.remote.request.RequestMeta;
import com.alibaba.nacos.api.remote.request.ServerLoaderInfoRequest;
import com.alibaba.nacos.api.remote.response.ServerLoaderInfoResponse;
import com.alibaba.nacos.common.utils.JacksonUtils;
import com.alibaba.nacos.core.remote.ConnectionManager;
import com.alibaba.nacos.core.remote.RequestHandler;
import com.alibaba.nacos.sys.env.EnvUtil;
@ -51,7 +50,6 @@ public class ServerLoaderInfoRequestHandler extends RequestHandler<ServerLoaderI
filter.put(RemoteConstants.LABEL_SOURCE, RemoteConstants.LABEL_SOURCE_SDK);
serverLoaderInfoResponse
.putMetricsValue("sdkConCount", String.valueOf(connectionManager.currentClientsCount(filter)));
serverLoaderInfoResponse.putMetricsValue("limitRule", JacksonUtils.toJson(connectionManager.getConnectionLimitRule()));
serverLoaderInfoResponse.putMetricsValue("load", String.valueOf(EnvUtil.getLoad()));
serverLoaderInfoResponse.putMetricsValue("cpu", String.valueOf(EnvUtil.getCpu()));

View File

@ -44,13 +44,6 @@ public class Loggers {
public static final Logger REMOTE_DIGEST = LoggerFactory.getLogger("com.alibaba.nacos.core.remote.digest");
public static final Logger TPS_CONTROL_DIGEST = LoggerFactory
.getLogger("com.alibaba.nacos.core.remote.control.digest");
public static final Logger TPS_CONTROL = LoggerFactory.getLogger("com.alibaba.nacos.core.remote.control");
public static final Logger TPS_CONTROL_DETAIL = LoggerFactory.getLogger("com.alibaba.nacos.core.remote.control.detail");
public static void setLogLevel(String logName, String level) {
switch (logName) {

View File

@ -138,15 +138,15 @@
</appender>
<!--TPS control -->
<appender name="tps-control"
<appender name="plugin-control-connection"
class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_HOME}/tps-control.log</file>
<file>${LOG_HOME}/plugin-control-connection.log</file>
<append>true</append>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>${LOG_HOME}/tps-control.log.%d{yyyy-MM-dd}.%i</fileNamePattern>
<fileNamePattern>${LOG_HOME}/plugin-control-connection.log.%d{yyyy-MM-dd}.%i</fileNamePattern>
<maxFileSize>2GB</maxFileSize>
<maxHistory>7</maxHistory>
<totalSizeCap>7GB</totalSizeCap>
<totalSizeCap>2GB</totalSizeCap>
<cleanHistoryOnStart>true</cleanHistoryOnStart>
</rollingPolicy>
<encoder>
@ -154,15 +154,15 @@
<charset>UTF-8</charset>
</encoder>
</appender>
<appender name="tps-control-detail"
<appender name="plugin-control-tps"
class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_HOME}/tps-control-detail.log</file>
<file>${LOG_HOME}/plugin-control-tps.log</file>
<append>true</append>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>${LOG_HOME}/tps-control-detail.log.%d{yyyy-MM-dd}.%i</fileNamePattern>
<fileNamePattern>${LOG_HOME}/plugin-control-tps.log.%d{yyyy-MM-dd}.%i</fileNamePattern>
<maxFileSize>2GB</maxFileSize>
<maxHistory>7</maxHistory>
<totalSizeCap>7GB</totalSizeCap>
<totalSizeCap>2GB</totalSizeCap>
<cleanHistoryOnStart>true</cleanHistoryOnStart>
</rollingPolicy>
<encoder>
@ -171,14 +171,14 @@
</encoder>
</appender>
<appender name="tps-control-digest"
<appender name="plugin-control"
class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_HOME}/tps-control-digest.log</file>
<file>${LOG_HOME}/plugin-control.log</file>
<append>true</append>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>${LOG_HOME}/tps-control-digest.log.%d{yyyy-MM-dd}.%i</fileNamePattern>
<fileNamePattern>${LOG_HOME}/plugin-control.%d{yyyy-MM-dd}.%i</fileNamePattern>
<maxFileSize>2GB</maxFileSize>
<maxHistory>7</maxHistory>
<maxHistory>14</maxHistory>
<totalSizeCap>7GB</totalSizeCap>
<cleanHistoryOnStart>true</cleanHistoryOnStart>
</rollingPolicy>
@ -261,18 +261,18 @@
</logger>
<!-- TPS Control-->
<logger name="com.alibaba.nacos.core.remote.control.digest" additivity="false">
<logger name="com.alibaba.nacos.plugin.control" additivity="false">
<level value="DEBUG"/>
<appender-ref ref="tps-control-digest"/>
<appender-ref ref="plugin-control"/>
</logger>
<logger name="com.alibaba.nacos.core.remote.control.detail" additivity="false">
<logger name="com.alibaba.nacos.plugin.control.tps" additivity="false">
<level value="DEBUG"/>
<appender-ref ref="tps-control-detail"/>
<appender-ref ref="plugin-control-tps"/>
</logger>
<logger name="com.alibaba.nacos.core.remote.control" additivity="false">
<logger name="com.alibaba.nacos.plugin.control.connection" additivity="false">
<level value="DEBUG"/>
<appender-ref ref="tps-control"/>
<appender-ref ref="plugin-control-connection"/>
</logger>

View File

@ -0,0 +1,19 @@
#
#
# Copyright 1999-2021 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.
#
#
com.alibaba.nacos.core.remote.LongConnectionMetricsCollector

View File

@ -19,24 +19,24 @@ package com.alibaba.nacos.core.remote;
import com.alibaba.nacos.api.exception.NacosException;
import com.alibaba.nacos.api.remote.RemoteConstants;
import com.alibaba.nacos.common.notify.NotifyCenter;
import com.alibaba.nacos.core.remote.event.ConnectionLimitRuleChangeEvent;
import com.alibaba.nacos.core.remote.grpc.GrpcConnection;
import com.alibaba.nacos.plugin.control.configs.ControlConfigs;
import com.alibaba.nacos.sys.env.EnvUtil;
import com.alibaba.nacos.sys.file.WatchFileCenter;
import io.grpc.netty.shaded.io.netty.channel.Channel;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Assert;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockedStatic;
import org.mockito.Mockito;
import org.mockito.junit.MockitoJUnitRunner;
import java.io.File;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
@ -54,7 +54,7 @@ public class ConnectionManagerTest {
private ConnectionManager connectionManager;
@Mock
private ClientConnectionEventListenerRegistry registry;
private ClientConnectionEventListenerRegistry clientConnectionEventListenerRegistry;
@InjectMocks
private GrpcConnection connection;
@ -69,6 +69,22 @@ public class ConnectionManagerTest {
private String clientIp = "1.1.1.1";
static MockedStatic<ControlConfigs> propertyUtilMockedStatic;
@BeforeClass
public static void setUpClass() {
propertyUtilMockedStatic = Mockito.mockStatic(ControlConfigs.class);
propertyUtilMockedStatic.when(ControlConfigs::getInstance).thenReturn(new ControlConfigs());
}
@AfterClass
public static void afterClass() {
if (propertyUtilMockedStatic != null) {
propertyUtilMockedStatic.close();
}
}
@Before
public void setUp() {
// create base file path
@ -78,9 +94,9 @@ public class ConnectionManagerTest {
}
connectId = UUID.randomUUID().toString();
connectionManager.start();
connectionManager.initLimitRue();
Mockito.when(channel.isOpen()).thenReturn(true);
Mockito.when(channel.isActive()).thenReturn(true);
connectionMeta.clientIp = clientIp;
Map<String, String> labels = new HashMap<>();
labels.put("key", "value");
@ -95,11 +111,6 @@ public class ConnectionManagerTest {
public void tearDown() {
connectionManager.unregister(connectId);
String tpsPath = Paths.get(EnvUtil.getNacosHome(), "data", "loader").toString();
WatchFileCenter.deregisterAllWatcher(tpsPath);
NotifyCenter.deregisterSubscriber(connectionManager);
NotifyCenter.deregisterPublisher(ConnectionLimitRuleChangeEvent.class);
}
@Test
@ -109,7 +120,7 @@ public class ConnectionManagerTest {
@Test
public void testTraced() {
Assert.assertTrue(connectionManager.traced(clientIp));
Assert.assertFalse(connectionManager.traced(clientIp));
}
@Test
@ -155,24 +166,5 @@ public class ConnectionManagerTest {
Assert.assertEquals(1, connectionManager.currentSdkClientCount());
}
@Test
public void testOnEvent() {
try {
String limitRule = "{\"monitorIpList\": [\"1.1.1.1\", \"2.2.2.2\"], \"countLimit\": 1}";
ConnectionLimitRuleChangeEvent limitRuleChangeEvent = new ConnectionLimitRuleChangeEvent(limitRule);
connectionManager.onEvent(limitRuleChangeEvent);
ConnectionManager.ConnectionLimitRule connectionLimitRule = connectionManager.getConnectionLimitRule();
Assert.assertEquals(1, connectionLimitRule.getCountLimit());
} catch (Exception e) {
e.printStackTrace();
Assert.fail(e.getMessage());
}
}
@Test
public void testGetSubscribeType() {
Assert.assertEquals(ConnectionLimitRuleChangeEvent.class, connectionManager.subscribeType());
}
}

View File

@ -18,7 +18,6 @@
package com.alibaba.nacos.core.remote;
import com.alibaba.nacos.api.remote.request.HealthCheckRequest;
import com.alibaba.nacos.core.remote.control.TpsMonitorManager;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
@ -45,9 +44,6 @@ public class RequestHandlerRegistryTest {
@InjectMocks
private RequestHandlerRegistry registry;
@Mock
private TpsMonitorManager tpsMonitorManager;
@InjectMocks
private ContextRefreshedEvent contextRefreshedEvent;

View File

@ -1,44 +0,0 @@
/*
* Copyright 1999-2021 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.nacos.core.remote.control;
import org.junit.Assert;
import org.junit.Test;
/**
* ${@link MonitorKeyMatcher} unit tests.
*
* @author chenglu
* @date 2021-06-18 13:11
*/
public class MonitorKeyMatcherTest {
@Test
public void testWithMatchType() {
boolean match1 = MonitorKeyMatcher.matchWithType("A:*", "A:ddd");
Assert.assertTrue(match1);
boolean match2 = MonitorKeyMatcher.matchWithType("A:*a", "A:dda");
Assert.assertTrue(match2);
boolean match3 = MonitorKeyMatcher.matchWithType("A:a*", "A:add");
Assert.assertTrue(match3);
boolean match4 = MonitorKeyMatcher.matchWithType("A:add", "A:add");
Assert.assertTrue(match4);
}
}

View File

@ -1,99 +0,0 @@
/*
* Copyright 1999-2021 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.nacos.core.remote.control;
import com.alibaba.nacos.common.utils.CollectionUtils;
import org.junit.Assert;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import java.util.Random;
import java.util.concurrent.TimeUnit;
import static org.junit.Assert.assertTrue;
public class TpsMonitorManagerTest {
static TpsMonitorManager tpsMonitorManager;
@BeforeClass
public static void setUpBeforeClass() throws InterruptedException {
tpsMonitorManager = new TpsMonitorManager();
TpsMonitorPoint publish = new TpsMonitorPoint("configPublish");
tpsMonitorManager.registerTpsControlPoint(publish);
TimeUnit.SECONDS.sleep(1);
TpsControlRule rule = new TpsControlRule();
rule.setPointRule(new TpsControlRule.Rule(5000, TimeUnit.SECONDS, "SUM", "intercept"));
rule.getMonitorKeyRule()
.putIfAbsent("testKey:a*b", new TpsControlRule.Rule(500, TimeUnit.SECONDS, "EACH", "intercept"));
rule.getMonitorKeyRule()
.putIfAbsent("testKey:*", new TpsControlRule.Rule(2000000, TimeUnit.SECONDS, "SUM", "intercept"));
publish.applyRule(rule);
}
@Before
public void setUp() throws InterruptedException {
// make sure different case will not effect each other.
TimeUnit.SECONDS.sleep(1);
}
@Test
public void testApplyTps() {
for (int i = 0; i < 100; i++) {
String value = "atg" + (new Random().nextInt(100) + 2) + "efb";
boolean pass = tpsMonitorManager
.applyTps("configPublish", "testconnectionId", CollectionUtils.list(new TestKey(value)));
assertTrue(pass);
try {
Thread.sleep(1L);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
@Test
public void testApplyTpsWithOverFlow() {
for (int i = 0; i < 1000; i++) {
String value = "atg" + (new Random().nextInt(100) + 2) + "efb";
boolean pass = tpsMonitorManager
.applyTps("configPublish", "testconnectionId", CollectionUtils.list(new TestKey(value)));
if (!pass) {
return;
}
try {
Thread.sleep(1L);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
Assert.fail("fail to limit.");
}
class TestKey extends MonitorKey {
public TestKey(String key) {
setKey(key);
}
@Override
public String getType() {
return "testKey";
}
}
}

View File

@ -1,51 +0,0 @@
/*
* Copyright 1999-2021 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.nacos.core.remote.control;
import org.junit.Test;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.util.Locale;
import static org.junit.Assert.assertEquals;
/**
* {@link TpsMonitorPoint} unit tests.
*
* @author chenglu
* @date 2021-06-18 14:02
*/
public class TpsMonitorPointTest {
@Test
public void testStaticMethod() {
//2022-08-23 14:23:53
assertEquals(1661235833000L, TpsMonitorPoint.getTrimMillsOfSecond(1661235833111L));
//2022-08-23 14:23:00
assertEquals(1661235780000L, TpsMonitorPoint.getTrimMillsOfMinute(1661235833111L));
//2022-08-23 14:00:00
assertEquals(1661234400000L, TpsMonitorPoint.getTrimMillsOfHour(1661235833111L));
assertEquals(LocalDateTime.of(2022, 8, 23, 14, 23, 53).atZone(ZoneOffset.ofHours(8))
.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").withZone(ZoneId.systemDefault())
.withLocale(Locale.getDefault())), TpsMonitorPoint.getTimeFormatOfSecond(1661235833111L));
}
}

View File

@ -1,63 +0,0 @@
/*
* Copyright 1999-2021 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.nacos.core.remote.control;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import java.util.concurrent.TimeUnit;
/**
* {@link TpsRecorder} unit test.
*
* @author chenglu
* @date 2021-06-21 18:23
*/
public class TpsRecorderTest {
TpsRecorder tpsRecorder;
long start;
@Before
public void init() {
start = System.currentTimeMillis();
tpsRecorder = new TpsRecorder(start, TimeUnit.SECONDS, TpsControlRule.Rule.MODEL_PROTO, 10);
}
@Test
public void testTpsSlot() {
long current = start + 100;
TpsRecorder.TpsSlot tpsSlot = tpsRecorder.createSlotIfAbsent(start);
Assert.assertNotNull(tpsSlot);
TpsRecorder.TpsSlot tpsSlot1 = tpsRecorder.getPoint(current);
Assert.assertNotNull(tpsSlot1);
}
@Test
public void testMonitorType() {
Assert.assertTrue(tpsRecorder.isProtoModel());
Assert.assertEquals(MonitorType.MONITOR.type, tpsRecorder.getMonitorType());
tpsRecorder.clearLimitRule();
Assert.assertFalse(tpsRecorder.isInterceptMode());
}
}

View File

@ -49,7 +49,6 @@ public class ServerLoaderInfoRequestHandlerTest {
public void testHandle() {
Mockito.when(connectionManager.currentClientsCount()).thenReturn(1);
Mockito.when(connectionManager.currentClientsCount(Mockito.any())).thenReturn(1);
Mockito.when(connectionManager.getConnectionLimitRule()).thenReturn(null);
ServerLoaderInfoRequest request = new ServerLoaderInfoRequest();
RequestMeta meta = new RequestMeta();

View File

@ -16,9 +16,9 @@
package com.alibaba.nacos.naming.monitor;
import com.alibaba.nacos.core.remote.control.TpsMonitorManager;
import com.alibaba.nacos.core.remote.control.TpsMonitorPoint;
import com.alibaba.nacos.sys.utils.ApplicationUtils;
import com.alibaba.nacos.plugin.control.ControlManagerCenter;
import com.alibaba.nacos.plugin.control.tps.TpsControlManager;
import com.alibaba.nacos.plugin.control.tps.request.TpsCheckRequest;
/**
* Tps and flow control monitor singleton for Naming.
@ -29,30 +29,29 @@ public class NamingTpsMonitor {
private static final NamingTpsMonitor INSTANCE = new NamingTpsMonitor();
private final TpsMonitorManager tpsMonitorManager;
private final TpsControlManager tpsControlManager = ControlManagerCenter.getInstance().getTpsControlManager();
private NamingTpsMonitor() {
this.tpsMonitorManager = ApplicationUtils.getBean(TpsMonitorManager.class);
registerPushMonitorPoint();
registerDistroMonitorPoint();
}
private void registerPushMonitorPoint() {
tpsMonitorManager.registerTpsControlPoint(new TpsMonitorPoint(TpsMonitorItem.NAMING_RPC_PUSH.name()));
tpsMonitorManager.registerTpsControlPoint(new TpsMonitorPoint(TpsMonitorItem.NAMING_RPC_PUSH_SUCCESS.name()));
tpsMonitorManager.registerTpsControlPoint(new TpsMonitorPoint(TpsMonitorItem.NAMING_RPC_PUSH_FAIL.name()));
tpsMonitorManager.registerTpsControlPoint(new TpsMonitorPoint(TpsMonitorItem.NAMING_UDP_PUSH.name()));
tpsMonitorManager.registerTpsControlPoint(new TpsMonitorPoint(TpsMonitorItem.NAMING_UDP_PUSH_SUCCESS.name()));
tpsMonitorManager.registerTpsControlPoint(new TpsMonitorPoint(TpsMonitorItem.NAMING_UDP_PUSH_FAIL.name()));
tpsControlManager.registerTpsPoint(TpsMonitorItem.NAMING_RPC_PUSH.name());
tpsControlManager.registerTpsPoint(TpsMonitorItem.NAMING_RPC_PUSH_SUCCESS.name());
tpsControlManager.registerTpsPoint(TpsMonitorItem.NAMING_RPC_PUSH_FAIL.name());
tpsControlManager.registerTpsPoint(TpsMonitorItem.NAMING_UDP_PUSH.name());
tpsControlManager.registerTpsPoint(TpsMonitorItem.NAMING_UDP_PUSH_SUCCESS.name());
tpsControlManager.registerTpsPoint(TpsMonitorItem.NAMING_UDP_PUSH_FAIL.name());
}
private void registerDistroMonitorPoint() {
tpsMonitorManager.registerTpsControlPoint(new TpsMonitorPoint(TpsMonitorItem.NAMING_DISTRO_SYNC.name()));
tpsMonitorManager.registerTpsControlPoint(new TpsMonitorPoint(TpsMonitorItem.NAMING_DISTRO_SYNC_SUCCESS.name()));
tpsMonitorManager.registerTpsControlPoint(new TpsMonitorPoint(TpsMonitorItem.NAMING_DISTRO_SYNC_FAIL.name()));
tpsMonitorManager.registerTpsControlPoint(new TpsMonitorPoint(TpsMonitorItem.NAMING_DISTRO_VERIFY.name()));
tpsMonitorManager.registerTpsControlPoint(new TpsMonitorPoint(TpsMonitorItem.NAMING_DISTRO_VERIFY_SUCCESS.name()));
tpsMonitorManager.registerTpsControlPoint(new TpsMonitorPoint(TpsMonitorItem.NAMING_DISTRO_VERIFY_FAIL.name()));
tpsControlManager.registerTpsPoint(TpsMonitorItem.NAMING_DISTRO_SYNC.name());
tpsControlManager.registerTpsPoint(TpsMonitorItem.NAMING_DISTRO_SYNC_SUCCESS.name());
tpsControlManager.registerTpsPoint(TpsMonitorItem.NAMING_DISTRO_SYNC_FAIL.name());
tpsControlManager.registerTpsPoint(TpsMonitorItem.NAMING_DISTRO_VERIFY.name());
tpsControlManager.registerTpsPoint(TpsMonitorItem.NAMING_DISTRO_VERIFY_SUCCESS.name());
tpsControlManager.registerTpsPoint(TpsMonitorItem.NAMING_DISTRO_VERIFY_FAIL.name());
}
public static NamingTpsMonitor getInstance() {
@ -66,8 +65,8 @@ public class NamingTpsMonitor {
* @param clientIp client ip
*/
public static void rpcPushSuccess(String clientId, String clientIp) {
INSTANCE.tpsMonitorManager.applyTpsForClientIp(TpsMonitorItem.NAMING_RPC_PUSH.name(), clientId, clientIp);
INSTANCE.tpsMonitorManager.applyTpsForClientIp(TpsMonitorItem.NAMING_RPC_PUSH_SUCCESS.name(), clientId, clientIp);
INSTANCE.tpsControlManager.check(new TpsCheckRequest(TpsMonitorItem.NAMING_RPC_PUSH.name(), clientId, clientIp));
INSTANCE.tpsControlManager.check(new TpsCheckRequest(TpsMonitorItem.NAMING_RPC_PUSH_SUCCESS.name(), clientId, clientIp));
}
/**
@ -77,8 +76,8 @@ public class NamingTpsMonitor {
* @param clientIp client ip
*/
public static void rpcPushFail(String clientId, String clientIp) {
INSTANCE.tpsMonitorManager.applyTpsForClientIp(TpsMonitorItem.NAMING_RPC_PUSH.name(), clientId, clientIp);
INSTANCE.tpsMonitorManager.applyTpsForClientIp(TpsMonitorItem.NAMING_RPC_PUSH_FAIL.name(), clientId, clientIp);
INSTANCE.tpsControlManager.check(new TpsCheckRequest(TpsMonitorItem.NAMING_RPC_PUSH.name(), clientId, clientIp));
INSTANCE.tpsControlManager.check(new TpsCheckRequest(TpsMonitorItem.NAMING_RPC_PUSH_FAIL.name(), clientId, clientIp));
}
/**
@ -88,8 +87,9 @@ public class NamingTpsMonitor {
* @param clientIp client ip
*/
public static void udpPushSuccess(String clientId, String clientIp) {
INSTANCE.tpsMonitorManager.applyTpsForClientIp(TpsMonitorItem.NAMING_UDP_PUSH.name(), clientId, clientIp);
INSTANCE.tpsMonitorManager.applyTpsForClientIp(TpsMonitorItem.NAMING_UDP_PUSH_SUCCESS.name(), clientId, clientIp);
INSTANCE.tpsControlManager.check(new TpsCheckRequest(TpsMonitorItem.NAMING_UDP_PUSH.name(), clientId, clientIp));
INSTANCE.tpsControlManager
.check(new TpsCheckRequest(TpsMonitorItem.NAMING_UDP_PUSH_SUCCESS.name(), clientId, clientIp));
}
/**
@ -99,8 +99,8 @@ public class NamingTpsMonitor {
* @param clientIp client ip
*/
public static void udpPushFail(String clientId, String clientIp) {
INSTANCE.tpsMonitorManager.applyTpsForClientIp(TpsMonitorItem.NAMING_UDP_PUSH.name(), clientId, clientIp);
INSTANCE.tpsMonitorManager.applyTpsForClientIp(TpsMonitorItem.NAMING_UDP_PUSH_FAIL.name(), clientId, clientIp);
INSTANCE.tpsControlManager.check(new TpsCheckRequest(TpsMonitorItem.NAMING_UDP_PUSH.name(), clientId, clientIp));
INSTANCE.tpsControlManager.check(new TpsCheckRequest(TpsMonitorItem.NAMING_UDP_PUSH_FAIL.name(), clientId, clientIp));
}
/**
@ -110,8 +110,9 @@ public class NamingTpsMonitor {
* @param clientIp client ip
*/
public static void distroSyncSuccess(String clientId, String clientIp) {
INSTANCE.tpsMonitorManager.applyTpsForClientIp(TpsMonitorItem.NAMING_DISTRO_SYNC.name(), clientId, clientIp);
INSTANCE.tpsMonitorManager.applyTpsForClientIp(TpsMonitorItem.NAMING_DISTRO_SYNC_SUCCESS.name(), clientId, clientIp);
INSTANCE.tpsControlManager.check(new TpsCheckRequest(TpsMonitorItem.NAMING_DISTRO_SYNC.name(), clientId, clientIp));
INSTANCE.tpsControlManager
.check(new TpsCheckRequest(TpsMonitorItem.NAMING_DISTRO_SYNC_SUCCESS.name(), clientId, clientIp));
}
/**
@ -121,8 +122,9 @@ public class NamingTpsMonitor {
* @param clientIp client ip
*/
public static void distroSyncFail(String clientId, String clientIp) {
INSTANCE.tpsMonitorManager.applyTpsForClientIp(TpsMonitorItem.NAMING_DISTRO_SYNC.name(), clientId, clientIp);
INSTANCE.tpsMonitorManager.applyTpsForClientIp(TpsMonitorItem.NAMING_DISTRO_SYNC_FAIL.name(), clientId, clientIp);
INSTANCE.tpsControlManager.check(new TpsCheckRequest(TpsMonitorItem.NAMING_DISTRO_SYNC.name(), clientId, clientIp));
INSTANCE.tpsControlManager
.check(new TpsCheckRequest(TpsMonitorItem.NAMING_DISTRO_SYNC_FAIL.name(), clientId, clientIp));
}
/**
@ -132,8 +134,9 @@ public class NamingTpsMonitor {
* @param clientIp client ip
*/
public static void distroVerifySuccess(String clientId, String clientIp) {
INSTANCE.tpsMonitorManager.applyTpsForClientIp(TpsMonitorItem.NAMING_DISTRO_VERIFY.name(), clientId, clientIp);
INSTANCE.tpsMonitorManager.applyTpsForClientIp(TpsMonitorItem.NAMING_DISTRO_VERIFY_SUCCESS.name(), clientId, clientIp);
INSTANCE.tpsControlManager.check(new TpsCheckRequest(TpsMonitorItem.NAMING_DISTRO_VERIFY.name(), clientId, clientIp));
INSTANCE.tpsControlManager
.check(new TpsCheckRequest(TpsMonitorItem.NAMING_DISTRO_VERIFY_SUCCESS.name(), clientId, clientIp));
}
/**
@ -143,7 +146,9 @@ public class NamingTpsMonitor {
* @param clientIp client ip
*/
public static void distroVerifyFail(String clientId, String clientIp) {
INSTANCE.tpsMonitorManager.applyTpsForClientIp(TpsMonitorItem.NAMING_DISTRO_VERIFY.name(), clientId, clientIp);
INSTANCE.tpsMonitorManager.applyTpsForClientIp(TpsMonitorItem.NAMING_DISTRO_VERIFY_FAIL.name(), clientId, clientIp);
INSTANCE.tpsControlManager.check(new TpsCheckRequest(TpsMonitorItem.NAMING_DISTRO_VERIFY.name(), clientId, clientIp));
INSTANCE.tpsControlManager
.check(new TpsCheckRequest(TpsMonitorItem.NAMING_DISTRO_VERIFY_FAIL.name(), clientId, clientIp));
}
}

View File

@ -28,7 +28,6 @@ import com.alibaba.nacos.core.distributed.distro.component.DistroCallback;
import com.alibaba.nacos.core.distributed.distro.entity.DistroData;
import com.alibaba.nacos.core.distributed.distro.entity.DistroKey;
import com.alibaba.nacos.core.distributed.distro.exception.DistroException;
import com.alibaba.nacos.core.remote.control.TpsMonitorManager;
import com.alibaba.nacos.naming.cluster.remote.response.DistroDataResponse;
import com.alibaba.nacos.sys.env.EnvUtil;
import com.alibaba.nacos.sys.utils.ApplicationUtils;
@ -61,9 +60,6 @@ public class DistroClientTransportAgentTest {
@Mock
ServerMemberManager memberManager;
@Mock
TpsMonitorManager tpsMonitorManager;
@Mock
ConfigurableApplicationContext context;
@ -79,7 +75,6 @@ public class DistroClientTransportAgentTest {
@Before
public void setUp() throws Exception {
when(context.getBean(TpsMonitorManager.class)).thenReturn(tpsMonitorManager);
ApplicationUtils.injectContext(context);
EnvUtil.setEnvironment(new MockEnvironment());
member = new Member();
@ -95,7 +90,6 @@ public class DistroClientTransportAgentTest {
return null;
}).when(clusterRpcClientProxy).asyncRequest(eq(member), any(), any());
// When run all project, the TpsNamingMonitor will be init by other unit test, will throw UnnecessaryStubbingException.
ApplicationUtils.getBean(TpsMonitorManager.class);
}
@After

View File

@ -18,9 +18,9 @@ package com.alibaba.nacos.naming.push.v2.hook;
import com.alibaba.nacos.api.naming.pojo.Instance;
import com.alibaba.nacos.api.naming.pojo.ServiceInfo;
import com.alibaba.nacos.core.remote.control.TpsMonitorManager;
import com.alibaba.nacos.naming.monitor.MetricsMonitor;
import com.alibaba.nacos.naming.pojo.Subscriber;
import com.alibaba.nacos.plugin.control.tps.TpsControlManager;
import com.alibaba.nacos.sys.utils.ApplicationUtils;
import org.junit.Before;
import org.junit.Test;
@ -49,7 +49,7 @@ public class NacosMonitorPushResultHookTest {
private Subscriber subscriber;
@Mock
private TpsMonitorManager tpsMonitorManager;
private TpsControlManager tpsControlManager;
@Mock
private ConfigurableApplicationContext context;
@ -72,7 +72,7 @@ public class NacosMonitorPushResultHookTest {
when(pushResult.getData()).thenReturn(serviceInfo);
when(pushResult.getSubscriber()).thenReturn(subscriber);
ApplicationUtils.injectContext(context);
when(context.getBean(TpsMonitorManager.class)).thenReturn(tpsMonitorManager);
when(context.getBean(TpsControlManager.class)).thenReturn(tpsControlManager);
}
@Test

View File

@ -17,7 +17,6 @@
package com.alibaba.nacos.naming.push.v2.task;
import com.alibaba.nacos.api.naming.pojo.ServiceInfo;
import com.alibaba.nacos.core.remote.control.TpsMonitorManager;
import com.alibaba.nacos.naming.core.v2.client.Client;
import com.alibaba.nacos.naming.core.v2.client.manager.ClientManager;
import com.alibaba.nacos.naming.core.v2.index.ClientServiceIndexesManager;
@ -66,9 +65,6 @@ public class PushExecuteTaskTest {
@Mock
private ServiceStorage serviceStorage;
@Mock
private TpsMonitorManager tpsMonitorManager;
@Mock
private NamingMetadataManager metadataManager;
@ -95,7 +91,6 @@ public class PushExecuteTaskTest {
when(delayTaskExecuteEngine.getMetadataManager()).thenReturn(metadataManager);
when(metadataManager.getServiceMetadata(service)).thenReturn(Optional.empty());
ApplicationUtils.injectContext(context);
when(context.getBean(TpsMonitorManager.class)).thenReturn(tpsMonitorManager);
}
@Test

45
plugin/control/pom.xml Normal file
View File

@ -0,0 +1,45 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright 1999-2020 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.
-->
<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>
<artifactId>nacos-plugin</artifactId>
<groupId>com.alibaba.nacos</groupId>
<version>${revision}</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>nacos-contrl-plugin</artifactId>
<name>nacos-control-plugin ${project.version}</name>
<url>http://nacos.io</url>
<dependencies>
<dependency>
<groupId>com.alibaba.nacos</groupId>
<artifactId>nacos-common</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.alibaba.nacos</groupId>
<artifactId>nacos-sys</artifactId>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,170 @@
/*
* Copyright 1999-2020 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.nacos.plugin.control;
import com.alibaba.nacos.common.notify.NotifyCenter;
import com.alibaba.nacos.common.spi.NacosServiceLoader;
import com.alibaba.nacos.plugin.control.configs.ControlConfigs;
import com.alibaba.nacos.plugin.control.connection.ConnectionControlManager;
import com.alibaba.nacos.plugin.control.connection.nacos.NacosConnectionControlManager;
import com.alibaba.nacos.plugin.control.event.ConnectionLimitRuleChangeEvent;
import com.alibaba.nacos.plugin.control.event.TpsControlRuleChangeEvent;
import com.alibaba.nacos.plugin.control.ruleactivator.NacosRuleParser;
import com.alibaba.nacos.plugin.control.ruleactivator.RuleParser;
import com.alibaba.nacos.plugin.control.ruleactivator.RuleStorageProxy;
import com.alibaba.nacos.plugin.control.tps.TpsControlManager;
import com.alibaba.nacos.plugin.control.tps.nacos.NacosTpsControlManager;
import java.util.Collection;
/**
* control manager center.
*
* @author shiyiyue
*/
public class ControlManagerCenter {
static ControlManagerCenter instance = null;
private TpsControlManager tpsControlManager;
private ConnectionControlManager connectionControlManager;
private RuleParser ruleParser;
private RuleStorageProxy ruleStorageProxy;
private void initRuleParser() {
Collection<RuleParser> ruleParsers = NacosServiceLoader.load(RuleParser.class);
String ruleParserName = ControlConfigs.getInstance().getRuleParser();
for (RuleParser ruleParserInternal : ruleParsers) {
if (ruleParserInternal.getName().equalsIgnoreCase(ruleParserName)) {
Loggers.CONTROL.info("Found rule parser of name={},class={}", ruleParserName,
ruleParserInternal.getClass().getSimpleName());
ruleParser = ruleParserInternal;
break;
}
}
if (ruleParser == null) {
Loggers.CONTROL.warn("Fail to rule parser of name " + ruleParserName);
ruleParser = new NacosRuleParser();
}
Collection<ConnectionControlManager> connectionControlManagers = NacosServiceLoader
.load(ConnectionControlManager.class);
String connectionManagerName = ControlConfigs.getInstance().getConnectionManager();
for (ConnectionControlManager connectionControlManagerInternal : connectionControlManagers) {
if (connectionControlManagerInternal.getName().equalsIgnoreCase(connectionManagerName)) {
Loggers.CONTROL.info("Found rule parser of name={},class={}", ruleParserName,
connectionControlManagerInternal.getClass().getSimpleName());
connectionControlManager = connectionControlManagerInternal;
break;
}
}
if (connectionControlManager == null) {
Loggers.CONTROL.warn("Fail to rule parser of name " + ruleParserName);
connectionControlManager = new NacosConnectionControlManager();
}
}
private void initConnectionManager() {
Collection<ConnectionControlManager> connectionControlManagers = NacosServiceLoader
.load(ConnectionControlManager.class);
String connectionManagerName = ControlConfigs.getInstance().getConnectionManager();
for (ConnectionControlManager connectionControlManagerInternal : connectionControlManagers) {
if (connectionControlManagerInternal.getName().equalsIgnoreCase(connectionManagerName)) {
Loggers.CONTROL.info("Found connection control manager of name={},class={}", connectionManagerName,
connectionControlManagerInternal.getClass().getSimpleName());
connectionControlManager = connectionControlManagerInternal;
break;
}
}
if (connectionControlManager == null) {
Loggers.CONTROL.warn("Fail to connection control manager of name " + connectionManagerName);
connectionControlManager = new NacosConnectionControlManager();
}
}
private void initTpsControlManager() {
Collection<TpsControlManager> tpsControlManagers = NacosServiceLoader.load(TpsControlManager.class);
String tpsManagerName = ControlConfigs.getInstance().getTpsManager();
for (TpsControlManager tpsControlManagerInternal : tpsControlManagers) {
if (tpsControlManagerInternal.getName().equalsIgnoreCase(tpsManagerName)) {
Loggers.CONTROL.info("Found tps control manager of name={},class={}", tpsManagerName,
tpsControlManagerInternal.getClass().getSimpleName());
tpsControlManager = tpsControlManagerInternal;
break;
}
}
if (tpsControlManager == null) {
Loggers.CONTROL.warn("Fail to found tps control manager of name " + tpsManagerName);
tpsControlManager = new NacosTpsControlManager();
}
}
private ControlManagerCenter() {
initTpsControlManager();
initRuleParser();
initConnectionManager();
ruleStorageProxy = new RuleStorageProxy();
}
public RuleStorageProxy getRuleStorageProxy() {
return ruleStorageProxy;
}
public RuleParser getRuleParser() {
return ruleParser;
}
public TpsControlManager getTpsControlManager() {
return tpsControlManager;
}
public ConnectionControlManager getConnectionControlManager() {
return connectionControlManager;
}
public static final ControlManagerCenter getInstance() {
if (instance == null) {
synchronized (ControlManagerCenter.class) {
if (instance == null) {
instance = new ControlManagerCenter();
}
}
}
return instance;
}
public void reloadTpsControlRule(String pointName, boolean external) {
NotifyCenter.publishEvent(new TpsControlRuleChangeEvent(pointName, external));
}
public void reloadConnectionControlRule(boolean external) {
NotifyCenter.publishEvent(new ConnectionLimitRuleChangeEvent(external));
}
}

View File

@ -0,0 +1,36 @@
/*
* Copyright 1999-2020 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.nacos.plugin.control;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* cotrol loggers.
*
* @author shiyiyue
*/
public class Loggers {
public static final Logger CONTROL = LoggerFactory.getLogger("com.alibaba.nacos.plugin.control");
public static final Logger TPS = LoggerFactory.getLogger("com.alibaba.nacos.plugin.control.tps");
public static final Logger CONNECTION = LoggerFactory.getLogger("com.alibaba.nacos.plugin.control.connection");
}

View File

@ -0,0 +1,133 @@
/*
* Copyright 1999-2020 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.nacos.plugin.control.configs;
import com.alibaba.nacos.plugin.control.Loggers;
import com.alibaba.nacos.sys.utils.ApplicationUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
/**
* control configs params.
*
* @author shiyiyue
*/
@Component
public class ControlConfigs {
private static ControlConfigs instance = null;
public static ControlConfigs getInstance() {
if (instance == null) {
synchronized (ControlConfigs.class) {
try {
instance = ApplicationUtils.getBean(ControlConfigs.class);
} catch (Throwable throwable) {
Loggers.CONTROL
.warn("Fail to get control configs bean from spring context,use default constructor instance",
throwable);
}
if (instance == null) {
instance = new ControlConfigs();
}
}
}
return instance;
}
public static void setInstance(ControlConfigs instance) {
ControlConfigs.instance = instance;
}
@Value("${nacos.plugin.control.tps.barrier.creator:nacos}")
private String tpsBarrierCreator = "nacos";
@Value("${nacos.plugin.control.tps.barrier.rule.creator:nacos}")
private String tpsRuleBarrierCreator = "nacos";
@Value("${nacos.plugin.control.connection.runtime.ejector:nacos}")
private String connectionRuntimeEjector = "nacos";
@Value("${nacos.plugin.control.connection.manager:nacos}")
private String connectionManager = "nacos";
@Value("${nacos.plugin.control.tps.manager:nacos}")
private String tpsManager = "nacos";
@Value("${nacos.plugin.control.rule.external.storage:}")
private String ruleExternalStorage = "";
@Value("${nacos.plugin.control.rule.parser:nacos}")
private String ruleParser = "nacos";
public String getTpsBarrierCreator() {
return tpsBarrierCreator;
}
public void setTpsBarrierCreator(String tpsBarrierCreator) {
this.tpsBarrierCreator = tpsBarrierCreator;
}
public String getTpsRuleBarrierCreator() {
return tpsRuleBarrierCreator;
}
public void setTpsRuleBarrierCreator(String tpsRuleBarrierCreator) {
this.tpsRuleBarrierCreator = tpsRuleBarrierCreator;
}
public String getRuleExternalStorage() {
return ruleExternalStorage;
}
public void setRuleExternalStorage(String ruleExternalStorage) {
this.ruleExternalStorage = ruleExternalStorage;
}
public String getRuleParser() {
return ruleParser;
}
public void setRuleParser(String ruleParser) {
this.ruleParser = ruleParser;
}
public String getConnectionManager() {
return connectionManager;
}
public void setConnectionManager(String connectionManager) {
this.connectionManager = connectionManager;
}
public String getConnectionRuntimeEjector() {
return connectionRuntimeEjector;
}
public void setConnectionRuntimeEjector(String connectionRuntimeEjector) {
this.connectionRuntimeEjector = connectionRuntimeEjector;
}
public String getTpsManager() {
return tpsManager;
}
public void setTpsManager(String tpsManager) {
this.tpsManager = tpsManager;
}
}

View File

@ -0,0 +1,136 @@
/*
* Copyright 1999-2020 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.nacos.plugin.control.connection;
import com.alibaba.nacos.common.executor.ExecutorFactory;
import com.alibaba.nacos.common.spi.NacosServiceLoader;
import com.alibaba.nacos.common.utils.StringUtils;
import com.alibaba.nacos.plugin.control.Loggers;
import com.alibaba.nacos.plugin.control.connection.request.ConnectionCheckRequest;
import com.alibaba.nacos.plugin.control.connection.response.ConnectionCheckResponse;
import com.alibaba.nacos.plugin.control.connection.rule.ConnectionControlRule;
import com.alibaba.nacos.plugin.control.ruleactivator.RuleParserProxy;
import com.alibaba.nacos.plugin.control.ruleactivator.RuleStorageProxy;
import java.util.Collection;
import java.util.Map;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
/**
* connection control manager.
*
* @author shiyiyue
*/
@SuppressWarnings("PMD.AbstractClassShouldStartWithAbstractNamingRule")
public abstract class ConnectionControlManager {
protected ConnectionControlRule connectionControlRule;
protected Collection<ConnectionMetricsCollector> metricsCollectorList;
/**
* get manager name.
*
* @return
*/
public abstract String getName();
private ScheduledExecutorService executorService;
public ConnectionControlManager() {
metricsCollectorList = NacosServiceLoader.load(ConnectionMetricsCollector.class);
Loggers.CONTROL.info("Load connection metrics collector,size={},{}", metricsCollectorList.size(),
metricsCollectorList);
initConnectionRule();
if (!metricsCollectorList.isEmpty()) {
initExecuteService();
startConnectionMetricsReport();
}
}
private void initExecuteService() {
executorService = ExecutorFactory.newSingleScheduledExecutorService(r -> {
Thread thread = new Thread(r, "nacos.plugin.control.connection.reporter");
thread.setDaemon(true);
return thread;
});
}
private void startConnectionMetricsReport() {
executorService.scheduleWithFixedDelay(new ConnectionMetricsReporter(), 0, 3000, TimeUnit.MILLISECONDS);
}
private void initConnectionRule() {
RuleStorageProxy ruleStorageProxy = RuleStorageProxy.getInstance();
String localRuleContent = ruleStorageProxy.getLocalDiskStorage().getConnectionRule();
if (StringUtils.isNotBlank(localRuleContent)) {
Loggers.CONTROL.info("Found local disk connection rule content on start up,value ={}", localRuleContent);
} else if (ruleStorageProxy.getExternalStorage() != null
&& ruleStorageProxy.getExternalStorage().getConnectionRule() != null) {
localRuleContent = ruleStorageProxy.getExternalStorage().getConnectionRule();
if (StringUtils.isNotBlank(localRuleContent)) {
Loggers.CONTROL
.info("Found persist disk connection rule content on start up ,value ={}", localRuleContent);
}
}
if (StringUtils.isNotBlank(localRuleContent)) {
connectionControlRule = RuleParserProxy.getInstance().parseConnectionRule(localRuleContent);
Loggers.CONTROL.info("init connection rule end");
} else {
Loggers.CONTROL.info("No connection rule content found ,use default empty rule ");
connectionControlRule = new ConnectionControlRule();
}
}
public ConnectionControlRule getConnectionLimitRule() {
return connectionControlRule;
}
/**
* apply connection rule.
*
* @param connectionControlRule not null.
*/
public abstract void applyConnectionLimitRule(ConnectionControlRule connectionControlRule);
/**
* check connection allowed.
*
* @param connectionCheckRequest connectionCheckRequest.
* @return
*/
public abstract ConnectionCheckResponse check(ConnectionCheckRequest connectionCheckRequest);
class ConnectionMetricsReporter implements Runnable {
@Override
public void run() {
Map<String, Integer> metricsTotalCount = metricsCollectorList.stream().collect(
Collectors.toMap(ConnectionMetricsCollector::getName, ConnectionMetricsCollector::getTotalCount));
int totalCount = metricsTotalCount.values().stream().mapToInt(Integer::intValue).sum();
Loggers.CONNECTION.info(String.format("ConnectionMetrics, totalCount = %s, detail = %s", totalCount,
metricsTotalCount.toString()));
}
}
}

View File

@ -0,0 +1,47 @@
/*
* Copyright 1999-2020 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.nacos.plugin.control.connection;
/**
* connection count metrics collector.
*
* @author shiyiyue
*/
public interface ConnectionMetricsCollector {
/**
* get collector name.
*
* @return
*/
String getName();
/**
* get total count.
*
* @return
*/
int getTotalCount();
/**
* get count for ip.
*
* @param ip ip.
* @return
*/
int getCountForIp(String ip);
}

View File

@ -0,0 +1,59 @@
/*
* Copyright 1999-2020 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.nacos.plugin.control.connection.nacos;
import com.alibaba.nacos.common.utils.JacksonUtils;
import com.alibaba.nacos.plugin.control.Loggers;
import com.alibaba.nacos.plugin.control.connection.ConnectionControlManager;
import com.alibaba.nacos.plugin.control.connection.request.ConnectionCheckRequest;
import com.alibaba.nacos.plugin.control.connection.response.ConnectionCheckCode;
import com.alibaba.nacos.plugin.control.connection.response.ConnectionCheckResponse;
import com.alibaba.nacos.plugin.control.connection.rule.ConnectionControlRule;
/**
* connection control manager.
*
* @author shiyiyue
*/
public class NacosConnectionControlManager extends ConnectionControlManager {
@Override
public String getName() {
return "nacos";
}
public NacosConnectionControlManager() {
super();
}
@Override
public void applyConnectionLimitRule(ConnectionControlRule connectionControlRule) {
super.connectionControlRule = connectionControlRule;
Loggers.CONTROL.info("Connection control rule updated to ->" + (this.connectionControlRule == null ? null
: JacksonUtils.toJson(this.connectionControlRule)));
}
@Override
public ConnectionCheckResponse check(ConnectionCheckRequest connectionCheckRequest) {
ConnectionCheckResponse connectionCheckResponse = new ConnectionCheckResponse();
connectionCheckResponse.setSuccess(true);
connectionCheckResponse.setCode(ConnectionCheckCode.CHECK_SKIP);
return connectionCheckResponse;
}
}

View File

@ -0,0 +1,73 @@
/*
* Copyright 1999-2020 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.nacos.plugin.control.connection.request;
import java.util.Map;
/**
* connection check request.
*
* @author shiyiyue
*/
public class ConnectionCheckRequest {
String clientIp;
String appName;
String source;
Map<String, String> labels;
public ConnectionCheckRequest(String clientIp, String appName, String source) {
this.appName = appName;
this.clientIp = clientIp;
this.source = source;
}
public String getSource() {
return source;
}
public void setSource(String source) {
this.source = source;
}
public Map<String, String> getLabels() {
return labels;
}
public void setLabels(Map<String, String> labels) {
this.labels = labels;
}
public String getClientIp() {
return clientIp;
}
public void setClientIp(String clientIp) {
this.clientIp = clientIp;
}
public String getAppName() {
return appName;
}
public void setAppName(String appName) {
this.appName = appName;
}
}

View File

@ -0,0 +1,45 @@
/*
* Copyright 1999-2020 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.nacos.plugin.control.connection.response;
/**
* conection check code.
*
* @author shiyiyue
*/
public class ConnectionCheckCode {
/**
* check pass.
*/
public static final int PASS_BY_TOTAL = 200;
/**
* skip.
*/
public static final int CHECK_SKIP = 100;
/**
* deny by total over limit.
*/
public static final int DENY_BY_TOTAL_OVER = 300;
/**
* pass by monitor type.
*/
public static final int PASS_BY_MONITOR = 205;
}

View File

@ -0,0 +1,54 @@
/*
* Copyright 1999-2020 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.nacos.plugin.control.connection.response;
/**
* connection check response.
* @author shiyiyue
*/
public class ConnectionCheckResponse {
private boolean success;
private String message;
private int code;
public boolean isSuccess() {
return success;
}
public void setSuccess(boolean success) {
this.success = success;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
}

View File

@ -0,0 +1,48 @@
/*
* Copyright 1999-2020 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.nacos.plugin.control.connection.rule;
import java.util.HashSet;
import java.util.Set;
/**
* connection control rule.
*
* @author shiyiyue
*/
public class ConnectionControlRule {
private Set<String> monitorIpList = new HashSet<>();
private int countLimit = -1;
public int getCountLimit() {
return countLimit;
}
public void setCountLimit(int countLimit) {
this.countLimit = countLimit;
}
public Set<String> getMonitorIpList() {
return monitorIpList;
}
public void setMonitorIpList(Set<String> monitorIpList) {
this.monitorIpList = monitorIpList;
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 1999-2018 Alibaba Group Holding Ltd.
* Copyright 1999-2020 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.
@ -14,7 +14,7 @@
* limitations under the License.
*/
package com.alibaba.nacos.core.remote.event;
package com.alibaba.nacos.plugin.control.event;
import com.alibaba.nacos.common.notify.Event;
@ -24,17 +24,17 @@ import com.alibaba.nacos.common.notify.Event;
*/
public class ConnectionLimitRuleChangeEvent extends Event {
String limitRule;
private boolean external;
public ConnectionLimitRuleChangeEvent(String limitRule) {
this.limitRule = limitRule;
public ConnectionLimitRuleChangeEvent(boolean external) {
this.external = external;
}
public String getLimitRule() {
return limitRule;
public boolean isExternal() {
return external;
}
public void setLimitRule(String limitRule) {
this.limitRule = limitRule;
public void setExternal(boolean external) {
this.external = external;
}
}

View File

@ -14,7 +14,7 @@
* limitations under the License.
*/
package com.alibaba.nacos.core.remote.control;
package com.alibaba.nacos.plugin.control.event;
import com.alibaba.nacos.common.notify.Event;
@ -26,13 +26,13 @@ import com.alibaba.nacos.common.notify.Event;
*/
public class TpsControlRuleChangeEvent extends Event {
String pointName;
private String pointName;
String ruleContent;
private boolean external;
public TpsControlRuleChangeEvent(String pointName, String ruleContent) {
public TpsControlRuleChangeEvent(String pointName, boolean external) {
this.pointName = pointName;
this.ruleContent = ruleContent;
this.external = external;
}
public String getPointName() {
@ -43,11 +43,11 @@ public class TpsControlRuleChangeEvent extends Event {
this.pointName = pointName;
}
public String getRuleContent() {
return ruleContent;
public boolean isExternal() {
return external;
}
public void setRuleContent(String ruleContent) {
this.ruleContent = ruleContent;
public void setExternal(boolean external) {
this.external = external;
}
}

View File

@ -0,0 +1,141 @@
/*
* Copyright 1999-2020 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.nacos.plugin.control.ruleactivator;
import com.alibaba.nacos.common.notify.Event;
import com.alibaba.nacos.common.notify.NotifyCenter;
import com.alibaba.nacos.common.notify.listener.Subscriber;
import com.alibaba.nacos.common.utils.StringUtils;
import com.alibaba.nacos.plugin.control.ControlManagerCenter;
import com.alibaba.nacos.plugin.control.Loggers;
import com.alibaba.nacos.plugin.control.connection.rule.ConnectionControlRule;
import com.alibaba.nacos.plugin.control.event.ConnectionLimitRuleChangeEvent;
import com.alibaba.nacos.plugin.control.event.TpsControlRuleChangeEvent;
import com.alibaba.nacos.plugin.control.tps.rule.TpsControlRule;
import org.slf4j.Logger;
import org.springframework.stereotype.Component;
/**
* control rule activator.
* @author shiyiyue
*
*/
@Component
public class ControlRuleChangeActivator {
private static final Logger LOGGER = Loggers.CONTROL;
TpsRuleChangeSubscriber tpsRuleChangeSubscriber = new TpsRuleChangeSubscriber();
ConnectionRuleChangeSubscriber connectionRuleChangeSubscriber = new ConnectionRuleChangeSubscriber();
public ControlRuleChangeActivator() {
NotifyCenter.registerSubscriber(tpsRuleChangeSubscriber);
NotifyCenter.registerSubscriber(connectionRuleChangeSubscriber);
}
class TpsRuleChangeSubscriber extends Subscriber<TpsControlRuleChangeEvent> {
@Override
public void onEvent(TpsControlRuleChangeEvent event) {
String pointName = event.getPointName();
LOGGER.info("Tps control rule change event receive,pointName={}, external={} ", pointName,
event.isExternal());
if (event == null || event.getPointName() == null) {
return;
}
try {
RuleStorageProxy ruleStorageProxy = ControlManagerCenter.getInstance().getRuleStorageProxy();
if (event.isExternal()) {
if (ruleStorageProxy.getExternalStorage() != null) {
String persistTpsRule = ruleStorageProxy.getExternalStorage().getTpsRule(pointName);
ruleStorageProxy.getLocalDiskStorage().saveTpsRule(pointName, persistTpsRule);
} else {
Loggers.CONTROL
.info("No external rule storage found,will load local disk instead,point name={}",
event.getPointName());
}
}
String tpsRuleContent = ruleStorageProxy.getLocalDiskStorage().getTpsRule(pointName);
TpsControlRule tpsControlRule = StringUtils.isBlank(tpsRuleContent) ? new TpsControlRule()
: ControlManagerCenter.getInstance().getRuleParser().parseTpsRule(tpsRuleContent);
ControlManagerCenter.getInstance().getTpsControlManager().applyTpsRule(pointName, tpsControlRule);
} catch (Exception e) {
LOGGER.warn("Tps control rule apply error ,error= ", e);
}
}
@Override
public Class<? extends Event> subscribeType() {
return TpsControlRuleChangeEvent.class;
}
}
class ConnectionRuleChangeSubscriber extends Subscriber<ConnectionLimitRuleChangeEvent> {
@Override
public void onEvent(ConnectionLimitRuleChangeEvent event) {
LOGGER.info("connection limit rule change event receive ,external:{}", event.isExternal());
try {
RuleStorageProxy ruleStorageProxy = ControlManagerCenter.getInstance().getRuleStorageProxy();
if (event.isExternal()) {
if (ruleStorageProxy.getExternalStorage() != null) {
String connectionRule = ruleStorageProxy.getExternalStorage().getConnectionRule();
ruleStorageProxy.getLocalDiskStorage().saveConnectionRule(connectionRule);
} else {
Loggers.CONTROL.info("No external rule storage found,will load local disk instead");
}
}
String limitRule = ruleStorageProxy.getLocalDiskStorage().getConnectionRule();
Loggers.CONTROL.info("start to apply connection rule content " + limitRule);
ConnectionControlRule connectionControlRule =
StringUtils.isBlank(limitRule) ? new ConnectionControlRule()
: ControlManagerCenter.getInstance().getRuleParser().parseConnectionRule(limitRule);
Loggers.CONTROL.info("end to apply connection rule content ");
if (connectionControlRule != null) {
ControlManagerCenter.getInstance().getConnectionControlManager()
.applyConnectionLimitRule(connectionControlRule);
} else {
LOGGER.info("Parse rule is null,Ignore illegal rule :{}", limitRule);
}
} catch (Exception e) {
LOGGER.error("Fail to parse connection limit rule ,persit:{}", event.isExternal(), e);
}
}
@Override
public Class<? extends Event> subscribeType() {
return ConnectionLimitRuleChangeEvent.class;
}
}
}

View File

@ -0,0 +1,26 @@
/*
* Copyright 1999-2020 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.nacos.plugin.control.ruleactivator;
/**
* external rule storage.
*
* @author shiyiyue
*/
public interface ExternalRuleStorage extends RuleStorage {
}

View File

@ -0,0 +1,107 @@
/*
* Copyright 1999-2020 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.nacos.plugin.control.ruleactivator;
import com.alibaba.nacos.api.common.Constants;
import com.alibaba.nacos.plugin.control.Loggers;
import com.alibaba.nacos.sys.env.EnvUtil;
import com.alibaba.nacos.sys.utils.DiskUtils;
import org.slf4j.Logger;
import java.io.File;
import java.io.IOException;
/**
* local disk storage.
*
* @author shiyiyue
*/
public class LocalDiskRuleStorage implements RuleStorage {
LocalDiskRuleStorage() {
}
private static final Logger LOGGER = Loggers.CONTROL;
private File checkTpsBaseDir() {
File baseDir = new File(EnvUtil.getNacosHome(), "data" + File.separator + "tps" + File.separator);
if (!baseDir.exists()) {
baseDir.mkdirs();
}
return baseDir;
}
private File getConnectionRuleFile() {
File baseDir = new File(EnvUtil.getNacosHome(), "data" + File.separator + "connection" + File.separator);
if (!baseDir.exists()) {
baseDir.mkdir();
}
return new File(baseDir, "limitRule");
}
@Override
public String getName() {
return "localdisk";
}
@Override
public void saveConnectionRule(String ruleContent) throws IOException {
File pointFile = getConnectionRuleFile();
if (!pointFile.exists()) {
pointFile.createNewFile();
}
DiskUtils.writeFile(pointFile, ruleContent.getBytes(Constants.ENCODE), false);
LOGGER.info("Save connection rule to local,pointName={}, ruleContent ={} ", ruleContent);
}
@Override
public String getConnectionRule() {
File connectionRuleFile = getConnectionRuleFile();
if (!connectionRuleFile.exists()) {
return null;
}
return DiskUtils.readFile(connectionRuleFile);
}
@Override
public void saveTpsRule(String pointName, String ruleContent) throws IOException {
File file = checkTpsBaseDir();
File tpsFile = new File(file, pointName);
if (!tpsFile.exists()) {
tpsFile.createNewFile();
}
if (ruleContent == null) {
DiskUtils.deleteQuietly(tpsFile);
} else {
DiskUtils.writeFile(tpsFile, ruleContent.getBytes(Constants.ENCODE), false);
LOGGER.info("Save tps rule to local,pointName={}, ruleContent ={} ", pointName, ruleContent);
}
}
@Override
public String getTpsRule(String pointName) {
File file = checkTpsBaseDir();
File tpsFile = new File(file, pointName);
if (!tpsFile.exists()) {
return null;
}
return DiskUtils.readFile(tpsFile);
}
}

View File

@ -0,0 +1,49 @@
/*
* Copyright 1999-2020 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.nacos.plugin.control.ruleactivator;
import com.alibaba.nacos.common.utils.JacksonUtils;
import com.alibaba.nacos.common.utils.StringUtils;
import com.alibaba.nacos.plugin.control.connection.rule.ConnectionControlRule;
import com.alibaba.nacos.plugin.control.tps.rule.TpsControlRule;
/**
* nacos rule parser.
*
* @author shiyiyue
*/
public class NacosRuleParser implements RuleParser {
@Override
public TpsControlRule parseTpsRule(String ruleContent) {
return StringUtils.isBlank(ruleContent) ? new TpsControlRule()
: JacksonUtils.toObj(ruleContent, TpsControlRule.class);
}
@Override
public ConnectionControlRule parseConnectionRule(String ruleContent) {
return StringUtils.isBlank(ruleContent) ? new ConnectionControlRule()
: JacksonUtils.toObj(ruleContent, ConnectionControlRule.class);
}
@Override
public String getName() {
return "nacos";
}
}

View File

@ -0,0 +1,51 @@
/*
* Copyright 1999-2020 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.nacos.plugin.control.ruleactivator;
import com.alibaba.nacos.plugin.control.connection.rule.ConnectionControlRule;
import com.alibaba.nacos.plugin.control.tps.rule.TpsControlRule;
/**
* parse rule content from raw string.
*
* @author shiyiyue
*/
public interface RuleParser {
/**
* parse tps rule content.
*
* @param ruleContent ruleContent.
* @return
*/
TpsControlRule parseTpsRule(String ruleContent);
/**
* parse connection rule.
*
* @param ruleContent ruleContent.
* @return
*/
ConnectionControlRule parseConnectionRule(String ruleContent);
/**
* get name.
*
* @return
*/
String getName();
}

View File

@ -0,0 +1,58 @@
/*
* Copyright 1999-2020 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.nacos.plugin.control.ruleactivator;
import com.alibaba.nacos.common.spi.NacosServiceLoader;
import com.alibaba.nacos.plugin.control.Loggers;
import com.alibaba.nacos.plugin.control.configs.ControlConfigs;
import org.slf4j.Logger;
import java.util.Collection;
/**
* rule parser proxy.
*
* @author shiyiyue
*/
public class RuleParserProxy {
private static final Logger LOGGER = Loggers.CONTROL;
private static RuleParser instance;
static {
Collection<RuleParser> ruleParsers = NacosServiceLoader.load(RuleParser.class);
String ruleParserName = ControlConfigs.getInstance().getRuleParser();
for (RuleParser ruleParser : ruleParsers) {
if (ruleParser.getName().equalsIgnoreCase(ruleParserName)) {
LOGGER.info("Found rule parser of name={},class={}", ruleParserName,
ruleParser.getClass().getSimpleName());
instance = ruleParser;
break;
}
}
if (instance == null) {
LOGGER.warn("Fail to rule parser of name " + ruleParserName);
instance = new NacosRuleParser();
}
}
public static RuleParser getInstance() {
return instance;
}
}

View File

@ -0,0 +1,66 @@
/*
* Copyright 1999-2020 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.nacos.plugin.control.ruleactivator;
/**
* rule storage.
*
* @author shiyiyue
* @date 2022-10-26 11:43:00
*/
public interface RuleStorage {
/**
* get storage name.
*
* @return
*/
String getName();
/**
* save connection rule to storage.
*
* @param ruleContent rule content.
* @throws Exception exception.
*/
void saveConnectionRule(String ruleContent) throws Exception;
/**
* get connection rule.
*
* @return
*/
String getConnectionRule();
/**
* save tps rule.
*
* @param pointName point name.
* @param ruleContent rule content.
* @throws Exception exception.
*/
void saveTpsRule(String pointName, String ruleContent) throws Exception;
/**
* get tps rule.
*
* @param pointName point name.
* @return
*/
String getTpsRule(String pointName);
}

View File

@ -0,0 +1,69 @@
/*
* Copyright 1999-2020 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.nacos.plugin.control.ruleactivator;
import com.alibaba.nacos.common.spi.NacosServiceLoader;
import com.alibaba.nacos.common.utils.StringUtils;
import com.alibaba.nacos.plugin.control.Loggers;
import com.alibaba.nacos.plugin.control.configs.ControlConfigs;
import org.slf4j.Logger;
import java.util.Collection;
/**
* rule storage proxy.
*
* @author shiyiyue
*/
public class RuleStorageProxy {
private static final Logger LOGGER = Loggers.CONTROL;
private static final RuleStorageProxy INSTANCE = new RuleStorageProxy();
private LocalDiskRuleStorage localDiskRuleStorage = new LocalDiskRuleStorage();
private ExternalRuleStorage externalRuleStorage = null;
public RuleStorageProxy() {
Collection<ExternalRuleStorage> persistRuleActivators = NacosServiceLoader.load(ExternalRuleStorage.class);
String rulePersistActivator = ControlConfigs.getInstance().getRuleExternalStorage();
for (ExternalRuleStorage persistRuleActivator : persistRuleActivators) {
if (persistRuleActivator.getName().equalsIgnoreCase(rulePersistActivator)) {
LOGGER.info("Found persist rule storage of name " + rulePersistActivator);
externalRuleStorage = persistRuleActivator;
break;
}
}
if (externalRuleStorage == null && StringUtils.isNotBlank(rulePersistActivator)) {
LOGGER.error("Fail to found persist rule storage of name " + rulePersistActivator);
}
}
public RuleStorage getLocalDiskStorage() {
return localDiskRuleStorage;
}
public RuleStorage getExternalStorage() {
return externalRuleStorage;
}
public static final RuleStorageProxy getInstance() {
return INSTANCE;
}
}

View File

@ -14,7 +14,7 @@
* limitations under the License.
*/
package com.alibaba.nacos.core.remote.control;
package com.alibaba.nacos.plugin.control.tps;
/**
* MonitorType.
@ -40,16 +40,4 @@ public enum MonitorType {
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getDesc() {
return desc;
}
public void setDesc(String desc) {
this.desc = desc;
}
}

View File

@ -0,0 +1,131 @@
/*
* Copyright 1999-2020 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.nacos.plugin.control.tps;
import com.alibaba.nacos.plugin.control.tps.request.BarrierCheckRequest;
import com.alibaba.nacos.plugin.control.tps.response.TpsCheckResponse;
import com.alibaba.nacos.plugin.control.tps.rule.RuleDetail;
import java.util.concurrent.TimeUnit;
/**
* rule barrier.
*
* @author shiyiyue
*/
@SuppressWarnings("PMD.AbstractClassShouldStartWithAbstractNamingRule")
public abstract class RuleBarrier {
private TimeUnit period;
private String pointName;
private long maxCount;
private String ruleName;
/**
* monitor/intercept.
*/
private String monitorType = MonitorType.MONITOR.type;
public String getRuleName() {
return ruleName;
}
public void setRuleName(String ruleName) {
this.ruleName = ruleName;
}
public String getPointName() {
return pointName;
}
public void setPointName(String pointName) {
this.pointName = pointName;
}
public TimeUnit getPeriod() {
return period;
}
public void setPeriod(TimeUnit period) {
this.period = period;
}
/**
* get barrier name.
*
* @return
*/
public abstract String getBarrierName();
public long getMaxCount() {
return maxCount;
}
public void setMaxCount(long maxCount) {
this.maxCount = maxCount;
}
public String getMonitorType() {
return monitorType;
}
public void setMonitorType(String monitorType) {
this.monitorType = monitorType;
}
public boolean isMonitorType() {
return MonitorType.MONITOR.type.equalsIgnoreCase(this.monitorType);
}
public String getLimitMsg() {
return String.format("[Period:%s,MaxCount:%s]", period, maxCount);
}
/**
* apply tps.
*
* @param barrierCheckRequest barrierCheckRequest.
* @return
*/
public abstract TpsCheckResponse applyTps(BarrierCheckRequest barrierCheckRequest);
/**
* apply rule detail.
*
* @param ruleDetail ruleDetail.
*/
public abstract void applyRuleDetail(RuleDetail ruleDetail);
/**
* get metrics.
*
* @param timeStamp timeStamp.
* @return
*/
public abstract TpsMetrics getMetrics(long timeStamp);
/**
* clear limit rule.
*/
public void clearLimitRule() {
this.maxCount = -1;
this.monitorType = MonitorType.MONITOR.getType();
}
}

View File

@ -0,0 +1,44 @@
/*
* Copyright 1999-2020 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.nacos.plugin.control.tps;
import java.util.concurrent.TimeUnit;
/**
* rule barrier creator.
*
* @author shiyiyue
*/
public interface RuleBarrierCreator {
/**
* create a count for time unit period.
*
* @param pointName pointName.
* @param ruleName ruleName.
* @param period period.
* @return
*/
RuleBarrier createRuleBarrier(String pointName, String ruleName, TimeUnit period);
/**
* rate count creator name.
*
* @return name.
*/
String name();
}

View File

@ -0,0 +1,94 @@
/*
* Copyright 1999-2020 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.nacos.plugin.control.tps;
import com.alibaba.nacos.common.spi.NacosServiceLoader;
import com.alibaba.nacos.plugin.control.Loggers;
import com.alibaba.nacos.plugin.control.configs.ControlConfigs;
import com.alibaba.nacos.plugin.control.tps.nacos.LocalSimpleCountBarrierCreator;
import com.alibaba.nacos.plugin.control.tps.request.TpsCheckRequest;
import com.alibaba.nacos.plugin.control.tps.response.TpsCheckResponse;
import com.alibaba.nacos.plugin.control.tps.rule.TpsControlRule;
import java.util.Collection;
import java.util.concurrent.TimeUnit;
/**
* tps barrier for tps point.
*
* @author shiyiyue
*/
@SuppressWarnings("PMD.AbstractClassShouldStartWithAbstractNamingRule")
public abstract class TpsBarrier {
protected String pointName;
protected RuleBarrier pointBarrier;
public TpsBarrier(String pointName) {
this.pointName = pointName;
pointBarrier = ruleBarrierCreator.createRuleBarrier(pointName, pointName, TimeUnit.SECONDS);
}
protected static RuleBarrierCreator ruleBarrierCreator;
static {
String tpsBarrierCreator = ControlConfigs.getInstance().getTpsBarrierCreator();
Collection<RuleBarrierCreator> loadedCreators = NacosServiceLoader.load(RuleBarrierCreator.class);
for (RuleBarrierCreator barrierCreator : loadedCreators) {
if (tpsBarrierCreator.equalsIgnoreCase(barrierCreator.name())) {
Loggers.CONTROL.info("Found tps rule creator of name : {}", tpsBarrierCreator);
ruleBarrierCreator = barrierCreator;
break;
}
}
if (ruleBarrierCreator == null) {
Loggers.CONTROL.warn("Fail to found tps rule creator of name : {},use default local simple creator",
tpsBarrierCreator);
ruleBarrierCreator = LocalSimpleCountBarrierCreator.getInstance();
}
}
public static void setRuleBarrierCreator(RuleBarrierCreator ruleBarrierCreatorInstance) {
ruleBarrierCreator = ruleBarrierCreatorInstance;
}
/**
* apply tps.
*
* @param tpsCheckRequest tpsCheckRequest.
* @return check current tps is allowed.
*/
public abstract TpsCheckResponse applyTps(TpsCheckRequest tpsCheckRequest);
public RuleBarrier getPointBarrier() {
return pointBarrier;
}
public String getPointName() {
return pointName;
}
/**
* apply rule.
*
* @param newControlRule newControlRule.
*/
public abstract void applyRule(TpsControlRule newControlRule);
}

View File

@ -14,26 +14,27 @@
* limitations under the License.
*/
package com.alibaba.nacos.core.remote.control;
package com.alibaba.nacos.plugin.control.tps;
/**
* MonitorType.
* tps barrier creator.
*
* @author liuzunfei
* @version $Id: MonitorType.java, v 0.1 2021年01月20日 20:38 PM liuzunfei Exp $
* @author shiyiyue
*/
@SuppressWarnings("PMD.AbstractClassShouldStartWithAbstractNamingRule")
public abstract class MonitorKeyParser {
public interface TpsBarrierCreator {
/**
* parse monitor key.
* get name.
*
* @param arg0 parameters.
* @return monitor key.
* @return
*/
public abstract MonitorKey parse(Object... arg0);
String getName();
/**
* create tps barrier.
*
* @param pointName pointName.
* @return
*/
TpsBarrier createTpsBarrier(String pointName);
}

View File

@ -0,0 +1,60 @@
/*
* Copyright 1999-2020 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.nacos.plugin.control.tps;
import com.alibaba.nacos.common.spi.NacosServiceLoader;
import com.alibaba.nacos.plugin.control.Loggers;
import com.alibaba.nacos.plugin.control.configs.ControlConfigs;
import com.alibaba.nacos.plugin.control.tps.nacos.DefaultNacosTpsBarrierCreator;
import java.util.Collection;
/**
* tps barrier creator proxy.
*
* @author shiyiyue
*/
public class TpsBarrierCreatorProxy {
static TpsBarrierCreator tpsBarrierCreator;
static {
String tpsRuleBarrierCreator = null;
try {
tpsRuleBarrierCreator = ControlConfigs.getInstance().getTpsRuleBarrierCreator();
Collection<TpsBarrierCreator> loadedCreators = NacosServiceLoader.load(TpsBarrierCreator.class);
for (TpsBarrierCreator barrierCreator : loadedCreators) {
if (tpsRuleBarrierCreator.equalsIgnoreCase(barrierCreator.getName())) {
Loggers.CONTROL.info("Found tps barrier creator of name : {}", tpsRuleBarrierCreator);
tpsBarrierCreator = barrierCreator;
break;
}
}
} catch (Throwable throwable) {
Loggers.CONTROL.warn("Fail to load tpsRuleBarrierCreator ", throwable);
}
if (tpsBarrierCreator == null) {
Loggers.CONTROL.warn("Fail to found tps barrier creator of name : {},use default local simple creator",
tpsRuleBarrierCreator);
tpsBarrierCreator = new DefaultNacosTpsBarrierCreator();
}
}
public static TpsBarrierCreator getTpsBarrierCreator() {
return tpsBarrierCreator;
}
}

View File

@ -0,0 +1,102 @@
/*
* Copyright 1999-2020 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.nacos.plugin.control.tps;
import com.alibaba.nacos.common.utils.StringUtils;
import com.alibaba.nacos.plugin.control.Loggers;
import com.alibaba.nacos.plugin.control.ruleactivator.RuleParserProxy;
import com.alibaba.nacos.plugin.control.ruleactivator.RuleStorageProxy;
import com.alibaba.nacos.plugin.control.tps.request.TpsCheckRequest;
import com.alibaba.nacos.plugin.control.tps.response.TpsCheckResponse;
import com.alibaba.nacos.plugin.control.tps.rule.TpsControlRule;
import java.util.Map;
/**
* abstract tps control manager.
*
* @author shiyiyue
*/
@SuppressWarnings("PMD.AbstractClassShouldStartWithAbstractNamingRule")
public abstract class TpsControlManager {
/**
* apple tps rule.
*
* @param pointName pointName.
*/
public abstract void registerTpsPoint(String pointName);
protected void initTpsRule(String pointName) {
RuleStorageProxy ruleStorageProxy = RuleStorageProxy.getInstance();
String localRuleContent = ruleStorageProxy.getLocalDiskStorage().getTpsRule(pointName);
if (StringUtils.isNotBlank(localRuleContent)) {
Loggers.CONTROL.info("Found local disk tps control rule of {},content ={}", pointName, localRuleContent);
} else if (ruleStorageProxy.getExternalStorage() != null
&& ruleStorageProxy.getExternalStorage().getTpsRule(pointName) != null) {
localRuleContent = ruleStorageProxy.getExternalStorage().getTpsRule(pointName);
if (StringUtils.isNotBlank(localRuleContent)) {
Loggers.CONTROL.info("Found external tps control rule of {},content ={}", pointName, localRuleContent);
}
}
if (StringUtils.isNotBlank(localRuleContent)) {
TpsControlRule tpsLimitRule = RuleParserProxy.getInstance().parseTpsRule(localRuleContent);
this.applyTpsRule(pointName, tpsLimitRule);
} else {
Loggers.CONTROL.info("No tps control rule of {} found ", pointName, localRuleContent);
}
}
/**
* get points.
*
* @return
*/
public abstract Map<String, TpsBarrier> getPoints();
/**
* get rules.
*
* @return
*/
public abstract Map<String, TpsControlRule> getRules();
/**
* apple tps rule.
*
* @param pointName pointName.
* @param rule rule.
*/
public abstract void applyTpsRule(String pointName, TpsControlRule rule);
/**
* check tps result.
*
* @param tpsRequest TpsRequest.
* @return check current tps is allowed.
*/
public abstract TpsCheckResponse check(TpsCheckRequest tpsRequest);
/**
* get control manager name.
*
* @return
*/
public abstract String getName();
}

View File

@ -0,0 +1,141 @@
/*
* Copyright 1999-2020 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.nacos.plugin.control.tps;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.TimeUnit;
/**
* tps metrics.
*
* @author shiyiyue
*/
public class TpsMetrics {
private String pointName;
private String type;
private long timeStamp;
private TimeUnit period;
private Counter counter;
public TpsMetrics(String pointName, String type, long timeStamp, TimeUnit period) {
this.pointName = pointName;
this.type = type;
this.timeStamp = timeStamp;
this.period = period;
}
@Override
public String toString() {
return "TpsMetrics{" + "pointName='" + pointName + '\'' + ", type='" + type + '\'' + ", timeStamp=" + timeStamp
+ ", period=" + period + ", counter=" + counter + '}';
}
public String getTimeFormatOfSecond(long timeStamp) {
String format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date(timeStamp));
return format;
}
public String getMsg() {
return String.join("|", pointName, type, period.name(), getTimeFormatOfSecond(timeStamp),
String.valueOf(counter.passCount), String.valueOf(counter.deniedCount));
}
public String getPointName() {
return pointName;
}
public void setPointName(String pointName) {
this.pointName = pointName;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public long getTimeStamp() {
return timeStamp;
}
public void setTimeStamp(long timeStamp) {
this.timeStamp = timeStamp;
}
public TimeUnit getPeriod() {
return period;
}
public void setPeriod(TimeUnit period) {
this.period = period;
}
public Counter getCounter() {
return counter;
}
public void setCounter(Counter counter) {
this.counter = counter;
}
public static class Counter {
private long passCount;
private long deniedCount;
public Counter(long passCount, long deniedCount) {
this.passCount = passCount;
this.deniedCount = deniedCount;
}
public long getPassCount() {
return passCount;
}
public void setPassCount(long passCount) {
this.passCount = passCount;
}
public long getDeniedCount() {
return deniedCount;
}
public void setDeniedCount(long deniedCount) {
this.deniedCount = deniedCount;
}
@Override
public String toString() {
return "{" + "passCount=" + passCount + ", deniedCount=" + deniedCount + '}';
}
public String getSimpleLog() {
return String.join("|", String.valueOf(passCount), String.valueOf(deniedCount));
}
}
}

View File

@ -14,31 +14,25 @@
* limitations under the License.
*/
package com.alibaba.nacos.core.remote.control;
package com.alibaba.nacos.plugin.control.tps.nacos;
import com.alibaba.nacos.plugin.control.tps.TpsBarrier;
import com.alibaba.nacos.plugin.control.tps.TpsBarrierCreator;
/**
* ConnectionIdMonitorKey.
* default nacos tps barrier creator.
*
* @author liuzunfei
* @version $Id: ConnectionIdMonitorKey.java, v 0.1 2021年01月20日 20:38 PM liuzunfei Exp $
* @author shiyiyue
*/
public class ConnectionIdMonitorKey extends MonitorKey {
public class DefaultNacosTpsBarrierCreator implements TpsBarrierCreator {
private static final String TYPE = "connectionId";
String key;
public ConnectionIdMonitorKey() {
}
public ConnectionIdMonitorKey(String clientIp) {
this.key = clientIp;
@Override
public String getName() {
return "nacos";
}
@Override
public String getType() {
return TYPE;
public TpsBarrier createTpsBarrier(String pointName) {
return new NacosTpsBarrier(pointName);
}
}

View File

@ -0,0 +1,49 @@
/*
* Copyright 1999-2020 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.nacos.plugin.control.tps.nacos;
import com.alibaba.nacos.plugin.control.tps.RuleBarrier;
import com.alibaba.nacos.plugin.control.tps.RuleBarrierCreator;
import java.util.concurrent.TimeUnit;
/**
* local simple count barrier creator.
*
* @author shiyiyue
*/
public class LocalSimpleCountBarrierCreator implements RuleBarrierCreator {
private static final LocalSimpleCountBarrierCreator INSTANCE = new LocalSimpleCountBarrierCreator();
public LocalSimpleCountBarrierCreator() {
}
public static final LocalSimpleCountBarrierCreator getInstance() {
return INSTANCE;
}
@Override
public RuleBarrier createRuleBarrier(String pointName, String ruleName, TimeUnit period) {
return new LocalSimpleCountRuleBarrier(pointName, ruleName, period);
}
@Override
public String name() {
return "localsimplecountor";
}
}

View File

@ -0,0 +1,145 @@
/*
* Copyright 1999-2020 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.nacos.plugin.control.tps.nacos;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;
/**
* local simple count rate counter.
*
* @author shiyiyue
*/
public class LocalSimpleCountRateCounter extends RateCounter {
private static final int DEFAULT_RECORD_SIZE = 10;
long startTime = System.currentTimeMillis();
private List<TpsSlot> slotList;
public LocalSimpleCountRateCounter(String name, TimeUnit period) {
super(name, period);
slotList = new ArrayList<>(DEFAULT_RECORD_SIZE);
for (int i = 0; i < DEFAULT_RECORD_SIZE; i++) {
slotList.add(new TpsSlot());
}
long now = System.currentTimeMillis();
if (period == TimeUnit.SECONDS) {
startTime = RateCounter.getTrimMillsOfSecond(now);
} else if (period == TimeUnit.MINUTES) {
startTime = RateCounter.getTrimMillsOfMinute(now);
} else if (period == TimeUnit.HOURS) {
startTime = RateCounter.getTrimMillsOfHour(now);
} else {
//second default
getTrimMillsOfSecond(now);
}
}
@Override
public long add(long timestamp, long count) {
return createSlotIfAbsent(timestamp).countHolder.count.addAndGet(count);
}
public void minus(long timestamp, long count) {
AtomicLong currentCount = createSlotIfAbsent(timestamp).countHolder.count;
currentCount.addAndGet(count * -1);
}
public long getCount(long timestamp) {
TpsSlot point = getPoint(timestamp);
return point == null ? 0L : point.countHolder.count.longValue();
}
/**
* get slot of the timestamp second,read only ,return nul if not exist.
*
* @param timeStamp the timestamp second.
* @return tps slot.
*/
private TpsSlot getPoint(long timeStamp) {
long distance = timeStamp - startTime;
long diff = (distance < 0 ? distance + getPeriod().toMillis(1) * DEFAULT_RECORD_SIZE : distance) / getPeriod()
.toMillis(1);
long currentWindowTime = startTime + diff * getPeriod().toMillis(1);
int index = (int) diff % DEFAULT_RECORD_SIZE;
TpsSlot tpsSlot = slotList.get(index);
if (tpsSlot.time != currentWindowTime) {
return null;
}
return tpsSlot;
}
/**
* get slot of the timestamp second,create if not exist.
*
* @param timeStamp the timestamp second.
* @return tps slot.
*/
public TpsSlot createSlotIfAbsent(long timeStamp) {
long distance = timeStamp - startTime;
long diff = (distance < 0 ? distance + getPeriod().toMillis(1) * DEFAULT_RECORD_SIZE : distance) / getPeriod()
.toMillis(1);
long currentWindowTime = startTime + diff * getPeriod().toMillis(1);
int index = (int) diff % DEFAULT_RECORD_SIZE;
TpsSlot tpsSlot = slotList.get(index);
if (tpsSlot.time != currentWindowTime) {
tpsSlot.reset(currentWindowTime);
}
return slotList.get(index);
}
static class TpsSlot {
long time = 0L;
private SlotCountHolder countHolder = new SlotCountHolder();
public void reset(long second) {
synchronized (this) {
if (this.time != second) {
this.time = second;
countHolder.count.set(0L);
countHolder.interceptedCount.set(0);
}
}
}
@Override
public String toString() {
return "TpsSlot{" + "time=" + time + ", countHolder=" + countHolder + '}';
}
}
static class SlotCountHolder {
AtomicLong count = new AtomicLong();
AtomicLong interceptedCount = new AtomicLong();
@Override
public String toString() {
return "{" + count + "|" + interceptedCount + '}';
}
}
}

Some files were not shown because too many files have changed in this diff Show More