过去的2025

今年的头疼比去年略微好一点,但是还是挺严重的,晚上的失眠也是依然非常严重,安眠药的效果已经很弱了,有时候用酒精麻痹下效果还好下,还好布洛芬止疼的效果目前还凑合,随意搜了下,这种状况还是挺多的,但是人的性格就是这样,基本是很难改变了,可以参考:https://www.zhihu.com/question/461572685/answer/3561814996、https://www.v2ex.com/t/1189014。另外发际线也在持续后退,M型越来越明显了。

M


有依然繁琐无用的流程问题令人十分百分千分厌烦,有依然存在的业务稳定性压力,本来是要提离职的,目前同意了部门给的一个选择,不再负责任何一项具体的业务,脱身出来只作为技术专家有问题时进行支持。后续看是否会有些好转吧,个人自身并没有太大的信心能放下,也只能说是常识一段时间看下。头疼和失眠问题是从24年中多了一堆乱七八糟的流程后几个月变得很严重的,在此之前2-3年只有业务稳定压力的时候头疼和失眠本身也会有,只是很偶发,没那么严重,后续个人如果能放下业务稳定性压力,也许会好转,当然前提时能放下。

此图像的alt属性为空;文件名为image.png
虽然不那么准确,但是可以看到趋势,这是在安眠药和酒精麻醉下才达到的,否则会差的更多

AI焦虑问题,实话实说,AI在25年进步还是很大的,从最基本的问答机器人进化到能做一些稍复杂的系列任务的智能体,比较简单或比较独立的代码和流程问题都处理的效果也很不错了,当然比较复杂的大型代码目前还是差强人意的程度也没有达到(比如claude-code-opus-4.5/4.6等效果依然不行),还是需要人工或其他方式不停的拆解任务或修改提示,才能达到比较好的效果。不过按照这两年的进化趋势,说不定26年就都能做得比较好了。


目前AI的使用上,除了一些华而不实的玩具外,主要还是先来搭个框架,做到了6、70分,人工再补充完善到7、80分还是可以的,还是能节省具体的写代码或搭框架的很多时间,只是将之前大部分边写边完善的步骤变换了下,变成了先写好比较完善的流程提示词,再和AI交互逐步完善,虽然AI的进一步完善很多时候的效果就不行了,最后这20%会很依赖人工来完善,如果继续依赖AI完善,浪费的时间和token实在是得不偿失。


总体来说目前AI极大缩小了初级牛马和中级牛马的差距,高级牛马的地位也岌岌可危。但是那个但是呢,作为牛马并不会变得更轻松,AI跑任务的时候,牛马就在并行干其他事情了,总体好像产出会变高,但是对个人的压力不会有任何的减轻,反而会更重了。

F-Stack ff_rss_check()优化介绍

1. 概述

本文档旨在介绍 F-Stack 中对 ff_rss_check() 函数的一项重要优化。该优化通过引入一个预计算的静态端口查找表,显著提升了应用程序作为客户端主动发起大量短连接时的性能,解决了原 ff_rss_check() 函数在高并发场景下可能成为性能瓶颈的问题。

核心优化提交:e54aa4317b5d81f9f8643e491d8ec0ec1e72282a

2. 背景与问题分析

ff_rss_check() 函数在 F-Stack 中负责一个关键任务:当应用程序作为客户端发起新的 TCP 连接时,为其分配合适的本地源端口。

  • 原有实现机制: 每次需要建立新连接时,都会动态调用 ff_rss_check()。该函数会根据目标服务的 IP 和端口(4元组信息),通过一个哈希计算来选择一个 RSS(Receive Side Scaling)友好的本地端口,以确保数据包能被高效地分发到正确的 CPU 核心上处理。
  • 性能瓶颈: 在需要频繁创建短连接(例如,处理 HTTP 请求且未开启 keep-alive)的场景下,每次连接建立都需要执行一次或多次端口选择和冲突检查。这个过程涉及多次的toeplitz_hash计算(平均每次耗时约300+tsc,平均次数至少为该端口的总队列(进程)数),在高并发压力下,会消耗可观的 CPU 资源,并成为限制连接建立速率的瓶颈。

3. 优化方案:静态 RSS 端口表

为了克服上述性能问题,优化方案的核心思想是:变“动态计算”为“静态查找”

  1. 预初始化静态表
    • 在应用程序启动阶段(ff_init() 时),根据配置文件中的预定义规则,预先计算并初始化一个静态的端口查找表(ff_rss_tbl),。
    • 该表包含了预先调用ff_rss_check()计算好的哪些本地端口发出去的包可以返回本进程对应的网卡队列,表中包含 {{远程地址,远程端口}:{本地地址, [所有可用本地端口]}}组合,以及一些辅助数据结构,如可用端口的起始、结束索引,上次选择的到的端口索引等。
  2. 高效的端口选择
    • 当需要建立新连接时,首先尝试从这个预先生成的静态表中进行查找。
    • 如果能找到一个与当前连接目标地址/端口匹配且未被占用的本地端口,则直接使用它。这个过程几乎是无锁且开销极低的(平均耗时约100-250tsc)
    • 最重要的是,静态查找表无需多次选择并计算RSS,只需要查找一次本地端口即可保证远程回包可以回到本进程的队列进行处理,极大提升了选择源端口的效率,进而提升总体的QPS性能。
  3. 优雅降级
    • 如果静态表中所有符合条件的端口都已被占用或该四元组未配置静态查找表,系统会自动降级到原有的 ff_rss_check() 动态计算流程,确保功能的正确性。

4. 配置说明

此功能需要通过配置文件(例如 config.ini)手动开启和定义。相关配置段如下:

# 启用或禁用静态 ff_rss_check 表。
# 若启用,F-Stack 将在 APP 启动时初始化该表。
# 此后当 APP 作为客户端连接服务器时,会首先尝试从该表中选择本地端口。
[ff_rss_check_tbl]
# 启用开关:0-禁用,1-启用。默认为 0。
enable = 1
# 定义端口分配规则(4元组)。
# 格式:<网卡端口ID> <本地地址 daddr> <远程地址 saddr> <远程端口 sport>
# 单个四元组内用空格分隔,多个四元组之间用分号分隔。
# 注意:saddr/sport 二元组最多支持 16 个,同一 saddr/sport 下最多支持 4 个 daddr。
# 因此,最多支持 64 种组合,超出的配置将被忽略。
rss_tbl=0 192.168.1.10 192.168.2.10 80;0 192.168.1.10 192.168.2.11 80;0 192.168.1.10 192.168.2.12 80

配置参数详解

  • enable: 总开关,必须设置为 1 才能启用此优化功能。
  • rss_tbl: 定义端口分配规则。每一条规则指定了网卡端口ID用于获取网卡的队列配置,对于特定的目标服务(saddr + sport),使用哪个预先分配好的本地地址(daddr)。

5. 性能提升

根据内部测试数据,该优化带来了显著的性能提升:

  • 测试场景: 客户端(wrk)向F-Stack Nginx发起http请求,使用长连接。F-Stack Nginx设置反向代理,作为客户端,频繁向多个远程服务器发起短 TCP 连接(未使用 keep-alive)。示例图如下所示:
  • 测试数据:如下表所示
  • 常规 QPS 提升约 2-6%左右,且提升的比列会随着进程数的增加而增加,因为进程数越多,原有的ff_rss_check()需要计算的平均次数就越多。
  • 某些特殊场景下提升可以达到35%以上,主要是因为在某些进程数的配置下,原始动态计算方式,随机选择源端口的次数会远超数学期望的总进程数+少量次数,导致需要大量额外调用ff_rss_check()进行计算,消耗了大量CPU资源。
    • 【注意】不同的upstream服务器配置(数量、IP、端口等)可能会导致不同的进程数存在类似问题,在本测试场景下配置8或16进程时,会出现该问题。导致该问题的具体原因则尚未完全明确,查看相关静态表、端口是否正在被占用和请求测试都正常。
    • 如下表所示分别为8和12进程Nginx调用ff_rss_check()次数的统计,可以看到8核时的随机选择次数明显超出了数学期望的(应该在8-12次左右),16核同理。
      • 其中randomtime表示内核参数net.inet.ip.portrange.randomtime的设置,因为F-Stack框架的特性,作为客户端选择源端口时此时完全随机选择效果更好,且F-Stack对随机性要求并不高,早前已经替换了性能好好很多的伪随机函数。
  • perf top截图

8进程间歇性随机(大部分不随机)时ff_rss_check()调用次数太多,热点很高

8进程完全随机,ff_rss_check()调用次数热点有一定降低

12进程,ff_rss_check()的热点则大幅下降

  • 8进程间歇随机和完全随机选择源端口QPS性能对比

6. 其他参数优化调整

F-Stack对应调整了部分参数,具体如下,其他业务如何配置可以根据自己业务的具体特点灵活调整

[freebsd.sysctl]
net.inet.tcp.delayed_ack=1 # 可以提升大并发的吞吐量
net.inet.tcp.fast_finwait2_recycle=1
net.inet.tcp.finwait2_timeout=5000
net.inet.tcp.maxtcptw=128 # 尽快释放TIME_WAIT状态的端口,增加空闲端口,减少in_pcblookup_local()的调用次数,进而减少ff_rss_check()的调用次数
net.inet.ip.portrange.randomized=1
# Always do random while connect to remote server.
# In some scenarios of F-Stack application, the performance can be improved to a certain extent, ablout 5%.
net.inet.ip.portrange.randomtime=0 # 对某些特定配置条件下原动态计算ff_rss_check()方式有一定性能提升

7. 总结

本次对 ff_rss_check() 的优化是 F-Stack 追求性优化能的一个典型例子。它通过以下方式解决了核心瓶颈:

  1. 空间换时间: 使用预计算的静态表换取运行时动态计算的开销。
  2. 减少调用次数: 极大降低了端口选择时的ff_rss_check()和in_pcblookup_local()的重试调用次数,实现总体系统2-6%,特殊极限场景35%以上的总体系统性能提升。
  3. 保证兼容: 通过降级机制保障了在任何情况下的功能正确性。

建议启用此功能的场景: 所有使用 F-Stack 作为客户端、需要高频率创建新连接(特别是短连接)的应用程序。启用后,只需在配置文件中预先定义好常用的目标服务地址,即可获得显著的性能收益。

【AI使用声明】本文初始版本使用DeepSeek-V3.1生成,然后进行人工调整;示意图片由元宝根据人工提供的提示词生成和修改

递归DNS如何选择权威DNS的IP-SRTT优化及实践

本文主要介绍递归DNS的SRTT本身机制及如何进行更进一步的优化,来达到更好的递归效果。都是字,不想弄图、表和数字,格式也有点乱,不想整理了,凑合看吧

1. SRTT本身机制

本节仅对SRTT进行简单的介绍供了解,不进行特别深入和详细的分析。

SRTT(Smoothed Round-Trip Time,平滑往返时延)是在递归DNS系统里,用加权平均算法计算出的上游DNS服务器的“响应时延”估值,它用于优化解析性能和选择最快的上游服务器。

延伸阅读

BIND的SRTT机制总体做的还是相对比较好的,可以参考 https://developer.aliyun.com/article/622410。

而Unbound社区版的SRTT做的则不够好,基本就是可用权威IP完全随机,递归的平均延迟非常高,所以在实际业务使用前必须进行优化,具体算法优化可以参考相关论文或BIND的实现等。

虽说递归DNS选择权威服务器时都希望选择延迟更低,但在最大限度降低递归时延的前提下,也要遵循一定的规则,如

  • 需要有一定的随机性选择非延迟最低的权威IP来自适应权威IP的RTT的波动和服务器的突然故障。
    • 如某个权威IP正常RTT很低,但偶发丢包被惩罚延迟后,需要能够逐步平滑降低其RTT。
    • 如果低RTT的权威IP突然故障,除了丢包惩罚延迟外,还需要随机性来选择其他权威DNS IP,防止单个IP不可用导致整个域名解析异常。
  • 标记不可用的IP也需要定时发送DNS请求。
    • 不可用的IP恢复可用后需要使用该机制探活标记为恢复可用
    • BIND和UNBOUND都会在15或30分钟完全重置一次权威IP的相关状态,其中包括RTT,以及IP的EDNS支持状态等。
    • 也可以低频率的对惩罚到标记不可用的IP做并发异步的DNS探活请求。
  • 不能向单一权威IP发送过于集中的请求,降低被权威封禁递归IP的概率。
    • 一个可探讨的方案介绍:如果本次不进行随机选择递归IP,选择最低RTT的IP时候再进行二次选择,1/2直接选择最低RTT的权威服务器,1/2在最低RTT差距一定范围内(如minrtt + 20ms,也认为有比较低的延迟)的其他权威服务器IP中随机选择。
    • 国内的各大权威DNS服务商的服务只要不是真实的攻击,常规的SRTT优化很是很难触发递归IP被封禁的场景的,但是一些单位、机构等自建的权威DNS,如果单个IP请求量较大则比较容易触发,所以这里也是递归DNS必须要考虑的一种场景。

2. SRTT的配套机制优化

只有SRTT本身的选择机制对一个递归DNS是不够的,还必须配合多个其他配套系统来使用,而这些配套系统对递归DNS的整体递归时延也是非常重要的。

2.1 某个权威DNS IP的SRTT生效范围

在实际业务中,存在以下两种场景会使多个域名使用相同的权威DNS IP

  • 不同的域名会使用相同的NameServer(NS)
  • 不同的域名使用的NS不同,但NS对应的IP相同(如腾讯云解析/DNSPod提供的个性化NS功能)

在我们的常规认知中,既然权威DNS IP相同,那么统一进行SRTT的探测和选择即可,这样多个域名的平均递归延迟都可以有效降低,如在每次权威IP状态重置后快速重新探测到RTT最低的权威DNS IP,且对所有域名生效。

但现实是相反的,多数递归DNS对这种场景的权威DNS IP探测是域名相关的,不同域名的权威DNS IP即使相同,也是分别独立探测和存储状态的(如Unbound的)。

对于某些特殊场景,这种做法可能有一定用处,如同一个IP对域名A支持EDNS,对域名B不支持等,但这极大增加了递归DNS解析时延(也浪费了一些内存,虽然现代服务器不怎么在意了)。

目前腾讯云/DNSPod的递归服务器的做法是所有域名只要权威DNS IP相同即共享一份SRTT的探测和选择结果,可以大幅提高选择RTT低的权威DNS概率(尤其是请求量不大的域名),降低解析延迟,不考虑一些特别极端的场景(也可以解析,但可能不是最优的结果,且此类极端场景在国内的权威DNS服务商中基本不存在)。

2.2 权威DNS IP的GEO优化

大家都知道普通域名的DNS解析可以做智能解析(分线路、GEO、基于延迟等),来给不同地域不同运营商的客户端分配不同的解析结果,达到降低延迟或节省成本的目的。那么对于给递归DNS使用的NameServer(NS)域名是否可以使用智能解析,来达到同样的目的呢?毕竟因为各种原因,国内大部分权威DNS服务商很难完全使用完全相同的Anycast IP在全球同时提供很高的高可用和很低的延迟,如果该方案可行,则权威DNS服务商有了一个变通的方式来同时满足高可用和低延迟的需求,如除了Anycast IP外还可以在不通地区提供一些本地的Unicast IP来降低本地递归DNS的访问延迟,比如目前腾讯解析/DNSPod和阿里云解析都做了该操作,那么效果如何呢。

答案是一半一半。

递归获取一个域名的权威DNS的NS IP有两个渠道,胶水记录和权威服务器的记录,其中NS域名在注册商/局设置胶水记录不支持智能解析,而在其本身权威DNS设置的解析记录则是可以支持设置智能解析线路的,下面分别进行介绍。

2.2.1 注册局的TLD服务器返回的glue record

  • 胶水记录(Glue Record)是一种特殊的DNS记录,用于解决域名解析过程中的循环依赖问题。当域名的权威DNS服务器(NS记录)是当前域名的子域名时,胶水记录是必须存在的(如后续示例所示),由域名所有者直接在注册商/局(父域DNS,如.com的TLD服务器)中提供子域名服务器的IP地址,确保递归DNS能够正确找到目标的权威DNS服务器。
dig @a.gtld-servers.net ns3.dnsv5.com

; <<>> DiG 9.11.26-RedHat-9.11.26-6.tl3 <<>> @a.gtld-servers.net ns3.dnsv5.com
; (2 servers found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 45009
;; flags: qr rd; QUERY: 1, ANSWER: 0, AUTHORITY: 2, ADDITIONAL: 11
;; WARNING: recursion requested but not available

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
;; QUESTION SECTION:
;ns3.dnsv5.com.                 IN      A

;; AUTHORITY SECTION:
dnsv5.com.              172800  IN      NS      ns3.dnsv5.com.
dnsv5.com.              172800  IN      NS      ns4.dnsv5.net.

# 因为NS域名ns3.dnsv5.com是请求域名dnsv5.com的子域名,此时胶水几率是必须返回的,否则后续继续向TLD服务器请求ns3.dnsv5.com还是返回上面两条NS记录,造成死循环
# 只返回tld相同的ns3.dnsv5.com的胶水记录,而不返回ns4.dnsv5.net的胶水记录
;; ADDITIONAL SECTION:
ns3.dnsv5.com.          172800  IN      A       1.12.0.17
ns3.dnsv5.com.          172800  IN      A       1.12.0.18
ns3.dnsv5.com.          172800  IN      A       1.12.14.17
ns3.dnsv5.com.          172800  IN      A       1.12.14.18
ns3.dnsv5.com.          172800  IN      A       101.227.168.52
ns3.dnsv5.com.          172800  IN      A       108.136.87.44
ns3.dnsv5.com.          172800  IN      A       163.177.5.75
ns3.dnsv5.com.          172800  IN      A       220.196.136.52
ns3.dnsv5.com.          172800  IN      AAAA    2402:4e00:1470:2::f
ns3.dnsv5.com.          172800  IN      A       35.165.107.227

;; Query time: 217 msec
;; SERVER: 192.5.6.30#53(192.5.6.30)
;; WHEN: Fri Aug 15 15:59:20 CST 2025
;; MSG SIZE  rcvd: 255
  • TLD服务器一般只有请求的域名的TLD和本域名设置的NS的TLD一致,会返回胶水记录的(这里大部分其实是非必须的,但是同时返回了胶水记录可以减少一次额外的请求,降低延迟),否则一般不返回胶水记录。如example.com设置NS为ns1.example.com, ns2.example.net, 那么这时候向TLD服务器请求example.com的时候,TLD服务器就会返回example.com的NS记录ns1.example.com和ns2.example.net,且同时返回ns1.example.com的A和AAAA记录(TLD相同,都是.com),但并不会返回ns2.example.net的胶水记录(TLD不同, .com vs .net),如上述和下述请求所示
dig @a.gtld-servers.net dnsov.net

; <<>> DiG 9.11.26-RedHat-9.11.26-6.tl3 <<>> @a.gtld-servers.net dnsov.net
; (2 servers found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 8363
;; flags: qr rd; QUERY: 1, ANSWER: 0, AUTHORITY: 2, ADDITIONAL: 11
;; WARNING: recursion requested but not available

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
;; QUESTION SECTION:
;dnsov.net.                     IN      A

;; AUTHORITY SECTION:
dnsov.net.              172800  IN      NS      ns3.dnsv5.com.
dnsov.net.              172800  IN      NS      ns4.dnsv5.net.

# 只返回tld相同的ns4.dnsv5.net的胶水记录,而不返回ns3.dnsv5.com的胶水记录,但其实这里的胶水记录并不是必须要返回的,因为dnsov.net和dnsv5.net是两个不同的域名,再次请求并不会造成死循环,但是同时返回了胶水记录可以减少一次额外的请求,降低延迟
;; ADDITIONAL SECTION:
ns4.dnsv5.net.          172800  IN      A       1.12.0.16
ns4.dnsv5.net.          172800  IN      A       1.12.0.19
ns4.dnsv5.net.          172800  IN      A       1.12.14.16
ns4.dnsv5.net.          172800  IN      A       1.12.14.19
ns4.dnsv5.net.          172800  IN      A       112.80.181.106
ns4.dnsv5.net.          172800  IN      A       117.135.128.152
ns4.dnsv5.net.          172800  IN      A       124.64.205.152
ns4.dnsv5.net.          172800  IN      A       13.37.58.163
ns4.dnsv5.net.          172800  IN      AAAA    2402:4e00:111:fff::8
ns4.dnsv5.net.          172800  IN      A       49.7.107.152

;; Query time: 192 msec
;; SERVER: 192.5.6.30#53(192.5.6.30)
;; WHEN: Fri Aug 15 16:00:30 CST 2025
;; MSG SIZE  rcvd: 261
  • 这里未介绍子域名授权场景的胶水记录,总体原则是一致的。

2.2.2 NS域名本身权威服务器返回的IP记录

NS域名(如ns3.dnsv5.com)在其权威DNS(如腾讯云解析/DNSPod)设置解析记录的时候,和普通域名可以一样设置智能解析(如境内设置一批IP,境外设置另一批IP等),和普通域名的使用没有区别。

2.2.3 递归DNS的行为及优化

递归DNS在进行迭代查询时,有可能从TLD服务器直接获取到NS域名的胶水记录,也可能从权威DNS获取,且两处获取的记录可能不同(部分检测系统会告警,但忽略即可),而权威DNS设置的IP支持智能解析,提供了更大的优化空间,一般相比胶水记录是更好的选择(且非专业技术人员常常忽视对胶水记录的维护),那递归DNS如何使用和优化呢。

常规的递归DNS(如BIND和UNBOUND)一般是这种行为(会略有不同,总体行为类似)

  • 如果向TLD服务器请求某个域名时,TLD服务器同时返回了NS的胶水记录,那么直接使用胶水记录作为NS的IP,不会再去向NS的权威请求记录
  • 如果向TLD服务器请求某个域名时,TLD服务器没有返回NS的胶水记录,则再去正常查询NS域名的记录时,可能回会权威请求NS记录,获取到权威可能设置的分线路智能解析结果,从而后续优先使用权威返回的解析记录
  • 有用户向递归DNS显示请求NS域名的A/AAAA记录,那么递归DNS必然会向NS域名的权威请求一次NS的A/AAAA记录(胶水记录不是其域名权威返回的,用户显式的请求去需要去获取权威设置的记录,而不能直接返回缓存的胶水记录),获取到权威可能设置的分线路智能解析结果,从而后续优先使用权威返回的解析记录
  • 如果权威返回的解析记录全部不可用,则重新回退到使用胶水记录提升可用性

可以看出来,常规的递归DNS不会主动去获取权威DNS的分线路智能解析结果,只会在被动获取到权威返回结果的时候,代替胶水记录进行优先使用,从腾讯云解析/DNSPod使用的实际效果看,权威解析才会设置的解析IP的流量比例总体并不低,这样设置还是有比较好的效果的(具体数据就不提供了)。

那对递归DNS能不能更进一步的进行优化呢,当然可以

  • 即使递归DNS从TLD服务器获取到了NS域名的胶水记录,在正常向胶水记录IP发起查询请求的同时,也自动异步去解析NS域名在权威的A/AAAA记录, 使用NS权威解析记录的比例可以大幅提高。
    • 根域名、TLD域名以及海外大厂的NS等域名不必做这个操作,因为记录一般都是一致的Anycast IP
    • 腾讯云/DNSPod所有对外提供服务的公共DNS、HTTPDNS、DoH、DoT、云内网DNS等的后端递归DNS都支持该项优化

F-Stack如何修改TCP连接的MSS


本文仅介绍F-Stack服务器作为被动接受连接的一方如何在不修改程序的前提下如何相对简单的修改TCP建连时的MSS选项

TCP数据包在经过某些网络设备(如负载均衡等)时可能会被增加某些头(如vlan、gre、ipip等),但是又没有正确的减小syn包中MSS选项的值,导致后续的数据包加上额外的头后可能超过1500的MTU而无法正常收发包,此时我们可能无法控制中间链路的网络设备,就需要修改应用服务器的MSS配置,但最好上还是应该由中间网络设备进行兼容。

F-Stack-1.21.5(FreeBSD-11.0)

FreeBSD-11.0的ipfw工具不支持直接修改tcp的mss选项的值,必须要同时借助netgraph(工具:ngctl)来实现,参考命令如下

# 创建 tcpmss 节点并将其连接到 ng_ipfw 节点
ff_ngctl mkpeer ipfw: tcpmss 100 msshook

# 设置mss和hook函数
# 该命令会报错ff_ngctl: recv incoming message: Operation not permitted,实际为回显结果时f-stack的ff_ngctl对freesbd的ngctl的一个兼容性问题,不影响设置效果,仅影响设置成功后的回显
ff_ngctl msg ipfw:100 config '{ inHook="msshook" outHook="msshook" maxMSS=1440 }'

# 将流量导入 tcpmss 节点
ff_ipfw add 300 netgraph 100 tcp from any to any tcpflags syn out via f-stack-0

# 让数据包在被修改后继续由 ipfw 处理, 可选,也可以配置在config.ini
ff_sysctl net.inet.ip.fw.one_pass=0


参考:ng_tcpmss man page

F-Stack-1.22+(FreeBSD-13.0)

FreeBSD-13.0的ipfw工具通过pmod模块引入的方式支持了直接修改tcp的mss选项值的功能,命令参考如下:

ff_ipfw add 1000 tcp-setmss 1440 tcp from any to any tcpflags syn out

【注意:】目前除了dev分支最新代码默认支持了ip_fw_pmod模块,其他分支和release版本需要先打上下面这个patch,在F-Stack的ipfw开启ip_fw_pmod模块功能:ff_ipfw support ip_fw_pmod and tcpmod for tcp-setmss.

F-Stack通用方式

因为F-Stack并未实际实现修改网卡MTU的功能,所以可以通过修改用户态FreeBSD协议栈中网卡设备MTU的操作,来达到减小mss选项的值(MTU – 40),但网卡实际的MTU还是1500,并没有减小,从而达到也可以正常收发超过设置的MTU(如1480)大小的包(如1500),但这里借助了F-Stack的feature(不是bug)来实现的,并不建议作为标准方案使用,参考命令如下

ff_ifconfig f-stack-0 mtu 1480

Linux协议栈

linux协议栈对标ipfw,使用iptables修改mss选项值的命令参考

iptables -t mangle -A OUTPUT -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --set-mss 1440

过去的2024

2025.5.24 更新

本来是5.21周三去进行哥哥的海葬,结果周三全天大雾取消了,改成了今天。早上六点集合,6点25左右发船,经过一个多小时后到达海葬海域,船不大,海浪有点大,大部分人吐的稀里哗啦。不过总算结束了一件事情,哥哥一路走好,活着的时候半生自由活动受限,现在至少可以算是自由的,可以顺着大海游遍全世界。

======================================================

频繁的头疼欲裂,需要靠大量的布洛芬和替扎尼定续命,如果有选择,早就不再继续卷了。

原有家庭本身最后一个人离去,多次住院,加部分截肢多坚持了一年半吧,临走前可能也是有点感应吧,做梦失眠头疼加剧。

小孩小学还是卷的厉害呀,目前看文理科都没有很好的天赋,也是顺其自然吧。

最后是工作的事情,24年最大的体验其实是一些老生常谈的东西,当业务增长不动的时候,各种流程就来了。

不能说流程没用,在合理的流程下,可以提高项目质量的下限,虽然对部分项目的质量上限没什么太大作用,但是在一个比较大的部门中确实不能否认它的作用。

但是流程本身是问题了提升质量服务的,而当前完全本末倒置了,已经达到了丧心病狂的程度,各种不合理和冗余的流程,完全是为了流程而流程,对质量提升的终极目标的效果非常有限。

而且严重拖后腿,花费特别大的精力,流程搞了一大堆,再一看实际产出,大部分直接呵呵了,虽然即使有产出也不一定对最后的产品收益就一定有多大帮助,但合理的产出总比不合理的流程合理多了。

看到的最大好处算是至少有了审计的基础。

其他吐槽的欲望都没有了,这就样吧,结束。

2024年某些”小众”递归DNS那些事儿

本文就不写运营商递归DNS和相对比较知名一些的公共DNS了,通过几个case聊聊2024年当前可以应答DNS请求、在国内用户量不小,但是又搜索不到什么公开信息的一些”小众“递归DNS的那些事儿,部分已经无法访问的递归DNS就不再包含进来了。这些递归DNS的用户体量并不比不少知名公共DNS小,只是基本不为人知,关注该领域的可以大概了解下。

使用这些DNS比较容易出现解析线路不准确(如国内用户解析到国外IP)、域名被劫持等场景,因为让所有用户修改DNS并不现实,一般还是更建议有端的客户端(如APP等)尽量使用HttpDNS或其他技术手段规避类似场景。

【注1】本文部分线索来自其他伙伴。

【注2】本文所有数据为简单测试,仅供参考。

case 1:183.2.x.x

该DNS疑似某供应商为园区、公司等场景提供的递归DNS,在本省的使用率不低。在24年5月的时候测试,使用该DNS会大概率将域名解析到国外的IP,会导致终端用户访问延迟升高,也造成该省份域名解析到国外IP的概率略微高于其他省份。但是在24年下半年测试没什么大的问题了,可以正常解析为本省份结果,只是解析结果没有区分运营商,基本不影响正常使用。

下图分别为24年5月和12月的测试递归DNS出口IP的结果对比

case 2:45.14.x.x、45.128.x.x

该组递归DNS分布于多个C段,多个海外其他国家,目前看国内各省份运营商皆有固网/wifi用户访问,设置比例大约在小万分之x,使用移动网络则不会访问该组递归DNS,疑似从2024.12.04 16:00+开始对外提供DNS解析服务。

该组递归DNS后端在进行递归请求时固定携带127.0.0.1作为ECS IP,大部分域名可以解析到默认线路,无省份运营商区分,解析到国外IP的概率倒是不高。

该组DNS貌似会随机劫持某些域名到其他IP,并302跳转到其他违规网站。历史测试截图如下。

注3】截止本文时间(2024.12.18),之前历史被劫持域名已经没有被劫持,且之前被劫持到的相关IP也已经无法访问,疑似已经被处理。

case 3:154.86.x.x

该组递归DNS分布于多个C段,部署于中国香港,目前看国内各省份运营商皆有固网/wifi用户访问,设置比例大约在小千分之x,使用移动网络则不会访问该组递归DNS,具体对外提供服务时间则不确定。

该组递归DNS后端在进行递归请求时会随机转发给国内外多个其他公共DNS,且并未携带ecs,大部分域名的解析结果IP为国外IP,会导致终端用户访问延迟升高,递归DNS后端出口IP测试结果如下所示,其中包括Google、UltraDNS等其他的公共DNS的递归出口IP

该组DNS因为是纯转发,所以部分特征会测试到转发目标公共DNS的特征,如NeuStar家的UltraDNS,但实际并非该集群本身的版本特征,也并非转发目标公共DNS部署的DNS缓存转发服务,部分特征测试结果如下所示:

dig version.bind chaos txt @154.86.x.x +short
“1733297354.ultradns”
dig hostname.bind chaos txt @154.86.x.x +short
“rcrsv2.jptyo1.ultradns”

dig version.bind chaos txt @154.86.x.x
version.bind. 86400 CH TXT “PowerDNS Recursor 5.1.1 (built Jul 22 2024 13:49:14 by root@localhost)”

dig version.bind chaos txt @154.86.x.x +short
version.bind. 75428 CH TXT “Q9-P-7.6”

该组DNS貌似会随机劫持某些过期域名到其他IP,并302跳转到其他网站(有点类似纠错页),当前(2024.12.18)跳转的目标网站实测无法访问,也未找到历史网页内容,且很多安全情报都会报告风险并进行访问拦截,没有什么实质性的影响。

F-Stack 对 HTTP/3 的支持使用说明

Nginx 主线在1.25.x 版本中已经加入了对 HTTP/3 的支持,F-Stack 在等待了两个小版本之后,也移植了 Nginx-1.25.2 版本到 F-Stack 上,目前可以支持HTTP/3的测试使用,本文介绍移植过程中的一些兼容性改造及使用注意事项。

主要兼容项

主要是 F-Stack 的一些接口兼容和 FreeBSD 不支持一些 Linux 的部分选项,而 Nginx 的自动配置检测的是 Linux 是否支持,需要进行一些修改。

  1. 对 F-Stack 的 ff_recvmsg 和 ff_sendmsg 接口进行修改,兼容 Linux 接口,主要是部分结构体字段类型不一致的兼容,虽然结构体编译对齐后总长度是一致的。
  2. 关闭了 BPF sockhash 功能(bpf,SO_COOKIE)的功能检测和开始,该功能主要用于通过从 bpf 的 socket_cookie 直接获取数据,提升性能。
  3. 关闭了 UDP_SEGMENT 功能,主要功能设置 UDP 分段大小。
  4. IP_PKTINFO 选项不探测是否支持,强制使用 FreeBSD 的 IP_RECVDSTADDR 和 IP_SENDSRCADDR 选项。
  5. IP_MTU_DISCOVER 选项不探测是否支持, 强制使用 FreeBSD 的 IP_DONTFRAG 选项。
  6. IPV6_MTU_DISCOVER 选项不探测是否支持, 强制使用 IPV6_DONTFRAG 选项,该选项目前 FreeBSD 和 Linux 都支持。

编译过程

SSL 库

此处以 OpenSSL quic 为例,可以参考以下方式编译

cd /data/
wget https://github.com/quictls/openssl/archive/refs/tags/OpenSSL_1_1_1v-quic1.tar.gz
tar xzvf OpenSSL_1_1_1v-quic1.tar.gz
cd /data/openssl-OpenSSL_1_1_1v-quic1/
./config enable-tls1_3 no-shared --prefix=/usr/local/OpenSSL_1_1_1v-quic1
make
make install_sw

DPDK 和 F-Stack lib

总体编译方式不变,额外需要注意的是如果系统的 OpenSSL 库版本与上面使用的 OpenSSL quic 版本不兼容时,编译 DPDK lib 库时需要也使用上面的OpenSSL quic 库(通过配置 PKG_CONFIG_PATH 使用),参考以下方式编译

export FF_PATH=/data/f-stack
export PKG_CONFIG_PATH=/usr/local/OpenSSL_1_1_1v-quic1/lib/pkgconfig:/usr/lib64/pkgconfig:/usr/local/lib64/pkgconfig:/usr/lib/pkgconfig

mkdir -p /data/f-stack
git clone https://github.com/F-Stack/f-stack.git /data/f-stack

# DPDK lib
cd /data/f-stack/dpdk/
meson -Denable_kmods=true build
ninja -C build
ninja -C build install

# F-Stack lib
cd /data/f-stack/lib/
make
make install

# ff tools
cd /data/f-stack/tools
make
make install

F-Stack Nginx-1.25.2

Nginx 可以参考以下参数进行编译,如果有更多额外需求,自行调整相关配置

export FF_PATH=/data/f-stack
export PKG_CONFIG_PATH=/usr/local/OpenSSL_1_1_1v-quic1/lib/pkgconfig:/usr/lib64/pkgconfig:/usr/local/lib64/pkgconfig:/usr/lib/pkgconfig

cd /data/f-stack/app/nginx-1.25.2/
./configure --prefix=/usr/local/nginx_fstack --with-ff_module --with-http_ssl_module --with-http_v2_module --with-http_v3_module --with-cc-opt=-I/usr/local/OpenSSL_1_1_1v-quic1/include --with-ld-opt='-L/usr/local/OpenSSL_1_1_1v-quic1/lib/'
make
make install

测试使用注意事项

  1. keepalive_timeout = 65 # 因为 Nginx 的 quic 中将 keepalive_timeout参数值作为了读超时时间,所以不能设置为 0
  2. listen 443 quic; # 监听HTTP/3的时候不能设置REUSEPORT,否则多进程会有异常
  3. ulimit -n 100000 # 调大该参数值
  4. 其他使用注意事项可以参考 F-Stack 和 HTTP/3 相关配置文档

性能测试对比

这里不考虑现网实际客户端访问网站的延迟对比,仅考虑 F-Stack Nginx 和源生 Nginx 的性能对比测试。

但是在尝试了多种客户端后,仅 curl8 测试成功,但是只能测试单连接的延迟,这里不太关注。其他压测客户端工具 wrk-quic、h2load、Nighthawk 等在编译测试时都遇到了各种各样的问题,暂时未能成功测试,性能对比数据暂时缺失,如果有人有压测客户端,欢迎进行对比测试并提供测试数据。

乐观 DNS 缓存那些事

本文主要内容包括乐观 DNS 缓存的介绍,现网使用中的优缺点,国内部分递归 DNS(不包含境外 DNS) 的使用情况数据,及如何规避乐观 DNS 等内容。

  • 约60%的运营商递归 DNS 开启了乐观 DNS

乐观 DNS 缓存介绍

乐观 DNS 缓存(Optimistic DNS、RFC8767:Serving Stale Data to Improve DNS Resiliency等,本文后续以乐观 DNS 代替),简单的说就是在客户端向递归 DNS 发起一个域名的 DNS 查询请求时,如果递归 DNS 缓存中的记录已经过期(缓存时间超过了 TTL 时间)时,还是会应答该过期记录的行为。

【注意:】准确的说目前运营商的递归 DNS、南京信风公共 DNS(114.114.114.114)和阿里云公共 DNS(223.5.5.5)等使用过期缓存应答行为与 RFC8764 中乐观 DNS 的行为是冲突的,并不是真正的乐观 DNS 缓存,但此处也先以乐观 DNS 称呼。

  • RFC8767 中的对乐观 DNS 行为的是只有权威 DNS 解析失败时, 比如DDoS 攻击、网络等其他原因导致的超时、REFUSE等错误时才向客户端应答过期的缓存记录数据,而目前部分国内支持乐观 DNS 行为的递归 DNS 则不关注权威应答成功或失败,直接给客户端应答缓存中的过期记录,再去异步向权威查询。
    • 使用过期缓存应答的客户端查询超时时间在标准 RFC 中建议为 1.8 秒,但从个人的经验看,这个值在现网环境中明显有点高了,可以考虑酌情降低,个人建议低于 400 ms
  • 最大过期时间(A maximum stale timer)即 TTL 过期多长时间内可以继续应答过期的记录,RFC 的建议是 1 – 3 天,而目前部分国内递归 DNS 在实践中该时间是无限的。

乐观 DNS 的优点

乐观 DNS 对终端用户的使用体验以及递归 DNS 服务提供商有一些好处,主要包括以下方面:

  • 在缓存的记录 TTL 过期后降低客户端的 DNS 解析时延,尤其是请求量不是很大的冷域名没有了冷启动过程,因为省却了同步的向各级权威 DNS (包括根 DNS, TLD DNS, zone 域名 DNS等 ) 递归(迭代) DNS 查询的过程。
    • 此处主要是部分国内非标准实现的乐观 DNS 的优点
    • 运营商递归 DNS 或云厂商等公共 DNS 中普遍使用缓存和递归分离的多级架构中,此方式工程实现上更为简单,缓存层可以无需保存递归过程中的客户端连接状态信息。
  • 权威 DNS (包括根 DNS, TLD DNS, zone 域名 DNS等 ) 遭受 DDoS 攻击或其他故障无法响应递归 DNS 的查询时,不影响客户端获取请求域名的记录,虽然 TTL 已经过期,但是比解析失败更好一些(”stale bread is better than no bread.”)。
    • 此处可以减少递归 DNS 提供商的收到的无法解析等情况问题反馈、咨询工单等。
    • 降低递归 DNS 的成本,包括网络带宽和计算资源成本等,权威失败时仅间隔性的重试到权威的请求。

乐观 DNS 的缺点

如果按照标准的 RFC8767 去实现乐观 DNS,这里其实并没有什么显著的缺点,因为只有在权威 DNS 不可用或不稳定时才会触发该功能,虽然可能会有一些解析错误的情况出现,但至少不会使情况变得更糟。

乐观 DNS 更多的缺点来自目前部分递归 DNS 的非标实现,主要包括以下方面:

  • 过期的解析记录在递归 DNS 缓存中可能长期(数小时、数天、甚至数月)无法刷新,还可能解析到旧的记录,尤其是对平时解析量不大的冷域名,此时过期的记录很可能早已无法访问。
    • 尤其是最大过期时间(A maximum stale timer)设置为无限时,而不是标准建议的 1-3 天或自行稍微斟酌修改的时间。
    • 此处会增加递归 DNS 提供商的收到的解析错误等情况问题反馈、咨询工单等,具体最终工单量增加还是减少需视客户业务和网络等情况单独去看。
  • 对需要频繁切换记录的域名不友好,如全局负载均衡或 CDN 调度等。
    • 如果域名的 DNS 查询请求量足够大,则可以减轻该影响。

个人观点

  • 基本支持标准 RFC 的乐观 DNS 行为。
  • 强烈反对部分递归 DNS 的乐观 DNS 的非标实现,见之前乐观 DNS 介绍中的注意事项,降低解析时延不能以解析到错误甚至不可用的 IP 为代价
  • 使用开源软件等自建自用递归 DNS 的场景,根据个人需求自行设置缓存规则即可。

国内乐观 DNS 测试

测试范围及方法

  • 测试范围:境内 31 个省份三大运营商(电信、联通、移动)共 93 个测试目标,及部分公共 DNS (119.29.29.29,114.114.114.114,223.5.5.5)共 3 个测试目标。
  • 测试的大体步骤如下所示,该测试过程会重复测试多次:
    • 步骤1,首先设置测试域名opt.test.example.com的 A 记录为1.1.1.1,并设置 TTL 为 600 秒。
      • 此处的测试域名和测试 IP 都非真实的测试域名和测试 IP,仅为说明测试过程。
    • 步骤2, 通过遍布全国的测试客户端(IDC 及 LastMile)请求测试域名,其中包括直接指定目标递归 DNS 进行解析(IDC)和 http 请求中包含的 DNS 解析(LastMile)。
      • 本过程的目的使客户端使用的递归 DNS 都解析并缓存过opt.test.example.com的 A 记录为1.1.1.1
    • 步骤3,等待步骤2的全部测试全部结束后,修改测试域名opt.test.example.com的 A 记录为2.2.2.2,并设置 TTL 为 600 秒。
    • 步骤4,等待不小于 1 小时(数倍 600 秒的 TTL )后,确保递归 DNS 缓存记录的 TTL 已经过期。
    • 步骤5,重复步骤2,通过全国各地的测试客户端重新请求测试域名。
      • 此处绝大部分测试客户端已经排除了客户端本地 DNS 缓存的影响,尽量直接向本地设置的递归 DNS 进行请求。
    • 步骤6,过滤掉干扰 DNS(如 LastMile 的路由器的内网 IP段等),仅保留本地运营商实际的递归 DNS 和目标公共 DNS 的数据记录进行统计,如果仍然存在会解析到已经过期的旧 IP 1.1.1.1,则认为该递归 DNS 开启了乐观 DNS,如果所有记录都为新的 IP2.2.2.2,则认为该递归 DNS 未开启乐观 DNS。
      • 为减少偶然情况的影响,以上测试步骤会执行多次。

测试数据

从目前目前国内的主流的递归 DNS 测试数据看,过半数(约60%左右)已经开启了乐观 DNS,本文仅列出测试数据,不详细点评。

公共 DNS 乐观 DNS 开启情况

本次测试了三家公共 DNS,119.29.29.29,114.114.114.114和223.5.5.5,其中119.29.29.29未开启乐观 DNS,114.114.114.114和223.5.5.5则开启了乐观 DNS。

运营商递归 DNS(LocalDNS/LDNS)乐观 DNS 开启情况

运营商递归 DNS 开启了乐观 DNS 的总体比列大约在 60% 左右,以下为详细数据

  • 该数据仅代表本次测试的统计情况。
IDC + LastMile 测试数据
  • 在所有的 93 个测试目标中共有 57 个测试目标测试到开启了乐观 DNS,占比 57 / 93 = 61.29%
  • 电信 15 个测试目标开启了乐观 DNS,占比 15 / 31 = 48.39%
  • 联通 25 个测试目标开启了乐观 DNS,占比 25 / 31 = 80.65%
  • 移动 17 个测试目标开启了乐观 DNS,占比 17 / 31 = 54.84%
IDC测试数据

虽然在 LastMile 的测试中,已经尽量规避了客户端本地 DNS 缓存的影响,但是我们仍不能保证一定未使用客户端本地缓存,所以单独列出 IDC 的测试数据。

在所有的 93 个测试目标我们通过各种渠道搜集到 81 个对应的 IDC 客户端,可以完全排除本地 DNS 缓存的影响,数据如下:

  • 共 47 个测试目标开启了乐观 DNS,占比 47 / 81 = 58.02%
  • 电信共测试了 27 个测试目标,其中 11 个测试目标开启了乐观 DNS,占比 11 / 27 = 40.74%
  • 联通共测试了 28 个测试目标,其中 22 个测试目标开启了乐观 DNS,占比 22 / 28 = 78.57%
  • 移动共测试了 28 个测试目标,其中 14 个测试目标开启了乐观 DNS,占比 14 / 28 = 50.00%

如何规避乐观DNS

如果业务域名的请求量较大,可以较快的触发递归 DNS 快速刷新到新的记录,那么乐观 DNS 可以微弱的降低解析时延,目前没有看到太明显的缺点,正常使用各递归 DNS 即可。

  • 请求量大的热域名,则缓存命中率高,总体时延降低有限。

然而在很多实际的业务中,对切换实时性要求比较高,但是访问量又没有那么大的中小域名,目前如此高比例非标准实现的乐观 DNS肯定会对业务存在一定的影响,此时就需要一些其他技术手段来对乐观 DNS 进行规避,下面介绍一些方法,其中部分方法比较常见,外网已经有很多的公开信息可以查询参考,此处就不展开详细介绍。

提前修改解析记录/保留旧IP一段时间

  • 如果是有规划的切换 IP 时,也可以考虑预留几天(如3天)的缓冲期,这段时间内新旧 IP 都保持可用,来降低 LocalDNS 会返回过期 IP 造成的影响。
    • 类似于修改域名的 NS,需要留几天的缓冲期保持新旧NS的记录都可用并同步更新
    • 不适用于临时或频繁的切换 IP
    • 即使保留 3 天的缓存,也不能保证完全刷新掉旧的IP
    • 感谢 @changlinli 提供

可以使用未开启乐观 DNS 的公共 DNS 来规避乐观 DNS,目前国内腾讯云 DNSPod 的公共 DNS 等是未开启乐观 DNS 可供选用。不同的使用方式有不同的接入方法,更多信息可以参考这里

使用未开启乐观 DNS 的 公共DNS

通用的公共 DNS

  • 需要修改 WIFI/路由器 等使用的 DNS IP,有一定使用门槛,且普通 DNS 请求容易被劫持。

DoH/DoT(DoQ)

  • 需要较高的 PC 操作系统版本/浏览器/手机 OS(主流IOS/Android均支持)等支持,并进行配置,也有一定的使用门槛。

HttpDNS/HttpsDNS

近年来的常规方式,在各大 APP 中广泛使用,更多 HttpDNS 的介绍

  • 优点:可以指定 HttpDNS 服务器,绕开 LocalDNS,DNS 控制自由度较高,且有丰富的统计信息查看等,即使在 HttpDNS 故障时,也可以降级回本地 LocalDNS
  • 缺点:使用场景受限,主要是在移动端 APP、PC 客户端等有端场景,其他的浏览器等场景使用受限;有改造接入门槛;请求次数收费等使用成本较高。

刷新递归 DNS 缓存

联系运营商主动刷新缓存

部分运营商(如天翼云域名无忧)提供了递归 DNS 缓存刷新接口,但是也存在比较多的问题价格很贵,使用不便等问题。

  • 价格很贵:三大运营商的缓存刷新服务一般为数千元/次,可以刷新某域名在本运营商所有省份的递归 DNS 缓存,如重大业务重大故障的切换需要刷新可以考虑使用,但是如果普通的切换也使用的话则明显成本太高。
  • 使用不便:一般需要单独联系各个渠道去购买或使用该功能,缺少一键全量刷新的手段。
  • 覆盖不全:除了三大运营商外,其他中小运营商和公共 DNS 很少提供公开的缓存刷新服务。

通过拨测客户端被动刷新缓存

通过各种不同的客户端或拨测工具,大量请求目标域名,触发对应的递归 DNS 被动的刷新缓存。

  • 公开免费的拨测工具对各种递归 DNS 的覆盖度不够完整。
  • 使用商业拨测工具大量拨测的价格也不低。