我刚刚解决了基础架构中的延迟问题,因为该代码段在每次运行代码时触发了对getaddrinfo
的调用:
sock = UDPSocket.open
sock.send("#{key}|#{value}", 0,
GRAPHITE_SERVER,
STATSD_PORT)
sock.close
由于我们使用statsd and graphite进行大容量事件和统计监视,因此我们有效地触发了每个API调用上的大量调用getaddrinfo
,并且每分钟可能触发数万次。
我修改了此代码,以使用我们的石墨服务器的内部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;
}
我认为此决定是有意为之的,很想了解更多原因。
答案 0 :(得分:1)
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来检索数据。
某些特定的DNS API,例如getdnsapi
会向调用者返回有关TTL的一些信息,请参见https://getdnsapi.net/documentation/spec/和示例6.2
6·2获取域名的IPv4和IPv6地址
此示例与上一个示例类似,不同之处在于它不仅检索地址之外的信息,因此遍历replies_tree。在这种情况下,它会同时获取地址及其TTL。
由于UDP是无状态的,因此任何地方都没有任何缓存层,因此任何新的send
必须以某种方式或形式触发解析。
您说:
“修改此代码以使用内部IP地址,而不是DNS名称”
您应该改为安装本地(在包装盒上)递归缓存名称服务器,例如unbound
。您所有的本地应用程序都将从中受益,并且DNS解析速度更快(取决于/etc/nsswitch.conf
,/etc/resolv.conf
和/etc/hosts
的设置方式)。
对于@Casper提示的相关错误报告,其核心似乎更多是有关IPv6与IPv4的问题,可以通过调整/etc/gai.conf
或等效方法或在打开连接时进行一些更巧妙的编程来解决,所谓的“快乐眼球算法”,您尝试同时解析A
和AAAA
,这意味着两个并行DNS查询(因为您不能根据协议将它们组合为一个),然后尝试使用最快的回来,如果您想进入现代阵营,则稍微偏爱AAAA
,这样您就可以在{{1}之后的毫秒内将A
发射一次},以解决您根本没有收到AAAA
或否定答案的情况。有关详细信息,请参见RFC6555。