Skip to content

Commit d7e780c

Browse files
authored
bugfix: the issue where the TC occasionally fails to go offline from the NamingServer (#6781)
1 parent 8af2d84 commit d7e780c

File tree

17 files changed

+186
-91
lines changed

17 files changed

+186
-91
lines changed

changes/en-us/2.x.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ Add changes here for all PR submitted to the 2.x branch.
2727
- [[#6769](https://github.com/apache/incubator-seata/pull/6769)] fix tcc fence deadLock
2828
- [[#6778](https://github.com/apache/incubator-seata/pull/6778)] fix namingserver node term
2929
- [[#6765](https://github.com/apache/incubator-seata/pull/6765)] fix MySQL driver loading by replacing custom classloader with system classloader for better compatibility and simplified process
30+
- [[#6781](https://github.com/apache/incubator-seata/pull/6781)] the issue where the TC occasionally fails to go offline from the NamingServer
3031

3132

3233
### optimize:

changes/zh-cn/2.x.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
- [[#6769](https://github.com/apache/incubator-seata/pull/6769)] 修复tcc fence死锁
2929
- [[#6778](https://github.com/apache/incubator-seata/pull/6778)] 修复namingserver的节点term为0问题
3030
- [[#6765](https://github.com/apache/incubator-seata/pull/6765)] 改进MySQL驱动加载机制,将自定义类加载器替换为系统类加载器,更兼容简化流程
31+
- [[#6781](https://github.com/apache/incubator-seata/pull/6781)] 修复tc下线时,由于定时任务没有先关闭,导致下线后还会被注册上,需要靠namingserver的健康检查来下线的bug
3132

3233

3334
### optimize:

namingserver/src/main/java/org/apache/seata/namingserver/entity/pojo/ClusterData.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -42,20 +42,20 @@ public class ClusterData {
4242
private String clusterType;
4343
private final Map<String, Unit> unitData;
4444

45-
private Lock lock = new ReentrantLock();
45+
private final Lock lock = new ReentrantLock();
4646

4747

4848
public ClusterData() {
49-
unitData = new ConcurrentHashMap<>(32);
49+
this.unitData = new ConcurrentHashMap<>();
5050
}
5151

5252
public ClusterData(String clusterName) {
53-
unitData = new ConcurrentHashMap<>(32);
53+
this.unitData = new ConcurrentHashMap<>();
5454
this.clusterName = clusterName;
5555
}
5656

5757
public ClusterData(String clusterName, String clusterType) {
58-
unitData = new ConcurrentHashMap<>(32);
58+
unitData = new ConcurrentHashMap<>();
5959
this.clusterName = clusterName;
6060
this.clusterType = clusterType;
6161
}

namingserver/src/main/java/org/apache/seata/namingserver/manager/NamingManager.java

Lines changed: 47 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import java.io.IOException;
2020
import java.net.InetSocketAddress;
2121
import java.util.HashMap;
22+
import java.util.HashSet;
2223
import java.util.List;
2324
import java.util.Map;
2425
import java.util.ArrayList;
@@ -167,47 +168,38 @@ public Result<String> createGroup(String namespace, String vGroup, String cluste
167168
return new Result<>(String.valueOf(closeableHttpResponse.getStatusLine().getStatusCode()),
168169
"add vGroup in new cluster failed");
169170
}
171+
LOGGER.info("namespace: {} add vGroup: {} in new cluster: {} successfully!", namespace, vGroup, clusterName);
170172
} catch (IOException e) {
171173
LOGGER.warn("add vGroup in new cluster failed");
172174
return new Result<>("500", "add vGroup in new cluster failed");
173175
}
174176
}
175-
addGroup(namespace,clusterName,unitName,vGroup);
176177
return new Result<>("200", "add vGroup successfully!");
177178
}
178179

179-
public Result<String> removeGroup(String namespace, String clusterName,String vGroup, String unitName) {
180-
List<Cluster> clusterList = getClusterListByVgroup(vGroup, namespace);
181-
for (Cluster cluster : clusterList) {
182-
if (!StringUtils.equals(clusterName, cluster.getClusterName())) {
183-
continue;
184-
}
185-
if (cluster.getUnitData() != null && cluster.getUnitData().size() > 0) {
186-
Unit unit = cluster.getUnitData().get(0);
187-
if (unit != null && unit.getNamingInstanceList() != null && unit.getNamingInstanceList().size() > 0) {
188-
Node node = unit.getNamingInstanceList().get(0);
189-
String httpUrl = NamingServerConstants.HTTP_PREFIX + node.getControl().getHost()
190-
+ NamingServerConstants.IP_PORT_SPLIT_CHAR + node.getControl().getPort()
191-
+ NamingServerConstants.HTTP_REMOVE_GROUP_SUFFIX;
192-
HashMap<String, String> params = new HashMap<>();
193-
params.put(CONSTANT_GROUP, vGroup);
194-
params.put(NamingServerConstants.CONSTANT_UNIT, unitName);
195-
Map<String, String> header = new HashMap<>();
196-
header.put(HTTP.CONTENT_TYPE, ContentType.APPLICATION_FORM_URLENCODED.getMimeType());
197-
try (CloseableHttpResponse closeableHttpResponse =
198-
HttpClientUtil.doGet(httpUrl, params, header, 3000)) {
199-
if (closeableHttpResponse == null
200-
|| closeableHttpResponse.getStatusLine().getStatusCode() != 200) {
201-
LOGGER.warn("remove vGroup in old cluster failed");
202-
return new Result<>(String.valueOf(closeableHttpResponse.getStatusLine().getStatusCode()),
203-
"removing vGroup " + vGroup + " in old cluster " + cluster + " failed");
204-
}
205-
} catch (IOException e) {
206-
LOGGER.warn("handle removing vGroup in old cluster failed");
207-
return new Result<>("500",
208-
"handle removing vGroup " + vGroup + " in old cluster " + cluster + " failed");
209-
}
180+
public Result<String> removeGroup(Unit unit, String vGroup, String clusterName, String namespace, String unitName) {
181+
if (unit != null && !CollectionUtils.isEmpty(unit.getNamingInstanceList())) {
182+
Node node = unit.getNamingInstanceList().get(0);
183+
String httpUrl = NamingServerConstants.HTTP_PREFIX + node.getControl().getHost()
184+
+ NamingServerConstants.IP_PORT_SPLIT_CHAR + node.getControl().getPort()
185+
+ NamingServerConstants.HTTP_REMOVE_GROUP_SUFFIX;
186+
HashMap<String, String> params = new HashMap<>();
187+
params.put(CONSTANT_GROUP, vGroup);
188+
params.put(NamingServerConstants.CONSTANT_UNIT, unitName);
189+
Map<String, String> header = new HashMap<>();
190+
header.put(HTTP.CONTENT_TYPE, ContentType.APPLICATION_FORM_URLENCODED.getMimeType());
191+
try (CloseableHttpResponse closeableHttpResponse = HttpClientUtil.doGet(httpUrl, params, header, 3000)) {
192+
if (closeableHttpResponse == null || closeableHttpResponse.getStatusLine().getStatusCode() != 200) {
193+
LOGGER.warn("remove vGroup in old cluster failed");
194+
return new Result<>(String.valueOf(closeableHttpResponse.getStatusLine().getStatusCode()),
195+
"removing vGroup " + vGroup + " in old cluster " + clusterName + " failed");
210196
}
197+
LOGGER.info("namespace: {} remove vGroup: {} in new cluster: {} successfully!", namespace, vGroup,
198+
clusterName);
199+
} catch (IOException e) {
200+
LOGGER.warn("handle removing vGroup in old cluster failed");
201+
return new Result<>("500",
202+
"handle removing vGroup " + vGroup + " in old cluster " + clusterName + " failed");
211203
}
212204
}
213205
return new Result<>("200", "remove group in old cluster successfully!");
@@ -248,20 +240,21 @@ public boolean registerInstance(NamingServerNode node, String namespace, String
248240
ClusterData clusterData = clusterDataHashMap.computeIfAbsent(clusterName,
249241
key -> new ClusterData(clusterName, (String)node.getMetadata().get("cluster-type")));
250242
boolean hasChanged = clusterData.registerInstance(node, unitName);
243+
Object mappingObj = node.getMetadata().get(CONSTANT_GROUP);
251244
// if extended metadata includes vgroup mapping relationship, add it in clusterData
252-
Optional.ofNullable(node.getMetadata().get(CONSTANT_GROUP)).ifPresent(mappingObj -> {
253-
if (mappingObj instanceof Map) {
254-
Map<String, String> vGroups = (Map<String, String>) mappingObj;
245+
if (mappingObj instanceof Map) {
246+
Map<String, String> vGroups = (Map<String, String>)mappingObj;
247+
if (!CollectionUtils.isEmpty(vGroups)) {
255248
vGroups.forEach((k, v) -> {
256249
// In non-raft mode, a unit is one-to-one with a node, and the unitName is stored on the node.
257250
// In raft mode, the unitName is equal to the raft-group, so the node's unitName cannot be used.
258251
addGroup(namespace, clusterName, StringUtils.isBlank(v) ? unitName : v, k);
259252
if (hasChanged) {
260-
notifyClusterChange(k,namespace, clusterName, unitName,node.getTerm());
253+
notifyClusterChange(k, namespace, clusterName, unitName, node.getTerm());
261254
}
262255
});
263256
}
264-
});
257+
}
265258
instanceLiveTable.put(
266259
new InetSocketAddress(node.getTransaction().getHost(), node.getTransaction().getPort()),
267260
System.currentTimeMillis());
@@ -367,24 +360,26 @@ public void instanceHeartBeatCheck() {
367360
}
368361

369362
public Result<String> changeGroup(String namespace, String vGroup, String clusterName, String unitName) {
370-
ConcurrentMap<String, NamespaceBO> namespaceMap =
371-
new ConcurrentHashMap<>(vGroupMap.get(vGroup));
363+
ConcurrentMap<String, NamespaceBO> namespaceMap = new ConcurrentHashMap<>(vGroupMap.get(vGroup));
364+
Set<String> currentNamespaces = namespaceMap.keySet();
365+
Map<String, Set<String>> namespaceClusters = new HashMap<>();
366+
for (String currentNamespace : currentNamespaces) {
367+
namespaceClusters.put(currentNamespace,
368+
new HashSet<>(namespaceMap.get(currentNamespace).getClusterMap().keySet()));
369+
}
372370
createGroup(namespace, vGroup, clusterName, unitName);
373371
AtomicReference<Result<String>> result = new AtomicReference<>();
374-
namespaceMap.forEach((currentNamespace, namespaceBO) -> namespaceBO.getClusterMap().forEach((currentCluster, clusterBO) -> {
375-
for (String currentUnitName : clusterBO.getUnitNames()) {
376-
if (StringUtils.isBlank(unitName)) {
377-
if (StringUtils.equalsIgnoreCase(clusterName, currentCluster)) {
378-
continue;
379-
}
380-
result.set(removeGroup(currentNamespace, currentCluster, vGroup, unitName));
381-
} else {
382-
if (!StringUtils.equalsIgnoreCase(unitName, currentUnitName)) {
383-
result.set(removeGroup(currentNamespace, currentCluster, vGroup, unitName));
384-
}
385-
}
372+
namespaceClusters.forEach((oldNamespace, clusters) -> {
373+
for (String cluster : clusters) {
374+
Optional.ofNullable(namespaceClusterDataMap.get(oldNamespace))
375+
.flatMap(map -> Optional.ofNullable(map.get(cluster))).ifPresent(clusterData -> {
376+
if (!CollectionUtils.isEmpty(clusterData.getUnitData())) {
377+
clusterData.getUnitData().forEach((unit, unitData) -> result
378+
.set(removeGroup(unitData, vGroup, cluster, oldNamespace, unitName)));
379+
}
380+
});
386381
}
387-
}));
382+
});
388383
return Optional.ofNullable(result.get()).orElseGet(() -> new Result<>("200", "change vGroup successfully!"));
389384
}
390385

script/client/conf/registry.conf

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
#
1717

1818
registry {
19-
# file 、nacos 、eureka、redis、zk、consul、etcd3、sofa、custom、raft
19+
# file 、nacos 、eureka、redis、zk、consul、etcd3、sofa、custom、raft、namingserver
2020
type = "file"
2121

2222
raft {
@@ -44,6 +44,11 @@ registry {
4444
##if use Nacos naming meta-data for SLB service registry, specify nacos address pattern rules here
4545
#slbPattern = ""
4646
}
47+
namingserver {
48+
server-addr = "127.0.0.1:8081"
49+
namespace = "public"
50+
heartbeat-period = 5000
51+
}
4752
eureka {
4853
serviceUrl = "http://localhost:8761/eureka"
4954
weight = "1"

script/client/spring/application.properties

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,9 @@ seata.registry.etcd3.server-addr=http://localhost:2379
133133

134134
seata.registry.eureka.weight=1
135135
seata.registry.eureka.service-url=http://localhost:8761/eureka
136+
seata.registry.namingserver.server-addr=127.0.0.1:8081
137+
seata.registry.namingserver.namespace=public
138+
seata.registry.namingserver.heartbeat-period=5000
136139

137140
seata.registry.nacos.application=seata-server
138141
seata.registry.nacos.server-addr=127.0.0.1:8848

script/client/spring/application.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,10 @@ seata:
131131
name:
132132
registry:
133133
type: file
134+
namingserver:
135+
server-addr: 127.0.0.1:8081
136+
namespace: public
137+
heartbeat-period: 5000
134138
raft:
135139
server-addr:
136140
metadata-max-age-ms: 30000

seata-spring-autoconfigure/seata-spring-autoconfigure-core/src/main/java/org/apache/seata/spring/boot/autoconfigure/SeataCoreEnvironmentPostProcessor.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
import org.apache.seata.spring.boot.autoconfigure.properties.registry.RegistryEtcd3Properties;
3535
import org.apache.seata.spring.boot.autoconfigure.properties.registry.RegistryEurekaProperties;
3636
import org.apache.seata.spring.boot.autoconfigure.properties.registry.RegistryNacosProperties;
37+
import org.apache.seata.spring.boot.autoconfigure.properties.registry.RegistryNamingServerProperties;
3738
import org.apache.seata.spring.boot.autoconfigure.properties.registry.RegistryProperties;
3839
import org.apache.seata.spring.boot.autoconfigure.properties.registry.RegistryRaftProperties;
3940
import org.apache.seata.spring.boot.autoconfigure.properties.registry.RegistryRedisProperties;
@@ -59,6 +60,7 @@
5960
import static org.apache.seata.spring.boot.autoconfigure.StarterConstants.REGISTRY_ETCD3_PREFIX;
6061
import static org.apache.seata.spring.boot.autoconfigure.StarterConstants.REGISTRY_EUREKA_PREFIX;
6162
import static org.apache.seata.spring.boot.autoconfigure.StarterConstants.REGISTRY_NACOS_PREFIX;
63+
import static org.apache.seata.spring.boot.autoconfigure.StarterConstants.REGISTRY_NAMINGSERVER_PREFIX;
6264
import static org.apache.seata.spring.boot.autoconfigure.StarterConstants.REGISTRY_PREFIX;
6365
import static org.apache.seata.spring.boot.autoconfigure.StarterConstants.REGISTRY_RAFT_PREFIX;
6466
import static org.apache.seata.spring.boot.autoconfigure.StarterConstants.REGISTRY_REDIS_PREFIX;
@@ -100,6 +102,7 @@ public static void init() {
100102
PROPERTY_BEAN_MAP.put(REGISTRY_ETCD3_PREFIX, RegistryEtcd3Properties.class);
101103
PROPERTY_BEAN_MAP.put(REGISTRY_EUREKA_PREFIX, RegistryEurekaProperties.class);
102104
PROPERTY_BEAN_MAP.put(REGISTRY_NACOS_PREFIX, RegistryNacosProperties.class);
105+
PROPERTY_BEAN_MAP.put(REGISTRY_NAMINGSERVER_PREFIX, RegistryNamingServerProperties.class);
103106
PROPERTY_BEAN_MAP.put(REGISTRY_REDIS_PREFIX, RegistryRedisProperties.class);
104107
PROPERTY_BEAN_MAP.put(REGISTRY_SOFA_PREFIX, RegistrySofaProperties.class);
105108
PROPERTY_BEAN_MAP.put(REGISTRY_ZK_PREFIX, RegistryZooKeeperProperties.class);

seata-spring-autoconfigure/seata-spring-autoconfigure-core/src/main/java/org/apache/seata/spring/boot/autoconfigure/StarterConstants.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ public interface StarterConstants {
5353
String REGISTRY_REDIS_PREFIX = REGISTRY_PREFIX + ".redis";
5454
String REGISTRY_ZK_PREFIX = REGISTRY_PREFIX + ".zk";
5555
String REGISTRY_CONSUL_PREFIX = REGISTRY_PREFIX + ".consul";
56+
String REGISTRY_NAMINGSERVER_PREFIX = REGISTRY_PREFIX + ".namingserver";
5657
String REGISTRY_ETCD3_PREFIX = REGISTRY_PREFIX + ".etcd3";
5758
String REGISTRY_SOFA_PREFIX = REGISTRY_PREFIX + ".sofa";
5859
String REGISTRY_CUSTOM_PREFIX = REGISTRY_PREFIX + ".custom";
@@ -96,4 +97,5 @@ public interface StarterConstants {
9697
String SPECIAL_KEY_GROUPLIST = "grouplist";
9798
String SPECIAL_KEY_SERVICE = "service";
9899
String SPECIAL_KEY_VGROUP_MAPPING = "vgroupMapping";
100+
99101
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
package org.apache.seata.spring.boot.autoconfigure.properties.registry;
18+
19+
import org.springframework.boot.context.properties.ConfigurationProperties;
20+
import org.springframework.stereotype.Component;
21+
22+
import static org.apache.seata.spring.boot.autoconfigure.StarterConstants.REGISTRY_NAMINGSERVER_PREFIX;
23+
24+
@Component
25+
@ConfigurationProperties(prefix = REGISTRY_NAMINGSERVER_PREFIX)
26+
public class RegistryNamingServerProperties {
27+
private String cluster = "default";
28+
private String serverAddr = "127.0.0.1:8081";
29+
private String namespace = "public";
30+
31+
private int heartbeatPeriod = 5000;
32+
33+
public String getCluster() {
34+
return cluster;
35+
}
36+
37+
public RegistryNamingServerProperties setCluster(String cluster) {
38+
this.cluster = cluster;
39+
return this;
40+
}
41+
42+
public String getServerAddr() {
43+
return serverAddr;
44+
}
45+
46+
public RegistryNamingServerProperties setServerAddr(String serverAddr) {
47+
this.serverAddr = serverAddr;
48+
return this;
49+
}
50+
51+
public String getNamespace() {
52+
return namespace;
53+
}
54+
55+
public void setNamespace(String namespace) {
56+
this.namespace = namespace;
57+
}
58+
59+
public int getHeartbeatPeriod() {
60+
return heartbeatPeriod;
61+
}
62+
63+
public void setHeartbeatPeriod(int heartbeatPeriod) {
64+
this.heartbeatPeriod = heartbeatPeriod;
65+
}
66+
}

0 commit comments

Comments
 (0)