代码之家  ›  专栏  ›  技术社区  ›  esilver rmaddy

为什么UDPSocket.send总是在Ruby中调用getaddrinfo?

  •  3
  • esilver rmaddy  · 技术社区  · 6 年前

    getaddrinfo 每次运行代码时:

    sock = UDPSocket.open
    sock.send("#{key}|#{value}", 0,
              GRAPHITE_SERVER,
              STATSD_PORT)
    sock.close
    

    statsd and graphite 对于大容量的事件和统计信息监视,我们有效地触发了许多调用 每一次API调用,每分钟可能有数万次。

    我修改了这段代码以使用graphite服务器的内部IP地址,而不是DNS名称,并能够解决延迟问题(可能是因为内部AWS VPC DNS服务器无法处理如此大的请求量)。

    现在我的问题已经解决了,我想知道为什么Ruby中的UDP实现没有使用缓存的IP地址值(可能基于域名条目的TTL)。 Here is the relevant line 在函数中,您可以看到 rsock_addrinfo

    static VALUE
    udp_send(int argc, VALUE *argv, VALUE sock)
    {
        VALUE flags, host, port;
        struct udp_send_arg arg;
        VALUE ret;
    
        if (argc == 2 || argc == 3) {
        return rsock_bsock_send(argc, argv, sock);
        }
        rb_scan_args(argc, argv, "4", &arg.sarg.mesg, &flags, &host, &port);
    
        StringValue(arg.sarg.mesg);
        GetOpenFile(sock, arg.fptr);
        arg.sarg.fd = arg.fptr->fd;
        arg.sarg.flags = NUM2INT(flags);
        arg.res = rsock_addrinfo(host, port, rsock_fd_family(arg.fptr->fd), SOCK_DGRAM, 0);
        ret = rb_ensure(udp_send_internal, (VALUE)&arg,
                rsock_freeaddrinfo, (VALUE)arg.res);
        if (!ret) rsock_sys_fail_host_port("sendto(2)", host, port);
        return ret;
    }
    

    我认为这个决定是有意的,我想了解更多的原因。

    1 回复  |  直到 6 年前
        1
  •  1
  •   Patrick Mevzek James Dean    6 年前

    getaddrinfo 不返回关于TTL的数据。。。因为它可能根本没有它,因为解析不一定是通过DNS完成的(可以是 hosts 文件、LDAP等。请参阅 /etc/nsswitch.conf

    从其手册中可以看到返回的结构:

    int getaddrinfo(const char *hostname, const char *servname, const struct addrinfo *hints, struct addrinfo **res);
    
     struct addrinfo {
             int ai_flags;           /* input flags */
             int ai_family;          /* protocol family for socket */
             int ai_socktype;        /* socket type */
             int ai_protocol;        /* protocol for socket */
             socklen_t ai_addrlen;   /* length of socket-address */
             struct sockaddr *ai_addr; /* socket-address for socket */
             char *ai_canonname;     /* canonical name for service location */
             struct addrinfo *ai_next; /* pointer to next in list */
     };
    

    在成功调用getaddrinfo()之后,*res是指向一个或多个addrinfo结构的链接列表的指针。

    getaddrinfo 是否进行缓存,因为 getaddrinfo

    一些特定的DNS API,比如 getdnsapi 会将TTL的一些信息反馈给呼叫者,请参见 https://getdnsapi.net/documentation/spec/ 例6.2

    此示例与上一个示例类似,只是它检索的信息比仅检索地址的信息多,因此它会遍历应答树。在本例中,它同时获取地址和它们的ttl。

    在任何地方都没有任何缓存层,因为UDP是无状态的,所以 send 必须以某种方式或形式触发解决方案。

    你说:

    修改此代码以使用内部IP地址,而不是DNS名称

    unbound . 您的所有本地应用程序都将从中受益,以及更快的DNS解析(取决于 /etc/nsswitch.conf格式 , /etc/resolv.conf /etc/hosts

    对于@Casper暗示的相关bug报告,其核心似乎更像是IPv6与IPv4之间的问题,可以通过调整 /etc/gai.conf 或者做一些更聪明的编程来打开连接,用所谓的“快乐眼球算法”来解决这两个问题 A AAAA 同时,这意味着两个并行的DNS查询(因为您不能在每个协议中将它们组合成一个查询),并尝试使用返回最快的查询,稍微倾向于 如果你想在现代化的营地里这样你就可以 一个 抓住一个你根本得不到答复的案子 AAAA级 RFC6555 详细情况。