Skip to content

Commit ff95939

Browse files
authored
[ISSUE alibaba#14016]: when import or export, chose healthy node to do (alibaba#14028)
1 parent fbce299 commit ff95939

2 files changed

Lines changed: 44 additions & 11 deletions

File tree

console/src/main/java/com/alibaba/nacos/console/handler/impl/remote/config/ConfigImportAndExportService.java

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,19 +17,23 @@
1717
package com.alibaba.nacos.console.handler.impl.remote.config;
1818

1919
import com.alibaba.nacos.api.common.Constants;
20+
import com.alibaba.nacos.api.common.NodeState;
2021
import com.alibaba.nacos.api.config.model.SameConfigPolicy;
2122
import com.alibaba.nacos.api.exception.NacosException;
2223
import com.alibaba.nacos.api.exception.runtime.NacosRuntimeException;
24+
import com.alibaba.nacos.api.model.response.NacosMember;
2325
import com.alibaba.nacos.api.model.v2.Result;
2426
import com.alibaba.nacos.auth.config.NacosAuthConfig;
2527
import com.alibaba.nacos.auth.config.NacosAuthConfigHolder;
2628
import com.alibaba.nacos.common.http.HttpUtils;
2729
import com.alibaba.nacos.common.http.param.Query;
30+
import com.alibaba.nacos.common.utils.CollectionUtils;
2831
import com.alibaba.nacos.common.utils.IoUtils;
2932
import com.alibaba.nacos.common.utils.JacksonUtils;
3033
import com.alibaba.nacos.common.utils.StringUtils;
3134
import com.alibaba.nacos.config.server.utils.RequestUtil;
3235
import com.alibaba.nacos.console.config.NacosConsoleAuthConfig;
36+
import com.alibaba.nacos.console.handler.core.ClusterHandler;
3337
import com.alibaba.nacos.console.handler.impl.remote.EnabledRemoteHandler;
3438
import com.alibaba.nacos.core.cluster.Member;
3539
import com.alibaba.nacos.core.cluster.NacosMemberManager;
@@ -64,6 +68,7 @@
6468
import java.util.Collection;
6569
import java.util.List;
6670
import java.util.Map;
71+
import java.util.stream.Collectors;
6772

6873
/**
6974
* Nacos config import and export service.
@@ -81,9 +86,12 @@ public class ConfigImportAndExportService {
8186
private static final String REMOTE_CONFIG_EXPORT_URL = "http://%s%s/v3/admin/cs/config/export";
8287

8388
private final NacosMemberManager memberManager;
89+
90+
private final ClusterHandler remoteClusterHandler;
8491

85-
public ConfigImportAndExportService(NacosMemberManager memberManager) {
92+
public ConfigImportAndExportService(NacosMemberManager memberManager, ClusterHandler remoteClusterHandler) {
8693
this.memberManager = memberManager;
94+
this.remoteClusterHandler = remoteClusterHandler;
8795
}
8896

8997
/**
@@ -98,9 +106,9 @@ public ConfigImportAndExportService(NacosMemberManager memberManager) {
98106
* @return Maps of import success and failed count
99107
*/
100108
public Result<Map<String, Object>> importConfig(String sourceUser, String namespaceId, SameConfigPolicy policy,
101-
MultipartFile importFile, String sourceIp, String sourceApp) {
109+
MultipartFile importFile, String sourceIp, String sourceApp) throws NacosException {
102110
String serverContextPath = getServerContextPath();
103-
Member serverMember = randomOneMember();
111+
Member serverMember = randomOneHealthyMember();
104112
String url = String.format(REMOTE_CONFIG_IMPORT_URL, serverMember.getAddress(), serverContextPath);
105113
try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
106114
Query query = Query.newInstance().addParam("namespaceId", namespaceId).addParam("srcUser", sourceUser);
@@ -145,7 +153,7 @@ public Result<Map<String, Object>> importConfig(String sourceUser, String namesp
145153
public ResponseEntity<byte[]> exportConfig(String dataId, String group, String namespaceId, String appName,
146154
List<Long> ids) throws Exception {
147155
String serverContextPath = getServerContextPath();
148-
Member serverMember = randomOneMember();
156+
Member serverMember = randomOneHealthyMember();
149157
String url = String.format(REMOTE_CONFIG_EXPORT_URL, serverMember.getAddress(), serverContextPath);
150158
try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
151159
Query query = Query.newInstance().addParam("namespaceId", namespaceId).addParam("dataId", dataId)
@@ -176,8 +184,19 @@ private String getServerContextPath() {
176184
return EnvUtil.getProperty("nacos.console.remote.server.context-path", "/nacos");
177185
}
178186

179-
private Member randomOneMember() {
187+
private Member randomOneHealthyMember() throws NacosException {
188+
// all nodes in cluster.conf
180189
Collection<Member> allMembers = memberManager.allMembers();
190+
// nodes with state in cluster
191+
Collection<? extends NacosMember> membersWithState = remoteClusterHandler.getNodeList("");
192+
Map<String, NodeState> nodeStateMap = membersWithState
193+
.stream().collect(Collectors.toMap(NacosMember::getAddress, NacosMember::getState));
194+
// remove unhealthy nodes
195+
allMembers.removeIf(node -> !NodeState.UP.equals(nodeStateMap.get(node.getAddress())));
196+
if (CollectionUtils.isEmpty(allMembers)) {
197+
throw new NacosRuntimeException(NacosException.SERVER_ERROR, "No healthy server node found.");
198+
}
199+
181200
return allMembers.parallelStream().findAny().orElseThrow();
182201
}
183202

console/src/test/java/com/alibaba/nacos/console/handler/impl/remote/config/ConfigImportAndExportServiceTest.java

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,13 @@
1616

1717
package com.alibaba.nacos.console.handler.impl.remote.config;
1818

19+
import com.alibaba.nacos.api.common.NodeState;
1920
import com.alibaba.nacos.api.config.model.SameConfigPolicy;
21+
import com.alibaba.nacos.api.exception.NacosException;
2022
import com.alibaba.nacos.api.exception.runtime.NacosRuntimeException;
2123
import com.alibaba.nacos.api.model.v2.Result;
2224
import com.alibaba.nacos.common.utils.JacksonUtils;
25+
import com.alibaba.nacos.console.handler.core.ClusterHandler;
2326
import com.alibaba.nacos.core.cluster.Member;
2427
import com.alibaba.nacos.core.cluster.NacosMemberManager;
2528
import com.alibaba.nacos.sys.env.EnvUtil;
@@ -51,6 +54,8 @@
5154
import java.io.InputStream;
5255
import java.util.Collections;
5356
import java.util.Map;
57+
import java.util.Collection;
58+
import java.util.HashSet;
5459

5560
import static org.junit.jupiter.api.Assertions.assertEquals;
5661
import static org.junit.jupiter.api.Assertions.assertThrows;
@@ -63,6 +68,8 @@ class ConfigImportAndExportServiceTest {
6368

6469
@Mock
6570
NacosMemberManager memberManager;
71+
@Mock
72+
ClusterHandler clusterHandler;
6673

6774
@Mock
6875
CloseableHttpClient httpClient;
@@ -74,18 +81,23 @@ class ConfigImportAndExportServiceTest {
7481
private ConfigurableEnvironment cachedEnvironment;
7582

7683
@BeforeEach
77-
void setUp() {
84+
void setUp() throws NacosException {
7885
cachedEnvironment = EnvUtil.getEnvironment();
7986
MockEnvironment environment = new MockEnvironment();
8087
environment.setProperty("nacos.core.auth.admin.enabled", "false");
8188
EnvUtil.setEnvironment(environment);
82-
service = new ConfigImportAndExportService(memberManager);
89+
service = new ConfigImportAndExportService(memberManager, clusterHandler);
8390
httpClientMock = Mockito.mockStatic(HttpClients.class);
8491
httpClientMock.when(HttpClients::createDefault).thenReturn(httpClient);
8592
Member member = new Member();
8693
member.setIp("127.0.0.1");
8794
member.setPort(8080);
88-
when(memberManager.allMembers()).thenReturn(Collections.singleton(member));
95+
member.setState(NodeState.UP);
96+
97+
Collection nacosMembers = new HashSet<>();
98+
nacosMembers.add(member);
99+
when(memberManager.allMembers()).thenReturn(nacosMembers);
100+
when(clusterHandler.getNodeList(any())).thenReturn(nacosMembers);
89101
}
90102

91103
@AfterEach
@@ -95,7 +107,7 @@ void tearDown() {
95107
}
96108

97109
@Test
98-
void importConfig() throws IOException {
110+
void importConfig() throws IOException, NacosException {
99111
MultipartFile mockFile = Mockito.mock(MultipartFile.class);
100112
when(mockFile.getContentType()).thenReturn(MediaType.MULTIPART_FORM_DATA_VALUE);
101113
when(mockFile.getOriginalFilename()).thenReturn("file");
@@ -167,9 +179,10 @@ void exportConfigWithIoException() throws Exception {
167179
}
168180

169181
@Test
170-
void exportHttpClientResponseHandlerHandleResponse() throws ProtocolException, IOException {
182+
void exportHttpClientResponseHandlerHandleResponse() throws ProtocolException, IOException, NacosException {
171183
// remove lenient warning
172184
memberManager.allMembers();
185+
clusterHandler.getNodeList("");
173186
ClassicHttpResponse mockResponse = Mockito.mock(ClassicHttpResponse.class);
174187
when(mockResponse.getHeader("Content-Disposition")).thenReturn(
175188
new BasicHeader("Content-Disposition", "testDisposition"));
@@ -186,9 +199,10 @@ void exportHttpClientResponseHandlerHandleResponse() throws ProtocolException, I
186199
}
187200

188201
@Test
189-
void exportHttpClientResponseHandlerHandleResponseWithException() throws ProtocolException {
202+
void exportHttpClientResponseHandlerHandleResponseWithException() throws ProtocolException, NacosException {
190203
// remove lenient warning
191204
memberManager.allMembers();
205+
clusterHandler.getNodeList("");
192206
ClassicHttpResponse mockResponse = Mockito.mock(ClassicHttpResponse.class);
193207
when(mockResponse.getHeader("Content-Disposition")).thenThrow(new ProtocolException());
194208
ConfigImportAndExportService.ExportHttpClientResponseHandler handler = new ConfigImportAndExportService.ExportHttpClientResponseHandler();

0 commit comments

Comments
 (0)