2.SO_REUSEPORT的不同之处linux 3.9内核支持了BSD系统早已有之的SO_REUSEPORT这个socket选项 , 有了它就可以支持UDP的负载均衡啦 , 哈哈!在给出具体做法和实例之前 , 我们先看一下它是如何做到的 , 和之前的版本一样 , 在查找目标socket的时候 , compute_score的逻辑几乎没有什么变化 , 不同的是 , 在compute_score之后 , 数据包的源IP和源端口也参与了运算 , 而不再像老版本那样只是严格按照目标IP地址和目标端口那样作为查找键参与运算 , 这样即使按照目标IP和目标端口已经查找到了一个匹配的socket , 也会按照其源IP和源端口继续下去 , 以便在多个匹配的socket中选择一个 , 算法是固定的 , 因此可以保证:
a.相同4元组的数据包总是匹配到相同的一个socket;
b.不同4元组的数据包可能会被hash到不同的socket 。
以上的没有什么神奇之处 , 其实现和之前的非负载均衡版本唯一不同的是就是在查找目标socket的时候让源IP地址和源端口也参与了进来 , 而这并不破坏4元组的唯一性 , 所谓的唯一性 , 其目的只是要保证同一数据发给特定的目标socket , 保证4元组唯一只是实现这一目标的手段而已 , 目的和手段是万万不能混淆的 , 意识到这一点之后 , 绑定相同端口相同IP地址的socket同时接收数据就是理所当然的了 , 只要保证来自同一IP地址和同一端口的数据总是发送给同一socket即可 , 而算法本身保证了这一点 。
socket查找算法是重要的 , 查找算法没有使用任何随机因素比如随机数之类的 , 而是使用固定的计算选择一个目标socket , 这足以保证只要是固定的源IP地址和源端口 , 算出的目标socket位置就是固定的 , 核心代码如下:
sk_nulls_for_each_rcu(sk, node, &hslot->head) {score = compute_score(sk, net, saddr, hnum, sport,daddr, dport, dif);if (score > badness) {result = sk;badness = score;reuseport = sk->sk_reuseport;if (reuseport) {hash = inet_ehashfn(net, daddr, hnum,saddr, htons(sport));matches = 1;}} else if (score == badness && reuseport) {matches++;if (((u64)hash * matches) >> 32 == 0)result = sk;hash = next_pseudo_random32(hash);}}
SO_REUSEPORT是一个其它UNIX平台古已有之的选项 , Linux终于也支持了 , 本文没有讨论其对TCP的影响 , 因为其他人讨论的已经很多了 , 毕竟TCP的应用要多得多 , TCP的进程绑定机制以及其本身的连接绑定机制也使得SO_REUSEPORT对TCP的影响很好理解 , Listen状态的多个绑定相同IP地址和相同端口的TCP socket很容易根据syn源IP和端口来为其绑定一个流并且记住它 。但是SO_REUSEPORT对于UDP的影响 , 我没有搜到什么太多的资料 , 加之我又特别需要UDP服务的负载均衡 , 因此我决定自己做一个 。
需要C/C++ Linux服务器架构师学习资料私信“资料”(资料包括C/C++ , Linux , golang技术 , Nginx , ZeroMQ , MySQL , redis , fastdfs , MongoDB , ZK , 流媒体 , CDN , P2P , K8S , Docker , TCP/IP , 协程 , DPDK , ffmpeg等) , 免费分享
文章插图
3.实现一个多进程的UDP服务3.1.只为OpenVPNOpenVPN , 一个充满矛盾的存在 , 它没有多处理 , 多多少少和UDP有关 , 它提倡用UDP , 多多少少和TCP有关(是一定有关系的 , 别跟我讲TCP的细节 , 什么按序 , 重传 , 慢启动 , 滑动窗口之类的 , 好好看看OpenVPN的man吧...) , 然而TCP的多处理 , 可谓汗牛充栋了 。
如今 , UDP也可以多处理了 , 好事儿!迫不及待想对它动个小手术 , 但是手术之前 , 还有几件事情要确认 。
3.2.多进程的UDP有了SO_REUSEPORT支持之后 , 第一个反应以及第一个动作 , 那就是试一下效果 。初始设计是极其简单的 , 那就是创建多个UDP socket , 绑定到相同的IP地址和相同的端口 , 然后poll , poll到谁有POLLIN事件了就recvfrom 。此时不断得使用不同的进程往该这些socket绑定的IP地址和端口发送数据 , 你会发现 , 和之前不支持SO_REUSEPORT的时候不一样了 , 数据会发送到不同的UDP socket上 , 并且可以保证只要源地址和源端口是同一个 , 那么发送到的socket也是同一个 , 如果换一下源端口 , 那么就可能发送到另外的socket了 。这个实验的代码及其简单 , 代码就不贴了 。这个实验证明了通过SO_REUSEPORT选项做UDP的负载均衡是可行的 。
推荐阅读
- 用 Python 开发一个 「聊天室」
- Linux两种处理模式reactor模式proactor模式
- 彩农茶友天下,彩农茶友天下
- 翡翠|单调的翡翠无事牌,为何会受到这么多人的喜爱?我帮你分析一下
- 右眼下眼皮痉挛
- 上眼皮过敏红肿痒
- OPPO|高通骁龙888旗舰芯下放!OPPO K10 Pro明天预售
- 浏览器到底哪个好用?浅谈各大浏览器使用心得
- Windows AD域下批量自动配置客户端有线网络认证功能
- 威武雄壮万贵妃万贞儿死后下 万贞儿万贵妃