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
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
|
import
java.io.UnsupportedEncodingException;
import
java.security.MessageDigest;
import
java.security.NoSuchAlgorithmException;
import
java.util.*;
/**
* 模拟一致性Hash算法
* 这段代码网上找的,输出各个节点的负载是差不多的,但是我在想可以自己写一个达到完全的负载均衡
* 比如:VIRTUAL_NODE_COUNT = 150
* 维护0~(2^32)/150的数字,新加入节点就取(2^32)/150内一个没有使用过的数,
* 然后在这个数的基础上面加150次的(2^32)/150可以生成150个分布均衡的虚拟节点。
* 如果有新增或者减少节点需要维护。
*
* @author [email protected]
* @date 2016/9/1 9:26
*/
public
class
ConsistencyHash {
// 环的所有节点
private
TreeMap
null
;
// 真实服务器节点
private
List
new
ArrayList();
// 设置虚拟节点数目
// 太多会影响性能,太少又会导致负载不均衡,一般说来,经验值是150,
// 当然根据集群规模和负载均衡的精度需求,这个值应该根据具体情况具体对待。
private
int
VIRTUAL_NODE_COUNT =
150
;
/**
* 初始化一致环
*/
public
void
init() {
// 加入五台真实服务器
realNodes.add(
"192.168.0.0-服务器0"
);
realNodes.add(
"192.168.0.1-服务器1"
);
realNodes.add(
"192.168.0.2-服务器2"
);
realNodes.add(
"192.168.0.3-服务器3"
);
realNodes.add(
"192.168.0.4-服务器4"
);
// 构造每台真实服务器的虚拟节点
allNodes =
new
TreeMap<>();
for
(
int
i =
0
; i < realNodes.size(); i++) {
Object nodeInfo = realNodes.get(i);
for
(
int
j =
0
; j < VIRTUAL_NODE_COUNT; j++) {
allNodes.put(hash(computeMd5(
"NODE-"
+ i +
"-VIRTUAL-"
+ j),
0
), nodeInfo);
// allNodes.put(hash(nodeInfo.toString() + j), nodeInfo);
}
}
}
/**
* 计算MD5值
*/
public
byte
[] computeMd5(String k) {
MessageDigest md5;
try
{
md5 = MessageDigest.getInstance(
"MD5"
);
}
catch
(NoSuchAlgorithmException e) {
throw
new
RuntimeException(
"MD5 not supported"
, e);
}
md5.reset();
byte
[] keyBytes =
null
;
try
{
keyBytes = k.getBytes(
"UTF-8"
);
}
catch
(UnsupportedEncodingException e) {
throw
new
RuntimeException(
"Unknown string :"
+ k, e);
}
md5.update(keyBytes);
return
md5.digest();
}
/**
* 根据2^32把节点分布到环上面
*
* @param digest
* @param nTime
* @return
*/
public
long
hash(
byte
[] digest,
int
nTime) {
long
rv = ((
long
) (digest[
3
+ nTime *
4
] &
0xFF
) <<
24
)
| ((
long
) (digest[
2
+ nTime *
4
] &
0xFF
) <<
16
)
| ((
long
) (digest[
1
+ nTime *
4
] &
0xFF
) <<
8
)
| (digest[
0
+ nTime *
4
] &
0xFF
);
return
rv & 0xffffffffL;
/* Truncate to 32-bits */
}
/**
* 根据key的hash值取得服务器节点信息
*
* @param hash
* @return
*/
public
Object getNodeInfo(
long
hash) {
Long key = hash;
SortedMap
if
(tailMap.isEmpty()) {
key = allNodes.firstKey();
}
else
{
key = tailMap.firstKey();
}
return
allNodes.get(key);
}
public
static
void
main(String[] args) {
ConsistencyHash consistencyHash =
new
ConsistencyHash();
consistencyHash.init();
// 循环50次,是为了取500个数来测试效果,当然也可以用其他任何的数据来测试
int
_0 =
0
;
int
_1 =
0
;
int
_2 =
0
;
int
_3 =
0
;
int
_4 =
0
;
Random ran =
new
Random();
for
(
int
i =
0
; i <
500
; i++) {
// 随便取一个数的md5
byte
[] ranNum = consistencyHash.computeMd5(String.valueOf(i));
// 分配到随即的hash环上面
long
key = consistencyHash.hash(ranNum,
2
);
// long key = consistencyHash.hash(ranNum, ran.nextInt(consistencyHash.VIRTUAL_NODE_COUNT));
// 获取他所属服务器的信息
// System.out.println(consistencyHash.getNodeInfo(key));
if
(consistencyHash.getNodeInfo(key).equals(
"192.168.0.0-服务器0"
)){
_0++;
}
else
if
(consistencyHash.getNodeInfo(key).equals(
"192.168.0.1-服务器1"
)){
_1++;
}
else
if
(consistencyHash.getNodeInfo(key).equals(
"192.168.0.2-服务器2"
)){
_2++;
}
else
if
(consistencyHash.getNodeInfo(key).equals(
"192.168.0.3-服务器3"
)){
_3++;
}
else
if
(consistencyHash.getNodeInfo(key).equals(
"192.168.0.4-服务器4"
)){
_4++;
}
else
{
System.out.println(
"error"
);
}
}
// 输出每台服务器负载情况
System.out.println(
"_0 = "
+ _0);
System.out.println(
"_1 = "
+ _1);
System.out.println(
"_2 = "
+ _2);
System.out.println(
"_3 = "
+ _3);
System.out.println(
"_4 = "
+ _4);
}
}
|