From 6b6d13ca7642efa2bf6521c47eb55b750a36403b Mon Sep 17 00:00:00 2001 From: Eric Zhao Date: Mon, 30 Jul 2018 16:36:41 +0800 Subject: [PATCH] Add Nacos datasource integration Signed-off-by: Eric Zhao --- .../property/DynamicSentinelProperty.java | 4 +- sentinel-extension/pom.xml | 1 + .../sentinel-datasource-extension/pom.xml | 5 +- .../datasource/AbstractDataSource.java | 4 +- .../sentinel-datasource-nacos/pom.xml | 31 ++++ .../datasource/nacos/NacosDataSource.java | 134 ++++++++++++++++++ 6 files changed, 171 insertions(+), 8 deletions(-) create mode 100644 sentinel-extension/sentinel-datasource-nacos/pom.xml create mode 100644 sentinel-extension/sentinel-datasource-nacos/src/main/java/com/alibaba/csp/sentinel/datasource/nacos/NacosDataSource.java diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/property/DynamicSentinelProperty.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/property/DynamicSentinelProperty.java index 50716397..3b2bc0cd 100755 --- a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/property/DynamicSentinelProperty.java +++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/property/DynamicSentinelProperty.java @@ -59,12 +59,12 @@ public class DynamicSentinelProperty implements SentinelProperty { } - public boolean isEqual(T oldValue, T newValue) { + private boolean isEqual(T oldValue, T newValue) { if (oldValue == null && newValue == null) { return true; } - if (oldValue == null && newValue != null) { + if (oldValue == null) { return false; } diff --git a/sentinel-extension/pom.xml b/sentinel-extension/pom.xml index 12b2b568..ba9b729f 100755 --- a/sentinel-extension/pom.xml +++ b/sentinel-extension/pom.xml @@ -13,6 +13,7 @@ sentinel-datasource-extension + sentinel-datasource-nacos diff --git a/sentinel-extension/sentinel-datasource-extension/pom.xml b/sentinel-extension/sentinel-datasource-extension/pom.xml index 26b24310..8ac1e294 100755 --- a/sentinel-extension/sentinel-datasource-extension/pom.xml +++ b/sentinel-extension/sentinel-datasource-extension/pom.xml @@ -17,10 +17,7 @@ com.alibaba.csp sentinel-core - - com.alibaba - fastjson - + junit junit diff --git a/sentinel-extension/sentinel-datasource-extension/src/main/java/com/alibaba/csp/sentinel/datasource/AbstractDataSource.java b/sentinel-extension/sentinel-datasource-extension/src/main/java/com/alibaba/csp/sentinel/datasource/AbstractDataSource.java index f9e060b2..ec5b66d9 100755 --- a/sentinel-extension/sentinel-datasource-extension/src/main/java/com/alibaba/csp/sentinel/datasource/AbstractDataSource.java +++ b/sentinel-extension/sentinel-datasource-extension/src/main/java/com/alibaba/csp/sentinel/datasource/AbstractDataSource.java @@ -20,8 +20,8 @@ import com.alibaba.csp.sentinel.property.SentinelProperty; public abstract class AbstractDataSource implements DataSource { - protected ConfigParser parser; - protected SentinelProperty property; + protected final ConfigParser parser; + protected final SentinelProperty property; public AbstractDataSource(ConfigParser parser) { if (parser == null) { diff --git a/sentinel-extension/sentinel-datasource-nacos/pom.xml b/sentinel-extension/sentinel-datasource-nacos/pom.xml new file mode 100644 index 00000000..a488272c --- /dev/null +++ b/sentinel-extension/sentinel-datasource-nacos/pom.xml @@ -0,0 +1,31 @@ + + + + sentinel-extension + com.alibaba.csp + 0.1.1-SNAPSHOT + + 4.0.0 + + sentinel-datasource-nacos + jar + + + 0.1.0 + + + + + com.alibaba.csp + sentinel-datasource-extension + + + + com.alibaba.nacos + nacos-client + ${nacos.version} + + + \ No newline at end of file diff --git a/sentinel-extension/sentinel-datasource-nacos/src/main/java/com/alibaba/csp/sentinel/datasource/nacos/NacosDataSource.java b/sentinel-extension/sentinel-datasource-nacos/src/main/java/com/alibaba/csp/sentinel/datasource/nacos/NacosDataSource.java new file mode 100644 index 00000000..4da113a8 --- /dev/null +++ b/sentinel-extension/sentinel-datasource-nacos/src/main/java/com/alibaba/csp/sentinel/datasource/nacos/NacosDataSource.java @@ -0,0 +1,134 @@ +/* + * Copyright 1999-2018 Alibaba Group Holding Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.csp.sentinel.datasource.nacos; + +import java.util.concurrent.ArrayBlockingQueue; +import java.util.concurrent.Executor; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; + +import com.alibaba.csp.sentinel.concurrent.NamedThreadFactory; +import com.alibaba.csp.sentinel.datasource.AbstractDataSource; +import com.alibaba.csp.sentinel.datasource.ConfigParser; +import com.alibaba.csp.sentinel.datasource.DataSource; +import com.alibaba.csp.sentinel.log.RecordLog; +import com.alibaba.csp.sentinel.util.StringUtil; +import com.alibaba.nacos.api.NacosFactory; +import com.alibaba.nacos.api.config.ConfigService; +import com.alibaba.nacos.api.config.listener.Listener; + +/** + * A {@link DataSource} with Nacos backend. When the data in Nacos backend has been modified, + * Nacos will automatically push the new value so that the dynamic configuration can be real-time. + * + * @author Eric Zhao + */ +public class NacosDataSource extends AbstractDataSource { + + private static final int DEFAULT_TIMEOUT = 3000; + + /** + * Single-thread pool. Once the thread pool is blocked, we throw up the old task. + */ + private final ExecutorService pool = new ThreadPoolExecutor(1, 1, 0, TimeUnit.MILLISECONDS, + new ArrayBlockingQueue(1), new NamedThreadFactory("sentinel-nacos-ds-update"), + new ThreadPoolExecutor.DiscardOldestPolicy()); + + private final Listener configListener; + private final String groupId; + private final String dataId; + + /** + * Note: The Nacos config might be null if its initialization failed. + */ + private ConfigService configService = null; + + /** + * Constructs an DataSource with Nacos backend. + * + * @param serverAddr server address of Nacos, cannot be empty + * @param groupId group ID, cannot be empty + * @param dataId data ID, cannot be empty + * @param parser customized data parser, cannot be empty + */ + public NacosDataSource(final String serverAddr, final String groupId, final String dataId, + ConfigParser parser) { + super(parser); + if (StringUtil.isBlank(serverAddr) || StringUtil.isBlank(groupId) || StringUtil.isBlank(dataId)) { + throw new IllegalArgumentException(String.format("Bad argument: serverAddr=[%s], groupId=[%s], dataId=[%s]", + serverAddr, groupId, dataId)); + } + this.groupId = groupId; + this.dataId = dataId; + this.configListener = new Listener() { + @Override + public Executor getExecutor() { + return pool; + } + + @Override + public void receiveConfigInfo(final String configInfo) { + RecordLog.info(String.format("[NacosDataSource] New property value received for (%s, %s, %s): %s", + serverAddr, dataId, groupId, configInfo)); + T newValue = NacosDataSource.this.parser.parse(configInfo); + // Update the new value to the property. + getProperty().updateValue(newValue); + } + }; + initNacosListener(serverAddr); + loadInitialConfig(); + } + + private void loadInitialConfig() { + try { + T newValue = loadConfig(); + if (newValue == null) { + RecordLog.info("[NacosDataSource] WARN: initial config is null, you may have to check your data source"); + } + getProperty().updateValue(newValue); + } catch (Exception ex) { + RecordLog.info("[NacosDataSource] Error when loading initial config", ex); + } + } + + private void initNacosListener(String serverAddr) { + try { + this.configService = NacosFactory.createConfigService(serverAddr); + // Add config listener. + configService.addListener(dataId, groupId, configListener); + } catch (Exception e) { + RecordLog.info("[NacosDataSource] Error occurred when initializing Nacos data source", e); + e.printStackTrace(); + } + } + + @Override + public String readSource() throws Exception { + if (configService == null) { + throw new IllegalStateException("Nacos config service has not been initialized or error occurred"); + } + return configService.getConfig(dataId, groupId, DEFAULT_TIMEOUT); + } + + @Override + public void close() { + if (configService != null) { + configService.removeListener(dataId, groupId, configListener); + } + pool.shutdownNow(); + } +}