【问题标题】:Using SRV DNS records with the python requests library将 SRV DNS 记录与 python 请求库一起使用
【发布时间】:2025-12-16 22:25:02
【问题描述】:

是否可以让 Python 请求库使用 SRV 记录解析领事域名,并在发出请求时使用正确的 IP 地址和端口?

例如,假设我在端口 8080 上使用 IP 地址 172.18.0.5 运行 serviceA,并且该服务已向 consul 注册。并且鉴于主机的 DNS 设置为使用 consul 来解析查询。我可以提出如下要求:

requests.get('http://serviceA.service.consul')

并让它等同于请求:

requests.get('http://172.18.0.5:8080')

【问题讨论】:

    标签: python dns python-requests consul


    【解决方案1】:

    我最终为使用this answer 的请求编写了一个补丁。由于请求库的更新,我不得不进行一些更改。此补丁适用于请求版本 2.11.1。

    我使用 dnspython 库来解析 SRV 记录,它希望 consul 正在侦听 DNS 请求的 IP 地址和端口可用作环境变量 CONSUL_DNS_IP_PORT。要使用补丁,请从补丁所在的任何模块导入requests_use_srv_records 函数,然后调用它。它只会尝试对以.service.consul结尾的主机使用consul SRV记录,其他主机会定期解析。

    这是补丁:

    # Python Imports
    import os
    from socket import error as SocketError, timeout as SocketTimeout
    
    # 3rd Party Imports
    from dns import resolver
    from requests.packages.urllib3.connection import HTTPConnection
    from requests.packages.urllib3.exceptions import (NewConnectionError,
                                                      ConnectTimeoutError)
    from requests.packages.urllib3.util import connection
    
    def resolve_srv_record(host):
    
        consul_dns_ip_port = os.environ.get('CONSUL_DNS_IP_PORT', 
                                            '172.17.0.1:53')
        consul_dns_ip, consul_dns_port = consul_dns_ip_port.split(':')
    
        res = resolver.Resolver()
        res.port = consul_dns_port
        res.nameservers = [consul_dns_ip]
    
        ans = resolver.query(host, 'SRV')
    
        return ans.response.additional[0].items[0].address, ans[0].port
    
    def patched_new_conn(self):
    
        if self.host.endswith('.service.consul'):
            hostname, port = resolve_srv_record(self.host)
        else:
            hostname = self.host
            port = self.port
    
        extra_kw = {}
    
        if self.source_address:
            extra_kw['source_address'] = self.source_address
    
        if self.socket_options:
            extra_kw['socket_options'] = self.socket_options
    
        try:
            conn = connection.create_connection((hostname, port),
                                                self.timeout,
                                                **extra_kw)
    
        except SocketTimeout as e:
            raise ConnectTimeoutError(
                self, "Connection to %s timed out. (connect timeout=%s)" %
                (self.host, self.timeout))
    
        except SocketError as e:
            raise NewConnectionError(
                self, "Failed to establish a new connection: %s" % e)
    
        return conn
    
    def requests_use_srv_records():
        HTTPConnection._new_conn = patched_new_conn
    

    【讨论】:

      【解决方案2】:

      不,你不能,除非你重写requests

      SRV 记录旨在查找服务。
      在这种情况下,您已经指示使用 http。所以客户端只会查询serviceA.service.consul的A或AAAA记录。

      【讨论】: