任务

研究 divide 底层原理,ip端口探活

divide 插件底层原理

hash 负载策略

聚焦于 divide-plugin 中的 HashLoadBalance。看到虚拟节点的参数,猜测是采用一致性哈希的负载均衡策略。

整体的策略是:

  • 针对每个真实服务的 url 在哈希环上分配 5 个虚拟节点
  • 通过调用服务的真实IP计算得到的哈希值,找到离它最近的服务节点;若没找到,则默认取哈希环的第一个服务节点
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
//虚拟节点的个数为 5
private static final int VIRTUAL_NODE_NUM = 5;

//入参为配置的服务的列表,及调用服务的真实际IP
public DivideUpstream doSelect(final List<DivideUpstream> upstreamList, final String ip) {
//用跳表存储服务节点
final ConcurrentSkipListMap<Long, DivideUpstream> treeMap = new ConcurrentSkipListMap<>();
for (DivideUpstream address : upstreamList) {
//每个服务设置 5 个虚拟节点
for (int i = 0; i < VIRTUAL_NODE_NUM; i++) {
//通过服务的 url 计算哈希值
long addressHash = hash("SOUL-" + address.getUpstreamUrl() + "-HASH-" + i);
//存储到跳表中
treeMap.put(addressHash, address);
}
}
//通过 IP 计算哈希值
long hash = hash(String.valueOf(ip));
//返回离该哈希值最近的节点
SortedMap<Long, DivideUpstream> lastRing = treeMap.tailMap(hash);
if (!lastRing.isEmpty()) {
return lastRing.get(lastRing.firstKey());
}
//如果离该哈希值最近的节点为空,则返回哈希环的第一个节点
return treeMap.firstEntry().getValue();
}

ip 端口探活

聚焦于 soul-admin 中的 UpstreamCheckService 。

可以看到 soul.upstream.checksoul.upstream.scheduledTime 在代码中体现为两个变量。

1
2
3
4
5
6
7
8
9
10
11
/**
* 是否开启ip探活
**/
@Value("${soul.upstream.check:true}")
private boolean check;

/**
* 时间间隔
**/
@Value("${soul.upstream.scheduledTime:10}")
private int scheduledTime;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
public void setup() {
...
//线程池隔 scheduledTime 执行一次 scheduled 方法
if (check) {
new ScheduledThreadPoolExecutor(Runtime.getRuntime().availableProcessors(), SoulThreadFactory.create("scheduled-upstream-task", false))
.scheduleWithFixedDelay(this::scheduled, 10, scheduledTime, TimeUnit.SECONDS);
}
}

private void scheduled() {
//检查每个服务的节点
if (UPSTREAM_MAP.size() > 0) {
UPSTREAM_MAP.forEach(this::check);
}
}

//检查的方法
private void check(final String selectorName, final List<DivideUpstream> upstreamList) {
...
for (DivideUpstream divideUpstream : upstreamList) {
//检查服务的 url 是否正常连接
final boolean pass = UpstreamCheckUtils.checkUrl(divideUpstream.getUpstreamUrl());
if (pass) {
...
successList.add(divideUpstream);
} else {
//检查 url 不能正常连接,则设置状态为 false,打印日志
divideUpstream.setStatus(false);
log.error("check the url={} is fail ", divideUpstream.getUpstreamUrl());
}
}
...
if (successList.size() > 0) {
//重置服务的列表
UPSTREAM_MAP.put(selectorName, successList);
updateSelectorHandler(selectorName, successList);
} else {
//如果没有服务活着,那就把选择器给删了
UPSTREAM_MAP.remove(selectorName);
updateSelectorHandler(selectorName, null);
}
}

后续安排

  • springcloud插件的底层原理

Link