diff --git a/ruoyi-common/ruoyi-common-alibaba-bom/pom.xml b/ruoyi-common/ruoyi-common-alibaba-bom/pom.xml
index 47dce94d..11f12752 100644
--- a/ruoyi-common/ruoyi-common-alibaba-bom/pom.xml
+++ b/ruoyi-common/ruoyi-common-alibaba-bom/pom.xml
@@ -166,6 +166,12 @@
${dubbo.version}
+
+ org.apache.dubbo
+ dubbo-metadata-report-redis
+ ${dubbo.version}
+
+
com.alibaba.spring
spring-context-support
diff --git a/ruoyi-common/ruoyi-common-dubbo/pom.xml b/ruoyi-common/ruoyi-common-dubbo/pom.xml
index 4ff44a9d..b5cee969 100644
--- a/ruoyi-common/ruoyi-common-dubbo/pom.xml
+++ b/ruoyi-common/ruoyi-common-dubbo/pom.xml
@@ -36,6 +36,21 @@
dubbo-spring-boot-actuator
+
+ org.apache.dubbo
+ dubbo-metadata-report-redis
+
+
+ redis.clients
+ jedis
+
+
+
+
+ redis.clients
+ jedis
+ 5.1.0
+
org.projectlombok
lombok
diff --git a/ruoyi-common/ruoyi-common-dubbo/src/main/java/org/apache/dubbo/metadata/report/support/AbstractMetadataReport.java b/ruoyi-common/ruoyi-common-dubbo/src/main/java/org/apache/dubbo/metadata/report/support/AbstractMetadataReport.java
new file mode 100644
index 00000000..6c53681c
--- /dev/null
+++ b/ruoyi-common/ruoyi-common-dubbo/src/main/java/org/apache/dubbo/metadata/report/support/AbstractMetadataReport.java
@@ -0,0 +1,576 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.dubbo.metadata.report.support;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.logger.ErrorTypeAwareLogger;
+import org.apache.dubbo.common.logger.LoggerFactory;
+import org.apache.dubbo.common.utils.CollectionUtils;
+import org.apache.dubbo.common.utils.ConfigUtils;
+import org.apache.dubbo.common.utils.JsonUtils;
+import org.apache.dubbo.common.utils.NamedThreadFactory;
+import org.apache.dubbo.metadata.definition.model.FullServiceDefinition;
+import org.apache.dubbo.metadata.definition.model.ServiceDefinition;
+import org.apache.dubbo.metadata.report.MetadataReport;
+import org.apache.dubbo.metadata.report.identifier.KeyTypeEnum;
+import org.apache.dubbo.metadata.report.identifier.MetadataIdentifier;
+import org.apache.dubbo.metadata.report.identifier.ServiceMetadataIdentifier;
+import org.apache.dubbo.metadata.report.identifier.SubscriberMetadataIdentifier;
+import org.apache.dubbo.metrics.event.MetricsEventBus;
+import org.apache.dubbo.metrics.metadata.event.MetadataEvent;
+import org.apache.dubbo.rpc.model.ApplicationModel;
+
+import java.io.*;
+import java.nio.channels.FileChannel;
+import java.nio.channels.FileLock;
+import java.util.*;
+import java.util.concurrent.*;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicLong;
+
+import static org.apache.dubbo.common.constants.CommonConstants.*;
+import static org.apache.dubbo.common.constants.LoggerCodeConstants.COMMON_UNEXPECTED_EXCEPTION;
+import static org.apache.dubbo.common.constants.LoggerCodeConstants.PROXY_FAILED_EXPORT_SERVICE;
+import static org.apache.dubbo.common.utils.StringUtils.replace;
+import static org.apache.dubbo.metadata.report.support.Constants.*;
+
+public abstract class AbstractMetadataReport implements MetadataReport {
+
+ protected static final String DEFAULT_ROOT = "dubbo";
+
+ protected static final int ONE_DAY_IN_MILLISECONDS = 60 * 24 * 60 * 1000;
+ private static final int FOUR_HOURS_IN_MILLISECONDS = 60 * 4 * 60 * 1000;
+ // Log output
+ protected final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(getClass());
+
+ // Local disk cache, where the special key value.registries records the list of metadata centers, and the others are
+ // the list of notified service providers
+ final Properties properties = new Properties();
+ private final ExecutorService reportCacheExecutor =
+ Executors.newFixedThreadPool(1, new NamedThreadFactory("DubboSaveMetadataReport", true));
+ final Map allMetadataReports = new ConcurrentHashMap<>(4);
+
+ private final AtomicLong lastCacheChanged = new AtomicLong();
+ final Map failedReports = new ConcurrentHashMap<>(4);
+ private URL reportURL;
+ boolean syncReport;
+ // Local disk cache file
+ File file;
+ private AtomicBoolean initialized = new AtomicBoolean(false);
+ public MetadataReportRetry metadataReportRetry;
+ private ScheduledExecutorService reportTimerScheduler;
+
+ private final boolean reportMetadata;
+ private final boolean reportDefinition;
+ protected ApplicationModel applicationModel;
+
+ public AbstractMetadataReport(URL reportServerURL) {
+ setUrl(reportServerURL);
+ applicationModel = reportServerURL.getOrDefaultApplicationModel();
+
+ boolean localCacheEnabled = reportServerURL.getParameter(REGISTRY_LOCAL_FILE_CACHE_ENABLED, true);
+ // Start file save timer
+ String defaultFilename = System.getProperty("user.home") + DUBBO_METADATA
+ + reportServerURL.getApplication()
+ + "-" + replace(reportServerURL.getAddress(), ":", "-")
+ + CACHE;
+ String filename = reportServerURL.getParameter(FILE_KEY, defaultFilename);
+ File file = null;
+ if (localCacheEnabled && ConfigUtils.isNotEmpty(filename)) {
+ file = new File(filename);
+ if (!file.exists()
+ && file.getParentFile() != null
+ && !file.getParentFile().exists()) {
+ if (!file.getParentFile().mkdirs()) {
+ throw new IllegalArgumentException("Invalid service store file " + file
+ + ", cause: Failed to create directory " + file.getParentFile() + "!");
+ }
+ }
+ // if this file exists, firstly delete it.
+ if (!initialized.getAndSet(true) && file.exists()) {
+ file.delete();
+ }
+ }
+ this.file = file;
+ loadProperties();
+ syncReport = reportServerURL.getParameter(SYNC_REPORT_KEY, false);
+ metadataReportRetry = new MetadataReportRetry(
+ reportServerURL.getParameter(RETRY_TIMES_KEY, DEFAULT_METADATA_REPORT_RETRY_TIMES),
+ reportServerURL.getParameter(RETRY_PERIOD_KEY, DEFAULT_METADATA_REPORT_RETRY_PERIOD));
+ // cycle report the data switch
+ if (reportServerURL.getParameter(CYCLE_REPORT_KEY, DEFAULT_METADATA_REPORT_CYCLE_REPORT)) {
+ reportTimerScheduler = Executors.newSingleThreadScheduledExecutor(
+ new NamedThreadFactory("DubboMetadataReportTimer", true));
+ reportTimerScheduler.scheduleAtFixedRate(
+ this::publishAll, calculateStartTime(), ONE_DAY_IN_MILLISECONDS, TimeUnit.MILLISECONDS);
+ }
+
+ this.reportMetadata = reportServerURL.getParameter(REPORT_METADATA_KEY, false);
+ this.reportDefinition = reportServerURL.getParameter(REPORT_DEFINITION_KEY, true);
+ }
+
+ public URL getUrl() {
+ return reportURL;
+ }
+
+ protected void setUrl(URL url) {
+ if (url == null) {
+ throw new IllegalArgumentException("metadataReport url == null");
+ }
+ this.reportURL = url;
+ }
+
+ private void doSaveProperties(long version) {
+ if (version < lastCacheChanged.get()) {
+ return;
+ }
+ if (file == null) {
+ return;
+ }
+ // Save
+ try {
+ File lockfile = new File(file.getAbsolutePath() + ".lock");
+ if (!lockfile.exists()) {
+ lockfile.createNewFile();
+ }
+ try (RandomAccessFile raf = new RandomAccessFile(lockfile, "rw");
+ FileChannel channel = raf.getChannel()) {
+ FileLock lock = channel.tryLock();
+ if (lock == null) {
+ throw new IOException(
+ "Can not lock the metadataReport cache file " + file.getAbsolutePath()
+ + ", ignore and retry later, maybe multi java process use the file, please config: dubbo.metadata.file=xxx.properties");
+ }
+ // Save
+ try {
+ if (!file.exists()) {
+ file.createNewFile();
+ }
+
+ Properties tmpProperties;
+ if (!syncReport) {
+ // When syncReport = false, properties.setProperty and properties.store are called from the same
+ // thread(reportCacheExecutor), so deep copy is not required
+ tmpProperties = properties;
+ } else {
+ // Using store method and setProperty method of the this.properties will cause lock contention
+ // under multi-threading, so deep copy a new container
+ tmpProperties = new Properties();
+ Set> entries = properties.entrySet();
+ for (Map.Entry