Linux最新的内核已经支持了UDP的reuseport选项,这个机制可以很好地为UDP的负载均衡服务,如果不了解可以bing一下。它之所以可以做负载均衡,就是它通过一个固定的5元组来计算一个固定hash,然后基于这个固定hash将一个数据包分发到固定的socket,如果IP地址不发生变化,一切都会很好,但是IP地址在移动环境下会发生变化,这就意味着5元组信息发生了变化,那么重新计算的hash将也会发生变化(不发生变化那是碰撞了!),这就意味着这个变化了IP的客户端发出的下一个UDP数据包将可能被分发给别的socket,这在基于UDP的长连接服务中是不希望发生的。以下是__udp4_lib_lookup核心代码:
begin:
result = NULL;
badness = -1;
sk_nulls_for_each_rcu(sk, node, &hslot->head) {
// sessionID版本的hash计算,服务端不要鉴别sport/saddr为妙!
score = compute_score(sk, net, saddr, hnum, sport,
daddr, dport, dif);
if (score > badness) {
result = sk;
badness = score;
reuseport = sk->sk_reuseport;
if (reuseport) {
// 5元组流版本,根据4元组计算一个hash值
//hash = inet_ehashfn(net, daddr, hnum, saddr, htons(sport));
// sid流版本,基于sessionID计算hash。
// 问题是这个sid怎么传到这里...大修吧
hash = sid_based_hash(sid, );
matches = 1;
}
} else if (score == badness && reuseport) {
matches++;
// 是否由该sk替换上次匹配到的sk,就看hash值的影响了
if (((u64)hash * matches) >> 32 == 0) {
result = sk;
}
hash = hash * 1664525 + 1013904223;
}
}
/*
* if the nulls value we got at the end of this lookup is
* not the expected one, we must restart lookup.
* We probably met an item that was moved to another chain.
*/
if (get_nulls_value(node) != slot)
goto begin;