zoukankan      html  css  js  c++  java
  • 重新实现reuseport逻辑,实现一致性哈希

    有部分应用场景采用的仍然是无连接协议,例如 DNS、StatsD 等,都是采用的 UDP 。

    UDP 不是面向连接的,所以不能像 TCP 通过建立多个连接来提高对服务器的并发访问,如果通过多线程共享一个 UDP Socket 可能会无法充分利用所有的 CPU 资源。

    这里简单介绍其优化方法,当然,这里的策略也适用与像 ICMP 这样的协议。

    Reuse Port

    Google 为了解决 DNS 服务器性能问题,就给 Linux 内核打了一个 SO_REUSEPORT 的 Patch,用来优化 UDP 服务器在多核机器上的性能。

    REUSEPORT 允许多线程 (或多进程) 服务器的每个线程都可以监听同一个端口,并且每个线程都拥有一个独立的 Socket,而不是所有线程都共享同一个 Socket。

    如果没有设置该选项,那么在尝试绑定同一个端口时会返回报错。

    分发策略

    使用了 ReusePort 策略之后,因为存在了多个 Socket ,那么服务端收到客户端发送的报文后,会按照四元组 <ClientIP ClientPort ServerIP ServerPort> 的 Hash 值进行包的分发,目的有如下几个

    • 保证同一个客户度过来的包会发送到同一个 Socket 上;
    • 当客户端量足够多时,基本可以将请求均衡到所有的 Socket 上。

    这也就意味着在压测时,需要使用尽量多的客户端,以保证压力的均衡。

    Recvmmsg

    这是一个批量的系统 API 接口,可以从 Socket 中一次读取多个 UDP 数据包,而像 recvmsg()recvfrom() 一次只能读取一个报文。

    注意,通过该接口读取的多个报文不一定是属于同一台机器的,大部分是属于不同的机器。

     server

    root@ubuntu:~/c++# cat udpserv.c 
    #include <stdlib.h>
    #include <stdio.h>
    #include <unistd.h>
    #include <string.h>
    #include <sys/socket.h>
    #include <netinet/in.h>
    
    #define PORT     9080
    #define MAXLINE 1024
    
    int main(int argc, char **argv)
    {
            int sockfd;
            char buffer[MAXLINE];
            struct sockaddr_in server, client;
            int optval = 1;
            int len;
            int ret;
    
            memset(&server, 0, sizeof(server));
            memset(&client, 0, sizeof(client));
    
            if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
                    perror("socket");
                    return -1;
            }
            if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEPORT, &optval, sizeof(optval)) < 0) {
                    perror("bind");
                    return -1;
            }
    
            server.sin_family = AF_INET;
            server.sin_port = htons(PORT);
            server.sin_addr.s_addr = INADDR_ANY;
    
            if (bind(sockfd, (const struct sockaddr *)&server, sizeof(server)) < 0) {
                    perror("bind");
                    return -1;;
            }
    
            while (1) {
                    ret = recvfrom(sockfd, (char *)buffer, MAXLINE,
                                    MSG_WAITALL, ( struct sockaddr *) &client, &len);
    
                    char ipaddr[20];
                    inet_ntop(AF_INET, &client.sin_addr, ipaddr, sizeof(struct sockaddr));
                    printf("from IP: %s, port: %d and ", ipaddr, ntohs(client.sin_port));
                    buffer[ret] = '';
                    printf("recv :%s
    ", buffer);
            }
    
            return 0;
    }

     udp client

    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <string.h>
    #include <sys/socket.h>
    #include <netinet/in.h>
    
    #define PORT     9080
    #define MAXLINE 1024
    
    int main(int argc, char **argv)
    {
            int sockfd;
            char *ser = argv[1];
            char *buff = argv[2];
            struct sockaddr_in       server;
            int ret;
            int  len;
    
            if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
                    perror("socket");
                    return -1;
            }
    
            memset(&server, 0, sizeof(server));
    
            server.sin_family = AF_INET;
            server.sin_port = htons(PORT);
            server.sin_addr.s_addr = inet_addr(ser);;
    
            while (1) {
                    sendto(sockfd, (const char *)buff, strlen(buff),
                            MSG_CONFIRM, (const struct sockaddr *) &server, sizeof(server));
                    printf("%s
    ", buff);
    
                    sleep(1);
            }
    
            close(sockfd);
            return 0;
    }
     

     

     测试

     sever1

      sever2

     server3

    重启server1

    demo2 关掉server1后

    demo3 重启server1 

    server1

     重启server后原来在server2和server3的conn发生了迁移

    demo4 关闭client1<s1,s2,s3工作正常>

    重启client1 跑在了server2上而不是像以前一样跑在server1

    demo5 停掉client2

    重启client2

    再次重启83

     同一个client2的源端口改变了

    重新实现reuseport逻辑,实现一致性哈希

    关于Linux UDP/TCP reuseport 二三事: https://blog.csdn.net/dog250/article/details/80458669

  • 相关阅读:
    第二章 Java程序设计环境
    第一章 Java程序设计概述
    (五)Java工程化--Jenkins
    (二)Java工程化--Maven实践
    (四)Java工程化--Git基础
    (三)Java工程化--Git起步
    (一)Java工程化--Maven基础
    codeblocks 中文编码问题
    win10安装virtualbox发生严重错误
    利用ssh传输文件
  • 原文地址:https://www.cnblogs.com/dream397/p/14784238.html
Copyright © 2011-2022 走看看