Skip to content

Commit 9f41bdd

Browse files
feat:support polaris server nearby backup LB. (#615)
1 parent e6c991a commit 9f41bdd

File tree

12 files changed

+642
-11
lines changed

12 files changed

+642
-11
lines changed

polaris-common/polaris-config-default/src/main/resources/conf/default-config.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,8 @@ global:
6161
#描述:默认服务端埋点接入地址
6262
addresses:
6363
- 127.0.0.1:8091
64+
#描述:默认服务端地址负载均衡策略
65+
lbPolicy: roundRobin
6466
#描述: 访问server的连接协议,SDK会根据协议名称会加载对应的插件
6567
protocol: grpc
6668
#描述: 发起连接后的连接超时时间

polaris-common/polaris-config/src/main/java/com/tencent/polaris/api/config/consumer/LoadBalanceConfig.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,16 @@ public interface LoadBalanceConfig extends PluginConfig, Verifier {
4343
*/
4444
String LOAD_BALANCE_WEIGHTED_ROUND_ROBIN = "weightedRoundRobin";
4545

46+
/**
47+
* 轮询负载均衡插件名
48+
*/
49+
String LOAD_BALANCE_ROUND_ROBIN = "roundRobin";
50+
51+
/**
52+
* 就近主备负载均衡插件名
53+
*/
54+
String LOAD_BALANCE_NEARBY_BACKUP = "nearbyBackup";
55+
4656
/**
4757
* 负载均衡类型
4858
*

polaris-common/polaris-config/src/main/java/com/tencent/polaris/api/config/global/ServerConnectorConfig.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919

2020
import com.tencent.polaris.api.config.plugin.PluginConfig;
2121
import com.tencent.polaris.api.config.verify.Verifier;
22+
2223
import java.util.List;
2324
import java.util.Map;
2425

@@ -36,6 +37,13 @@ public interface ServerConnectorConfig extends PluginConfig, Verifier {
3637
*/
3738
List<String> getAddresses();
3839

40+
/**
41+
* 远端server地址负载均衡策略
42+
*
43+
* @return 负载均衡策略
44+
*/
45+
String getLbPolicy();
46+
3947
/**
4048
* 与server对接的协议,默认GRPC
4149
*

polaris-common/polaris-config/src/main/java/com/tencent/polaris/factory/config/global/ServerConnectorConfigImpl.java

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,16 @@
1919

2020
import com.fasterxml.jackson.annotation.JsonProperty;
2121
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
22+
import com.tencent.polaris.api.config.consumer.LoadBalanceConfig;
2223
import com.tencent.polaris.api.config.global.ServerConnectorConfig;
2324
import com.tencent.polaris.api.config.plugin.DefaultPlugins;
2425
import com.tencent.polaris.api.utils.CollectionUtils;
26+
import com.tencent.polaris.api.utils.StringUtils;
2527
import com.tencent.polaris.factory.config.plugin.PluginConfigImpl;
2628
import com.tencent.polaris.factory.util.ConfigUtils;
2729
import com.tencent.polaris.factory.util.TimeStrJsonDeserializer;
30+
31+
import java.util.ArrayList;
2832
import java.util.List;
2933
import java.util.Map;
3034
import java.util.concurrent.ConcurrentHashMap;
@@ -42,6 +46,8 @@ public class ServerConnectorConfigImpl extends PluginConfigImpl implements Serve
4246
@JsonProperty
4347
private List<String> addresses;
4448
@JsonProperty
49+
private String lbPolicy = LoadBalanceConfig.LOAD_BALANCE_ROUND_ROBIN;
50+
@JsonProperty
4551
private String protocol;
4652
@JsonProperty
4753
@JsonDeserialize(using = TimeStrJsonDeserializer.class)
@@ -78,6 +84,15 @@ public void setAddresses(List<String> addresses) {
7884
this.addresses = addresses;
7985
}
8086

87+
@Override
88+
public String getLbPolicy() {
89+
return lbPolicy;
90+
}
91+
92+
public void setLbPolicy(String lbPolicy) {
93+
this.lbPolicy = lbPolicy;
94+
}
95+
8196
@Override
8297
public String getProtocol() {
8398
return protocol;
@@ -199,6 +214,10 @@ public void verify() {
199214
throw new IllegalArgumentException(String.format("address [%s] of [%s] is invalid", address, protocol));
200215
}
201216
}
217+
List<String> targetLbPolicyList = new ArrayList<>();
218+
targetLbPolicyList.add(LoadBalanceConfig.LOAD_BALANCE_ROUND_ROBIN);
219+
targetLbPolicyList.add(LoadBalanceConfig.LOAD_BALANCE_NEARBY_BACKUP);
220+
ConfigUtils.validateIn(lbPolicy, targetLbPolicyList, "serverConnector.lbPolicy");
202221
ConfigUtils.validateString(id, "serverConnector.id");
203222
if (DefaultPlugins.SERVER_CONNECTOR_GRPC.equals(protocol)) {
204223
ConfigUtils.validateString(protocol, "serverConnector.protocol");
@@ -218,6 +237,9 @@ public void setDefault(Object defaultObject) {
218237
if (null == addresses) {
219238
setAddresses(serverConnectorConfig.getAddresses());
220239
}
240+
if (StringUtils.isBlank(lbPolicy)) {
241+
setLbPolicy(LoadBalanceConfig.LOAD_BALANCE_ROUND_ROBIN);
242+
}
221243
if (null == protocol) {
222244
setProtocol(serverConnectorConfig.getProtocol());
223245
}

polaris-common/polaris-config/src/main/java/com/tencent/polaris/factory/util/ConfigUtils.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,10 @@
1818
package com.tencent.polaris.factory.util;
1919

2020
import com.fasterxml.jackson.databind.ObjectMapper;
21+
import com.tencent.polaris.api.utils.CollectionUtils;
2122
import com.tencent.polaris.api.utils.StringUtils;
23+
24+
import java.util.List;
2225
import java.util.Map;
2326

2427
/**
@@ -114,4 +117,16 @@ public static void validateTrue(Boolean value, String name) {
114117
throw new IllegalArgumentException(name + " must not be false");
115118
}
116119
}
120+
121+
public static void validateIn(String value, List<String> targetList, String name) {
122+
if (StringUtils.isBlank(value)) {
123+
throw new IllegalArgumentException(name + " must not be empty");
124+
}
125+
if (CollectionUtils.isEmpty(targetList)) {
126+
throw new IllegalArgumentException("target list must not be empty");
127+
}
128+
if (!targetList.contains(value)) {
129+
throw new IllegalArgumentException(name + " must be one of " + targetList);
130+
}
131+
}
117132
}

polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/utils/IPAddressUtils.java

Lines changed: 61 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,10 @@
2020
import com.tencent.polaris.logging.LoggerFactory;
2121
import org.slf4j.Logger;
2222

23-
import java.net.Inet6Address;
24-
import java.net.InetAddress;
25-
import java.net.UnknownHostException;
23+
import java.io.BufferedReader;
24+
import java.io.IOException;
25+
import java.io.InputStreamReader;
26+
import java.net.*;
2627

2728
/**
2829
* @author Haotian Zhang
@@ -73,4 +74,61 @@ public static boolean checkIpv6Host(String host) {
7374
}
7475
return false;
7576
}
77+
78+
public static boolean detect(String host, int port, int timeout) {
79+
if (!ping(host)) {
80+
return connect(host, port, timeout);
81+
}
82+
return true;
83+
}
84+
85+
public static boolean ping(String host) {
86+
try {
87+
// 根据操作系统构造不同的ping命令
88+
String command;
89+
if (System.getProperty("os.name").startsWith("Windows")) {
90+
command = "ping -n 1 " + host;
91+
} else {
92+
command = "ping -c 1 " + host;
93+
}
94+
// 执行ping命令
95+
Process process = Runtime.getRuntime().exec(command);
96+
// 读取命令输出
97+
BufferedReader reader = new BufferedReader(
98+
new InputStreamReader(process.getInputStream()));
99+
String line;
100+
while ((line = reader.readLine()) != null) {
101+
LOG.trace(line);
102+
}
103+
// 等待命令执行完成
104+
int exitCode = process.waitFor();
105+
// 0表示成功,其他值表示失败
106+
if (exitCode != 0) {
107+
LOG.warn("ping {} with exit code: {}", host, exitCode);
108+
} else {
109+
LOG.debug("ping {} successfully with exit code: {}", host, exitCode);
110+
}
111+
return exitCode == 0;
112+
} catch (IOException | InterruptedException e) {
113+
LOG.warn("ping {} failed: {}", host, e.getMessage());
114+
}
115+
return false;
116+
}
117+
118+
public static boolean connect(String host, int port, int timeout) {
119+
try (Socket socket = new Socket()) {
120+
// 设置SO_REUSEADDR为false可以避免重用TIME_WAIT状态的端口
121+
socket.setReuseAddress(false);
122+
// 设置TCP_NODELAY禁用Nagle算法
123+
socket.setTcpNoDelay(true);
124+
// 绑定随机端口
125+
socket.bind(new InetSocketAddress(0));
126+
socket.connect(new InetSocketAddress(host, port), timeout); // 1秒超时
127+
LOG.debug("connect {}:{} successfully", host, port);
128+
return true;
129+
} catch (IOException e) {
130+
LOG.warn("connect {}:{} failed: {}", host, port, e.getMessage());
131+
}
132+
return false;
133+
}
76134
}

polaris-common/polaris-model/src/main/java/com/tencent/polaris/client/pojo/Node.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,11 @@ public Node(String host, int port) {
3737
this.port = port;
3838
}
3939

40+
public Node(Node another) {
41+
this.host = another.host;
42+
this.port = another.port;
43+
}
44+
4045
public String getHost() {
4146
return host;
4247
}

polaris-common/polaris-model/src/test/java/com/tencent/polaris/api/utils/IPAddressUtilsTest.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,4 +104,9 @@ public void testCheckIpv6Host_WithIpv6MappedIpv4() {
104104
// 测试IPv6映射的IPv4地址
105105
assertThat(IPAddressUtils.checkIpv6Host("::ffff:192.168.1.1")).isFalse();
106106
}
107+
108+
@Test
109+
public void testPing() {
110+
assertThat(IPAddressUtils.ping("localhost")).isTrue();
111+
}
107112
}
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
/*
2+
* Tencent is pleased to support the open source community by making polaris-java available.
3+
*
4+
* Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved.
5+
*
6+
* Licensed under the BSD 3-Clause License (the "License");
7+
* you may not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* https://opensource.org/licenses/BSD-3-Clause
11+
*
12+
* Unless required by applicable law or agreed to in writing, software distributed
13+
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
14+
* CONDITIONS OF ANY KIND, either express or implied. See the License for the
15+
* specific language governing permissions and limitations under the License.
16+
*/
17+
18+
package com.tencent.polaris.client.pojo;
19+
20+
import org.junit.Test;
21+
22+
import static org.assertj.core.api.Assertions.assertThat;
23+
24+
/**
25+
* Test for {@link Node}.
26+
*
27+
* @author Haotian Zhang
28+
*/
29+
public class NodeTest {
30+
31+
@Test
32+
public void testConstructorAndGetters() {
33+
// 测试构造器和getter方法
34+
Node node = new Node("localhost", 8080);
35+
36+
assertThat(node)
37+
.extracting(Node::getHost, Node::getPort)
38+
.containsExactly("localhost", 8080);
39+
}
40+
41+
@Test
42+
public void testCopyConstructor() {
43+
// 测试拷贝构造器
44+
Node original = new Node("localhost", 8081);
45+
Node copy = new Node(original);
46+
47+
assertThat(copy)
48+
.usingRecursiveComparison()
49+
.isEqualTo(original);
50+
}
51+
52+
@Test
53+
public void testGetHostPort() {
54+
// 测试getHostPort方法
55+
Node node = new Node("example.com", 443);
56+
57+
assertThat(node.getHostPort())
58+
.isEqualTo("example.com:443")
59+
.contains("example.com")
60+
.endsWith("443");
61+
}
62+
63+
@Test
64+
public void testEqualsAndHashCode() {
65+
// 测试equals和hashCode方法
66+
Node node1 = new Node("host1", 80);
67+
Node node2 = new Node("host1", 80);
68+
Node node3 = new Node("host2", 80);
69+
70+
assertThat(node1)
71+
.isEqualTo(node2)
72+
.hasSameHashCodeAs(node2)
73+
.isNotEqualTo(node3)
74+
.isNotEqualTo(null)
75+
.isNotEqualTo("string");
76+
}
77+
78+
@Test
79+
public void testToString() {
80+
// 测试toString方法
81+
Node node = new Node("testhost", 9999);
82+
83+
assertThat(node.toString())
84+
.isEqualTo("Node{host='testhost', port=9999}")
85+
.contains("testhost")
86+
.containsPattern("port=\\d+");
87+
}
88+
89+
@Test
90+
public void testIsAnyAddress() {
91+
// 测试isAnyAddress方法
92+
Node localNode = new Node("0.0.0.0", 8080);
93+
Node nonLocalNode = new Node("8.8.8.8", 53);
94+
Node invalidNode = new Node("invalid.host.name", 1234);
95+
96+
assertThat(localNode.isAnyAddress()).isTrue();
97+
assertThat(nonLocalNode.isAnyAddress()).isFalse();
98+
assertThat(invalidNode.isAnyAddress()).isFalse();
99+
}
100+
101+
@Test
102+
public void testIsAnyAddressWithNullHost() {
103+
// 测试null主机名的情况
104+
Node nullHostNode = new Node(null, 8080);
105+
106+
assertThat(nullHostNode.isAnyAddress()).isFalse();
107+
}
108+
109+
@Test
110+
public void testEdgeCases() {
111+
// 测试边界情况
112+
Node minPortNode = new Node("host", 0);
113+
Node maxPortNode = new Node("host", 65535);
114+
Node emptyHostNode = new Node("", 8080);
115+
116+
assertThat(minPortNode.getPort()).isZero();
117+
118+
assertThat(maxPortNode.getPort()).isEqualTo(65535);
119+
120+
assertThat(emptyHostNode.getHost()).isEmpty();
121+
}
122+
}

0 commit comments

Comments
 (0)