You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

求助内容

  1. 分析上述日志的含义;
  2. 排查高CPU占用及重复日志的原因;
  3. 提供添加重试次数计数器的方案。

相关背景

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与重复日志原因排查

  1. 无限重试循环:扩展的UDP连接重试逻辑未设置终止条件,当设备出现IPv6路由隐性异常(如运营商IPv6配置不稳定、本地安全软件拦截IPv6流量),连接会不断失败并重启,短时间内生成大量日志,同时CPU因频繁的连接创建/销毁、日志写入操作飙升
  2. 设备特定网络环境:600台仅4台出现问题,大概率是这些设备所处网络环境存在IPv6路由异常,但表面配置无差异
  3. 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.13 13:31:18