DNSProxyProvider高CPU占用问题排查求助(macOS网络扩展)
macOS网络扩展异常排查求助
我开发了一款兼容macOS 10.15及以上版本的网络扩展,功能是分析所有DNS流量并拦截可疑域名。多数设备运行正常,但4台设备(共600台)出现以下异常:
- 随机出现CPU占用飙升,仅能通过重启设备或扩展恢复;
- Console中短时间内生成大量重复网络日志(15秒约50万条);
- 已排查涉事设备与正常设备无网络配置差异;
- 扩展无法判定域名是否拦截时,会通过URLSession请求后端验证,该逻辑在其他设备正常。
异常日志内容
[C659 IPv6#3738d855.53 udp, attribution: developer] restart [C659 IPv6#3738d855.53 waiting parent-flow (unsatisfied (No network route), dns)] event: path:restart @14383.841s, uuid: C7F27BD5-E86F-4076-A03E-1BD6A9C4C405 [C659 IPv6#3738d855.53 waiting parent-flow (unsatisfied (No network route), dns)] event: path:unsatisfied @14383.841s, uuid: C7F27BD5-E86F-4076-A03E-1BD6A9C4C405 [C659 IPv6#3738d855.53 in_progress parent-flow (unsatisfied (No network route), dns)] event: flow:start_child @14383.841s nw_connection_report_state_with_handler_on_nw_queue [C659] reporting state preparing [C659.157 IPv6#3738d855.53 initial path ((null))] event: path:start @14383.841s [C659.157 IPv6#3738d855.53 waiting path (unsatisfied (No network route), dns)] event: path:unsatisfied @14383.842s, uuid: C7F27BD5-E86F-4076-A03E-1BD6A9C4C405 [C659.157 IPv6#3738d855.53 failed path (unsatisfied (No network route), dns)] event: null:null @14383.842s [C659 IPv6#3738d855.53 waiting parent-flow (unsatisfied (No network route), dns)] event: flow:child_failed @14383.842s nw_connection_report_state_with_handler_on_nw_queue [C659] reporting state waiting [C659 IPv6#3738d855.53 waiting parent-flow (unsatisfied (No network route), dns)] event: path:restart @14383.842s, uuid: C7F27BD5-E86F-4076-A03E-1BD6A9C4C405 [C659 IPv6#3738d855.53 udp, attribution: developer] restart
求助内容
- 分析上述日志的含义;
- 排查高CPU占用及重复日志的原因;
- 提供添加重试次数计数器的方案。
相关背景
UDP处理逻辑涉及自定义连接重试:扩展处理DNS请求时,若无法本地判定,会发起UDP连接请求后端验证,连接失败后触发重启逻辑。
日志含义分析
[C659 IPv6#3738d855.53 udp, attribution: developer] restart:由开发者代码触发的UDP连接(C659为连接ID)重启操作path:unsatisfied (No network route), dns:系统判定当前IPv6网络路径不可用,无有效路由,且问题触发点与DNS解析相关flow:start_child:主连接尝试创建子连接重试请求flow:child_failed:子连接尝试失败,返回路径不可用错误- 整体日志循环显示:连接因IPv6路由问题失败→触发重启→再次失败→再次重启,形成无限循环
高CPU与重复日志原因排查
- 无限重试循环:扩展的UDP连接重试逻辑未设置终止条件,当设备出现IPv6路由隐性异常(如运营商IPv6配置不稳定、本地安全软件拦截IPv6流量),连接会不断失败并重启,短时间内生成大量日志,同时CPU因频繁的连接创建/销毁、日志写入操作飙升
- 设备特定网络环境:600台仅4台出现问题,大概率是这些设备所处网络环境存在IPv6路由异常,但表面配置无差异
- URLSession重试叠加:后端验证的URLSession请求若也依赖网络路径,IPv6异常时可能触发其重试逻辑,进一步加剧资源占用
重试次数计数器实现方案
方案1:基于连接实例的计数器
在UDP连接管理类中添加重试计数属性,达到阈值后终止重试并执行降级逻辑:
class UDPConnectionManager { private var retryCount = 0 private let maxRetryCount = 5 // 可根据业务需求调整 func handleConnectionFailure() { retryCount += 1 guard retryCount <= maxRetryCount else { // 重试超限,执行降级:切换IPv4、返回默认处理结果、记录错误 NSLog("UDP连接重试次数超限,切换至IPv4连接") switchToIPv4Connection() return } // 继续重试连接 restartConnection() } func resetRetryCount() { retryCount = 0 } }
方案2:全局+时间窗口的频率限制器
针对同一域名的请求,在时间窗口内限制重试次数,避免单域名触发无限循环:
import Foundation class RetryRateLimiter { private var retryRecords = [String: (count: Int, timestamp: TimeInterval)]() private let maxRetryPerMinute = 10 // 每分钟最大重试次数 private let queue = DispatchQueue(label: "com.example.retryLimiter") func canRetry(for domain: String) -> Bool { return queue.sync { let now = Date().timeIntervalSince1970 if let record = retryRecords[domain] { if now - record.timestamp > 60 { // 超过1分钟,重置计数 retryRecords[domain] = (count: 1, timestamp: now) return true } else { return record.count < maxRetryPerMinute ? { retryRecords[domain] = (count: record.count + 1, timestamp: now) return true }() : false } } else { retryRecords[domain] = (count: 1, timestamp: now) return true } } } } // 使用示例 let limiter = RetryRateLimiter() func handleDNSRequest(domain: String) { guard limiter.canRetry(for: domain) else { // 超过频率限制,执行降级逻辑 return } // 发起UDP请求 }
额外优化建议
- 连接失败时优先尝试IPv4连接,避免因IPv6异常导致的循环
- 添加连接超时时间,避免长时间等待无响应的连接
- 限制Console日志输出频率,比如同一错误1分钟内仅输出1次,减少日志对CPU的消耗
内容的提问来源于stack exchange,提问作者C-Viorel
相关产品推荐
相关产品推荐

