DNS 解析 — Resolver、NormalResolver、AresResolver

第 16 课:DNS 解析 — Resolver、NormalResolver、AresResolver 对应源文件: trantor/net/Resolver.h — 异步 DNS 解析抽象接口 trantor/net/inner/NormalResolver.h / NormalResolver.cc — 基于 getaddrinfo 的线程池实现 trantor/net/inner/AresResolver.h — 基于 c-ares 的真异步实现 一、为什么 DNS 解析需要异步? getaddrinfo() 是系统标准 DNS 解析函数,但它是阻塞的——在 DNS 服务器响应之前,调用线程会一直挂起。 在 EventLoop 单线程模型中,如果直接调用 getaddrinfo(): EventLoop 线程被阻塞 该线程上所有其他连接的 I/O 事件、定时器全部停止响应 哪怕只是 100ms 的 DNS 查询,对游戏服务器来说都是灾难性的抖动 trantor 提供两种解决方案: 1 2 3 4 5 6 7 8 9 10 11 12 DNS 解析请求 │ ├── NormalResolver(默认) │ → 投递到 ConcurrentTaskQueue(阻塞线程池) │ → getaddrinfo() 在工作线程里阻塞 │ → 完成后回调(在工作线程里直接调用) │ └── AresResolver(c-ares 可用时) → 在 EventLoop 线程里全程非阻塞 → c-ares 管理 DNS socket,注册到 epoll → EventLoop 处理 DNS socket 的读写事件 → 完成后在 EventLoop 线程里调用回调 二、Resolver — 统一抽象接口 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 class Resolver { public: using Callback = std::function<void(const trantor::InetAddress&)>; using ResolverResultsCallback = std::function<void(const std::vector<trantor::InetAddress>&)>; // 工厂函数:根据编译配置选择实现 static std::shared_ptr<Resolver> newResolver( EventLoop *loop = nullptr, size_t timeout = 60); // timeout:DNS 缓存有效期(秒) // 解析单个地址 virtual void resolve(const std::string& hostname, const Callback& callback) = 0; // 解析所有地址(A/AAAA 记录,可能有多个 IP) virtual void resolve(const std::string& hostname, const ResolverResultsCallback& callback) = 0; static bool isCAresUsed(); // 当前是否在用 c-ares }; 两个回调的区别: ...

April 5, 2025 · 10 min · 1921 words