mirror of
https://gitee.com/dromara/RuoYi-Cloud-Plus.git
synced 2026-05-09 21:32:08 +08:00
@@ -2,7 +2,7 @@
|
||||
<configuration default="false" name="ruoyi-auth" type="docker-deploy" factoryName="dockerfile" server-name="Docker">
|
||||
<deployment type="dockerfile">
|
||||
<settings>
|
||||
<option name="imageTag" value="ruoyi/ruoyi-auth:2.6.0" />
|
||||
<option name="imageTag" value="ruoyi/ruoyi-auth:2.6.1" />
|
||||
<option name="buildOnly" value="true" />
|
||||
<option name="sourceFilePath" value="ruoyi-auth/Dockerfile" />
|
||||
</settings>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<configuration default="false" name="ruoyi-gateway" type="docker-deploy" factoryName="dockerfile" server-name="Docker">
|
||||
<deployment type="dockerfile">
|
||||
<settings>
|
||||
<option name="imageTag" value="ruoyi/ruoyi-gateway:2.6.0" />
|
||||
<option name="imageTag" value="ruoyi/ruoyi-gateway:2.6.1" />
|
||||
<option name="buildOnly" value="true" />
|
||||
<option name="sourceFilePath" value="ruoyi-gateway/Dockerfile" />
|
||||
</settings>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<configuration default="false" name="ruoyi-gen" type="docker-deploy" factoryName="dockerfile" server-name="Docker">
|
||||
<deployment type="dockerfile">
|
||||
<settings>
|
||||
<option name="imageTag" value="ruoyi/ruoyi-gen:2.6.0" />
|
||||
<option name="imageTag" value="ruoyi/ruoyi-gen:2.6.1" />
|
||||
<option name="buildOnly" value="true" />
|
||||
<option name="sourceFilePath" value="ruoyi-modules/ruoyi-gen/Dockerfile" />
|
||||
</settings>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<configuration default="false" name="ruoyi-job" type="docker-deploy" factoryName="dockerfile" server-name="Docker">
|
||||
<deployment type="dockerfile">
|
||||
<settings>
|
||||
<option name="imageTag" value="ruoyi/ruoyi-job:2.6.0" />
|
||||
<option name="imageTag" value="ruoyi/ruoyi-job:2.6.1" />
|
||||
<option name="buildOnly" value="true" />
|
||||
<option name="sourceFilePath" value="ruoyi-modules/ruoyi-job/Dockerfile" />
|
||||
</settings>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<configuration default="false" name="ruoyi-monitor" type="docker-deploy" factoryName="dockerfile" server-name="Docker">
|
||||
<deployment type="dockerfile">
|
||||
<settings>
|
||||
<option name="imageTag" value="ruoyi/ruoyi-monitor:2.6.0" />
|
||||
<option name="imageTag" value="ruoyi/ruoyi-monitor:2.6.1" />
|
||||
<option name="buildOnly" value="true" />
|
||||
<option name="sourceFilePath" value="ruoyi-visual/ruoyi-monitor/Dockerfile" />
|
||||
</settings>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<configuration default="false" name="ruoyi-nacos" type="docker-deploy" factoryName="dockerfile" server-name="Docker">
|
||||
<deployment type="dockerfile">
|
||||
<settings>
|
||||
<option name="imageTag" value="ruoyi/ruoyi-nacos:2.6.0" />
|
||||
<option name="imageTag" value="ruoyi/ruoyi-nacos:2.6.1" />
|
||||
<option name="buildOnly" value="true" />
|
||||
<option name="sourceFilePath" value="ruoyi-visual/ruoyi-nacos/Dockerfile" />
|
||||
</settings>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<configuration default="false" name="ruoyi-resource" type="docker-deploy" factoryName="dockerfile" server-name="Docker">
|
||||
<deployment type="dockerfile">
|
||||
<settings>
|
||||
<option name="imageTag" value="ruoyi/ruoyi-resource:2.6.0" />
|
||||
<option name="imageTag" value="ruoyi/ruoyi-resource:2.6.1" />
|
||||
<option name="buildOnly" value="true" />
|
||||
<option name="sourceFilePath" value="ruoyi-modules/ruoyi-resource/Dockerfile" />
|
||||
</settings>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<configuration default="false" name="ruoyi-seata-server" type="docker-deploy" factoryName="dockerfile" server-name="Docker">
|
||||
<deployment type="dockerfile">
|
||||
<settings>
|
||||
<option name="imageTag" value="ruoyi/ruoyi-seata-server:2.6.0" />
|
||||
<option name="imageTag" value="ruoyi/ruoyi-seata-server:2.6.1" />
|
||||
<option name="buildOnly" value="true" />
|
||||
<option name="sourceFilePath" value="ruoyi-visual/ruoyi-seata-server/Dockerfile" />
|
||||
</settings>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<configuration default="false" name="ruoyi-snailjob-server" type="docker-deploy" factoryName="dockerfile" server-name="Docker">
|
||||
<deployment type="dockerfile">
|
||||
<settings>
|
||||
<option name="imageTag" value="ruoyi/ruoyi-snailjob-server:2.6.0" />
|
||||
<option name="imageTag" value="ruoyi/ruoyi-snailjob-server:2.6.1" />
|
||||
<option name="buildOnly" value="true" />
|
||||
<option name="sourceFilePath" value="ruoyi-visual/ruoyi-snailjob-server/Dockerfile" />
|
||||
</settings>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<configuration default="false" name="ruoyi-system" type="docker-deploy" factoryName="dockerfile" server-name="Docker">
|
||||
<deployment type="dockerfile">
|
||||
<settings>
|
||||
<option name="imageTag" value="ruoyi/ruoyi-system:2.6.0" />
|
||||
<option name="imageTag" value="ruoyi/ruoyi-system:2.6.1" />
|
||||
<option name="buildOnly" value="true" />
|
||||
<option name="sourceFilePath" value="ruoyi-modules/ruoyi-system/Dockerfile" />
|
||||
</settings>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<configuration default="false" name="ruoyi-workflow" type="docker-deploy" factoryName="dockerfile" server-name="Docker">
|
||||
<deployment type="dockerfile">
|
||||
<settings>
|
||||
<option name="imageTag" value="ruoyi/ruoyi-workflow:2.6.0" />
|
||||
<option name="imageTag" value="ruoyi/ruoyi-workflow:2.6.1" />
|
||||
<option name="buildOnly" value="true" />
|
||||
<option name="sourceFilePath" value="ruoyi-modules/ruoyi-workflow/Dockerfile" />
|
||||
</settings>
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
[](https://gitcode.com/dromara/RuoYi-Cloud-Plus)
|
||||
[](https://gitee.com/dromara/RuoYi-Cloud-Plus/blob/2.X/LICENSE)
|
||||
<br>
|
||||
[](https://gitee.com/dromara/RuoYi-Cloud-Plus)
|
||||
[](https://gitee.com/dromara/RuoYi-Cloud-Plus)
|
||||
[]()
|
||||
[]()
|
||||
[]()
|
||||
|
||||
35
pom.xml
35
pom.xml
@@ -13,28 +13,28 @@
|
||||
<description>Dromara RuoYi-Cloud-Plus微服务系统</description>
|
||||
|
||||
<properties>
|
||||
<revision>2.6.0</revision>
|
||||
<revision>2.6.1</revision>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
|
||||
<java.version>17</java.version>
|
||||
<spring-boot.version>3.5.12</spring-boot.version>
|
||||
<spring-cloud.version>2025.0.1</spring-cloud.version>
|
||||
<spring-boot-admin.version>3.5.6</spring-boot-admin.version>
|
||||
<spring-boot.version>3.5.14</spring-boot.version>
|
||||
<spring-cloud.version>2025.0.2</spring-cloud.version>
|
||||
<spring-boot-admin.version>3.5.8</spring-boot-admin.version>
|
||||
<mybatis.version>3.5.19</mybatis.version>
|
||||
<mybatis-plus.version>3.5.16</mybatis-plus.version>
|
||||
<p6spy.version>3.9.1</p6spy.version>
|
||||
<dynamic-ds.version>4.3.1</dynamic-ds.version>
|
||||
<velocity.version>2.3</velocity.version>
|
||||
<swagger.core.version>2.2.41</swagger.core.version>
|
||||
<springdoc.version>2.8.15</springdoc.version>
|
||||
<swagger.core.version>2.2.47</swagger.core.version>
|
||||
<springdoc.version>2.8.17</springdoc.version>
|
||||
<therapi-javadoc.version>0.15.0</therapi-javadoc.version>
|
||||
<fastexcel.version>1.3.0</fastexcel.version>
|
||||
<hutool.version>5.8.43</hutool.version>
|
||||
<redisson.version>3.52.0</redisson.version>
|
||||
<lock4j.version>2.2.7</lock4j.version>
|
||||
<snailjob.version>1.9.0</snailjob.version>
|
||||
<satoken.version>1.44.0</satoken.version>
|
||||
<lombok.version>1.18.42</lombok.version>
|
||||
<snailjob.version>1.10.0</snailjob.version>
|
||||
<satoken.version>1.45.0</satoken.version>
|
||||
<lombok.version>1.18.44</lombok.version>
|
||||
<logstash.version>7.4</logstash.version>
|
||||
<easy-es.version>3.0.1</easy-es.version>
|
||||
<elasticsearch-client.version>7.17.28</elasticsearch-client.version>
|
||||
@@ -44,19 +44,19 @@
|
||||
<mapstruct-plus.lombok.version>0.2.0</mapstruct-plus.lombok.version>
|
||||
<justauth.version>1.16.7</justauth.version>
|
||||
<!-- 离线IP地址定位库 -->
|
||||
<ip2region.version>3.3.4</ip2region.version>
|
||||
<ip2region.version>3.3.7</ip2region.version>
|
||||
<!-- 临时修复 fastjson 漏洞 -->
|
||||
<fastjson.version>1.2.83</fastjson.version>
|
||||
<!-- OSS 配置 -->
|
||||
<aws.sdk.version>2.28.22</aws.sdk.version>
|
||||
<!-- SMS 配置 -->
|
||||
<sms4j.version>3.3.4</sms4j.version>
|
||||
<sms4j.version>3.3.5</sms4j.version>
|
||||
<!-- 面向运行时的D-ORM依赖 -->
|
||||
<anyline.version>8.7.3-20251210</anyline.version>
|
||||
<anyline.version>8.7.3-20260319</anyline.version>
|
||||
<!-- 工作流配置 -->
|
||||
<warm-flow.version>1.8.4</warm-flow.version>
|
||||
<warm-flow.version>1.8.5</warm-flow.version>
|
||||
<!-- mq配置 -->
|
||||
<rocketmq.version>2.3.4</rocketmq.version>
|
||||
<rocketmq.version>2.3.5</rocketmq.version>
|
||||
|
||||
<!-- 插件版本 -->
|
||||
<maven-compiler-plugin.version>3.14.0</maven-compiler-plugin.version>
|
||||
@@ -350,13 +350,6 @@
|
||||
<version>${fastjson.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- dubbo 专用 -->
|
||||
<dependency>
|
||||
<groupId>redis.clients</groupId>
|
||||
<artifactId>jedis</artifactId>
|
||||
<version>5.1.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.github.linpeilie</groupId>
|
||||
<artifactId>mapstruct-plus-spring-boot-starter</artifactId>
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
</description>
|
||||
|
||||
<properties>
|
||||
<revision>2.6.0</revision>
|
||||
<revision>2.6.1</revision>
|
||||
</properties>
|
||||
|
||||
<dependencyManagement>
|
||||
|
||||
@@ -14,6 +14,7 @@ import org.dromara.common.core.constant.Constants;
|
||||
import org.dromara.common.core.utils.MessageUtils;
|
||||
import org.dromara.common.core.utils.ServletUtils;
|
||||
import org.dromara.common.core.utils.SpringUtils;
|
||||
import org.dromara.common.core.utils.StringUtils;
|
||||
import org.dromara.common.core.utils.ip.AddressUtils;
|
||||
import org.dromara.common.log.event.LogininforEvent;
|
||||
import org.dromara.common.redis.utils.RedisUtils;
|
||||
@@ -77,7 +78,7 @@ public class UserActionListener implements SaTokenListener {
|
||||
SpringUtils.context().publishEvent(logininforEvent);
|
||||
// 更新登录信息
|
||||
remoteUserService.recordLoginInfo((Long) loginParameter.getExtra(LoginHelper.USER_KEY), ip);
|
||||
log.info("user doLogin, useId:{}, token:{}", loginId, tokenValue);
|
||||
log.info("user doLogin, useId:{}, token:***{}", loginId, StringUtils.right(tokenValue, 8));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -89,7 +90,7 @@ public class UserActionListener implements SaTokenListener {
|
||||
TenantHelper.dynamic(tenantId, () -> {
|
||||
RedisUtils.deleteObject(CacheConstants.ONLINE_TOKEN_KEY + tokenValue);
|
||||
});
|
||||
log.info("user doLogout, useId:{}, token:{}", loginId, tokenValue);
|
||||
log.info("user doLogout, useId:{}, token:***{}", loginId, StringUtils.right(tokenValue, 8));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -101,7 +102,7 @@ public class UserActionListener implements SaTokenListener {
|
||||
TenantHelper.dynamic(tenantId, () -> {
|
||||
RedisUtils.deleteObject(CacheConstants.ONLINE_TOKEN_KEY + tokenValue);
|
||||
});
|
||||
log.info("user doLogoutByLoginId, useId:{}, token:{}", loginId, tokenValue);
|
||||
log.info("user doLogoutByLoginId, useId:{}, token:***{}", loginId, StringUtils.right(tokenValue, 8));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -113,7 +114,7 @@ public class UserActionListener implements SaTokenListener {
|
||||
TenantHelper.dynamic(tenantId, () -> {
|
||||
RedisUtils.deleteObject(CacheConstants.ONLINE_TOKEN_KEY + tokenValue);
|
||||
});
|
||||
log.info("user doReplaced, useId:{}, token:{}", loginId, tokenValue);
|
||||
log.info("user doReplaced, useId:{}, token:***{}", loginId, StringUtils.right(tokenValue, 8));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
</description>
|
||||
|
||||
<properties>
|
||||
<revision>2.6.0</revision>
|
||||
<revision>2.6.1</revision>
|
||||
<spring-cloud-alibaba.version>2025.0.0.0</spring-cloud-alibaba.version>
|
||||
<seata.version>2.5.0</seata.version>
|
||||
<nacos.client.version>2.5.1</nacos.client.version>
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
</description>
|
||||
|
||||
<properties>
|
||||
<revision>2.6.0</revision>
|
||||
<revision>2.6.1</revision>
|
||||
</properties>
|
||||
|
||||
<dependencyManagement>
|
||||
|
||||
@@ -37,20 +37,10 @@
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.dubbo.extensions</groupId>
|
||||
<artifactId>dubbo-metadata-report-redis</artifactId>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>redis.clients</groupId>
|
||||
<artifactId>jedis</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>redis.clients</groupId>
|
||||
<artifactId>jedis</artifactId>
|
||||
<version>5.2.0</version>
|
||||
<groupId>org.dromara</groupId>
|
||||
<artifactId>ruoyi-common-redis</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
|
||||
@@ -1,538 +0,0 @@
|
||||
/*
|
||||
* 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.store.redis;
|
||||
|
||||
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
|
||||
import org.apache.dubbo.common.URL;
|
||||
import org.apache.dubbo.common.config.configcenter.ConfigItem;
|
||||
import org.apache.dubbo.common.logger.ErrorTypeAwareLogger;
|
||||
import org.apache.dubbo.common.logger.LoggerFactory;
|
||||
import org.apache.dubbo.common.utils.*;
|
||||
import org.apache.dubbo.metadata.MappingChangedEvent;
|
||||
import org.apache.dubbo.metadata.MappingListener;
|
||||
import org.apache.dubbo.metadata.MetadataInfo;
|
||||
import org.apache.dubbo.metadata.ServiceNameMapping;
|
||||
import org.apache.dubbo.metadata.report.identifier.*;
|
||||
import org.apache.dubbo.metadata.report.support.AbstractMetadataReport;
|
||||
import org.apache.dubbo.rpc.RpcException;
|
||||
import redis.clients.jedis.*;
|
||||
import redis.clients.jedis.params.SetParams;
|
||||
import redis.clients.jedis.util.JedisClusterCRC16;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import static org.apache.dubbo.common.constants.CommonConstants.*;
|
||||
import static org.apache.dubbo.common.constants.LoggerCodeConstants.TRANSPORT_FAILED_RESPONSE;
|
||||
import static org.apache.dubbo.metadata.MetadataConstants.META_DATA_STORE_TAG;
|
||||
import static org.apache.dubbo.metadata.ServiceNameMapping.DEFAULT_MAPPING_GROUP;
|
||||
import static org.apache.dubbo.metadata.ServiceNameMapping.getAppNames;
|
||||
import static org.apache.dubbo.metadata.report.support.Constants.DEFAULT_METADATA_REPORT_CYCLE_REPORT;
|
||||
|
||||
/**
|
||||
* RedisMetadataReport
|
||||
*/
|
||||
public class RedisMetadataReport extends AbstractMetadataReport {
|
||||
|
||||
private static final String REDIS_DATABASE_KEY = "database";
|
||||
private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(RedisMetadataReport.class);
|
||||
|
||||
// protected , for test
|
||||
protected JedisPool pool;
|
||||
private Set<HostAndPort> jedisClusterNodes;
|
||||
private int timeout;
|
||||
private String username;
|
||||
private String password;
|
||||
private final String root;
|
||||
private final ConcurrentHashMap<String, MappingDataListener> mappingDataListenerMap = new ConcurrentHashMap<>();
|
||||
private SetParams jedisParams = SetParams.setParams();
|
||||
|
||||
public RedisMetadataReport(URL url) {
|
||||
super(url);
|
||||
timeout = url.getParameter(TIMEOUT_KEY, DEFAULT_TIMEOUT);
|
||||
username = url.getUsername();
|
||||
password = url.getPassword();
|
||||
this.root = url.getGroup(DEFAULT_ROOT);
|
||||
if (url.getParameter(CYCLE_REPORT_KEY, DEFAULT_METADATA_REPORT_CYCLE_REPORT)) {
|
||||
// ttl default is twice the cycle-report time
|
||||
jedisParams.px(ONE_DAY_IN_MILLISECONDS * 2);
|
||||
}
|
||||
if (url.getParameter(CLUSTER_KEY, false)) {
|
||||
jedisClusterNodes = new HashSet<>();
|
||||
List<URL> urls = url.getBackupUrls();
|
||||
for (URL tmpUrl : urls) {
|
||||
jedisClusterNodes.add(new HostAndPort(tmpUrl.getHost(), tmpUrl.getPort()));
|
||||
}
|
||||
} else {
|
||||
int database = url.getParameter(REDIS_DATABASE_KEY, 0);
|
||||
pool = new JedisPool(new JedisPoolConfig(), url.getHost(), url.getPort(), timeout, username, password, database);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doStoreProviderMetadata(MetadataIdentifier providerMetadataIdentifier, String serviceDefinitions) {
|
||||
this.storeMetadata(providerMetadataIdentifier, serviceDefinitions, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doStoreConsumerMetadata(MetadataIdentifier consumerMetadataIdentifier, String value) {
|
||||
this.storeMetadata(consumerMetadataIdentifier, value, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doSaveMetadata(ServiceMetadataIdentifier serviceMetadataIdentifier, URL url) {
|
||||
this.storeMetadata(serviceMetadataIdentifier, URL.encode(url.toFullString()), false);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doRemoveMetadata(ServiceMetadataIdentifier serviceMetadataIdentifier) {
|
||||
this.deleteMetadata(serviceMetadataIdentifier);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<String> doGetExportedURLs(ServiceMetadataIdentifier metadataIdentifier) {
|
||||
String content = getMetadata(metadataIdentifier);
|
||||
if (StringUtils.isEmpty(content)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
return new ArrayList<>(Arrays.asList(URL.decode(content)));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doSaveSubscriberData(SubscriberMetadataIdentifier subscriberMetadataIdentifier, String urlListStr) {
|
||||
this.storeMetadata(subscriberMetadataIdentifier, urlListStr, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String doGetSubscribedURLs(SubscriberMetadataIdentifier subscriberMetadataIdentifier) {
|
||||
return this.getMetadata(subscriberMetadataIdentifier);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getServiceDefinition(MetadataIdentifier metadataIdentifier) {
|
||||
return this.getMetadata(metadataIdentifier);
|
||||
}
|
||||
|
||||
private void storeMetadata(BaseMetadataIdentifier metadataIdentifier, String v, boolean ephemeral) {
|
||||
if (pool != null) {
|
||||
storeMetadataStandalone(metadataIdentifier, v, ephemeral);
|
||||
} else {
|
||||
storeMetadataInCluster(metadataIdentifier, v, ephemeral);
|
||||
}
|
||||
}
|
||||
|
||||
private void storeMetadataInCluster(BaseMetadataIdentifier metadataIdentifier, String v, boolean ephemeral) {
|
||||
try (JedisCluster jedisCluster =
|
||||
new JedisCluster(jedisClusterNodes, timeout, timeout, 2, password, new GenericObjectPoolConfig<>())) {
|
||||
if (ephemeral) {
|
||||
jedisCluster.set(metadataIdentifier.getIdentifierKey() + META_DATA_STORE_TAG, v, jedisParams);
|
||||
} else {
|
||||
jedisCluster.set(metadataIdentifier.getIdentifierKey() + META_DATA_STORE_TAG, v);
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
String msg =
|
||||
"Failed to put " + metadataIdentifier + " to redis cluster " + v + ", cause: " + e.getMessage();
|
||||
logger.error(TRANSPORT_FAILED_RESPONSE, "", "", msg, e);
|
||||
throw new RpcException(msg, e);
|
||||
}
|
||||
}
|
||||
|
||||
private void storeMetadataStandalone(BaseMetadataIdentifier metadataIdentifier, String v, boolean ephemeral) {
|
||||
try (Jedis jedis = pool.getResource()) {
|
||||
if (ephemeral) {
|
||||
jedis.set(metadataIdentifier.getUniqueKey(KeyTypeEnum.UNIQUE_KEY), v, jedisParams);
|
||||
} else {
|
||||
jedis.set(metadataIdentifier.getUniqueKey(KeyTypeEnum.UNIQUE_KEY), v);
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
String msg = "Failed to put " + metadataIdentifier + " to redis " + v + ", cause: " + e.getMessage();
|
||||
logger.error(TRANSPORT_FAILED_RESPONSE, "", "", msg, e);
|
||||
throw new RpcException(msg, e);
|
||||
}
|
||||
}
|
||||
|
||||
private void deleteMetadata(BaseMetadataIdentifier metadataIdentifier) {
|
||||
if (pool != null) {
|
||||
deleteMetadataStandalone(metadataIdentifier);
|
||||
} else {
|
||||
deleteMetadataInCluster(metadataIdentifier);
|
||||
}
|
||||
}
|
||||
|
||||
private void deleteMetadataInCluster(BaseMetadataIdentifier metadataIdentifier) {
|
||||
try (JedisCluster jedisCluster =
|
||||
new JedisCluster(jedisClusterNodes, timeout, timeout, 2, password, new GenericObjectPoolConfig<>())) {
|
||||
jedisCluster.del(metadataIdentifier.getIdentifierKey() + META_DATA_STORE_TAG);
|
||||
} catch (Throwable e) {
|
||||
String msg = "Failed to delete " + metadataIdentifier + " from redis cluster , cause: " + e.getMessage();
|
||||
logger.error(TRANSPORT_FAILED_RESPONSE, "", "", msg, e);
|
||||
throw new RpcException(msg, e);
|
||||
}
|
||||
}
|
||||
|
||||
private void deleteMetadataStandalone(BaseMetadataIdentifier metadataIdentifier) {
|
||||
try (Jedis jedis = pool.getResource()) {
|
||||
jedis.del(metadataIdentifier.getUniqueKey(KeyTypeEnum.UNIQUE_KEY));
|
||||
} catch (Throwable e) {
|
||||
String msg = "Failed to delete " + metadataIdentifier + " from redis , cause: " + e.getMessage();
|
||||
logger.error(TRANSPORT_FAILED_RESPONSE, "", "", msg, e);
|
||||
throw new RpcException(msg, e);
|
||||
}
|
||||
}
|
||||
|
||||
private String getMetadata(BaseMetadataIdentifier metadataIdentifier) {
|
||||
if (pool != null) {
|
||||
return getMetadataStandalone(metadataIdentifier);
|
||||
} else {
|
||||
return getMetadataInCluster(metadataIdentifier);
|
||||
}
|
||||
}
|
||||
|
||||
private String getMetadataInCluster(BaseMetadataIdentifier metadataIdentifier) {
|
||||
try (JedisCluster jedisCluster =
|
||||
new JedisCluster(jedisClusterNodes, timeout, timeout, 2, password, new GenericObjectPoolConfig<>())) {
|
||||
return jedisCluster.get(metadataIdentifier.getIdentifierKey() + META_DATA_STORE_TAG);
|
||||
} catch (Throwable e) {
|
||||
String msg = "Failed to get " + metadataIdentifier + " from redis cluster , cause: " + e.getMessage();
|
||||
logger.error(TRANSPORT_FAILED_RESPONSE, "", "", msg, e);
|
||||
throw new RpcException(msg, e);
|
||||
}
|
||||
}
|
||||
|
||||
private String getMetadataStandalone(BaseMetadataIdentifier metadataIdentifier) {
|
||||
try (Jedis jedis = pool.getResource()) {
|
||||
return jedis.get(metadataIdentifier.getUniqueKey(KeyTypeEnum.UNIQUE_KEY));
|
||||
} catch (Throwable e) {
|
||||
String msg = "Failed to get " + metadataIdentifier + " from redis , cause: " + e.getMessage();
|
||||
logger.error(TRANSPORT_FAILED_RESPONSE, "", "", msg, e);
|
||||
throw new RpcException(msg, e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Store class and application names using Redis hashes
|
||||
* key: default 'dubbo:mapping'
|
||||
* field: class (serviceInterface)
|
||||
* value: application_names
|
||||
* @param serviceInterface field(class)
|
||||
* @param defaultMappingGroup {@link ServiceNameMapping#DEFAULT_MAPPING_GROUP}
|
||||
* @param newConfigContent new application_names
|
||||
* @param ticket previous application_names
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public boolean registerServiceAppMapping(
|
||||
String serviceInterface, String defaultMappingGroup, String newConfigContent, Object ticket) {
|
||||
try {
|
||||
if (null != ticket && !(ticket instanceof String)) {
|
||||
throw new IllegalArgumentException("redis publishConfigCas requires stat type ticket");
|
||||
}
|
||||
String pathKey = buildMappingKey(defaultMappingGroup);
|
||||
|
||||
return storeMapping(pathKey, serviceInterface, newConfigContent, (String) ticket);
|
||||
} catch (Exception e) {
|
||||
logger.warn(TRANSPORT_FAILED_RESPONSE, "", "", "redis publishConfigCas failed.", e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean storeMapping(String key, String field, String value, String ticket) {
|
||||
if (pool != null) {
|
||||
return storeMappingStandalone(key, field, value, ticket);
|
||||
} else {
|
||||
return storeMappingInCluster(key, field, value, ticket);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* use 'watch' to implement cas.
|
||||
* Find information about slot distribution by key.
|
||||
*/
|
||||
private boolean storeMappingInCluster(String key, String field, String value, String ticket) {
|
||||
try (JedisCluster jedisCluster =
|
||||
new JedisCluster(jedisClusterNodes, timeout, timeout, 2, password, new GenericObjectPoolConfig<>())) {
|
||||
Jedis jedis = new Jedis(jedisCluster.getConnectionFromSlot(JedisClusterCRC16.getSlot(key)));
|
||||
jedis.watch(key);
|
||||
String oldValue = jedis.hget(key, field);
|
||||
if (null == oldValue || null == ticket || oldValue.equals(ticket)) {
|
||||
Transaction transaction = jedis.multi();
|
||||
transaction.hset(key, field, value);
|
||||
List<Object> result = transaction.exec();
|
||||
if (null != result) {
|
||||
jedisCluster.publish(buildPubSubKey(), field);
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
jedis.unwatch();
|
||||
}
|
||||
jedis.close();
|
||||
} catch (Throwable e) {
|
||||
String msg = "Failed to put " + key + ":" + field + " to redis " + value + ", cause: " + e.getMessage();
|
||||
logger.error(TRANSPORT_FAILED_RESPONSE, "", "", msg, e);
|
||||
throw new RpcException(msg, e);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* use 'watch' to implement cas.
|
||||
* Find information about slot distribution by key.
|
||||
*/
|
||||
private boolean storeMappingStandalone(String key, String field, String value, String ticket) {
|
||||
try (Jedis jedis = pool.getResource()) {
|
||||
jedis.watch(key);
|
||||
String oldValue = jedis.hget(key, field);
|
||||
if (null == oldValue || null == ticket || oldValue.equals(ticket)) {
|
||||
Transaction transaction = jedis.multi();
|
||||
transaction.hset(key, field, value);
|
||||
List<Object> result = transaction.exec();
|
||||
if (null != result) {
|
||||
jedis.publish(buildPubSubKey(), field);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
jedis.unwatch();
|
||||
} catch (Throwable e) {
|
||||
String msg = "Failed to put " + key + ":" + field + " to redis " + value + ", cause: " + e.getMessage();
|
||||
logger.error(TRANSPORT_FAILED_RESPONSE, "", "", msg, e);
|
||||
throw new RpcException(msg, e);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* build mapping key
|
||||
* @param defaultMappingGroup {@link ServiceNameMapping#DEFAULT_MAPPING_GROUP}
|
||||
* @return
|
||||
*/
|
||||
private String buildMappingKey(String defaultMappingGroup) {
|
||||
return this.root + GROUP_CHAR_SEPARATOR + defaultMappingGroup;
|
||||
}
|
||||
|
||||
/**
|
||||
* build pub/sub key
|
||||
*/
|
||||
private String buildPubSubKey() {
|
||||
return buildMappingKey(DEFAULT_MAPPING_GROUP) + GROUP_CHAR_SEPARATOR + QUEUES_KEY;
|
||||
}
|
||||
|
||||
/**
|
||||
* get content and use content to complete cas
|
||||
* @param serviceKey class
|
||||
* @param group {@link ServiceNameMapping#DEFAULT_MAPPING_GROUP}
|
||||
*/
|
||||
@Override
|
||||
public ConfigItem getConfigItem(String serviceKey, String group) {
|
||||
String key = buildMappingKey(group);
|
||||
String content = getMappingData(key, serviceKey);
|
||||
|
||||
return new ConfigItem(content, content);
|
||||
}
|
||||
|
||||
/**
|
||||
* get current application_names
|
||||
*/
|
||||
private String getMappingData(String key, String field) {
|
||||
if (pool != null) {
|
||||
return getMappingDataStandalone(key, field);
|
||||
} else {
|
||||
return getMappingDataInCluster(key, field);
|
||||
}
|
||||
}
|
||||
|
||||
private String getMappingDataInCluster(String key, String field) {
|
||||
try (JedisCluster jedisCluster =
|
||||
new JedisCluster(jedisClusterNodes, timeout, timeout, 2, password, new GenericObjectPoolConfig<>())) {
|
||||
return jedisCluster.hget(key, field);
|
||||
} catch (Throwable e) {
|
||||
String msg = "Failed to get " + key + ":" + field + " from redis cluster , cause: " + e.getMessage();
|
||||
logger.error(TRANSPORT_FAILED_RESPONSE, "", "", msg, e);
|
||||
throw new RpcException(msg, e);
|
||||
}
|
||||
}
|
||||
|
||||
private String getMappingDataStandalone(String key, String field) {
|
||||
try (Jedis jedis = pool.getResource()) {
|
||||
return jedis.hget(key, field);
|
||||
} catch (Throwable e) {
|
||||
String msg = "Failed to get " + key + ":" + field + " from redis , cause: " + e.getMessage();
|
||||
logger.error(TRANSPORT_FAILED_RESPONSE, "", "", msg, e);
|
||||
throw new RpcException(msg, e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* remove listener. If have no listener,thread will dead
|
||||
*/
|
||||
@Override
|
||||
public void removeServiceAppMappingListener(String serviceKey, MappingListener listener) {
|
||||
MappingDataListener mappingDataListener = mappingDataListenerMap.get(buildPubSubKey());
|
||||
if (null != mappingDataListener) {
|
||||
NotifySub notifySub = mappingDataListener.getNotifySub();
|
||||
notifySub.removeListener(serviceKey, listener);
|
||||
if (notifySub.isEmpty()) {
|
||||
mappingDataListener.shutdown();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Start a thread and subscribe to {@link this#buildPubSubKey()}.
|
||||
* Notify {@link MappingListener} if there is a change in the 'application_names' message.
|
||||
*/
|
||||
@Override
|
||||
public Set<String> getServiceAppMapping(String serviceKey, MappingListener listener, URL url) {
|
||||
MappingDataListener mappingDataListener =
|
||||
ConcurrentHashMapUtils.computeIfAbsent(mappingDataListenerMap, buildPubSubKey(), k -> {
|
||||
MappingDataListener dataListener = new MappingDataListener(buildPubSubKey());
|
||||
dataListener.start();
|
||||
return dataListener;
|
||||
});
|
||||
mappingDataListener.getNotifySub().addListener(serviceKey, listener);
|
||||
return this.getServiceAppMapping(serviceKey, url);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getServiceAppMapping(String serviceKey, URL url) {
|
||||
String key = buildMappingKey(DEFAULT_MAPPING_GROUP);
|
||||
return getAppNames(getMappingData(key, serviceKey));
|
||||
}
|
||||
|
||||
@Override
|
||||
public MetadataInfo getAppMetadata(SubscriberMetadataIdentifier identifier, Map<String, String> instanceMetadata) {
|
||||
String content = this.getMetadata(identifier);
|
||||
return JsonUtils.toJavaObject(content, MetadataInfo.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void publishAppMetadata(SubscriberMetadataIdentifier identifier, MetadataInfo metadataInfo) {
|
||||
this.storeMetadata(identifier, metadataInfo.getContent(), false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unPublishAppMetadata(SubscriberMetadataIdentifier identifier, MetadataInfo metadataInfo) {
|
||||
this.deleteMetadata(identifier);
|
||||
}
|
||||
|
||||
// for test
|
||||
public MappingDataListener getMappingDataListener() {
|
||||
return mappingDataListenerMap.get(buildPubSubKey());
|
||||
}
|
||||
|
||||
/**
|
||||
* Listen for changes in the 'application_names' message and notify the listener.
|
||||
*/
|
||||
class NotifySub extends JedisPubSub {
|
||||
|
||||
private final Map<String, Set<MappingListener>> listeners = new ConcurrentHashMap<>();
|
||||
|
||||
public void addListener(String key, MappingListener listener) {
|
||||
Set<MappingListener> listenerSet = listeners.computeIfAbsent(key, k -> new ConcurrentHashSet<>());
|
||||
listenerSet.add(listener);
|
||||
}
|
||||
|
||||
public void removeListener(String serviceKey, MappingListener listener) {
|
||||
Set<MappingListener> listenerSet = this.listeners.get(serviceKey);
|
||||
if (listenerSet != null) {
|
||||
listenerSet.remove(listener);
|
||||
if (listenerSet.isEmpty()) {
|
||||
this.listeners.remove(serviceKey);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Boolean isEmpty() {
|
||||
return this.listeners.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMessage(String key, String msg) {
|
||||
logger.info("sub from redis:" + key + " message:" + msg);
|
||||
String applicationNames = getMappingData(buildMappingKey(DEFAULT_MAPPING_GROUP), msg);
|
||||
MappingChangedEvent mappingChangedEvent = new MappingChangedEvent(msg, getAppNames(applicationNames));
|
||||
if (!CollectionUtils.isEmpty(listeners.get(msg))) {
|
||||
for (MappingListener mappingListener : listeners.get(msg)) {
|
||||
mappingListener.onEvent(mappingChangedEvent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPMessage(String pattern, String key, String msg) {
|
||||
onMessage(key, msg);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPSubscribe(String pattern, int subscribedChannels) {
|
||||
super.onPSubscribe(pattern, subscribedChannels);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Subscribe application names change message.
|
||||
*/
|
||||
class MappingDataListener extends Thread {
|
||||
|
||||
private String path;
|
||||
|
||||
private final NotifySub notifySub = new NotifySub();
|
||||
// for test
|
||||
protected volatile boolean running = true;
|
||||
|
||||
public MappingDataListener(String path) {
|
||||
this.path = path;
|
||||
}
|
||||
|
||||
public NotifySub getNotifySub() {
|
||||
return notifySub;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
while (running) {
|
||||
if (pool != null) {
|
||||
try (Jedis jedis = pool.getResource()) {
|
||||
jedis.subscribe(notifySub, path);
|
||||
} catch (Throwable e) {
|
||||
String msg = "Failed to subscribe " + path + ", cause: " + e.getMessage();
|
||||
logger.error(TRANSPORT_FAILED_RESPONSE, "", "", msg, e);
|
||||
throw new RpcException(msg, e);
|
||||
}
|
||||
} else {
|
||||
try (JedisCluster jedisCluster = new JedisCluster(
|
||||
jedisClusterNodes, timeout, timeout, 2, password, new GenericObjectPoolConfig<>())) {
|
||||
jedisCluster.subscribe(notifySub, path);
|
||||
} catch (Throwable e) {
|
||||
String msg = "Failed to subscribe " + path + ", cause: " + e.getMessage();
|
||||
logger.error(TRANSPORT_FAILED_RESPONSE, "", "", msg, e);
|
||||
throw new RpcException(msg, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void shutdown() {
|
||||
try {
|
||||
running = false;
|
||||
notifySub.unsubscribe(path);
|
||||
} catch (Throwable e) {
|
||||
String msg = "Failed to unsubscribe " + path + ", cause: " + e.getMessage();
|
||||
logger.error(TRANSPORT_FAILED_RESPONSE, "", "", msg, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,301 @@
|
||||
package org.apache.dubbo.metadata.store.redis;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.dubbo.common.URL;
|
||||
import org.apache.dubbo.common.config.configcenter.ConfigItem;
|
||||
import org.apache.dubbo.common.utils.CollectionUtils;
|
||||
import org.apache.dubbo.common.utils.ConcurrentHashSet;
|
||||
import org.apache.dubbo.metadata.MappingChangedEvent;
|
||||
import org.apache.dubbo.metadata.MappingListener;
|
||||
import org.apache.dubbo.metadata.MetadataInfo;
|
||||
import org.apache.dubbo.metadata.report.identifier.*;
|
||||
import org.apache.dubbo.metadata.report.support.AbstractMetadataReport;
|
||||
import org.apache.dubbo.rpc.RpcException;
|
||||
import org.dromara.common.core.utils.SpringUtils;
|
||||
import org.redisson.api.RScript;
|
||||
import org.redisson.api.RTopic;
|
||||
import org.redisson.api.RedissonClient;
|
||||
import org.redisson.client.codec.StringCodec;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import static org.apache.dubbo.common.constants.CommonConstants.*;
|
||||
import static org.apache.dubbo.metadata.MetadataConstants.META_DATA_STORE_TAG;
|
||||
import static org.apache.dubbo.metadata.ServiceNameMapping.DEFAULT_MAPPING_GROUP;
|
||||
import static org.apache.dubbo.metadata.ServiceNameMapping.getAppNames;
|
||||
import static org.apache.dubbo.metadata.report.support.Constants.DEFAULT_METADATA_REPORT_CYCLE_REPORT;
|
||||
|
||||
/**
|
||||
* 使用 Redisson 重新实现元数据中心
|
||||
*/
|
||||
@Slf4j
|
||||
public class RedissonMetadataReport extends AbstractMetadataReport {
|
||||
|
||||
// Lua script for atomic CAS on a hash field:
|
||||
// If the current value equals ticket (or field is absent, or ticket is empty), update and return 1; else return 0.
|
||||
private static final String CAS_LUA ="""
|
||||
local old = redis.call('HGET', KEYS[1], ARGV[1])
|
||||
if old == false or ARGV[3] == '' or old == ARGV[3] then
|
||||
redis.call('HSET', KEYS[1], ARGV[1], ARGV[2])
|
||||
return 1
|
||||
end
|
||||
return 0""";
|
||||
|
||||
private final String root;
|
||||
private final long ttlMs;
|
||||
|
||||
// Lazily initialized — Dubbo SPI creates this class before Spring is fully ready
|
||||
private volatile RedissonClient redissonClient;
|
||||
|
||||
// topic key → RTopic (keeps the subscription alive)
|
||||
private final ConcurrentHashMap<String, RTopic> topicMap = new ConcurrentHashMap<>();
|
||||
// serviceKey → listeners (for dispatching mapping change events)
|
||||
private final ConcurrentHashMap<String, Set<MappingListener>> listenerMap = new ConcurrentHashMap<>();
|
||||
|
||||
public RedissonMetadataReport(URL url) {
|
||||
super(url);
|
||||
this.root = url.getGroup(DEFAULT_ROOT);
|
||||
this.ttlMs = url.getParameter(CYCLE_REPORT_KEY, DEFAULT_METADATA_REPORT_CYCLE_REPORT)
|
||||
? ONE_DAY_IN_MILLISECONDS * 2L
|
||||
: 0L;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Lazy RedissonClient accessor
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
private RedissonClient getRedisson() {
|
||||
if (redissonClient == null) {
|
||||
synchronized (this) {
|
||||
if (redissonClient == null) {
|
||||
redissonClient = SpringUtils.getBean(RedissonClient.class);
|
||||
}
|
||||
}
|
||||
}
|
||||
return redissonClient;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// AbstractMetadataReport — provider / consumer metadata
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
@Override
|
||||
protected void doStoreProviderMetadata(MetadataIdentifier id, String serviceDefinitions) {
|
||||
storeMetadata(id.getUniqueKey(KeyTypeEnum.UNIQUE_KEY), serviceDefinitions, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doStoreConsumerMetadata(MetadataIdentifier id, String value) {
|
||||
storeMetadata(id.getUniqueKey(KeyTypeEnum.UNIQUE_KEY), value, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doSaveMetadata(ServiceMetadataIdentifier id, URL url) {
|
||||
storeMetadata(id.getIdentifierKey() + META_DATA_STORE_TAG, URL.encode(url.toFullString()), false);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doRemoveMetadata(ServiceMetadataIdentifier id) {
|
||||
deleteMetadata(id.getIdentifierKey() + META_DATA_STORE_TAG);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<String> doGetExportedURLs(ServiceMetadataIdentifier id) {
|
||||
String content = getMetadata(id.getIdentifierKey() + META_DATA_STORE_TAG);
|
||||
if (content == null || content.isEmpty()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
return Collections.singletonList(URL.decode(content));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doSaveSubscriberData(SubscriberMetadataIdentifier id, String urlListStr) {
|
||||
storeMetadata(id.getUniqueKey(KeyTypeEnum.UNIQUE_KEY), urlListStr, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String doGetSubscribedURLs(SubscriberMetadataIdentifier id) {
|
||||
return getMetadata(id.getUniqueKey(KeyTypeEnum.UNIQUE_KEY));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getServiceDefinition(MetadataIdentifier id) {
|
||||
return getMetadata(id.getUniqueKey(KeyTypeEnum.UNIQUE_KEY));
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// App-level metadata (Dubbo 3.x application-level service discovery)
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
@Override
|
||||
public void publishAppMetadata(SubscriberMetadataIdentifier id, MetadataInfo metadataInfo) {
|
||||
storeMetadata(id.getUniqueKey(KeyTypeEnum.UNIQUE_KEY), metadataInfo.getContent(), false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MetadataInfo getAppMetadata(SubscriberMetadataIdentifier id, Map<String, String> instanceMetadata) {
|
||||
String content = getMetadata(id.getUniqueKey(KeyTypeEnum.UNIQUE_KEY));
|
||||
return org.apache.dubbo.common.utils.JsonUtils.toJavaObject(content, MetadataInfo.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unPublishAppMetadata(SubscriberMetadataIdentifier id, MetadataInfo metadataInfo) {
|
||||
deleteMetadata(id.getUniqueKey(KeyTypeEnum.UNIQUE_KEY));
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Service-to-application mapping
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
@Override
|
||||
public boolean registerServiceAppMapping(
|
||||
String serviceInterface, String defaultMappingGroup, String newConfigContent, Object ticket) {
|
||||
try {
|
||||
if (ticket != null && !(ticket instanceof String)) {
|
||||
throw new IllegalArgumentException("Redis CAS requires a String ticket");
|
||||
}
|
||||
return storeMappingWithCas(
|
||||
buildMappingKey(defaultMappingGroup),
|
||||
serviceInterface,
|
||||
newConfigContent,
|
||||
(String) ticket);
|
||||
} catch (Exception e) {
|
||||
log.warn("registerServiceAppMapping failed.", e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConfigItem getConfigItem(String serviceKey, String group) {
|
||||
String key = buildMappingKey(group);
|
||||
String content = getMappingField(key, serviceKey);
|
||||
return new ConfigItem(content, content);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getServiceAppMapping(String serviceKey, MappingListener listener, URL url) {
|
||||
String pubSubKey = buildPubSubKey();
|
||||
// Register the RTopic listener once per pubSubKey
|
||||
topicMap.computeIfAbsent(pubSubKey, k -> {
|
||||
RTopic topic = getRedisson().getTopic(k, StringCodec.INSTANCE);
|
||||
topic.addListener(String.class, (channel, msg) -> {
|
||||
String applicationNames = getMappingField(buildMappingKey(DEFAULT_MAPPING_GROUP), msg);
|
||||
MappingChangedEvent event = new MappingChangedEvent(msg, getAppNames(applicationNames));
|
||||
Set<MappingListener> ls = listenerMap.get(msg);
|
||||
if (!CollectionUtils.isEmpty(ls)) {
|
||||
ls.forEach(l -> l.onEvent(event));
|
||||
}
|
||||
});
|
||||
return topic;
|
||||
});
|
||||
listenerMap.computeIfAbsent(serviceKey, k -> new ConcurrentHashSet<>()).add(listener);
|
||||
return getServiceAppMapping(serviceKey, url);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getServiceAppMapping(String serviceKey, URL url) {
|
||||
return getAppNames(getMappingField(buildMappingKey(DEFAULT_MAPPING_GROUP), serviceKey));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeServiceAppMappingListener(String serviceKey, MappingListener listener) {
|
||||
Set<MappingListener> ls = listenerMap.get(serviceKey);
|
||||
if (ls != null) {
|
||||
ls.remove(listener);
|
||||
if (ls.isEmpty()) {
|
||||
listenerMap.remove(serviceKey);
|
||||
// If no listeners remain for any key, remove the topic subscription
|
||||
if (listenerMap.isEmpty()) {
|
||||
RTopic topic = topicMap.remove(buildPubSubKey());
|
||||
if (topic != null) {
|
||||
topic.removeAllListeners();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Internal Redis helpers
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
private void storeMetadata(String key, String value, boolean ephemeral) {
|
||||
try {
|
||||
if (ephemeral && ttlMs > 0) {
|
||||
getRedisson().<String>getBucket(key, StringCodec.INSTANCE).set(value, Duration.ofMillis(ttlMs));
|
||||
} else {
|
||||
getRedisson().<String>getBucket(key, StringCodec.INSTANCE).set(value);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
String msg = "Failed to store metadata key=" + key + ", cause: " + e.getMessage();
|
||||
log.error(msg, e);
|
||||
throw new RpcException(msg, e);
|
||||
}
|
||||
}
|
||||
|
||||
private String getMetadata(String key) {
|
||||
try {
|
||||
return getRedisson().<String>getBucket(key, StringCodec.INSTANCE).get();
|
||||
} catch (Exception e) {
|
||||
String msg = "Failed to get metadata key=" + key + ", cause: " + e.getMessage();
|
||||
log.error(msg, e);
|
||||
throw new RpcException(msg, e);
|
||||
}
|
||||
}
|
||||
|
||||
private void deleteMetadata(String key) {
|
||||
try {
|
||||
getRedisson().getBucket(key, StringCodec.INSTANCE).delete();
|
||||
} catch (Exception e) {
|
||||
String msg = "Failed to delete metadata key=" + key + ", cause: " + e.getMessage();
|
||||
log.error(msg, e);
|
||||
throw new RpcException(msg, e);
|
||||
}
|
||||
}
|
||||
|
||||
private String getMappingField(String key, String field) {
|
||||
try {
|
||||
return getRedisson().<String, String>getMap(key, StringCodec.INSTANCE).get(field);
|
||||
} catch (Exception e) {
|
||||
String msg = "Failed to get mapping key=" + key + " field=" + field + ", cause: " + e.getMessage();
|
||||
log.error(msg, e);
|
||||
throw new RpcException(msg, e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Atomic CAS on a hash field via Lua script.
|
||||
* Updates field to newValue only when the current value equals ticket (or field is absent / ticket is null).
|
||||
* On success, publishes a change notification to the pub/sub channel.
|
||||
*/
|
||||
private boolean storeMappingWithCas(String key, String field, String newValue, String ticket) {
|
||||
try {
|
||||
Long result = getRedisson().getScript(StringCodec.INSTANCE).eval(
|
||||
RScript.Mode.READ_WRITE,
|
||||
CAS_LUA,
|
||||
RScript.ReturnType.INTEGER,
|
||||
Collections.singletonList(key),
|
||||
field, newValue, ticket == null ? "" : ticket
|
||||
);
|
||||
if (Long.valueOf(1L).equals(result)) {
|
||||
getRedisson().getTopic(buildPubSubKey(), StringCodec.INSTANCE).publish(field);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
} catch (Exception e) {
|
||||
String msg = "Failed to store mapping key=" + key + " field=" + field + ", cause: " + e.getMessage();
|
||||
log.error(msg, e);
|
||||
throw new RpcException(msg, e);
|
||||
}
|
||||
}
|
||||
|
||||
private String buildMappingKey(String mappingGroup) {
|
||||
return this.root + GROUP_CHAR_SEPARATOR + mappingGroup;
|
||||
}
|
||||
|
||||
private String buildPubSubKey() {
|
||||
return buildMappingKey(DEFAULT_MAPPING_GROUP) + GROUP_CHAR_SEPARATOR + QUEUES_KEY;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package org.apache.dubbo.metadata.store.redis;
|
||||
|
||||
import org.apache.dubbo.common.URL;
|
||||
import org.apache.dubbo.metadata.report.MetadataReport;
|
||||
import org.apache.dubbo.metadata.report.support.AbstractMetadataReportFactory;
|
||||
|
||||
/**
|
||||
* RedisMetadataReportFactory.
|
||||
*/
|
||||
public class RedissonMetadataReportFactory extends AbstractMetadataReportFactory {
|
||||
|
||||
@Override
|
||||
public MetadataReport createMetadataReport(URL url) {
|
||||
return new RedissonMetadataReport(url);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
redis=org.apache.dubbo.metadata.store.redis.RedissonMetadataReportFactory
|
||||
@@ -19,6 +19,8 @@ dubbo:
|
||||
password: ${spring.cloud.nacos.password}
|
||||
parameters:
|
||||
namespace: ${spring.profiles.active}
|
||||
# 已经采用框架内 Redisson 实现元数据中心
|
||||
# 以下配置不用管占位用 不然 dubbo 会直接用 nacos 当注册中心 直接配好框架自带的 Redisson 即可
|
||||
metadata-report:
|
||||
address: redis://${spring.data.redis.host:localhost}:${spring.data.redis.port:6379}
|
||||
group: DUBBO_GROUP
|
||||
|
||||
@@ -33,7 +33,7 @@ public class ExcelBigNumberConvert implements Converter<Long> {
|
||||
|
||||
@Override
|
||||
public Long convertToJavaData(ReadCellData<?> cellData, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) {
|
||||
return Convert.toLong(cellData.getData());
|
||||
return Convert.toLong(cellData.getStringValue());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -130,8 +130,9 @@ public class ExcelDownHandler implements SheetWriteHandler {
|
||||
}
|
||||
if (ObjectUtil.isNotEmpty(options)) {
|
||||
// 仅当下拉可选项不为空时执行
|
||||
if (options.size() > 20) {
|
||||
// 这里限制如果可选项大于20,则使用额外表形式
|
||||
int totalCharacter = options.stream().mapToInt(String::length).sum() + options.size();
|
||||
if (options.size() > 20 || totalCharacter > 255) {
|
||||
// 这里限制如果可选项大于20 或 总字符数超过255,则使用额外表形式
|
||||
dropDownWithSheet(helper, workbook, sheet, index, options);
|
||||
} else {
|
||||
// 否则使用固定值形式
|
||||
|
||||
@@ -12,6 +12,7 @@ import org.aspectj.lang.annotation.AfterThrowing;
|
||||
import org.aspectj.lang.annotation.Aspect;
|
||||
import org.aspectj.lang.annotation.Before;
|
||||
import org.dromara.common.core.constant.GlobalConstants;
|
||||
import org.dromara.common.core.constant.HttpStatus;
|
||||
import org.dromara.common.core.domain.R;
|
||||
import org.dromara.common.core.exception.ServiceException;
|
||||
import org.dromara.common.core.utils.MessageUtils;
|
||||
@@ -76,16 +77,16 @@ public class RepeatSubmitAspect {
|
||||
*/
|
||||
@AfterReturning(pointcut = "@annotation(repeatSubmit)", returning = "jsonResult")
|
||||
public void doAfterReturning(JoinPoint joinPoint, RepeatSubmit repeatSubmit, Object jsonResult) {
|
||||
if (jsonResult instanceof R<?> r) {
|
||||
try {
|
||||
try {
|
||||
if (jsonResult instanceof R<?> r) {
|
||||
// 成功则不删除redis数据 保证在有效时间内无法重复提交
|
||||
if (r.getCode() == R.SUCCESS) {
|
||||
if (r.getCode() == HttpStatus.SUCCESS) {
|
||||
return;
|
||||
}
|
||||
RedisUtils.deleteObject(KEY_CACHE.get());
|
||||
} finally {
|
||||
KEY_CACHE.remove();
|
||||
}
|
||||
} finally {
|
||||
KEY_CACHE.remove();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -46,6 +46,24 @@ public class DataBaseHelper {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定数据源对应的数据库类型
|
||||
*
|
||||
* @param dsName 数据源名称
|
||||
* @return 指定数据库对应的 DataBaseType 枚举,找不到时默认返回 MY_SQL
|
||||
* @throws ServiceException 当获取数据库连接或元数据出现异常时抛出业务异常
|
||||
*/
|
||||
public static DataBaseType getDataBaseType(String dsName) {
|
||||
DataSource dataSource = DS.getDataSource(dsName);
|
||||
try (Connection conn = dataSource.getConnection()) {
|
||||
DatabaseMetaData metaData = conn.getMetaData();
|
||||
String databaseProductName = metaData.getDatabaseProductName();
|
||||
return DataBaseType.find(databaseProductName);
|
||||
} catch (SQLException e) {
|
||||
throw new RuntimeException("获取数据库类型失败", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据当前数据库类型,生成兼容的 FIND_IN_SET 语句片段
|
||||
* <p>
|
||||
|
||||
@@ -65,9 +65,10 @@ public class RedisConfiguration {
|
||||
// 缓存 Lua 脚本 减少网络传输(redisson 大部分的功能都是基于 Lua 脚本实现)
|
||||
.setUseScriptCache(true)
|
||||
.setCodec(codec);
|
||||
if (SpringUtils.isVirtual()) {
|
||||
config.setNettyExecutor(new VirtualThreadTaskExecutor("redisson-"));
|
||||
}
|
||||
// netty 对虚拟线程适配有问题 暂时禁止使用
|
||||
//if (SpringUtils.isVirtual()) {
|
||||
// config.setNettyExecutor(new VirtualThreadTaskExecutor("redisson-"));
|
||||
//}
|
||||
RedissonProperties.SingleServerConfig singleServerConfig = redissonProperties.getSingleServerConfig();
|
||||
if (ObjectUtil.isNotNull(singleServerConfig)) {
|
||||
// 使用单机模式
|
||||
|
||||
@@ -46,6 +46,11 @@
|
||||
<artifactId>caffeine</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-webmvc</artifactId>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
|
||||
@@ -2,7 +2,6 @@ package org.dromara.common.web.handler;
|
||||
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.hutool.http.HttpStatus;
|
||||
import com.fasterxml.jackson.core.JsonParseException;
|
||||
import jakarta.servlet.ServletException;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.validation.ConstraintViolation;
|
||||
@@ -12,8 +11,10 @@ import org.dromara.common.core.domain.R;
|
||||
import org.dromara.common.core.exception.ServiceException;
|
||||
import org.dromara.common.core.exception.SseException;
|
||||
import org.dromara.common.core.exception.base.BaseException;
|
||||
import org.dromara.common.core.utils.SpringUtils;
|
||||
import org.dromara.common.core.utils.StreamUtils;
|
||||
import org.dromara.common.json.utils.JsonUtils;
|
||||
import org.springframework.boot.json.JsonParseException;
|
||||
import org.springframework.context.MessageSourceResolvable;
|
||||
import org.springframework.context.support.DefaultMessageSourceResolvable;
|
||||
import org.springframework.expression.ExpressionException;
|
||||
@@ -80,7 +81,7 @@ public class GlobalExceptionHandler {
|
||||
public R<Void> handleServletException(ServletException e, HttpServletRequest request) {
|
||||
String requestURI = request.getRequestURI();
|
||||
log.error("请求地址'{}',发生未知异常.", requestURI, e);
|
||||
return R.fail(e.getMessage());
|
||||
return R.fail("发生未知异常,请联系管理员");
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -119,7 +120,7 @@ public class GlobalExceptionHandler {
|
||||
public R<Void> handleNoHandlerFoundException(NoHandlerFoundException e, HttpServletRequest request) {
|
||||
String requestURI = request.getRequestURI();
|
||||
log.error("请求地址'{}'不存在.", requestURI);
|
||||
return R.fail(HttpStatus.HTTP_NOT_FOUND, e.getMessage());
|
||||
return R.fail(HttpStatus.HTTP_NOT_FOUND, "请求地址不存在");
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -150,7 +151,7 @@ public class GlobalExceptionHandler {
|
||||
public R<Void> handleRuntimeException(RuntimeException e, HttpServletRequest request) {
|
||||
String requestURI = request.getRequestURI();
|
||||
log.error("请求地址'{}',发生未知异常.", requestURI, e);
|
||||
return R.fail(e.getMessage());
|
||||
return R.fail("发生未知异常,请联系管理员");
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -160,7 +161,7 @@ public class GlobalExceptionHandler {
|
||||
public R<Void> handleException(Exception e, HttpServletRequest request) {
|
||||
String requestURI = request.getRequestURI();
|
||||
log.error("请求地址'{}',发生系统异常.", requestURI, e);
|
||||
return R.fail(e.getMessage());
|
||||
return R.fail("发生系统异常,请联系管理员");
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Binary file not shown.
@@ -331,7 +331,7 @@ public class GenTableServiceImpl implements IGenTableService {
|
||||
VelocityContext context = VelocityUtils.prepareContext(table);
|
||||
|
||||
// 获取模板列表
|
||||
List<String> templates = VelocityUtils.getTemplateList(table.getTplCategory());
|
||||
List<String> templates = VelocityUtils.getTemplateList(table.getTplCategory(), table.getDataName());
|
||||
for (String template : templates) {
|
||||
// 渲染模板
|
||||
StringWriter sw = new StringWriter();
|
||||
@@ -374,7 +374,7 @@ public class GenTableServiceImpl implements IGenTableService {
|
||||
VelocityContext context = VelocityUtils.prepareContext(table);
|
||||
|
||||
// 获取模板列表
|
||||
List<String> templates = VelocityUtils.getTemplateList(table.getTplCategory());
|
||||
List<String> templates = VelocityUtils.getTemplateList(table.getTplCategory(), table.getDataName());
|
||||
for (String template : templates) {
|
||||
if (!StringUtils.containsAny(template, "sql.vm", "api.ts.vm", "types.ts.vm", "index.vue.vm", "index-tree.vue.vm")) {
|
||||
// 渲染模板
|
||||
@@ -478,7 +478,7 @@ public class GenTableServiceImpl implements IGenTableService {
|
||||
VelocityContext context = VelocityUtils.prepareContext(table);
|
||||
|
||||
// 获取模板列表
|
||||
List<String> templates = VelocityUtils.getTemplateList(table.getTplCategory());
|
||||
List<String> templates = VelocityUtils.getTemplateList(table.getTplCategory(), table.getDataName());
|
||||
for (String template : templates) {
|
||||
// 渲染模板
|
||||
StringWriter sw = new StringWriter();
|
||||
|
||||
@@ -109,7 +109,7 @@ public class VelocityUtils {
|
||||
*
|
||||
* @return 模板列表
|
||||
*/
|
||||
public static List<String> getTemplateList(String tplCategory) {
|
||||
public static List<String> getTemplateList(String tplCategory, String dsName) {
|
||||
List<String> templates = new ArrayList<>();
|
||||
templates.add("vm/java/domain.java.vm");
|
||||
templates.add("vm/java/vo.java.vm");
|
||||
@@ -119,7 +119,7 @@ public class VelocityUtils {
|
||||
templates.add("vm/java/serviceImpl.java.vm");
|
||||
templates.add("vm/java/controller.java.vm");
|
||||
templates.add("vm/xml/mapper.xml.vm");
|
||||
DataBaseType dataBaseType = DataBaseHelper.getDataBaseType();
|
||||
DataBaseType dataBaseType = DataBaseHelper.getDataBaseType(dsName);
|
||||
if (dataBaseType.isOracle()) {
|
||||
templates.add("vm/sql/oracle/sql.vm");
|
||||
} else if (dataBaseType.isPostgreSql()) {
|
||||
|
||||
@@ -104,6 +104,6 @@ public class SysDeptVo implements Serializable {
|
||||
/**
|
||||
* 子菜单
|
||||
*/
|
||||
private List<SysDept> children = new ArrayList<>();
|
||||
private List<SysDeptVo> children = new ArrayList<>();
|
||||
|
||||
}
|
||||
|
||||
@@ -404,6 +404,7 @@ public class RemoteUserServiceImpl implements RemoteUserService {
|
||||
* @param userIds 用户 ID 列表
|
||||
* @return Map,其中 key 为用户 ID,value 为对应的用户昵称
|
||||
*/
|
||||
@Override
|
||||
public Map<Long, String> selectUserNicksByIds(List<Long> userIds) {
|
||||
if (CollUtil.isEmpty(userIds)) {
|
||||
return Collections.emptyMap();
|
||||
|
||||
@@ -235,8 +235,9 @@ public class FlwTaskServiceImpl implements IFlwTaskService {
|
||||
if (BusinessStatusEnum.isDraftOrCancelOrBack(ins.getFlowStatus())) {
|
||||
variables.put(SUBMIT, true);
|
||||
}
|
||||
Map<String, Object> insVariableMap = ins.getVariableMap();
|
||||
// 设置弹窗处理人
|
||||
Map<String, Object> assigneeMap = setPopAssigneeMap(completeTaskBo.getAssigneeMap(), ins.getVariableMap());
|
||||
Map<String, Object> assigneeMap = setPopAssigneeMap(completeTaskBo.getAssigneeMap(), insVariableMap);
|
||||
if (CollUtil.isNotEmpty(assigneeMap)) {
|
||||
variables.putAll(assigneeMap);
|
||||
}
|
||||
@@ -252,7 +253,7 @@ public class FlwTaskServiceImpl implements IFlwTaskService {
|
||||
.flowStatus(BusinessStatusEnum.WAITING.getStatus())
|
||||
.hisStatus(TaskStatusEnum.PASS.getStatus())
|
||||
.hisTaskExt(completeTaskBo.getFileId());
|
||||
Boolean autoPass = Convert.toBool(variables.getOrDefault(AUTO_PASS, false));
|
||||
Boolean autoPass = Convert.toBool(insVariableMap.getOrDefault(AUTO_PASS, false));
|
||||
skipTask(taskId, flowParams, flowTask.getInstanceId(), autoPass);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -88,6 +88,7 @@
|
||||
and b.del_flag = '0'
|
||||
and c.del_flag = '0'
|
||||
and a.node_type in ('1','3','4')
|
||||
and a.flow_status <![CDATA[ <> ]]> 'copy'
|
||||
) t
|
||||
${ew.getCustomSqlSegment}
|
||||
</select>
|
||||
|
||||
@@ -32,13 +32,6 @@ dubbo:
|
||||
consumer:
|
||||
# 超时时间
|
||||
timeout: 3000
|
||||
metadata-report:
|
||||
# Redis集群开关
|
||||
cluster: false
|
||||
parameters:
|
||||
# 集群地址 cluster 为 true 生效
|
||||
# 集群把所有Redis集群节点写到这里就行了
|
||||
backup: 127.0.0.1:6379,127.0.0.1:6381
|
||||
# 自定义配置
|
||||
custom:
|
||||
# 全局请求log
|
||||
|
||||
@@ -27,7 +27,7 @@ services:
|
||||
network_mode: "host"
|
||||
|
||||
nacos:
|
||||
image: ruoyi/ruoyi-nacos:2.6.0
|
||||
image: ruoyi/ruoyi-nacos:2.6.1
|
||||
container_name: nacos
|
||||
ports:
|
||||
- "8848:8848"
|
||||
@@ -95,7 +95,7 @@ services:
|
||||
network_mode: "host"
|
||||
|
||||
seata-server:
|
||||
image: ruoyi/ruoyi-seata-server:2.6.0
|
||||
image: ruoyi/ruoyi-seata-server:2.6.1
|
||||
container_name: seata-server
|
||||
ports:
|
||||
- "7091:7091"
|
||||
@@ -134,7 +134,7 @@ services:
|
||||
network_mode: "host"
|
||||
|
||||
ruoyi-monitor:
|
||||
image: ruoyi/ruoyi-monitor:2.6.0
|
||||
image: ruoyi/ruoyi-monitor:2.6.1
|
||||
container_name: ruoyi-monitor
|
||||
environment:
|
||||
# 时区上海
|
||||
@@ -150,7 +150,7 @@ services:
|
||||
network_mode: "host"
|
||||
|
||||
ruoyi-snailjob-server:
|
||||
image: ruoyi/ruoyi-snailjob-server:2.6.0
|
||||
image: ruoyi/ruoyi-snailjob-server:2.6.1
|
||||
container_name: ruoyi-snailjob-server
|
||||
environment:
|
||||
# 时区上海
|
||||
@@ -164,7 +164,7 @@ services:
|
||||
network_mode: "host"
|
||||
|
||||
ruoyi-gateway:
|
||||
image: ruoyi/ruoyi-gateway:2.6.0
|
||||
image: ruoyi/ruoyi-gateway:2.6.1
|
||||
container_name: ruoyi-gateway
|
||||
environment:
|
||||
# 时区上海
|
||||
@@ -180,7 +180,7 @@ services:
|
||||
network_mode: "host"
|
||||
|
||||
ruoyi-auth:
|
||||
image: ruoyi/ruoyi-auth:2.6.0
|
||||
image: ruoyi/ruoyi-auth:2.6.1
|
||||
container_name: ruoyi-auth
|
||||
environment:
|
||||
# 时区上海
|
||||
@@ -196,7 +196,7 @@ services:
|
||||
network_mode: "host"
|
||||
|
||||
ruoyi-system:
|
||||
image: ruoyi/ruoyi-system:2.6.0
|
||||
image: ruoyi/ruoyi-system:2.6.1
|
||||
container_name: ruoyi-system
|
||||
environment:
|
||||
# 时区上海
|
||||
@@ -212,7 +212,7 @@ services:
|
||||
network_mode: "host"
|
||||
|
||||
ruoyi-gen:
|
||||
image: ruoyi/ruoyi-gen:2.6.0
|
||||
image: ruoyi/ruoyi-gen:2.6.1
|
||||
container_name: ruoyi-gen
|
||||
environment:
|
||||
# 时区上海
|
||||
@@ -228,7 +228,7 @@ services:
|
||||
network_mode: "host"
|
||||
|
||||
ruoyi-job:
|
||||
image: ruoyi/ruoyi-job:2.6.0
|
||||
image: ruoyi/ruoyi-job:2.6.1
|
||||
container_name: ruoyi-job
|
||||
environment:
|
||||
# 时区上海
|
||||
@@ -246,7 +246,7 @@ services:
|
||||
network_mode: "host"
|
||||
|
||||
ruoyi-resource:
|
||||
image: ruoyi/ruoyi-resource:2.6.0
|
||||
image: ruoyi/ruoyi-resource:2.6.1
|
||||
container_name: ruoyi-resource
|
||||
environment:
|
||||
# 时区上海
|
||||
@@ -262,7 +262,7 @@ services:
|
||||
network_mode: "host"
|
||||
|
||||
ruoyi-workflow:
|
||||
image: ruoyi/ruoyi-workflow:2.6.0
|
||||
image: ruoyi/ruoyi-workflow:2.6.1
|
||||
container_name: ruoyi-workflow
|
||||
environment:
|
||||
# 时区上海
|
||||
|
||||
@@ -484,6 +484,7 @@ CREATE TABLE sj_job
|
||||
(
|
||||
id number GENERATED ALWAYS AS IDENTITY,
|
||||
namespace_id varchar2(64) DEFAULT '764d604ec6fc45f68cd92514c40e9e1a' NULL,
|
||||
biz_id varchar2(64) NOT NULL,
|
||||
group_name varchar2(64) NULL,
|
||||
job_name varchar2(64) NULL,
|
||||
args_str clob DEFAULT NULL NULL,
|
||||
@@ -519,9 +520,11 @@ ALTER TABLE sj_job
|
||||
CREATE INDEX idx_sj_job_01 ON sj_job (namespace_id, group_name);
|
||||
CREATE INDEX idx_sj_job_02 ON sj_job (job_status, bucket_index);
|
||||
CREATE INDEX idx_sj_job_03 ON sj_job (create_dt);
|
||||
CREATE UNIQUE INDEX uk_sj_job_01 ON sj_job (namespace_id, biz_id);
|
||||
|
||||
COMMENT ON COLUMN sj_job.id IS '主键';
|
||||
COMMENT ON COLUMN sj_job.namespace_id IS '命名空间id';
|
||||
COMMENT ON COLUMN sj_job.biz_id IS '业务ID';
|
||||
COMMENT ON COLUMN sj_job.group_name IS '组名称';
|
||||
COMMENT ON COLUMN sj_job.job_name IS '名称';
|
||||
COMMENT ON COLUMN sj_job.args_str IS '执行方法参数';
|
||||
@@ -551,7 +554,7 @@ COMMENT ON COLUMN sj_job.create_dt IS '创建时间';
|
||||
COMMENT ON COLUMN sj_job.update_dt IS '修改时间';
|
||||
COMMENT ON TABLE sj_job IS '任务信息';
|
||||
|
||||
INSERT INTO sj_job(namespace_id, group_name, job_name, args_str, args_type, next_trigger_at, job_status, task_type, route_key, executor_type, executor_info, trigger_type, trigger_interval, block_strategy,executor_timeout, max_retry_times, parallel_num, retry_interval, bucket_index, resident, notify_ids, owner_id, labels, description, ext_attrs, deleted, create_dt, update_dt) VALUES ('dev', 'ruoyi_group', 'demo-job', NULL, 1, 1710344035622, 1, 1, 4, 1, 'testJobExecutor', 2, '60', 1, 60, 3, 1, 1, 116, 0, '', 1, '','', '', 0, sysdate, sysdate);
|
||||
INSERT INTO sj_job(namespace_id, biz_id, group_name, job_name, args_str, args_type, next_trigger_at, job_status, task_type, route_key, executor_type, executor_info, trigger_type, trigger_interval, block_strategy,executor_timeout, max_retry_times, parallel_num, retry_interval, bucket_index, resident, notify_ids, owner_id, labels, description, ext_attrs, deleted, create_dt, update_dt) VALUES ('dev', 'demo-job', 'ruoyi_group', 'demo-job', NULL, 1, 1710344035622, 1, 1, 4, 1, 'testJobExecutor', 2, '60', 1, 60, 3, 1, 1, 116, 0, '', 1, '','', '', 0, sysdate, sysdate);
|
||||
|
||||
-- sj_job_log_message
|
||||
CREATE TABLE sj_job_log_message
|
||||
@@ -738,7 +741,7 @@ CREATE TABLE sj_retry_summary
|
||||
id number GENERATED ALWAYS AS IDENTITY,
|
||||
namespace_id varchar2(64) DEFAULT '764d604ec6fc45f68cd92514c40e9e1a' NULL,
|
||||
group_name varchar2(64) DEFAULT '' NULL,
|
||||
scene_name varchar2(64) DEFAULT '' NULL,
|
||||
scene_name varchar2(50) DEFAULT '' NULL,
|
||||
trigger_at date DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||
running_num number DEFAULT 0 NOT NULL,
|
||||
finish_num number DEFAULT 0 NOT NULL,
|
||||
@@ -774,6 +777,7 @@ CREATE TABLE sj_workflow
|
||||
id number GENERATED ALWAYS AS IDENTITY,
|
||||
workflow_name varchar2(64) NULL,
|
||||
namespace_id varchar2(64) DEFAULT '764d604ec6fc45f68cd92514c40e9e1a' NULL,
|
||||
biz_id varchar2(64) NOT NULL,
|
||||
group_name varchar2(64) NULL,
|
||||
workflow_status smallint DEFAULT 1 NOT NULL,
|
||||
trigger_type smallint NOT NULL,
|
||||
@@ -799,10 +803,12 @@ ALTER TABLE sj_workflow
|
||||
|
||||
CREATE INDEX idx_sj_workflow_01 ON sj_workflow (create_dt);
|
||||
CREATE INDEX idx_sj_workflow_02 ON sj_workflow (namespace_id, group_name);
|
||||
CREATE UNIQUE INDEX uk_sj_workflow_01 ON sj_workflow (namespace_id, biz_id);
|
||||
|
||||
COMMENT ON COLUMN sj_workflow.id IS '主键';
|
||||
COMMENT ON COLUMN sj_workflow.workflow_name IS '工作流名称';
|
||||
COMMENT ON COLUMN sj_workflow.namespace_id IS '命名空间id';
|
||||
COMMENT ON COLUMN sj_workflow.biz_id IS '业务ID';
|
||||
COMMENT ON COLUMN sj_workflow.group_name IS '组名称';
|
||||
COMMENT ON COLUMN sj_workflow.workflow_status IS '工作流状态 0、关闭、1、开启';
|
||||
COMMENT ON COLUMN sj_workflow.trigger_type IS '触发类型 1.CRON 表达式 2. 固定时间';
|
||||
|
||||
@@ -445,6 +445,7 @@ CREATE TABLE sj_job
|
||||
(
|
||||
id bigserial PRIMARY KEY,
|
||||
namespace_id varchar(64) NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a',
|
||||
biz_id varchar(64) NOT NULL,
|
||||
group_name varchar(64) NOT NULL,
|
||||
job_name varchar(64) NOT NULL,
|
||||
args_str text NULL DEFAULT NULL,
|
||||
@@ -477,9 +478,11 @@ CREATE TABLE sj_job
|
||||
CREATE INDEX idx_sj_job_01 ON sj_job (namespace_id, group_name);
|
||||
CREATE INDEX idx_sj_job_02 ON sj_job (job_status, bucket_index);
|
||||
CREATE INDEX idx_sj_job_03 ON sj_job (create_dt);
|
||||
CREATE UNIQUE INDEX uk_sj_job_01 ON sj_job (namespace_id, biz_id);
|
||||
|
||||
COMMENT ON COLUMN sj_job.id IS '主键';
|
||||
COMMENT ON COLUMN sj_job.namespace_id IS '命名空间id';
|
||||
COMMENT ON COLUMN sj_job.biz_id IS '业务ID';
|
||||
COMMENT ON COLUMN sj_job.group_name IS '组名称';
|
||||
COMMENT ON COLUMN sj_job.job_name IS '名称';
|
||||
COMMENT ON COLUMN sj_job.args_str IS '执行方法参数';
|
||||
@@ -509,7 +512,7 @@ COMMENT ON COLUMN sj_job.create_dt IS '创建时间';
|
||||
COMMENT ON COLUMN sj_job.update_dt IS '修改时间';
|
||||
COMMENT ON TABLE sj_job IS '任务信息';
|
||||
|
||||
INSERT INTO sj_job VALUES (1, 'dev', 'ruoyi_group', 'demo-job', null, 1, 1710344035622, 1, 1, 4, 1, 'testJobExecutor', 2, '60', 1, 60, 3, 1, 1, 116, 0, '', 1, '', '', '', 0, now(), now());
|
||||
INSERT INTO sj_job VALUES (1, 'dev', 'demo-job', 'ruoyi_group', 'demo-job', null, 1, 1710344035622, 1, 1, 4, 1, 'testJobExecutor', 2, '60', 1, 60, 3, 1, 1, 116, 0, '', 1, '', '', '', 0, now(), now());
|
||||
|
||||
-- sj_job_log_message
|
||||
CREATE TABLE sj_job_log_message
|
||||
@@ -684,7 +687,7 @@ CREATE TABLE sj_retry_summary
|
||||
id bigserial PRIMARY KEY,
|
||||
namespace_id varchar(64) NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a',
|
||||
group_name varchar(64) NOT NULL DEFAULT '',
|
||||
scene_name varchar(64) NOT NULL DEFAULT '',
|
||||
scene_name varchar(50) NOT NULL DEFAULT '',
|
||||
trigger_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
running_num int NOT NULL DEFAULT 0,
|
||||
finish_num int NOT NULL DEFAULT 0,
|
||||
@@ -717,6 +720,7 @@ CREATE TABLE sj_workflow
|
||||
id bigserial PRIMARY KEY,
|
||||
workflow_name varchar(64) NOT NULL,
|
||||
namespace_id varchar(64) NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a',
|
||||
biz_id varchar(64) NOT NULL,
|
||||
group_name varchar(64) NOT NULL,
|
||||
workflow_status smallint NOT NULL DEFAULT 1,
|
||||
trigger_type smallint NOT NULL,
|
||||
@@ -739,10 +743,12 @@ CREATE TABLE sj_workflow
|
||||
|
||||
CREATE INDEX idx_sj_workflow_01 ON sj_workflow (create_dt);
|
||||
CREATE INDEX idx_sj_workflow_02 ON sj_workflow (namespace_id, group_name);
|
||||
CREATE UNIQUE INDEX uk_sj_workflow_01 ON sj_workflow (namespace_id, biz_id);
|
||||
|
||||
COMMENT ON COLUMN sj_workflow.id IS '主键';
|
||||
COMMENT ON COLUMN sj_workflow.workflow_name IS '工作流名称';
|
||||
COMMENT ON COLUMN sj_workflow.namespace_id IS '命名空间id';
|
||||
COMMENT ON COLUMN sj_workflow.biz_id IS '业务ID';
|
||||
COMMENT ON COLUMN sj_workflow.group_name IS '组名称';
|
||||
COMMENT ON COLUMN sj_workflow.workflow_status IS '工作流状态 0、关闭、1、开启';
|
||||
COMMENT ON COLUMN sj_workflow.trigger_type IS '触发类型 1.CRON 表达式 2. 固定时间';
|
||||
|
||||
@@ -280,6 +280,7 @@ CREATE TABLE `sj_job`
|
||||
(
|
||||
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键',
|
||||
`namespace_id` varchar(64) NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a' COMMENT '命名空间id',
|
||||
`biz_id` varchar(64) NOT NULL COMMENT '业务ID',
|
||||
`group_name` varchar(64) NOT NULL COMMENT '组名称',
|
||||
`job_name` varchar(64) NOT NULL COMMENT '名称',
|
||||
`args_str` text DEFAULT NULL COMMENT '执行方法参数',
|
||||
@@ -310,12 +311,13 @@ CREATE TABLE `sj_job`
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `idx_namespace_id_group_name` (`namespace_id`, `group_name`),
|
||||
KEY `idx_job_status_bucket_index` (`job_status`, `bucket_index`),
|
||||
KEY `idx_create_dt` (`create_dt`)
|
||||
KEY `idx_create_dt` (`create_dt`),
|
||||
UNIQUE KEY `uk_sj_job_01` (`namespace_id`, `biz_id`)
|
||||
) ENGINE = InnoDB
|
||||
AUTO_INCREMENT = 0
|
||||
DEFAULT CHARSET = utf8mb4 COMMENT ='任务信息';
|
||||
|
||||
INSERT INTO `sj_job` VALUES (1, 'dev', 'ruoyi_group', 'demo-job', null, 1, 1710344035622, 1, 1, 4, 1, 'testJobExecutor', 2, '60', 1, 60, 3, 1, 1, 116, 0, '', 1, '','', '', 0 , now(), now());
|
||||
INSERT INTO `sj_job` VALUES (1, 'dev', 'demo-job', 'ruoyi_group', 'demo-job', null, 1, 1710344035622, 1, 1, 4, 1, 'testJobExecutor', 2, '60', 1, 60, 3, 1, 1, 116, 0, '', 1, '','', '', 0 , now(), now());
|
||||
|
||||
CREATE TABLE `sj_job_log_message`
|
||||
(
|
||||
@@ -443,6 +445,7 @@ CREATE TABLE `sj_workflow`
|
||||
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键',
|
||||
`workflow_name` varchar(64) NOT NULL COMMENT '工作流名称',
|
||||
`namespace_id` varchar(64) NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a' COMMENT '命名空间id',
|
||||
`biz_id` varchar(64) NOT NULL COMMENT '业务ID',
|
||||
`group_name` varchar(64) NOT NULL COMMENT '组名称',
|
||||
`workflow_status` tinyint(4) NOT NULL DEFAULT 1 COMMENT '工作流状态 0、关闭、1、开启',
|
||||
`trigger_type` tinyint(4) NOT NULL COMMENT '触发类型 1.CRON 表达式 2. 固定时间',
|
||||
@@ -463,7 +466,8 @@ CREATE TABLE `sj_workflow`
|
||||
`update_dt` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间',
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `idx_create_dt` (`create_dt`),
|
||||
KEY `idx_namespace_id_group_name` (`namespace_id`, `group_name`)
|
||||
KEY `idx_namespace_id_group_name` (`namespace_id`, `group_name`),
|
||||
UNIQUE KEY `uk_sj_workflow_01` (`namespace_id`, `biz_id`)
|
||||
) ENGINE = InnoDB
|
||||
AUTO_INCREMENT = 0
|
||||
DEFAULT CHARSET = utf8mb4 COMMENT ='工作流';
|
||||
|
||||
Reference in New Issue
Block a user