gpt4 book ai didi

python - 使用 Python 和 Twisted 的 DNS 服务器 - 异步操作的问题

转载 作者:太空宇宙 更新时间:2023-11-03 11:31:35 24 4
gpt4 key购买 nike

我正在尝试使用 Python 2.7 在 Twisted 中创建一个数据库驱动的 DNS 服务器(专门用于仅处理 MX 记录,并向上游传递所有其他内容)。下面的代码有效(就获取结果而言),但不是异步操作。相反,任何传入的 DNS 请求都会阻止整个程序接受任何其他请求,直到第一个请求得到答复。我们需要它来扩展,目前我们无法弄清楚哪里出了问题。如果有人有一个工作示例可以分享,或者看到了这个问题,我们将永远感激不已。

import settings
import db

from twisted.names import dns, server, client, cache
from twisted.application import service, internet
from twisted.internet import defer


class DNSResolver(client.Resolver):
def __init__(self, servers):
client.Resolver.__init__(self, servers=servers)

@defer.inlineCallbacks
def _lookup_mx_records(self, hostname, timeout):

# Check the DB to see if we handle this domain.
mx_results = yield db.get_domain_mx_record_list(hostname)
if mx_results:
defer.returnValue(
[([dns.RRHeader(hostname, dns.MX, dns.IN, settings.DNS_TTL,
dns.Record_MX(priority, forward, settings.DNS_TTL))
for forward, priority in mx_results]),
(), ()])

# If the hostname isn't in the DB, we forward
# to our upstream DNS provider (8.8.8.8).
else:
i = yield self._lookup(hostname, dns.IN, dns.MX, timeout)
defer.returnValue(i)

def lookupMailExchange(self, name, timeout=None):
"""
The twisted function which is called when an MX record lookup is requested.
:param name: The domain name being queried for (e.g. example.org).
:param timeout: Time in seconds to wait for the query response. (optional, default: None)
:return: A DNS response for the record query.
"""

return self._lookup_mx_records(name, timeout)


# App name, UID, GID to run as. (root/root for port 53 bind)
application = service.Application('db_driven_dns', 1, 1)

# Set the secondary resolver
db_dns_resolver = DNSResolver(settings.DNS_NAMESERVERS)

# Create the protocol handlers
f = server.DNSServerFactory(caches=[cache.CacheResolver()], clients=[db_dns_resolver])
p = dns.DNSDatagramProtocol(f)
f.noisy = p.noisy = False

# Register as a tcp and udp service
ret = service.MultiService()
PORT=53

for (klass, arg) in [(internet.TCPServer, f), (internet.UDPServer, p)]:
s = klass(PORT, arg)
s.setServiceParent(ret)

# Run all of the above as a twistd application
ret.setServiceParent(service.IServiceCollection(application))

编辑#1

blakev 建议我可能没有正确使用生成器(这当然是可能的)。但是,如果我稍微简化一下,甚至不使用数据库,我仍然无法一次处理多个 DNS 请求。为了测试这一点,我已经剥离了类(class)。接下来是我的整个可运行测试文件。即使在我的服务器的这个高度精简的版本中,Twisted 也不会接受更多的请求,直到第一个请求进入。

import sys
import logging

from twisted.names import dns, server, client, cache
from twisted.application import service, internet
from twisted.internet import defer


class DNSResolver(client.Resolver):
def __init__(self, servers):
client.Resolver.__init__(self, servers=servers)

def lookupMailExchange(self, name, timeout=None):
"""
The twisted function which is called when an MX record lookup is requested.
:param name: The domain name being queried for (e.g. example.org).
:param timeout: Time in seconds to wait for the query response. (optional, default: None)
:return: A DNS response for the record query.
"""
logging.critical("Query for " + name)

return defer.succeed([
(dns.RRHeader(name, dns.MX, dns.IN, 600,
dns.Record_MX(1, "10.0.0.9", 600)),), (), ()
])

# App name, UID, GID to run as. (root/root for port 53 bind)
application = service.Application('db_driven_dns', 1, 1)

# Set the secondary resolver
db_dns_resolver = DNSResolver( [("8.8.8.8", 53), ("8.8.4.4", 53)] )

# Create the protocol handlers
f = server.DNSServerFactory(caches=[cache.CacheResolver()], clients=[db_dns_resolver])
p = dns.DNSDatagramProtocol(f)
f.noisy = p.noisy = False

# Register as a tcp and udp service
ret = service.MultiService()
PORT=53

for (klass, arg) in [(internet.TCPServer, f), (internet.UDPServer, p)]:
s = klass(PORT, arg)
s.setServiceParent(ret)

# Run all of the above as a twistd application
ret.setServiceParent(service.IServiceCollection(application))


# If called directly, instruct the user to run it through twistd
if __name__ == '__main__':
print "Usage: sudo twistd -y %s (background) OR sudo twistd -noy %s (foreground)" % (sys.argv[0], sys.argv[0])

最佳答案

马特,

我试过你最新的例子,效果很好。我认为您可能测试错了。

在您后来的评论中,您谈到了在查找方法中使用 time.sleep(5) 来模拟缓慢的响应。

你不能那样做。它会阻塞 react 堆。如果你想模拟延迟,使用 reactor.callLater 来触发延迟

例如

def lookupMailExchange(self, name, timeout=None):
d = defer.Deferred()
self._reactor.callLater(
5, d.callback,
[(dns.RRHeader(name, dns.MX, dns.IN, 600,
dns.Record_MX(1, "mail.example.com", 600)),), (), ()]
)
return d

这是我的测试方式:

time bash -c 'for n in "google.com" "yahoo.com"; do dig -p 10053 @127.0.0.1 "$n" MX +short +tries=1 +notcp +time=10 & done; wait'

并且输出显示两个响应都在 5 秒后返回

1 10.0.0.9.
1 10.0.0.9.

real 0m5.019s
user 0m0.015s
sys 0m0.013s

同样,您需要确保对数据库的调用不会阻塞:

其他几点:

关于python - 使用 Python 和 Twisted 的 DNS 服务器 - 异步操作的问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18577197/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com