1

我在让 PHP 尊重启用 RoundRobin 的 DNS 条目时遇到问题。该条目(例如 domain.example.com)分配了三个可能的 IP 地址。RoundRobin 工作(用 ping、telnet、wget 等测试)。不幸的是,当使用 PHP SOAP 扩展,甚至是普通的 file_get_contents 时,它总是连接到 DNS 中指定的第一个 IP 地址。令人惊讶的是,gethostbyname 函数认为 RoundRobin 非常好。我在每台服务器中放置了一个输出 1 或 2 或 3 的文件,并在另一台服务器上执行了几次脚本:

var_dump(file_get_contents('http://domain.example.com/test.html'));
var_dump(gethostbyname('domain.example.com'));

第一行总是打印“1”(从第一个 IP 地址开始)。第二行随机输出三个可能的 IP 地址之一。

问题:有没有人有类似的问题?至少在发出 SOAP 请求时,如何强制 PHP 在 DNS 中尊重 RoundRobin?

编辑 没有 DNS 缓存,也没有代理。如前所述,ping、telnet、wget 等在放置测试脚本的同一台服务器上工作正常。

4

1 回答 1

1

我们最近遇到了与您类似的问题。我们发现file_get_contents()最终调用getaddrinfo(),它实现了一些来自RFC3484的排序算法。

来自php 源代码的片段

PHPAPI int php_network_getaddresses(const char *host, int socktype, struct sockaddr ***sal, zend_string **error_string)
{
    // skip...

    if ((n = getaddrinfo(host, NULL, &hints, &res))) {
        if (error_string) {
            *error_string = strpprintf(0, "php_network_getaddresses: getaddrinfo failed: %s", PHP_GAI_STRERROR(n));
            php_error_docref(NULL, E_WARNING, "%s",     ZSTR_VAL(*error_string));
        } else {
            php_error_docref(NULL, E_WARNING, "php_network_getaddresses: getaddrinfo failed: %s", PHP_GAI_STRERROR(n));
        }
    return 0;
    }

    //skip...
}

我们仍在寻找更好的方法来解决这个问题。

编辑:

RFC3484 的第 6 节规定了目标地址选择算法,第 9 条规则与 IPv4 相关。

规则 9:使用最长匹配前缀。

当 DA 和 DB 属于同一个地址族时(都是 IPv6 或都是 IPv4):如果 CommonPrefixLen(DA, Source(DA)) > CommonPrefixLen(DB, Source(DB)),则首选 DA。同样,如果 CommonPrefixLen(DA, Source(DA)) < CommonPrefixLen(DB, Source(DB)),则首选 DB。

假设我们有源地址192.168.1.100/24和四个候选目标地址192.168.1.33/24, 192.168.1.44/24, 192.168.2.55/24, 192.168.2.66/24

上述地址以二进制格式表示

S  192.168.1.100    11000000.10101000.00000001.01100100
-------------------------------------------------------
DA 192.168.1.33     11000000.10101000.00000001.00100001
DB 192.168.1.44     11000000.10101000.00000001.00101100
DC 192.168.2.55     11000000.10101000.00000010.00110111
DD 192.168.2.66     11000000.10101000.00000010.01000010

你可以看到

CommonPrefixLen(DA, S) == CommonPrefixLen(DB, S) == 25 >
CommonPrefixLen(DC, S) == CommonPrefixLen(DD, S) == 22

所以DAor DB,在原始 DNS 查询中最先出现的,将被选为glibc实现中的最终目的地。

于 2015-10-23T07:21:28.623 回答