plantegg

java tcp mysql performance network docker Linux

刘志军:中国高铁之父的权力与陨落

来源:YouTube 视频《睡遍”红楼梦”的刘志军:高铁之父的死缓内幕》字幕整理与总结

一、农民之子的崛起之路

刘志军 1953 年出生于湖北黄冈一个贫苦农民家庭,上面两个姐姐,下面一弟一妹。家境极差,父母甚至要卖房子才能供他勉强读到初中。更不利的是,因祖上曾雇过长工,家庭被划为”富农”成分,找正式工作都困难。

1972 年,初中毕业的刘志军赶上铁路系统招工,成为武汉铁路分局的一名养路工——铁路系统最底层、最艰苦的岗位。但他很快因字迹工整、做事细致脱颖而出,被调去做文书,后进入团委系统,一路升至武汉铁路分局团委书记。

关键转折来自武汉铁路分局副局长洪宗全的赏识。洪宗全不仅提拔了刘志军,还将他送到西南交通大学学运输管理,弥补了学历短板。刘志军随后与洪宗全的女儿黄丽萍结婚,借岳父之力从团委转入实权部门。1984 年起几乎一年一个台阶,1987 年即做到武汉分局党委书记。

然而刘志军一达到目的便抛弃妻子——洪宗全退休后他立刻离婚,黄丽萍曾在单位贴小字报控诉其”忘恩负义”。此事在体制内引发巨大反感,刘志军被贬至广州铁路局任政治部副主任。但他很快娶了第二任妻子(据报道其父曾任某军区司令),1988 年便杀回武汉铁路分局当局长,级别已高于前岳父退休时。

此后一路飞升:1993 年任沈阳铁路局局长,1996 年任铁道部副部长,2002 年成为中央委员,2003 年初正式就任铁道部长。

二、独立王国与跨越式发展

铁道部在中国部委体系中极为特殊:200 万职工,数万亿资产,政企合一,军事化管理,拥有自己的公安、检察院、法院、医院、学校和企业,被称为”独立王国”。部内人称部长为”首长”。

然而,当时中国铁路运力严重不足,主干线长期超负荷运转,春运年年告急,铁路已成制约经济发展的瓶颈。

前任部长傅志寰曾推动”网运分离”改革(参照欧洲模式,将铁路基础设施与运输业务拆分),但因铁路系统封闭性太强、利益集团阻力巨大,加上 2001 年后中央倾向于做大国有垄断企业而非拆分,改革未能成功。

刘志军上台后立即叫停”网运分离”,代之以”铁路跨越式发展”——集中权力、资金、资源,以非常规方式跳过传统发展阶段,直接追赶发达国家水平。傅志寰要分权,刘志军要集权。

三、中国高铁从无到有

3.1 顶层规划

2003 年上任不久,刘志军推动启动中国第一个《中长期铁路网规划》,提出”四纵四横”客运专线设想(总长约 1.2 万公里,设计时速 200 公里以上),到 2020 年全国铁路营业里程达 10 万公里,预计总投入 2 万亿——自美国州际公路系统以来全球投资金额最高的公共工程之一。

为何选高铁而非磁悬浮?上海浦东机场到市区的磁悬浮实验线证明:磁悬浮对载重有严格限制,无法满足中国的大运量需求。

3.2 技术引进与博弈

中国从上世纪 90 年代就尝试自研高速列车(”南建”、”先锋”、”中华之星”、”长白山”等),但整体技术不成熟,核心部件依赖进口,国产方案最高时速仅约 160 公里。

2004 年,国务院确定”引进先进技术、联合设计生产、打造中国品牌”的思路。铁道部采取”战略买家”打法:

  • 国内仅允许南车四方和北车长客两家与外方谈判
  • 其余企业不准私下接触外国厂商
  • 以中国市场的巨大体量为筹码

在压力与诱惑下,川崎重工、阿尔斯通、庞巴迪、西门子等国际巨头不仅接受技术转让条件,还在价格上被一再压低。

  • 2004 年 6 月:时速 200 公里动车组招标
  • 2005 年 10 月:时速 300 公里项目招标
  • 2007 年 4 月 18 日:第六次大提速,时速 200-250 公里动车组正式运行
  • 2008 年 8 月:京津城际铁路通车(中国第一条设计时速 350 公里的高铁)

3.3 自主研发的突破

2008 年铁道部与科技部签署行动计划,目标直接研制时速 350 公里以上的国产高速列车。集结了中科院、清华、北大等近 30 家科研院所和近 50 家骨干企业。

2010 年 5 月,CRH380A 电力动车组在长春下线。同年 12 月 3 日,在京沪高铁试验段跑出 486.1 公里时速,刷新世界纪录。

3.4 “部省合作”的资金机制

刘志军创设”部省合作”机制——铁道部与全国 31 个省市自治区签署战略合作协议,双方共同出资成立合资铁路公司。地方政府承担征地拆迁,并以权益性投资方式投入超过 4000 亿。

投资规模爆炸式增长:

  • 2003 年:533 亿
  • 2010 年:7075 亿(翻了十几倍,一度超过国防军费)
  • 任内在建铁路约 1.8 万公里,再建铁路接近 3 万公里
  • 全国铁路运营里程从 2002 年的 7.19 万公里增至 2010 年的 9.1 万公里

3.5 四万亿的助推

2008 年全球金融危机后,温家宝推出 4 万亿刺激计划,其中约 1.5 万亿投向铁路等基础设施。中长期规划随之调整:铁路里程目标从 10 万提高到 12 万公里以上,高铁里程从 1.2 万提高到 1.6 万公里。

四、刘志军的管理风格

刘志军在铁道部大权独揽、说一不二,被称为”刘疯子”。半夜翻材料发现问题就召集负责人开会,当场研究、当场定方案、当场敲时间表。反对者一律撤职。

每逢高铁提速或验收,他几乎都亲自坐进驾驶室——以自身安全为赌注倒逼施工质量。有人统计他曾连续几十个小时沿提速线路检查不下车。

另一面,他对风水颇为在意,重大工程开工前会请人选黄道吉日。

五、代价与事故

债务深渊

到 2011 年上半年,铁道部负债已超 2 万亿,负债率接近 60%。

重大安全事故

  • 2008 年南方雪灾:京广铁路南端几乎瘫痪,超 20 万人被困广州火车站
  • 2008 年 4 月 28 日胶济铁路事故:列车相撞,72 人死亡,刘志军受到记过处分
  • 2011 年温州动车事故:40 人死亡、192 人受伤

六、腐败与权力掮客

6.1 弟弟刘志祥

刘志祥在哥哥庇荫下升至武汉铁路分局副局长,但纯粹是个祸害:

  • 控制汉口火车站几乎所有卧铺票和热门座位票,将外部电脑接入配票系统倒卖车票
  • 非法所得 4000 多万
  • 2002 年指使承包商杀害举报人高铁柱
  • 2005 年被纪委双规,最终被判死缓,后改判 16 年

6.2 丁书苗(丁羽心)——“高铁一姐”

丁书苗,1955 年生,山西商人,外号”傻粮”。从收鸡蛋、开饭店、跑煤炭运输起家,靠极强的人际关系能力逐步打入铁路系统核心。约 1997 年经人牵线结识刘志军。

高铁投资全面启动后,丁书苗成为最大的权力掮客:

  • 2007-2010 年间帮助 23 家投标公司中标 57 个高铁项目,标的总额 1858 亿
  • 收取 1.5%-3.8% 的中介费,个人获利超 20 亿
  • 其中 53 个项目由刘志军亲自打招呼

丁书苗还为刘志军提供多种”服务”:

  • 捞人:2007 年铁道部政治部主任何洪达被查,丁书苗花 4400 万试图摆平(未成功)
  • 跑官:胶济铁路事故后刘志军想调离铁道部,丁书苗花 500 万找人打点(同样被骗)
  • 安排女色:2009 年出资 5000 万参与拍摄新版《红楼梦》,据传帮刘志军潜规则剧组女演员
  • 慈善洗白:汶川地震捐 1.14 亿、玉树地震捐 1580 万、舟曲泥石流捐 2300 万、捐 1.5 亿购买”母亲健康快车”。甚至为了做慈善还行贿国务院扶贫办官员范振玉 4000 多万

6.3 刘志军的受贿

刘志军本人在金钱上相当谨慎——很少直接拿钱,巨额利益基本留在丁书苗名下。官媒曾宣称查封 374 套房产、总价值 8 亿,但法庭证实这些基本在丁书苗名下。

法院最终认定刘志军受贿约 6460 万。民间传言的”刘百亿”与司法认定差距巨大,关键在于大量资产被认定为丁书苗个人所有——如果不这么切割,死缓判决就难以自圆其说。

七、落幕

事发经过

2010 年夏,国家审计署在跟踪审计京沪高铁时发现资金异常,顺藤摸瓜查到丁书苗,刘志军问题随之浮出水面。

2011 年 1 月春运期间,刘志军突然消失十多天,后重新露面,连续乘坐和巡查各条高铁线路超过 7000 公里——更像一场告别。

2011 年 2 月 11 日夜,刘志军被中纪委带走。

态度与判决

在秦城监狱中,刘志军:

  • 告诉律师”无论生死都不上诉”
  • 对审查意见全部签字、不做反对
  • 从未检举揭发过任何其他人
  • 嘱托律师转告女儿”千万不要从政”
  • 与律师聊历史人物(从胡适到傅斯年),推荐《南渡北归》
  • 能从记忆中细述每一条高铁线路、每一个高铁站的设计方案

刘志军放弃辩护的逻辑:不与组织讨价还价,以换取活命。高铁利益盘子巨大,必然牵涉更高层,但他从未检举任何人。

2013 年 7 月,刘志军一审被判死缓。

丁书苗被判 20 年,罚金 25 亿,没收财产 2000 万。

据英国《每日电讯报》报道

有消息人士称不判刘志军死刑,与其和胡锦涛之子胡海峰、前政治局委员王兆国之子王新亮的关系有关,也有说法称江泽民出面保了他。

八、后续影响

铁道部的终结

2013 年,国务院撤销铁道部,行政职能并入交通运输部和新组建的国家铁路局,企业职能划入新组建的中国铁路总公司,实现政企分开。”独立王国”的日子结束。

继任者同样落马

接替刘志军的盛光祖(铁道部最后一任部长),上任第二天即立”廉政军令状”,但 2023 年 12 月因受贿 6300 万被判 15 年。

高铁的长期回报

  • 降速后安全隐患大幅降低
  • 京沪高铁实现盈利
  • 部分当年被质疑的线路逐渐具备长期回报能力
  • 客运分流释放了传统铁路的货运能力
  • 但大量中西部高铁线依然长期依赖补贴和债务滚动

九、核心启示

视频最后指出,中国高铁的发展模式与中国过去几十年的整体发展模式高度相似——靠超大规模投资换取高速增长。这种模式短期极其有效,但当投资规模无限放大而约束机制滞后时,债务堆积、权力与资本勾连、寻租空间成倍放大。效率和腐败、发展和风险往往同时出现。

视频认为,如果反腐只寄希望于官员的自觉自律,本身就是反人性的。真正的问题从来不只是”谁贪了”,而是有没有一套制度让人”不敢贪、不能贪、贪了必然付出无法承受的代价”。


注:以上内容基于视频字幕自动转录整理,部分细节可能因语音识别误差存在偏差。


附录:核心事实声明可信度校验

对视频中 25 条核心事实声明进行了逐条校验,主要来源为中文/英文维基百科及公开报道。

汇总

判定 数量 占比
确认 15 60%
部分准确 5 20%
无法确认 2 8%
有争议 1 4%
不准确 0 0%

总体评价:视频事实陈述质量较高,大部分核心事实与权威来源吻合。无明显的事实错误。

逐条校验

确认(15 条)

# 声明 核查结果
1 刘志军 1953 年出生于湖北黄冈,父母是农民 维基百科确认:1953.1.29 出生于湖北省黄冈专区鄂城县
2 1972 年成为武汉铁路分局养路工 维基百科确认:”1972年2月成为武昌工务段养路工”
3 2003 年担任铁道部长 维基百科确认:2003.3.17 正式就任
4 前任部长傅志寰推动”网运分离”改革 维基百科”傅志寰”条目确认
5 2004 年启动《中长期铁路网规划》,目标 10 万公里 维基百科确认:2004.1 经国务院常务会议通过
7 刘志军提出”跨越式发展”替代”网运分离” 维基百科确认:2002.12 正式提出
8 2004 年启动 200km/h 动车组招标,2005 年启动 300km/h 招标 维基百科确认
9 2007.4.18 第六次大提速 维基百科确认
10 2008.8 京津城际通车,中国第一条 350km/h 高铁 维基百科确认:2008.8.1 通车
14 2008.4.28 胶济铁路事故 72 人死亡 维基百科确认
15 温州动车事故 40 死 192 伤 英文维基百科确认
19 受贿金额法院认定约 6400 万 维基百科确认:6460.54 万元
20 2013.7 被判死缓 维基百科确认:2013.7.8 判决
22 丁书苗被判 20 年,罚款 25 亿 维基百科确认:2014.12.16 判决
23 盛光祖 2023.12 被判 15 年 维基百科确认:2023.12.12 判决

部分准确(5 条)

# 声明 偏差说明
6 上海浦东磁悬浮是”实验” 准确说是”示范运营线”,已正式商业运营,并非纯实验线
11 CRH380A 跑出 486.1km/h 严格来说是 CRH380AL(16 编组加长版),非标准 CRH380A(8 编组)
13 雪灾致超 20 万人被困广州站 中文维基记载 20 万,但英文维基引用数据为高峰 50-80 万人,视频数字偏保守
16 刘志祥因”涉黑”被判死缓 正式罪名为故意伤害罪等,无”黑社会性质组织罪”;判决时间维基记载 2006 年
21 铁道部”并入交通运输部” 简化说法。实际一分为三:行政职责→交通运输部 + 国家铁路局,企业职责→中国铁路总公司

无法确认(2 条)

# 声明 说明
17 丁书苗帮 23 家公司中标 57 个项目,标的额 1858 亿 维基百科记载金额为 1788 亿(非 1858 亿),”23 家””57 个”未找到权威来源直接佐证
24 丁书苗汶川地震捐款 1.14 亿 在可获取的权威来源中未找到该具体数字

有争议(1 条)

# 声明 说明
25 日本东海道新干线是当时世界上唯一盈利的高铁 过度简化。法国巴黎-里昂 TGV 线也被认为盈利。更准确的说法是”少数盈利的高铁线路之一”

值得注意的额外信息

维基百科提供了视频未提及的一些补充信息:

  • 丁书苗行贿何洪达的金额,维基百科记载为 4390 万(视频说 4400 万,基本吻合)
  • 刘志军 2015 年 12 月已由死缓减为无期徒刑
  • 盛光祖的 15 年实际由两项罪名合并:受贿罪 14 年 + 利用影响力受贿罪 7 年

釜底抽薪——中国真正的危机

来源:YouTube 视频(老梁评论节目)
链接:https://www.youtube.com/watch?v=cqY3hyxDu9o
核心主题:中国面临的不是金融危机,而是财政危机;年轻人的”不接盘”正在釜底抽薪

一、核心论点:中国怕的不是金融危机,而是财政危机

视频开篇即提出一个判断:中国真正的风险不在金融领域,而在财政领域。理由有三:

  1. 庞大的官僚体系高度依赖财政——党政机关、维稳系统、国计民生系统全部需要财政资源维系,一天没钱就运转不了。
  2. 中央集权的资源分配模式——权力自上而下分配资源,权力越集中,维系权力的财政成本越高。上面集权,就必须为下面兜底。
  3. 吃财政饭的人数量庞大——不仅是公务员和事业单位,大量民企、个体户的收入也间接依赖政府投资和财政安排(给政府送菜、承包工程、服务外包等)。

二、财政危机的七大征兆

视频列举了判断财政危机是否来临的明显标志:

序号 征兆 现状
1 公务员降薪、奖金延缓发放 已出现,沿海发达地区也有
2 教师、医生工资拖欠 部分地区出现
3 公共服务收缩(学校、医院) 已出现
4 罚款/收费增多、追缴历史税款 明显出现,非税收入上升
5 政府对民企欠款加重 严重,工程款拖欠数年
6 中央转移支付比例加大 全国多数地区依赖加深
7 社保个人负担比例提高 缴费年限延长、基数提高、比例提升

视频特别指出一个”尚未发生的标志”:维稳系统(公安、网信、监控)人员的工资尚未被削减——如果这一天到来,意味着财政危机已全面爆发。

现阶段的判断:政府未必有钱继续”治理”社会,但依然有钱”控制”社会。

三、从土地财政到债务财政

视频梳理了财政模式的演变:

  • 过去 20-30 年:土地财政 —— 政府卖地 → 房价预期上涨 → 下一块地更值钱 → 财政充裕。本质是”卖未来的土地”。
  • 当下:债务财政 —— 土地卖不出去 → 靠借债维系运转 → 借新还旧。本质是”卖未来政府的信用”。

关键数据(视频引用):

  • 中央国债:约 41 万亿
  • 地方债务:约 55 万亿
  • 合计公开债务:约 96 万亿
  • 加上隐性债务、银行坏账等:估算可能接近 200 万亿

视频强调:高负债本身不可怕,可怕的是丧失信心。引用温家宝的话”信心比真金白银更重要”。

四、年轻人的”六不”——釜底抽薪

视频的核心论述是:年轻人正在以理性选择的方式,系统性地退出国家财政体系的运转链条。这不是政治反对,而是基于生活现实的冷静判断。

1. 不买房

  • 房价不可能回到 2019-2021 高点,社会共识已形成
  • 421 家庭结构下,长辈留下的房产够用
  • 消费降级下,二手房完全可以接受
  • 土地财政的恶性循环:卖不出地 → 政府没钱 → 老百姓也没钱 → 更不消费 → 更买不了房

2. 不结婚

  • 计划生育后遗症:适龄男性比女性多约 3600 万
  • 彩礼回潮不是道德退步,而是供需失衡的市场化结果
  • 经济下行叠加高结婚成本,很多年轻男性选择单身

3. 不生孩子

  • 人口红利消失,劳动力持续减少
  • 养老和医疗体系难以为继

4. 不创业

  • “大众创业、万众创新”的口号已被现实击碎
  • 2026 年上半年:倒闭 205 万家企业,新开 165 万家,净减少 40 万家
  • 经济下行期创业 ≈ 败家,家长宁愿孩子啃老也不愿孩子创业

5. 不炒股

  • A 股出现结构性分化:国家战略清单上的企业涨,其他大面积下跌
  • 视频以宇树科技为例:8 月 19 日上市,发行价 150 元,最高冲到 1100 元,三天内跌 40%
  • A 股从诞生起就是为国企/战略企业输血的工具,散户是韭菜
  • 越来越多人发誓不再碰 A 股

6. 不交社保(最致命)

  • 灵活就业人群中数千万人断缴或不缴社保
  • 年轻人算得很清楚:替代率低 + 通胀贬值 + 未来供养人口减少 = 不划算
  • 缴费年限延长(15→20 年)、基数提高、比例提升,进一步劝退

视频的核心比喻:这”六不”就是年轻人从政府这口大锅底下把柴火抽走——釜底抽薪。

五、从增量分配到存量争夺

视频还指出一个并行的结构性变化:

  • 过去:经济增长期,蛋糕不断做大,增量分配→各方都能获利→制度矛盾被掩盖
  • 现在:蛋糕做不大了,增量分配完毕→各方争夺存量
    • 中央 vs 地方
    • 银行 vs 财政
    • 国企 vs 民企
    • 权贵集团之间
    • “公共利益部门化,部门利益法治化”现象加剧

在存量争夺中,老百姓始终是弱势群体,没有资格和能力参与争夺。即使改革到来,离权力最近的人也最先获益(类比苏联解体后的寡头)。

六、结论

视频最终的呼吁:

扎扎实实做好民生,少谈宏大叙事,多讲民生福祉。
民生是最扎实的意识形态。

引用结尾:“四面湖山归眼底,万家忧乐到心头。”


核心观点可信度校验

以下为独立核查结果,对视频中涉及的具体数据和事实逐一验证。

1. 政府债务规模 —— 部分可信(数字偏高 10-20%)

项目 视频说法 实际数据
中央国债 ~41 万亿 2024 年底约 35-40 万亿,2025-2026 接近 41 万亿 — 基本准确
地方债 ~55 万亿 2024 年底约 42-46 万亿 — 偏高 15-25%
合计公开债务 ~96 万亿 实际约 77-86 万亿 — 偏高 10-20%
含隐性债务总量 ~200 万亿 国债(40)+地方债券(45)+LGFV(57-80)+政策性银行债(38) = 142-165 万亿;200 万亿属最大化口径上限

来源:IMF、Trading Economics、Wikipedia “National debt of China”


2. 公务员降薪 —— 可信度较高

  • 土地出让收入 2023→2025(4.87→4.15 万亿)两年降幅约 15%,属于缓降
  • 2026 年加速恶化:上半年仅 9,778 亿(同比 -31.5%,对比 2025H1 的 14,271 亿)。考虑到土地出让的强季节性(2025 年 H2 是 H1 的 1.9 倍),若下半年维持同比 -31.5% 则全年约 2.84 万亿,降幅约 -37%;从 2021 年峰值 8.7 万亿算起,五年缩水约 67%
  • 土地出让金曾占地方综合财力 30-40%,收入断崖直接冲击支付能力
  • 2022 年起 Bloomberg、SCMP、Reuters 报道了浙江、广东、江苏、上海等地降薪现象
  • Bloomberg 2024 年 7 月专题报道退回绩效奖金
  • 2026 年 8 月 SCMP 报道副总理丁薛祥提出财政重置方案,侧面印证

判断:大方向有充分财政数据支撑。退回年终奖是部分地区个案,非普遍政策。


3. 追缴历史税款 / 非税收入增长 —— 可信度高(硬证据)

中国财政部官方数据:

年份 非税收入(亿元) 同比增速
2023 35,655 -3.7%
2024 44,730 +25.4%
2025 39,682 -11.3%
  • 2024 年非税收入暴增 25.4%,一年多出约 9000 亿元,几乎完全对冲了税收的下降(同期税收 -3.4%、土地出让 -16%)
  • 国务院 2024 年专门发文禁止”逐利性执法”和违规异地执法
  • 最高法 2025 年 2 月要求规范”趋利性执法”

判断:政府自己发文禁止,说明现象确实严重。非税收入增长不全来自”追缴”,也包括罚没收入、国有资产处置等渠道,但视频大方向完全成立。


4. 适龄男女比例失衡 3600 万 —— 部分可信

  • 2020 年第七次人口普查:男性 7.233 亿 vs 女性 6.884 亿,全年龄段男性多出约 3490 万(与 3600 万偏差约 3%)
  • 15-64 岁年龄段:男性多出约 2850 万
  • 15-39 岁核心适婚年龄段:男性多出约 985 万
  • 堪萨斯大学 2016 年研究指出,”3000 万缺失女性”中有 1000-1500 万可能是延迟户籍登记的统计假象

判断:全年龄段数字基本准确,但视频将此标注为”适龄”有误导性——适婚年龄段的实际差异远小于全年龄段总差异。


5. 2026 年上半年企业倒闭 205 万家 —— 无法验证

  • 官方数据(国家市场监管总局,2025 年 1 月):截至 2024 年底有 1.89 亿个注册市场主体,同比增长 3.1% — 总量仍正增长
  • 仅餐饮行业一个行业,2025 年上半年就有 161 万家店关闭
  • 中国官方通常不公布注销/倒闭明细数据

判断:找不到权威来源确认这组具体数字。可能来自企查查等商业数据库,但”关门”不等于”注销”,口径不同。


6. 年轻人断缴社保几千万 —— 基本可信(甚至偏保守)

NBS 2025 年统计公报:

  • 全国就业人员 7.25 亿
  • 工伤保险仅覆盖 3.05 亿(与城镇就业 4.75 亿相差约 1.7 亿)
  • 失业保险仅 2.49 亿,不到城镇就业的一半
  • 灵活就业总量约 2 亿人(人社部多次公布的数字)

判断:从工伤保险覆盖缺口(~1.7 亿)来看,”几千万”不交或断交社保在数量级上合理,甚至偏保守。


7. 2030 年社保体系崩溃 —— 部分可信,时间点有误

  • 最知名预测来自中国社科院 2019 年《中国养老金精算报告 2019-2050》
  • 预测城镇职工基本养老保险基金累计结余将在 2027 年前后达峰值,到 2035 年前后结余归零
  • 视频说 2030 年,实际应为 2035 年(偏差 5 年)
  • “崩溃”用词夸大——结余耗尽不等于体系崩溃,政府仍可通过财政转移支付继续发放
  • 2024 年 9 月中国已通过延迟退休法案(男性逐步提至 63 岁),对冲措施在推进

8. 宇树科技上市数据 —— 可信度高

项目 视频说法 实际数据
上市日期 8 月 19 日 2026-08-19 科创板 688836 — 准确
发行价 150 元 募资 61 亿 / 发行 4045 万股 ≈ 150.8 元 — 准确
最高价 1100 元 52 周最高 1100.00 元 — 准确
跌幅 三天跌 40% 截至 8/28 收盘 585 元,从最高价跌约 46.8% — 方向准确

来源:StockAnalysis (688836)、CNN 2026-08-18、BBC 中文 2026-08-19


9. 土地财政→债务财政 —— 可信度高(完整证据链)

期间 土地出让金(亿元) 同比 较 2021 峰值
2021 全年 87,051(峰值) — —
2022 全年 66,854 -23% -23%
2023 全年 57,996 -13% -33%
2024 全年 48,745 -16% -44%
2025 全年 41,518 -15% -52%
2025 H1 14,271 -6.5% —
2026 H1 9,778 -31.5% —

2025→2026 上半年同比从 -6.5% 骤降至 -31.5%,加速恶化明显。考虑土地出让的强季节性(2025 年 H2 是 H1 的 1.9 倍),若下半年维持同比 -31.5%,则 2026 全年约 2.84 万亿,较峰值缩水约 67%。同期:

  • 财政综合赤字率从 GDP 的 1-2% 飙升至 2024 年 7.7%,2025 年预计 8.5-9%(Rhodium Group)
  • 政府债券占银行资产从 2014 年 4.1% 升至 2025 年 14.7%
  • Rhodium Group 明确将此描述为 “regime shift”

来源:中国财政部、Reuters、Rhodium Group、Caixin Global、PIIE


总评

# 观点 可信度 备注
1 政府债务规模 部分可信 方向对,数字偏高 10-20%
2 公务员降薪 较高 有财政数据和国际媒体报道支撑
3 追缴历史税款 高 非税收入暴增 25.4% 有财政部官方铁证
4 男女失衡 3600 万 部分可信 全年龄段约 3490 万,”适龄”标签有误导
5 企业倒闭 205 万家 无法验证 无权威来源
6 断缴社保几千万 基本可信 甚至偏保守
7 2030 社保崩溃 部分可信 应为 2035 年,”崩溃”用词夸大
8 宇树科技数据 高 核心数据全部可验证
9 土地→债务财政 高 完整证据链
10 长鑫科技 部分可信 名称有误,疑指长鑫科技

整体评价:视频的大方向判断多数有据可查,尤其是财政转型、非税收入暴增、公务员降薪等核心论点有扎实的官方数据支撑。但在具体数字上有向”更严重”方向取整和夸大的倾向(债务规模偏高、社保预测提前 5 年),个别数据无法验证(企业倒闭数据)。作为时事评论节目,整体信息质量中等偏上,核心论证逻辑成立,但观众需注意具体数字的精确性。

问题:为什么你的 SYN 包被丢了——求答案

江湖有个传说:一旦设置了 net.ipv4.tcp_tw_recycle 在 NAT 场景下(多个客户端经过一个 LVS 访问一个服务端,NAT 后服务端看到的客户端是同一个 IP) 就会发生丢包

也可以看看这节:https://xiaolincoding.com/network/3_tcp/syn_drop.html#%E5%9D%91%E7%88%B9%E7%9A%84-tcp-tw-recycle

我们来重现和分析一下这个问题,这应该会是个连续剧,我会根据大家实验情况来推进后面的内容

客户端

我们默认 99 的 ECS 就行,默认配置。

1
yum install tcptraceroute //装一下这个工具,可以对端口进行探测

想要持续对端口探测,类似 ping 一样,就装一下下面的 shell 脚本(tcpping , tcpping 里面会调用 tcptraceroute ):

介绍:http://www.vdberg.org/~richard/tcpping.html

脚本我放仓库里了:https://github.com/plantegg/programmer_case/commit/7c59a7d3666db2fe5af9ba598104cd88cad52497

探测端口通不通:

1
tcpping -d 1.2.3.4 22

通的话会收到:

1
seq 0: tcp response from e237  <syn,ack>  0.088 ms

端口不存在肯定不通,且会被 RST

1
seq 3: tcp response from e237  <rst,ack>  0.076 ms

端口在但是偶尔不通,是今天的主角:

1
2
3
4
5
6
Thu Aug  8 16:48:44 CST 2024
traceroute to , 255 hops max, 60 byte packets
seq 13: tcp response from <syn,ack> 28.850 ms //通
Thu Aug 8 16:48:52 CST 2024
traceroute to , 255 hops max, 60 byte packets
seq 21: no response (timeout) //不通

服务端

建议找一个 3.10 内核的机器,4.13 开始已经没有 tcp_tw_recycle 这个参数了

1
2
3
4
5
6
#sysctl -a |egrep "recycle|timestamp"
net.ipv4.tcp_timestamps = 1
net.ipv4.tcp_tw_recycle = 1 //这个参数默认是 0,必须改成 1

#uname -r
3.10.0-1062.18.1.el7.x86_64 //我在阿里云上买的一个 CentOS7,版本只要是 4.12 之前的都行

实验步骤

  1. 服务端随便起一个 http 服务端口,比如 1234
  2. 在客户端上执行 tcpping -d 服务端 ip 1234 //这一步可以看到 100% 是通的
  3. 在客户端执行下 curl 服务端 ip:1234 //可以很快看到 tcpping 开始报偶尔通/偶尔不通

重现效果,如下图,本来一次都没有 timeout,但我 curl 一下立即就 timeout 了

img

请分析,为什么 curl 一下 tcpping 就不通了?

跨 Die 热迁移迁移导致的性能问题

场景

在 ARM 上,本来业务跑在 Die0 上,如果通过taskset 将 CPU 绑到 Die1,经过一段时间运行,内存也会慢慢随着释放/新分配慢慢都迁移到 Die1, 但是这之后仍然发现性能比在 Die0 上要差很多。而在 Intel X86 上没碰到过这个问题

分析

下图是内存带宽使用率监控数据,可以看到跨 Die 绑定后原来的 Die0 带宽急剧增加(7.5% -> 13%)这是因为内存都需要跨 Die 访问了,随着跨 Die 访问并慢慢将内存迁移到 Die1 后内存带宽使用率回落到 5%:

img

同时可以看到 CPU 使用率也从 35% 飙升到 76%,随着内存的迁移完毕,CPU 使用率回落到 48%,但仍然比最开始的 35% 高不少:

img

Intel X86 下跨 Die 迁移

Intel 基本都是一路就是一个 Die,所以你可以理解这里的跨 Die 就是跨路/Socket 迁移:

img

img

结论

ARM 比 X86 差这么多的原因是内存页表跨 Die 访问导致的,业务同学测试了开启 THP 负载的影响,从结果看,THP on 可有效降低cpu水位,但是依然存在跨die迁移cpu水位上升的问题。

alios 对应的 patch:https://gitee.com/anolis/cloud-kernel/pulls/2254/commits 不区分 x86 和 arm

页表是进程创建时在内核空间分配好的,所以迁移内存时并不会迁移页表。通过测试页表跨die迁移的poc,验证了跨die页表访问是导致本文所述问题的根本原因

课后作业:

去了解下内存页表/THP(透明大页)

学习 CPU 访问内存延时是怎么回事,然后学习 CPU 和内存速度的差异,最后再去看看大学的计算机组成原理

学习下 taskset/perf 等命令

CPU 使用率高就一定有效率吗?

背景

最近碰到一个客户业务跑在8C ECS 上,随着业务压力增加 CPU使用率也即将跑满,于是考虑将 8C 升级到16C,事实是升级后业务 RT 反而略有增加,这个事情也超出了所有程序员们的预料,所以我们接下来分析下这个场景

分析

通过采集升配前后、以前和正常时段的火焰图对比发现CPU 增加主要是消耗在 自旋锁上了:

image-20240202085410669

用一个案例来解释下自旋锁和锁,如果我们要用多线程对一个整数进行计数,要保证线程安全的话,可以加锁(synchronized), 这个加锁操作也有人叫悲观锁,抢不到锁就让出这个线程的CPU 调度(代价上下文切换一次,几千个时钟周期)

另外一种是用自旋锁(CAS、spin_lock) 来实现,抢不到锁就耍赖占住CPU 死磕不停滴抢(CPU 使用率一直100%),自旋锁的设计主要是针对抢锁概率小、并发低的场景。这两种方案针对场景不一样各有优缺点

假如你的机器是8C,你有100个线程来对这个整数进行计数的话,你用synchronized 方式来实现会发现CPU 使用率永远达不到50%

image-20240202090428778

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#taskset -a -c 56-63 java LockAccumulator 100 1000000000
累加结果: 1000000000 and time:84267

Performance counter stats for 'taskset -a -c 56-63 java -Djava.library.path=. LockAccumulator 100 100000000':

17785.271791 task-clock (msec) # 2.662 CPUs utilized
110,351 context-switches # 0.006 M/sec
10,094 cpu-migrations # 0.568 K/sec
11,724 page-faults # 0.659 K/sec
44,187,609,686 cycles # 2.485 GHz
<not supported> stalled-cycles-frontend
<not supported> stalled-cycles-backend
22,588,807,670 instructions # 0.51 insns per cycle
6,919,355,610 branches # 389.050 M/sec
28,707,025 branch-misses # 0.41% of all branches

如果我们改成自旋锁版本的实现,8个核CPU 都是100%

image-20240202090714845

以下代码累加次数只有加锁版本的10%,时间还长了很多,也就是效率产出实在是低

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#taskset -a -c 56-63 java -Djava.library.path=. SpinLockAccumulator 100 100000000
累加结果: 100000000
操作耗时: 106593 毫秒

Performance counter stats for 'taskset -a -c 56-63 java -Djava.library.path=. SpinLockAccumulator 100 100000000':

85363.429249 task-clock (msec) # 7.909 CPUs utilized
23,010 context-switches # 0.270 K/sec
1,262 cpu-migrations # 0.015 K/sec
13,403 page-faults # 0.157 K/sec
213,191,037,155 cycles # 2.497 GHz
<not supported> stalled-cycles-frontend
<not supported> stalled-cycles-backend
43,523,454,723 instructions # 0.20 insns per cycle
10,306,663,291 branches # 120.739 M/sec
14,704,466 branch-misses # 0.14% of all branches

代码

我放在了github 上,有个带调X86 平台 pause 指令的汇编,Java 中要用JNI 来调用,ChatGPT4帮我写的,并给了编译、运行方案:

1
2
3
4
5
6
7
8
9
10
javac SpinLockAccumulator.java
javah -jni SpinLockAccumulator

# Assuming GCC is installed and the above C code is in SpinLockAccumulator.c
gcc -shared -o libpause.so -fPIC SpinLockAccumulator.c

java -Djava.library.path=. SpinLockAccumulator

实际gcc编译要带上jdk的头文件:
gcc -I/opt/openjdk/include/ -I/opt/openjdk/include/linux/ -shared -o libpause.so -fPIC SpinLockAccumulator.c

在MySQL INNODB 里怎么优化这个自旋锁

MySQL 在自旋锁抢锁的时候每次会调 ut_delay(底层会掉CPU指令,让CPU暂停一下但是不让出——避免上下文切换),发现性能好了几倍。这是MySQL 的官方文档:https://dev.mysql.com/doc/refman/5.7/en/innodb-performance-spin_lock_polling.html

所以我们继续在以上代码的基础上在自旋的时候故意让CPU pause(50个), 这个优化详细案例:https://plantegg.github.io/2019/12/16/Intel%20PAUSE%E6%8C%87%E4%BB%A4%E5%8F%98%E5%8C%96%E6%98%AF%E5%A6%82%E4%BD%95%E5%BD%B1%E5%93%8D%E8%87%AA%E6%97%8B%E9%94%81%E4%BB%A5%E5%8F%8AMySQL%E7%9A%84%E6%80%A7%E8%83%BD%E7%9A%84/

该你动手了

随便找一台x86 机器,笔记本也可以,macOS 也行,核数多一些效果更明显。只要有Java环境,就用我编译好的class、libpause.so 理论上也行,不兼容的话按代码那一节再重新编译一下

可以做的实验:

  • 重复我前面两个运行,看CPU 使用率以及最终耗时
  • 尝试优化待pause版本的自旋锁实现,是不是要比没有pause性能反而要好
  • 尝试让线程sleep 一下,效果是不是要好?
  • 尝试减少线程数量,慢慢是不是发现自旋锁版本的性能越来越好了

改变线程数量运行对比:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
//自旋锁版本线程数对总时间影响很明显,且线程少的话性能要比加锁版本好,这符合自旋锁的设定:大概率不需要抢就用自旋锁
#taskset -a -c 56-63 java -Djava.library.path=. SpinLockAccumulator 1 100000000
累加结果: 100000000
操作耗时: 2542 毫秒

#taskset -a -c 56-63 java -Djava.library.path=. SpinLockAccumulator 2 100000000
累加结果: 100000000
操作耗时: 2773 毫秒

#taskset -a -c 56-63 java -Djava.library.path=. SpinLockAccumulator 4 100000000
累加结果: 100000000
操作耗时: 4109 毫秒

#taskset -a -c 56-63 java -Djava.library.path=. SpinLockAccumulator 8 100000000
累加结果: 100000000
操作耗时: 11931 毫秒

#taskset -a -c 56-63 java -Djava.library.path=. SpinLockAccumulator 16 100000000
累加结果: 100000000
操作耗时: 13476 毫秒


//加锁版本线程数变化对总时间影响不那么大
#taskset -a -c 56-63 java -Djava.library.path=. LockAccumulator 16 100000000
累加结果: 100000000 and time:9074

#taskset -a -c 56-63 java -Djava.library.path=. LockAccumulator 8 100000000
累加结果: 100000000 and time:8832

#taskset -a -c 56-63 java -Djava.library.path=. LockAccumulator 4 100000000
累加结果: 100000000 and time:7330

#taskset -a -c 56-63 java -Djava.library.path=. LockAccumulator 2 100000000
累加结果: 100000000 and time:6298

#taskset -a -c 56-63 java -Djava.library.path=. LockAccumulator 1 100000000
累加结果: 100000000 and time:3143

设定100并发下,改变机器核数对比:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
//16核机器跑3次 耗时稳定在12秒以上,CPU使用率 1600%
#taskset -a -c 48-63 java -Djava.library.path=. SpinLockAccumulator 100 10000000
累加结果: 10000000
操作耗时: 12860 毫秒

#taskset -a -c 48-63 java -Djava.library.path=. SpinLockAccumulator 100 10000000
累加结果: 10000000
操作耗时: 12949 毫秒

#taskset -a -c 48-63 java -Djava.library.path=. SpinLockAccumulator 100 10000000
累加结果: 10000000
操作耗时: 13692 毫秒

//8核机器跑3次,耗时稳定5秒左右,CPU使用率 800%
#taskset -a -c 56-63 java -Djava.library.path=. SpinLockAccumulator 100 10000000
累加结果: 10000000
操作耗时: 6773 毫秒

#taskset -a -c 56-63 java -Djava.library.path=. SpinLockAccumulator 100 10000000
累加结果: 10000000
操作耗时: 5557 毫秒

#taskset -a -c 56-63 java -Djava.library.path=. SpinLockAccumulator 100 10000000
累加结果: 10000000
操作耗时: 2724 毫秒

总结

以后应该不会再对升配后CPU 使用率也上去了,但是最终效率反而没变展现得很惊诧了

从CPU 使用率、上下文切换上理解自旋锁(乐观锁)和锁(悲观锁)

MySQL 里对自旋锁的优化,增加配置 innodb_spin_wait_delay 来增加不同场景下DBA 的干预手段

这篇文章主要功劳要给 ChatGPT4 ,里面所有演示代码都是它完成的

相关阅读

流量一样但为什么CPU使用率差别很大 同样也是跟CPU 要效率,不过这个案例不是因为自旋锁导致CPU 率高,而是内存延时导致的

今日短平快,ECS从16核升配到48核后性能没有任何提升(Netflix) 也是CPU 使用率高没有产出,cacheline伪共享导致的

听风扇声音来定位性能瓶颈

你要是把这个案例以及上面三个案例综合看明白了,相当于把计算机组成原理就学明白了。这里最核心的就是“内存墙”,也就是内存速度没有跟上CPU的发展速度,导致整个计算机内绝大多场景下读写内存缓慢成为主要的瓶颈

为什么你的 SYN 包被丢 net.ipv4.tcp_tw_recycle

本来这是我计划在知识星球里要写的连续剧,我打算好好多写几篇的(每篇都计划重现一个场景/坑点),后来没看到任何一个同学参与,这样的话写了你们看完也没有体感,所以我直接公布答案吧,还能节省点你们的时间,记住干货就好:不要开 net.ipv4.tcp_tw_recycle

作为全网最权威/最全面的 net.ipv4.tcp_tw_recycle 问题分析还是从知识星球分享出来,希望更多的人避免踩坑

答案

首先不通了是因为服务端开启了 net.ipv4.tcp_tw_recycle,需要判断握手包的时间得保持递增(T2 - T1 >1)

tcpping 一直是通的,因为服务端没有记录到 T1,T1 是每次 FIN 断开时记录,T2 是每个 SYN 包中携带。当 curl 然后断开时走了 FIN 服务端记录下 T1,下次 tcpping 就可以比较了,所以有一半概率不通,直到 1 分钟后 T1 一直没有跟新,超过 60 秒的 T1 失效,后面连接正常

为什么要有 net.ipv4.tcp_tw_recycle?

net.ipv4.tcp_tw_recycle 是一个 Linux 内核参数,用于控制 TCP 连接的 TIME_WAIT 状态的处理方式。这个参数的主要作用是加速 TIME_WAIT 套接字的回收。

参考:Coping with the TCP TIME-WAIT state on busy Linux servers

PAWS(Protection Against Wrapped Sequences)

TCP 包的 seq 是有限的(4字节 32bit),会在达到最大值后回绕到零,这种情况称为”seq回绕”,seq 回绕后怎么判断这个 seq 是重复的(丢弃) 还是可以接受的?

引入 PAWS 的目的是确保即使seq 回绕发生,也能正确地处理序列号,除了 seq 外额外在 TCP options 里面增加了 timestamp 来作为维护数据包的seq 正确的判断。时间戳随每个数据包发送,并且单调增加,因此即使序列号回绕,接收方也可以使用时间戳来确定数据包的真实顺序,这就是 PAWS

PAWS会检查syn 网络包的 timestamps ,来判断这个syn包的发送时间是否早于上一次同 ip/stream(3.10 是 per ip/4.10 是 per stream) 的 fin包,如果早就扔掉,这也是导致syn 握手失败的一个高发原因,尤其是在NAT场景下。原本 PAWS 是每个连接的维度,但同时开启tcp_timestamp和tcp_tw_recycle之后,PAWS就变成per host粒度了

1
2
3
timestamp为TCP/IP协议栈提供了两个功能:  
a. 更加准确的RTT测量数据,尤其是有丢包时 -- RTTM
b. 保证了在极端情况下,TCP的可靠性 -- PAWS

不同 OS 内核版本因为 timestamp 生成不一样导致 PAWS 行为还不一样,通过参数来控制:net.ipv4.tcp_timestamps

服务端如何通过判断时间戳来丢包?

对同一个 src-ip 记录最后一次 FIN 包的时间戳为 T1,当这个 src-ip 有 SYN 包时取 SYN 包中的时间戳为 T2

如果 T2-T1 小于 1 就扔掉这个 SYN 包

一旦发生这种 SYN 包被丢弃,对应的监控指标(LINUX_MIB_PAWSPASSIVEREJECTED):

1
2
3
4
5
6
7
8
9
10
11
12
//第二个指标包含第一个,passive connections rejected 了也一定会是 SYN dropped
#netstat -s |egrep "SYNs to LISTEN sockets dropped|passive connections rejected because"
960055 passive connections rejected because of time stamp
1049368 SYNs to LISTEN sockets dropped

#netstat -s |egrep "SYNs to LISTEN sockets dropped|passive connections rejected because"
960535 passive connections rejected because of time stamp
1049848 SYNs to LISTEN sockets dropped

#netstat -s |egrep "SYNs to LISTEN sockets dropped|passive connections rejected because"
961015 passive connections rejected because of time stamp
1050328 SYNs to LISTEN sockets dropped

这个指标也很重要,我喜欢这种

服务端丢包条件更多细节

服务端设置 net.ipv4.tcp_tw_recycle 为 1 是必要条件,然后同时满足了这两个条件:

  1. (u32)get_seconds() - tm->tcpm_ts_stamp < TCP_PAWS_MSL(=60):容易满足,几乎总是满足。对比的是本地时间。收到syn的本地时间相比上次收包记录的本地时间,小于60s
  2. (s32)(tm->tcpm_ts - req->ts_recent) > TCP_PAWS_WINDOW(=1):对比的是tcp时间戳,上次更新的tcp时间戳 - 这次syn的tcp时间戳,大于1(并且小于231)。也就是这次syn的tcp时间戳,如果小于上次记录到的时间戳(ms级),就会被丢掉。

这里tm和req对应什么?一个四元组,还是ip地址,还是其他?3.10对应的是ip地址(不同内核版本不一样)

上次记录的时间戳是什么?注意这里对比的都是tm时间,是在连接关闭相关阶段,通过tcp_remember_stamp或tcp_tw_remember_stamp函数记录的,具体情况比较多。

服务端将客户端的时间戳保存在哪里?

6u(2.6.32)代码:

由于inet_timewait_sock在连接进入tw状态会被释放掉,其中记录最近一次接收报文的timestamp信息会丢失;VJ 的思路,把此tcp stamp信息放入路由cache表的rtable中struct inet_peer中,rtable中只保srcIP,dstIP的PATH信息,没有端口号信息,也就是同src-dstIP(即使端口不同)的所有连接受同一个timestamp限制。

7u(3.10.0)代码:

3.5版本以后的内核版本不再使用rtable记录,tcp stamp信息改为存放在目标地址出接口net中存放的tcp_metrics_block,timestamp判断逻辑跟6u比增加了“如果之前有记录timestamp且在一个MSL内,而本次连接无timestamp时,请求被丢弃”的逻辑,这么修改的原因参见:

https://patchwork.ozlabs.org/patch/380021/

https://patchwork.ozlabs.org/patch/379163/

2017 年的这个讨论https://patchwork.ozlabs.org/project/netdev/patch/20170315203046.158791-1-soheil.kdev@gmail.com/ 要去掉这个全局存放,改成可以按客户端 port 来记录

客户端如何生成时间戳?

  • 3.10 内核是按 客户端 ip 来生成 timestamp,也就是不管跟谁通信都是全局单调递增
  • 4.19(4.12)是按 ip 对(per-destination timestamp**)**来生 timestamp ,也就是一对 ip 之间保证单调递增;
  • 4.10之前是 per-client 生成递增 timestamp ,4.10 改成 per-connection 生成递增 timestamp(导致了兼容 net.ipv4.tcp_tw_recycle问题严重),4.11 改成 per-destination-host 生成递增 timestamp(downgrade to per-host timestamp offsets);4.12 去掉 net.ipv4.tcp_tw_recycle 参数永远解决问题

有哪些场景会触发 net.ipv4.tcp_tw_recycle 丢包

服务端的内核参数 net.ipv4.tcp_tw_recycle(4.12内核 中删除这个参数了) 和 net.ipv4.tcp_timestamps 的值都为 1时,服务器会检查每一个 SYN报文中的时间戳(Timestamp,跟同一ip下最近一次 FIN包时间对比),若 Timestamp 不是递增的关系,就扔掉这个SYN包(诊断:netstat -s | grep “ passive connections rejected because of time stamp”),常见触发时间戳非递增场景:

  1. 4.10 内核,一直必现大概率性丢包。4.11 改成了 per-destination host的算法 //内核改来改去也是坑点
  2. tcpping 这种时间戳按连接随机的,必现大概率持续丢包
  3. 同一个客户端通过直连或者 NAT 后两条链路到同一个服务端,客户端生成时间戳是 by dst ip,导致大概率持续丢包
  4. 经过NAT/LVS 后多个客户端被当成一个客户端,小概率偶尔出现——通过 tc qdisc 可以来构造丢包重现该场景
  5. 网路链路复杂/链路长容易导致包乱序,进而出发丢包,取决于网络会小概率出现
  6. 客户端修改 net.ipv4.tcp_timestamps
    • 1->0,触发持续60秒大概率必现的丢包,60秒后恢复
    • 0->1 持续大概率一直丢包60秒; 60秒过后如果网络延时略高且客户端并发大一直有上一次 FIN 时间戳大于后续SYN 会一直概率性丢包持续下去;如果停掉所有流量,重启客户端流量,恢复正常
    • 2->1 丢包,情况同2
    • 1->2 不触发丢包

其它 SYN 连不上的场景延伸阅读:程序员如何学习和构建网络知识体系

一些特殊场景

这些特殊场景很可怕,不知不觉会产生 T2 不大于 T1 的情况,导致连接异常

DNAT/ENAT

请求经过 DNAT 后 Server 端看到的 src-ip 是 client 的 IP,客户端同时通过直连(绿色)和走 LVS(黑色)两条链路就会大概率不通:

image-20240822161109563

没有挥手断开场景

有些 HA 探测都是握手/select 1/ RESET 连接,不走 FIN 四次挥手(比如 Jedis,见小作业应用断开连接的时候如何让 OS 走 RST 流程:https://articles.zsxq.com/id_v0mhaadx3cx5.html ),Server 端没有机会记录 T1,也就永远不会触发丢包,看着一切正常,直到某天来了个用户 curl 一下系统就崩了

比如 Jedis 就是直接 RST 断开连接,从不走 FIN 四次挥手

延伸

如果服务端所用端口是 time_wait 状态,这时新连接 SYN 握手包刚好和 time_wait 的5元组重复,这个时候服务端不会回复 SYN+ACK 而是回复 time_wait 前的ack

其它

Server 在握手的第三阶段(TCP_NEW_SYN_RECV),等待对端进行握手的第三步回 ACK时候,如果收到RST 内核会对报文进行PAWS校验,如果 RST 带的 timestamp(TVal) 不递增就会因为通不过 PAWS 校验而被扔掉

https://github.com/torvalds/linux/commit/7faee5c0d514162853a343d93e4a0b6bb8bfec21 这个 commit 去掉了TCP_SKB_CB(skb)->when = tcp_time_stamp,导致 3.18 的内核版本linger close主动发送的 RST 中 ts_val为0,而修复的commit在 675ee231d960af2af3606b4480324e26797eb010,直到 4.10 才合并进内核

参考资料

per-connection random offset:https://lwn.net/Articles/708021/

一次 Sysbench opening tables 卡慢的分析过程

背景

用 Sysbench 随便跑个压力,然后我用如下命令起压力,只达到了我预期的性能的 10%

1
sysbench --mysql-user=root --mysql-password=123 --mysql-db=sbtest --mysql-host=e237 --mysql-port=3306 --tables=64 --threads=256 --table-size=2000000 --range-size=5 --db-ps-mode=disable --skip-trx=on --mysql-ignore-errors=all --time=1200 --report-interval=1 --histogram=off oltp_point_select run

分析

看了下 MySQL 的进程状态,CPU 消耗很低,再看 processlist 都是 Opening tables,这问题我熟啊,table_open_cache 设置太小,直接干大 10 倍,悲催的是性能依然没有任何变化看了下 MySQL 的进程状态,CPU 消耗很低,再看 processlist 都是 Opening tables,这问题我熟啊,table_open_cache 设置太小,直接干大 10 倍,悲催的是性能依然没有任何变化

image-20241022175210162

image-20241023104502834

难道还有别的地方限制了?我去查了下 status 发现 Table_open_cache_overflows 一直是 0,从状态来看 table_open_cache 肯定够了:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
#mysql -he237 -P3306 -uroot -p123 -e "show global status like '%open%' "
mysql: [Warning] Using a password on the command line interface can be insecure.
+----------------------------+---------+
| Variable_name | Value |
+----------------------------+---------+
| Com_ha_open | 0 |
| Com_show_open_tables | 0 |
| Innodb_num_open_files | 48 |
| Open_files | 14 |
| Open_streams | 0 |
| Open_table_definitions | 159 |
| Open_tables | 1161 |
| Opened_files | 173 |
| Opened_table_definitions | 138 |
| Opened_tables | 1168 |
| Slave_open_temp_tables | 0 |
| Table_open_cache_hits | 8125315 |
| Table_open_cache_misses | 1168 |
| Table_open_cache_overflows | 0 |
+----------------------------+---------+

#mysql -he237 -P3306 -uroot -p123 -e "show global status like '%Table_open%' "
mysql: [Warning] Using a password on the command line interface can be insecure.
+----------------------------+---------+
| Variable_name | Value |
+----------------------------+---------+
| Table_open_cache_hits | 9039467 |
| Table_open_cache_misses | 1170 |
| Table_open_cache_overflows | 0 |
+----------------------------+---------+

#mysql -he237 -P3306 -uroot -p123 -e "show global variables like '%Table_open%' "
mysql: [Warning] Using a password on the command line interface can be insecure.
+----------------------------+-------+
| Variable_name | Value |
+----------------------------+-------+
| table_open_cache | 8192 |
| table_open_cache_instances | 16 |
+----------------------------+-------+

这些有点难绷,因为我用的别人的 sysbench, 于是自己编译了一个重压性能一下就正常了,于是我开始 dump 别人的 sysbench 完整参数,最后发现是我使用的时候配置错误将:–tables=32 设置成了 –tables=64 也就是我的 database 总共只有 32 张表,而我压测的时候写成了 64 张,还有 32 张表不存在导致。

而别人的 sysbench 默认添加了:–mysql-ignore-errors=all 也就是把报错信息都忽略了,导致控制台看不到异常信息

碰到这种问题怎么办?

我们经常碰到业务代码把报错信息吃掉了(类似设置了 –mysql-ignore-errors=all ),同时 SQL 里面拼错了表明或者写错了 Database 名也导致表不存在

所以这里的必杀技(银弹) 抓包(或者堆栈热点分析):

image-20241023101835801

上图中只要不是 1146 的都是表明正确的请求,可以看到 RT 是 0.1-0.2 毫秒之间;但是 response Error 1146 报错的 RT 就很大了,同时抓包里 1146 也给出了错误原因

1
2
3
4
5
+-------+------+---------------------------------------+
| Level | Code | Message |
+-------+------+---------------------------------------+
| Error | 1146 | Table 'sbtest.sbtest42' doesn't exist |
+-------+------+---------------------------------------+

正常时 50 万 QPS 的 RT:

1
2
3
	 time								port	avg_rt	svc_rt  up_rt    QPS  	drop	rtt
2024-10-23 10:14:57 P3306 227 228 0 532688 0 34
2024-10-23 10:14:58 P3306 227 228 0 533439 0 34

异常时 5 万 QPS 的 RT:

1
2
3
4
5
6
7
	 time								port	avg_rt	svc_rt  up_rt    QPS  	drop	rtt
2024-10-23 10:13:56 P3306 2201 2201 0 58910 0 34
2024-10-23 10:13:57 P3306 2195 2195 0 59141 0 34
2024-10-23 10:13:58 P3306 2203 2203 0 58923 0 34
2024-10-23 10:13:59 P3306 2190 2191 0 59266 0 34
2024-10-23 10:14:00 P3306 2198 2198 0 59018 0 34
2024-10-23 10:14:01 P3306 2242 2242 0 57926 0 34

从 RT 确实可以看出来是 3306 端口返回/响应慢了,我在 MySQLD 的日志里也搜索了,应该是没有记录这种 1146 错误

如果多看几次 processlist 的话还会发现 Opening table 的 SQL 对应的表明都是大于 31 的,表名小的 SQL 就不会出现 Opening table

总结

这个问题我第一时间没有想到抓包,显示根据经验 Opening tables 就是打开表慢了,然后调大 cache 参数,还不好用就觉得超出我的理解有点慌!

然后想到去比较参数/版本的差异,运气好发现了参数的差异;如果运气不好我重新编译然后复制白屏的命令参数估计还是发现不了。

所以我在想有什么更好的办法能识别这种问题,最后的结论居然还是抓个包看看,并且真管用,正好和这篇方法论呼应一下:https://articles.zsxq.com/id_mnp5z56gl0wi.html

延伸

很多时候开发很坑人,把业务异常堆栈吃了不输出,就拿这个例子来说也有业务写错表名,然后报错又不输出就会出现和问题一样的问题,导致分析问题的时候发现很奇怪好好的系统就是慢,这个时候除了抓包还可以通过 perf/jstack 去看看堆栈,抓下热点

推上也有一些讨论,可以参考下别人的思路:https://x.com/plantegg/status/1851066206163521712

一次网络连接残留的分析

本来放在知识星球的收费文章,也网络直播给星球成员讲解过这个问题以及这篇文章的内容,作删减和调整后也发博客吧

问题描述

LVS TCP 探活一般是 3 次握手(验证服务节点还在)后立即发送一个 RST packet 来断开连接(效率高,不需要走四次挥手),但是在我们的LVS 后面的 RS 上发现有大量的探活连接残留,需要分析为什么?

一通分析下来发现是 RST 包 和第三次握手的 ack 到达对端乱序了,导致 RST 被drop 掉了。但是还需要进一步分析 drop 的时候和 RST 包里面带的 timestamp 有没有关系?

可以用 Scapy 来实验验证如下 4 个场景:

  1. 正常三次握手,然后发送 RST 看看是否被 drop —— 期望 RST 不被 drop,连接正常释放,作为对比项
  2. 正常 2 次握手,然后立即发送 RST(正常带 timestamp),再发送 ack(制造乱序),看看 RST 会不会被 drop,如果 RST drop 后连接还能正常握手成功并残留吗?
  3. 正常 2 次握手,然后立即发送 RST(不带 timestamp),再发送 ack(制造乱序),看看 RST 会不会被 drop
  4. 正常 2 次握手,然后立即发送 RST(带 timestamp,但是 timestamp 为 0),再发送 ack(制造乱序),看看 RST 会不会被 drop

重现场景构造如下:通过客户端+服务端来尝试重现,客户端用 scapy 来构造任意网络包,服务端通过 python 起一个 WEB 服务

客户端

因为最新的 scapy 需要 python3.7 ,可以搞一个内核版本较高的 Linux 来测试(星球统一 99 块的实验 ECS 就符合要求),安装命令大概是这样:yum install python3-scapy

用 scapy 脚本构造如上 3 个场景的网络包,代码和使用帮助我放到这里了:https://github.com/plantegg/programmer_case/commit/e71ade38050c48170c7d6fb5922f78188a96435b#diff-3d18b8aa76586e6c59227e020ba22ef1ef8c5416764d0a923b198ad824996eda

如果需要构造带 timestamp 的RST 用如下代码段,乱序通过调整 ack和 RST 的顺序来实现

1
2
3
4
5
6
7
8
9
10
11
# 构造 ACK 包
ack = TCP(sport=source_port,
dport=target_port,
flags='A',
seq=syn_ack.ack,
ack=syn_ack.seq + 1,
options=[('NOP', None), ('NOP', None),
('Timestamp', (int(time.time()), 0))]) //重点调整这里的时间戳,以及 rst 和 ack 包的顺序

# 发送 ACK
send(ip/ack)

在scapy 机器上drop 掉OS 自动发送的 RST(因为连接是 scapy 伪造的,OS 收到 syn+ack 后会 OS系统会发 RST(这个 RST不带 timestamp))

1
2
3
4
iptables -A OUTPUT -p tcp --dport 8000 --tcp-flags RST RST  ! --tcp-option 8 -j DROP

//清理
iptables -D OUTPUT -p tcp --dport 8000 --tcp-flags RST RST ! --tcp-option 8 -j DROP

scapy 构造的包流程,可以看到不走内核 tcp 协议栈,也不走 nf_hook(防火墙),不受上面的 iptables 规则限制,所以能发送到服务端:

1
2
3
4
5
6
7
8
9
10
***************** c7d8ea00 ***************
[100167.011693] [__dev_queue_xmit ] TCP: 172.26.137.131:8146 -> 172.26.137.130:8000 seq:12346, ack:0, flags:R
[100167.011702] [dev_hard_start_xmit ] TCP: 172.26.137.131:8146 -> 172.26.137.130:8000 seq:12346, ack:0, flags:R *skb is successfully sent to the NIC driver*
[100167.011714] [consume_skb ] TCP: 172.26.137.131:8146 -> 172.26.137.130:8000 seq:12346, ack:0, flags:R *packet is freed (normally)*

***************** c700e300 ***************
[100167.024811] [__dev_queue_xmit ] TCP: 172.26.137.131:8146 -> 172.26.137.130:8000 seq:12346, ack:2680597246, flags:A
[100167.024821] [dev_hard_start_xmit ] TCP: 172.26.137.131:8146 -> 172.26.137.130:8000 seq:12346, ack:2680597246, flags:A *skb is successfully sent to the NIC driver*
[100167.024891] [consume_skb ] TCP: 172.26.137.131:8146 -> 172.26.137.130:8000 seq:12346, ack:2680597246, flags:A *packet is freed (normally)*

Server 端

先记住一个知识点,后面看内核调用堆栈会用得上确认是否被丢包

一个网络包正常处理流程最后调 consume_skb 来释放,如果网络包需要 Drop 就调 kfree_skb 来丢包

server端 安装 netstrace来监控包是否被drop,并通过 python 拉起一个端口:

1
python -m http.server 8000

tcpdump 确认 8000 端口收到的包

在 8000端口机器上执行抓包验证收到的包顺序和所携带的 timestamp,包含 3 个场景的包:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#tcpdump -i eth0 port 8000 -nn
//场景 2:正常 2 次握手,然后立即发送 RST(带 timestamp)
13:56:26.614701 IP 172.26.137.131.54321 > 172.26.137.130.8000: Flags [S], seq 2754757912, win 8192, options [mss 1460,nop,nop,TS val 1732514186 ecr 0], length 0
13:56:26.614815 IP 172.26.137.130.8000 > 172.26.137.131.54321: Flags [S.], seq 1579697129, ack 2754757913, win 65160, options [mss 1460,nop,nop,TS val 2888180099 ecr 1732514186], length 0
13:56:26.633997 IP 172.26.137.131.54321 > 172.26.137.130.8000: Flags [R], seq 2754757913, win 8192, options [mss 1460,nop,nop,TS val 1732514186 ecr 0], length 0 //留意端口号 54321 和 seq 2754757913 跟 nettrace 对应
13:56:26.654954 IP 172.26.137.131.54321 > 172.26.137.130.8000: Flags [.], ack 1, win 8192, options [nop,nop,TS val 1732514186 ecr 0], length 0
13:56:26.655042 IP 172.26.137.130.8000 > 172.26.137.131.54321: Flags [R], seq 1579697130, win 0, length 0

//场景 3:正常 2 次握手,然后立即发送 RST(不带 timestamp), 注意这里的 tcp options 是 null
13:56:28.993723 IP 172.26.137.131.12345 > 172.26.137.130.8000: Flags [S], seq 54243194, win 8192, options [mss 1460,nop,nop,TS val 1732514188 ecr 0], length 0
13:56:28.993809 IP 172.26.137.130.8000 > 172.26.137.131.12345: Flags [S.], seq 1983242893, ack 54243195, win 65160, options [mss 1460,nop,nop,TS val 2888182478 ecr 1732514188], length 0
13:56:29.012982 IP 172.26.137.131.12345 > 172.26.137.130.8000: Flags [R], seq 54243195, win 8192, length 0 //留意端口号 12345 和 seq 54243195 跟 nettrace 对应
13:56:29.029886 IP 172.26.137.131.12345 > 172.26.137.130.8000: Flags [.], ack 1, win 8192, options [nop,nop,TS val 1732514189 ecr 0], length 0
13:56:29.029983 IP 172.26.137.130.8000 > 172.26.137.131.12345: Flags [R], seq 1983242894, win 0, length 0 //OS 触发
13:56:29.050888 IP 172.26.137.131.12345 > 172.26.137.130.8000: Flags [R], seq 54243195, win 8192, length 0

//场景 1:正常握手,然后 RST
13:56:30.399672 IP 172.26.137.131.22345 > 172.26.137.130.8000: Flags [S], seq 1038081714, win 8192, options [mss 1460,nop,nop,TS val 1732514190 ecr 0], length 0
13:56:30.399770 IP 172.26.137.130.8000 > 172.26.137.131.22345: Flags [S.], seq 3263478059, ack 1038081715, win 65160, options [mss 1460,nop,nop,TS val 2888183884 ecr 1732514190], length 0
13:56:30.426005 IP 172.26.137.131.22345 > 172.26.137.130.8000: Flags [.], ack 1, win 8192, options [nop,nop,TS val 1732514190 ecr 0], length 0
13:56:30.448876 IP 172.26.137.131.22345 > 172.26.137.130.8000: Flags [R], seq 1038081715, win 8192, length 0

场景 1:正常三次握手后再 RST,作为对比

netstrace 命令和结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
#netstat -P 8000
***************** c22c8c00,c22c8000 ***************
[4912187.018483] [__ip_local_out ] TCP: 172.26.137.130:8000 -> 172.26.137.131:22345 seq:3263478059, ack:1038081715, flags:SA
[4912187.018485] [nf_hook_slow ] TCP: 172.26.137.130:8000 -> 172.26.137.131:22345 seq:3263478059, ack:1038081715, flags:SA *ipv4 in chain: OUTPUT*
[4912187.018487] [nft_do_chain ] TCP: 172.26.137.130:8000 -> 172.26.137.131:22345 seq:3263478059, ack:1038081715, flags:SA *iptables table:, chain:OUTPUT*
[4912187.018489] [nft_do_chain ] TCP: 172.26.137.130:8000 -> 172.26.137.131:22345 seq:3263478059, ack:1038081715, flags:SA *iptables table:, chain:OUTPUT*
[4912187.018493] [ip_output ] TCP: 172.26.137.130:8000 -> 172.26.137.131:22345 seq:3263478059, ack:1038081715, flags:SA
[4912187.018495] [nf_hook_slow ] TCP: 172.26.137.130:8000 -> 172.26.137.131:22345 seq:3263478059, ack:1038081715, flags:SA *ipv4 in chain: POST_ROUTING*
[4912187.018496] [nft_do_chain ] TCP: 172.26.137.130:8000 -> 172.26.137.131:22345 seq:3263478059, ack:1038081715, flags:SA *iptables table:, chain:POSTROU*
[4912187.018499] [ip_finish_output ] TCP: 172.26.137.130:8000 -> 172.26.137.131:22345 seq:3263478059, ack:1038081715, flags:SA
[4912187.018502] [ip_finish_output2 ] TCP: 172.26.137.130:8000 -> 172.26.137.131:22345 seq:3263478059, ack:1038081715, flags:SA
[4912187.018506] [__dev_queue_xmit ] TCP: 172.26.137.130:8000 -> 172.26.137.131:22345 seq:3263478059, ack:1038081715, flags:SA
[4912187.018510] [dev_hard_start_xmit ] TCP: 172.26.137.130:8000 -> 172.26.137.131:22345 seq:3263478059, ack:1038081715, flags:SA *skb is successfully sent to the NIC driver*
[4912187.018512] [skb_clone ] TCP: 172.26.137.130:8000 -> 172.26.137.131:22345 seq:3263478059, ack:1038081715, flags:SA
[4912187.018516] [tpacket_rcv ] TCP: 172.26.137.130:8000 -> 172.26.137.131:22345 seq:3263478059, ack:1038081715, flags:SA
[4912187.018519] [consume_skb ] TCP: 172.26.137.130:8000 -> 172.26.137.131:22345 seq:3263478059, ack:1038081715, flags:SA *packet is freed (normally)*
[4912187.018533] [consume_skb ] TCP: 172.26.137.130:8000 -> 172.26.137.131:22345 seq:3263478059, ack:1038081715, flags:SA *packet is freed (normally)*

***************** c22c8a00,c22c8f00 ***************
[4912187.044742] [napi_gro_receive_entry] TCP: 172.26.137.131:22345 -> 172.26.137.130:8000 seq:1038081715, ack:3263478060, flags:A
[4912187.044749] [dev_gro_receive ] TCP: 172.26.137.131:22345 -> 172.26.137.130:8000 seq:1038081715, ack:3263478060, flags:A
[4912187.044751] [__netif_receive_skb_core] TCP: 172.26.137.131:22345 -> 172.26.137.130:8000 seq:1038081715, ack:3263478060, flags:A
[4912187.044753] [tpacket_rcv ] TCP: 172.26.137.131:22345 -> 172.26.137.130:8000 seq:1038081715, ack:3263478060, flags:A
[4912187.044758] [ip_rcv ] TCP: 172.26.137.131:22345 -> 172.26.137.130:8000 seq:1038081715, ack:3263478060, flags:A
[4912187.044760] [ip_rcv_core ] TCP: 172.26.137.131:22345 -> 172.26.137.130:8000 seq:1038081715, ack:3263478060, flags:A
[4912187.044762] [skb_clone ] TCP: 172.26.137.131:22345 -> 172.26.137.130:8000 seq:1038081715, ack:3263478060, flags:A
[4912187.044766] [nf_hook_slow ] TCP: 172.26.137.131:22345 -> 172.26.137.130:8000 seq:1038081715, ack:3263478060, flags:A *ipv4 in chain: PRE_ROUTING*
[4912187.044769] [nft_do_chain ] TCP: 172.26.137.131:22345 -> 172.26.137.130:8000 seq:1038081715, ack:3263478060, flags:A *iptables table:, chain:PREROUT*
[4912187.044772] [ip_rcv_finish ] TCP: 172.26.137.131:22345 -> 172.26.137.130:8000 seq:1038081715, ack:3263478060, flags:A
[4912187.044776] [ip_route_input_slow ] TCP: 172.26.137.131:22345 -> 172.26.137.130:8000 seq:1038081715, ack:3263478060, flags:A
[4912187.044781] [fib_validate_source ] TCP: 172.26.137.131:22345 -> 172.26.137.130:8000 seq:1038081715, ack:3263478060, flags:A
[4912187.044785] [ip_local_deliver ] TCP: 172.26.137.131:22345 -> 172.26.137.130:8000 seq:1038081715, ack:3263478060, flags:A
[4912187.044786] [nf_hook_slow ] TCP: 172.26.137.131:22345 -> 172.26.137.130:8000 seq:1038081715, ack:3263478060, flags:A *ipv4 in chain: INPUT*
[4912187.044787] [nft_do_chain ] TCP: 172.26.137.131:22345 -> 172.26.137.130:8000 seq:1038081715, ack:3263478060, flags:A *iptables table:, chain:INPUT*
[4912187.044789] [nft_do_chain ] TCP: 172.26.137.131:22345 -> 172.26.137.130:8000 seq:1038081715, ack:3263478060, flags:A *iptables table:, chain:INPUT*
[4912187.044791] [ip_local_deliver_finish] TCP: 172.26.137.131:22345 -> 172.26.137.130:8000 seq:1038081715, ack:3263478060, flags:A
[4912187.044794] [tcp_v4_rcv ] TCP: 172.26.137.131:22345 -> 172.26.137.130:8000 seq:1038081715, ack:3263478060, flags:A
[4912187.044806] [tcp_child_process ] TCP: 172.26.137.131:22345 -> 172.26.137.130:8000 seq:1038081715, ack:3263478060, flags:A
[4912187.044810] [tcp_rcv_state_process] TCP: 172.26.137.131:22345 -> 172.26.137.130:8000 seq:1038081715, ack:3263478060, flags:A *TCP socket state has changed*
[4912187.044813] [tcp_ack ] TCP: 172.26.137.131:22345 -> 172.26.137.130:8000 seq:1038081715, ack:3263478060, flags:A
[4912187.044818] [__kfree_skb ] TCP: 172.26.137.131:22345 -> 172.26.137.130:8000 seq:1038081715, ack:3263478060, flags:A
[4912187.044825] [packet_rcv ] TCP: 172.26.137.131:22345 -> 172.26.137.130:8000 seq:1038081715, ack:3263478060, flags:A
[4912187.044827] [consume_skb ] TCP: 172.26.137.131:22345 -> 172.26.137.130:8000 seq:1038081715, ack:3263478060, flags:A *packet is freed (normally)*

***************** c22c8900,c22c8a00 ***************
[4912187.067611] [napi_gro_receive_entry] TCP: 172.26.137.131:22345 -> 172.26.137.130:8000 seq:1038081715, ack:3263478060, flags:R
[4912187.067617] [dev_gro_receive ] TCP: 172.26.137.131:22345 -> 172.26.137.130:8000 seq:1038081715, ack:3263478060, flags:R
[4912187.067622] [__netif_receive_skb_core] TCP: 172.26.137.131:22345 -> 172.26.137.130:8000 seq:1038081715, ack:3263478060, flags:R
[4912187.067624] [tpacket_rcv ] TCP: 172.26.137.131:22345 -> 172.26.137.130:8000 seq:1038081715, ack:3263478060, flags:R
[4912187.067628] [ip_rcv ] TCP: 172.26.137.131:22345 -> 172.26.137.130:8000 seq:1038081715, ack:3263478060, flags:R
[4912187.067630] [ip_rcv_core ] TCP: 172.26.137.131:22345 -> 172.26.137.130:8000 seq:1038081715, ack:3263478060, flags:R
[4912187.067631] [skb_clone ] TCP: 172.26.137.131:22345 -> 172.26.137.130:8000 seq:1038081715, ack:3263478060, flags:R
[4912187.067634] [nf_hook_slow ] TCP: 172.26.137.131:22345 -> 172.26.137.130:8000 seq:1038081715, ack:3263478060, flags:R *ipv4 in chain: PRE_ROUTING*
[4912187.067636] [nft_do_chain ] TCP: 172.26.137.131:22345 -> 172.26.137.130:8000 seq:1038081715, ack:3263478060, flags:R *iptables table:, chain:PREROUT*
[4912187.067639] [ip_rcv_finish ] TCP: 172.26.137.131:22345 -> 172.26.137.130:8000 seq:1038081715, ack:3263478060, flags:R
[4912187.067640] [ip_local_deliver ] TCP: 172.26.137.131:22345 -> 172.26.137.130:8000 seq:1038081715, ack:3263478060, flags:R
[4912187.067642] [nf_hook_slow ] TCP: 172.26.137.131:22345 -> 172.26.137.130:8000 seq:1038081715, ack:3263478060, flags:R *ipv4 in chain: INPUT*
[4912187.067643] [nft_do_chain ] TCP: 172.26.137.131:22345 -> 172.26.137.130:8000 seq:1038081715, ack:3263478060, flags:R *iptables table:, chain:INPUT*
[4912187.067644] [nft_do_chain ] TCP: 172.26.137.131:22345 -> 172.26.137.130:8000 seq:1038081715, ack:3263478060, flags:R *iptables table:, chain:INPUT*
[4912187.067646] [ip_local_deliver_finish] TCP: 172.26.137.131:22345 -> 172.26.137.130:8000 seq:1038081715, ack:3263478060, flags:R
[4912187.067648] [tcp_v4_rcv ] TCP: 172.26.137.131:22345 -> 172.26.137.130:8000 seq:1038081715, ack:3263478060, flags:R
[4912187.067650] [tcp_filter ] TCP: 172.26.137.131:22345 -> 172.26.137.130:8000 seq:1038081715, ack:3263478060, flags:R
[4912187.067651] [tcp_v4_do_rcv ] TCP: 172.26.137.131:22345 -> 172.26.137.130:8000 seq:1038081715, ack:3263478060, flags:R
[4912187.067653] [tcp_rcv_established ] TCP: 172.26.137.131:22345 -> 172.26.137.130:8000 seq:1038081715, ack:3263478060, flags:R
[4912187.067659] [__kfree_skb ] TCP: 172.26.137.131:22345 -> 172.26.137.130:8000 seq:1038081715, ack:3263478060, flags:R
[4912187.067685] [packet_rcv ] TCP: 172.26.137.131:22345 -> 172.26.137.130:8000 seq:1038081715, ack:3263478060, flags:R
[4912187.067687] [consume_skb ] TCP: 172.26.137.131:22345 -> 172.26.137.130:8000 seq:1038081715, ack:3263478060, flags:R *packet is freed (normally)* //RST packet 被正常处理,没有发生 drop

场景 2:正常 2 次握手,然后立即发送 RST(带 timestamp)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
#netstat -P 8000
//场景 2:正常 2 次握手,然后立即发送 RST(带 timestamp)—— RST 被 drop 了
***************** c22c8900,c22c8300 *************** //8000 端口回复的 syn+ack
[4912183.233533] [__ip_local_out ] TCP: 172.26.137.130:8000 -> 172.26.137.131:54321 seq:1579697129, ack:2754757913, flags:SA
[4912183.233535] [nf_hook_slow ] TCP: 172.26.137.130:8000 -> 172.26.137.131:54321 seq:1579697129, ack:2754757913, flags:SA *ipv4 in chain: OUTPUT*
[4912183.233537] [nft_do_chain ] TCP: 172.26.137.130:8000 -> 172.26.137.131:54321 seq:1579697129, ack:2754757913, flags:SA *iptables table:, chain:OUTPUT*
[4912183.233538] [nft_do_chain ] TCP: 172.26.137.130:8000 -> 172.26.137.131:54321 seq:1579697129, ack:2754757913, flags:SA *iptables table:, chain:OUTPUT*
[4912183.233541] [ip_output ] TCP: 172.26.137.130:8000 -> 172.26.137.131:54321 seq:1579697129, ack:2754757913, flags:SA
[4912183.233542] [nf_hook_slow ] TCP: 172.26.137.130:8000 -> 172.26.137.131:54321 seq:1579697129, ack:2754757913, flags:SA *ipv4 in chain: POST_ROUTING*
[4912183.233543] [nft_do_chain ] TCP: 172.26.137.130:8000 -> 172.26.137.131:54321 seq:1579697129, ack:2754757913, flags:SA *iptables table:, chain:POSTROU*
[4912183.233546] [ip_finish_output ] TCP: 172.26.137.130:8000 -> 172.26.137.131:54321 seq:1579697129, ack:2754757913, flags:SA
[4912183.233549] [ip_finish_output2 ] TCP: 172.26.137.130:8000 -> 172.26.137.131:54321 seq:1579697129, ack:2754757913, flags:SA
[4912183.233552] [__dev_queue_xmit ] TCP: 172.26.137.130:8000 -> 172.26.137.131:54321 seq:1579697129, ack:2754757913, flags:SA
[4912183.233555] [dev_hard_start_xmit ] TCP: 172.26.137.130:8000 -> 172.26.137.131:54321 seq:1579697129, ack:2754757913, flags:SA *skb is successfully sent to the NIC driver*
[4912183.233557] [skb_clone ] TCP: 172.26.137.130:8000 -> 172.26.137.131:54321 seq:1579697129, ack:2754757913, flags:SA
[4912183.233561] [tpacket_rcv ] TCP: 172.26.137.130:8000 -> 172.26.137.131:54321 seq:1579697129, ack:2754757913, flags:SA
[4912183.233565] [consume_skb ] TCP: 172.26.137.130:8000 -> 172.26.137.131:54321 seq:1579697129, ack:2754757913, flags:SA *packet is freed (normally)*
[4912183.233581] [consume_skb ] TCP: 172.26.137.130:8000 -> 172.26.137.131:54321 seq:1579697129, ack:2754757913, flags:SA *packet is freed (normally)*

***************** c22c8000,c22c8c00 ***************//客户端发送的 RST 比 ack 先到
[4912183.252733] [napi_gro_receive_entry] TCP: 172.26.137.131:54321 -> 172.26.137.130:8000 seq:2754757913, ack:1579697130, flags:R
[4912183.252741] [dev_gro_receive ] TCP: 172.26.137.131:54321 -> 172.26.137.130:8000 seq:2754757913, ack:1579697130, flags:R
[4912183.252743] [__netif_receive_skb_core] TCP: 172.26.137.131:54321 -> 172.26.137.130:8000 seq:2754757913, ack:1579697130, flags:R
[4912183.252745] [tpacket_rcv ] TCP: 172.26.137.131:54321 -> 172.26.137.130:8000 seq:2754757913, ack:1579697130, flags:R
[4912183.252749] [ip_rcv ] TCP: 172.26.137.131:54321 -> 172.26.137.130:8000 seq:2754757913, ack:1579697130, flags:R
[4912183.252750] [ip_rcv_core ] TCP: 172.26.137.131:54321 -> 172.26.137.130:8000 seq:2754757913, ack:1579697130, flags:R
[4912183.252752] [skb_clone ] TCP: 172.26.137.131:54321 -> 172.26.137.130:8000 seq:2754757913, ack:1579697130, flags:R
[4912183.252757] [nf_hook_slow ] TCP: 172.26.137.131:54321 -> 172.26.137.130:8000 seq:2754757913, ack:1579697130, flags:R *ipv4 in chain: PRE_ROUTING*
[4912183.252759] [nft_do_chain ] TCP: 172.26.137.131:54321 -> 172.26.137.130:8000 seq:2754757913, ack:1579697130, flags:R *iptables table:, chain:PREROUT*
[4912183.252761] [ip_rcv_finish ] TCP: 172.26.137.131:54321 -> 172.26.137.130:8000 seq:2754757913, ack:1579697130, flags:R
[4912183.252765] [ip_route_input_slow ] TCP: 172.26.137.131:54321 -> 172.26.137.130:8000 seq:2754757913, ack:1579697130, flags:R
[4912183.252771] [fib_validate_source ] TCP: 172.26.137.131:54321 -> 172.26.137.130:8000 seq:2754757913, ack:1579697130, flags:R
[4912183.252773] [ip_local_deliver ] TCP: 172.26.137.131:54321 -> 172.26.137.130:8000 seq:2754757913, ack:1579697130, flags:R
[4912183.252775] [nf_hook_slow ] TCP: 172.26.137.131:54321 -> 172.26.137.130:8000 seq:2754757913, ack:1579697130, flags:R *ipv4 in chain: INPUT*
[4912183.252777] [nft_do_chain ] TCP: 172.26.137.131:54321 -> 172.26.137.130:8000 seq:2754757913, ack:1579697130, flags:R *iptables table:, chain:INPUT*
[4912183.252779] [nft_do_chain ] TCP: 172.26.137.131:54321 -> 172.26.137.130:8000 seq:2754757913, ack:1579697130, flags:R *iptables table:, chain:INPUT*
[4912183.252782] [ip_local_deliver_finish] TCP: 172.26.137.131:54321 -> 172.26.137.130:8000 seq:2754757913, ack:1579697130, flags:R
[4912183.252783] [tcp_v4_rcv ] TCP: 172.26.137.131:54321 -> 172.26.137.130:8000 seq:2754757913, ack:1579697130, flags:R
[4912183.252789] [kfree_skb ] TCP: 172.26.137.131:54321 -> 172.26.137.130:8000 seq:2754757913, ack:1579697130, flags:R *tcp_v4_rcv+0x65* *packet is dropped by kernel* //被 drop 了
[4912183.252792] [packet_rcv ] TCP: 172.26.137.131:54321 -> 172.26.137.130:8000 seq:2754757913, ack:1579697130, flags:R
[4912183.252794] [consume_skb ] TCP: 172.26.137.131:54321 -> 172.26.137.130:8000 seq:2754757913, ack:1579697130, flags:R *packet is freed (normally)*

***************** c22c8900,c22c8200 ***************
[4912183.273690] [napi_gro_receive_entry] TCP: 172.26.137.131:54321 -> 172.26.137.130:8000 seq:2754757913, ack:1579697130, flags:A
[4912183.273697] [dev_gro_receive ] TCP: 172.26.137.131:54321 -> 172.26.137.130:8000 seq:2754757913, ack:1579697130, flags:A
[4912183.273700] [__netif_receive_skb_core] TCP: 172.26.137.131:54321 -> 172.26.137.130:8000 seq:2754757913, ack:1579697130, flags:A
[4912183.273701] [tpacket_rcv ] TCP: 172.26.137.131:54321 -> 172.26.137.130:8000 seq:2754757913, ack:1579697130, flags:A
[4912183.273705] [ip_rcv ] TCP: 172.26.137.131:54321 -> 172.26.137.130:8000 seq:2754757913, ack:1579697130, flags:A
[4912183.273707] [ip_rcv_core ] TCP: 172.26.137.131:54321 -> 172.26.137.130:8000 seq:2754757913, ack:1579697130, flags:A
[4912183.273708] [skb_clone ] TCP: 172.26.137.131:54321 -> 172.26.137.130:8000 seq:2754757913, ack:1579697130, flags:A
[4912183.273711] [nf_hook_slow ] TCP: 172.26.137.131:54321 -> 172.26.137.130:8000 seq:2754757913, ack:1579697130, flags:A *ipv4 in chain: PRE_ROUTING*
[4912183.273714] [nft_do_chain ] TCP: 172.26.137.131:54321 -> 172.26.137.130:8000 seq:2754757913, ack:1579697130, flags:A *iptables table:, chain:PREROUT*
[4912183.273716] [ip_rcv_finish ] TCP: 172.26.137.131:54321 -> 172.26.137.130:8000 seq:2754757913, ack:1579697130, flags:A
[4912183.273719] [ip_route_input_slow ] TCP: 172.26.137.131:54321 -> 172.26.137.130:8000 seq:2754757913, ack:1579697130, flags:A
[4912183.273724] [fib_validate_source ] TCP: 172.26.137.131:54321 -> 172.26.137.130:8000 seq:2754757913, ack:1579697130, flags:A
[4912183.273726] [ip_local_deliver ] TCP: 172.26.137.131:54321 -> 172.26.137.130:8000 seq:2754757913, ack:1579697130, flags:A
[4912183.273728] [nf_hook_slow ] TCP: 172.26.137.131:54321 -> 172.26.137.130:8000 seq:2754757913, ack:1579697130, flags:A *ipv4 in chain: INPUT*
[4912183.273733] [nft_do_chain ] TCP: 172.26.137.131:54321 -> 172.26.137.130:8000 seq:2754757913, ack:1579697130, flags:A *iptables table:, chain:INPUT*
[4912183.273735] [nft_do_chain ] TCP: 172.26.137.131:54321 -> 172.26.137.130:8000 seq:2754757913, ack:1579697130, flags:A *iptables table:, chain:INPUT*
[4912183.273737] [ip_local_deliver_finish] TCP: 172.26.137.131:54321 -> 172.26.137.130:8000 seq:2754757913, ack:1579697130, flags:A
[4912183.273738] [tcp_v4_rcv ] TCP: 172.26.137.131:54321 -> 172.26.137.130:8000 seq:2754757913, ack:1579697130, flags:A
[4912183.273742] [__inet_lookup_listener] TCP: 172.26.137.131:54321 -> 172.26.137.130:8000 seq:2754757913, ack:1579697130, flags:A
[4912183.273744] [tcp_filter ] TCP: 172.26.137.131:54321 -> 172.26.137.130:8000 seq:2754757913, ack:1579697130, flags:A
[4912183.273746] [tcp_v4_do_rcv ] TCP: 172.26.137.131:54321 -> 172.26.137.130:8000 seq:2754757913, ack:1579697130, flags:A
[4912183.273750] [tcp_rcv_state_process] TCP: 172.26.137.131:54321 -> 172.26.137.130:8000 seq:2754757913, ack:1579697130, flags:A *TCP socket state has changed*
[4912183.273754] [tcp_v4_send_reset ] TCP: 172.26.137.131:54321 -> 172.26.137.130:8000 seq:2754757913, ack:1579697130, flags:A
[4912183.273798] [kfree_skb ] TCP: 172.26.137.131:54321 -> 172.26.137.130:8000 seq:2754757913, ack:1579697130, flags:A *tcp_v4_do_rcv+0x6c* *packet is dropped by kernel*
[4912183.273801] [packet_rcv ] TCP: 172.26.137.131:54321 -> 172.26.137.130:8000 seq:2754757913, ack:1579697130, flags:A
[4912183.273803] [consume_skb ] TCP: 172.26.137.131:54321 -> 172.26.137.130:8000 seq:2754757913, ack:1579697130, flags:A *packet is freed (normally)*

场景 3:正常 2 次握手,然后立即发送 RST(不带 timestamp)

可以看到 RST 被 drop 然后 握手失败

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
***************** c22c8900,c22c8f00 ***************
[4912185.612533] [__ip_local_out ] TCP: 172.26.137.130:8000 -> 172.26.137.131:12345 seq:1983242893, ack:54243195, flags:SA
[4912185.612535] [nf_hook_slow ] TCP: 172.26.137.130:8000 -> 172.26.137.131:12345 seq:1983242893, ack:54243195, flags:SA *ipv4 in chain: OUTPUT*
[4912185.612536] [nft_do_chain ] TCP: 172.26.137.130:8000 -> 172.26.137.131:12345 seq:1983242893, ack:54243195, flags:SA *iptables table:, chain:OUTPUT*
[4912185.612538] [nft_do_chain ] TCP: 172.26.137.130:8000 -> 172.26.137.131:12345 seq:1983242893, ack:54243195, flags:SA *iptables table:, chain:OUTPUT*
[4912185.612539] [ip_output ] TCP: 172.26.137.130:8000 -> 172.26.137.131:12345 seq:1983242893, ack:54243195, flags:SA
[4912185.612541] [nf_hook_slow ] TCP: 172.26.137.130:8000 -> 172.26.137.131:12345 seq:1983242893, ack:54243195, flags:SA *ipv4 in chain: POST_ROUTING*
[4912185.612542] [nft_do_chain ] TCP: 172.26.137.130:8000 -> 172.26.137.131:12345 seq:1983242893, ack:54243195, flags:SA *iptables table:, chain:POSTROU*
[4912185.612544] [ip_finish_output ] TCP: 172.26.137.130:8000 -> 172.26.137.131:12345 seq:1983242893, ack:54243195, flags:SA
[4912185.612546] [ip_finish_output2 ] TCP: 172.26.137.130:8000 -> 172.26.137.131:12345 seq:1983242893, ack:54243195, flags:SA
[4912185.612547] [__dev_queue_xmit ] TCP: 172.26.137.130:8000 -> 172.26.137.131:12345 seq:1983242893, ack:54243195, flags:SA
[4912185.612550] [dev_hard_start_xmit ] TCP: 172.26.137.130:8000 -> 172.26.137.131:12345 seq:1983242893, ack:54243195, flags:SA *skb is successfully sent to the NIC driver*
[4912185.612552] [skb_clone ] TCP: 172.26.137.130:8000 -> 172.26.137.131:12345 seq:1983242893, ack:54243195, flags:SA
[4912185.612555] [tpacket_rcv ] TCP: 172.26.137.130:8000 -> 172.26.137.131:12345 seq:1983242893, ack:54243195, flags:SA
[4912185.612558] [consume_skb ] TCP: 172.26.137.130:8000 -> 172.26.137.131:12345 seq:1983242893, ack:54243195, flags:SA *packet is freed (normally)*
[4912185.612573] [consume_skb ] TCP: 172.26.137.130:8000 -> 172.26.137.131:12345 seq:1983242893, ack:54243195, flags:SA *packet is freed (normally)*

***************** c22c8f00,c22c8800 ***************
[4912185.631719] [napi_gro_receive_entry] TCP: 172.26.137.131:12345 -> 172.26.137.130:8000 seq:54243195, ack:1983242894, flags:R
[4912185.631726] [dev_gro_receive ] TCP: 172.26.137.131:12345 -> 172.26.137.130:8000 seq:54243195, ack:1983242894, flags:R
[4912185.631728] [__netif_receive_skb_core] TCP: 172.26.137.131:12345 -> 172.26.137.130:8000 seq:54243195, ack:1983242894, flags:R
[4912185.631730] [tpacket_rcv ] TCP: 172.26.137.131:12345 -> 172.26.137.130:8000 seq:54243195, ack:1983242894, flags:R
[4912185.631734] [ip_rcv ] TCP: 172.26.137.131:12345 -> 172.26.137.130:8000 seq:54243195, ack:1983242894, flags:R
[4912185.631736] [ip_rcv_core ] TCP: 172.26.137.131:12345 -> 172.26.137.130:8000 seq:54243195, ack:1983242894, flags:R
[4912185.631737] [skb_clone ] TCP: 172.26.137.131:12345 -> 172.26.137.130:8000 seq:54243195, ack:1983242894, flags:R
[4912185.631744] [nf_hook_slow ] TCP: 172.26.137.131:12345 -> 172.26.137.130:8000 seq:54243195, ack:1983242894, flags:R *ipv4 in chain: PRE_ROUTING*
[4912185.631746] [nft_do_chain ] TCP: 172.26.137.131:12345 -> 172.26.137.130:8000 seq:54243195, ack:1983242894, flags:R *iptables table:, chain:PREROUT*
[4912185.631748] [ip_rcv_finish ] TCP: 172.26.137.131:12345 -> 172.26.137.130:8000 seq:54243195, ack:1983242894, flags:R
[4912185.631754] [ip_route_input_slow ] TCP: 172.26.137.131:12345 -> 172.26.137.130:8000 seq:54243195, ack:1983242894, flags:R
[4912185.631759] [fib_validate_source ] TCP: 172.26.137.131:12345 -> 172.26.137.130:8000 seq:54243195, ack:1983242894, flags:R
[4912185.631762] [ip_local_deliver ] TCP: 172.26.137.131:12345 -> 172.26.137.130:8000 seq:54243195, ack:1983242894, flags:R
[4912185.631763] [nf_hook_slow ] TCP: 172.26.137.131:12345 -> 172.26.137.130:8000 seq:54243195, ack:1983242894, flags:R *ipv4 in chain: INPUT*
[4912185.631765] [nft_do_chain ] TCP: 172.26.137.131:12345 -> 172.26.137.130:8000 seq:54243195, ack:1983242894, flags:R *iptables table:, chain:INPUT*
[4912185.631767] [nft_do_chain ] TCP: 172.26.137.131:12345 -> 172.26.137.130:8000 seq:54243195, ack:1983242894, flags:R *iptables table:, chain:INPUT*
[4912185.631770] [ip_local_deliver_finish] TCP: 172.26.137.131:12345 -> 172.26.137.130:8000 seq:54243195, ack:1983242894, flags:R
[4912185.631772] [tcp_v4_rcv ] TCP: 172.26.137.131:12345 -> 172.26.137.130:8000 seq:54243195, ack:1983242894, flags:R
[4912185.631777] [kfree_skb ] TCP: 172.26.137.131:12345 -> 172.26.137.130:8000 seq:54243195, ack:1983242894, flags:R *tcp_v4_rcv+0x65* *packet is dropped by kernel*
[4912185.631780] [packet_rcv ] TCP: 172.26.137.131:12345 -> 172.26.137.130:8000 seq:54243195, ack:1983242894, flags:R
[4912185.631783] [consume_skb ] TCP: 172.26.137.131:12345 -> 172.26.137.130:8000 seq:54243195, ack:1983242894, flags:R *packet is freed (normally)*

***************** c22c8600,c22c8100 ***************
[4912185.648623] [napi_gro_receive_entry] TCP: 172.26.137.131:12345 -> 172.26.137.130:8000 seq:54243195, ack:1983242894, flags:A
[4912185.648630] [dev_gro_receive ] TCP: 172.26.137.131:12345 -> 172.26.137.130:8000 seq:54243195, ack:1983242894, flags:A
[4912185.648632] [__netif_receive_skb_core] TCP: 172.26.137.131:12345 -> 172.26.137.130:8000 seq:54243195, ack:1983242894, flags:A
[4912185.648633] [tpacket_rcv ] TCP: 172.26.137.131:12345 -> 172.26.137.130:8000 seq:54243195, ack:1983242894, flags:A
[4912185.648637] [ip_rcv ] TCP: 172.26.137.131:12345 -> 172.26.137.130:8000 seq:54243195, ack:1983242894, flags:A
[4912185.648639] [ip_rcv_core ] TCP: 172.26.137.131:12345 -> 172.26.137.130:8000 seq:54243195, ack:1983242894, flags:A
[4912185.648640] [skb_clone ] TCP: 172.26.137.131:12345 -> 172.26.137.130:8000 seq:54243195, ack:1983242894, flags:A
[4912185.648643] [nf_hook_slow ] TCP: 172.26.137.131:12345 -> 172.26.137.130:8000 seq:54243195, ack:1983242894, flags:A *ipv4 in chain: PRE_ROUTING*
[4912185.648645] [nft_do_chain ] TCP: 172.26.137.131:12345 -> 172.26.137.130:8000 seq:54243195, ack:1983242894, flags:A *iptables table:, chain:PREROUT*
[4912185.648647] [ip_rcv_finish ] TCP: 172.26.137.131:12345 -> 172.26.137.130:8000 seq:54243195, ack:1983242894, flags:A
[4912185.648650] [ip_route_input_slow ] TCP: 172.26.137.131:12345 -> 172.26.137.130:8000 seq:54243195, ack:1983242894, flags:A
[4912185.648656] [fib_validate_source ] TCP: 172.26.137.131:12345 -> 172.26.137.130:8000 seq:54243195, ack:1983242894, flags:A
[4912185.648659] [ip_local_deliver ] TCP: 172.26.137.131:12345 -> 172.26.137.130:8000 seq:54243195, ack:1983242894, flags:A
[4912185.648660] [nf_hook_slow ] TCP: 172.26.137.131:12345 -> 172.26.137.130:8000 seq:54243195, ack:1983242894, flags:A *ipv4 in chain: INPUT*
[4912185.648662] [nft_do_chain ] TCP: 172.26.137.131:12345 -> 172.26.137.130:8000 seq:54243195, ack:1983242894, flags:A *iptables table:, chain:INPUT*
[4912185.648664] [nft_do_chain ] TCP: 172.26.137.131:12345 -> 172.26.137.130:8000 seq:54243195, ack:1983242894, flags:A *iptables table:, chain:INPUT*
[4912185.648667] [ip_local_deliver_finish] TCP: 172.26.137.131:12345 -> 172.26.137.130:8000 seq:54243195, ack:1983242894, flags:A
[4912185.648672] [tcp_v4_rcv ] TCP: 172.26.137.131:12345 -> 172.26.137.130:8000 seq:54243195, ack:1983242894, flags:A
[4912185.648677] [__inet_lookup_listener] TCP: 172.26.137.131:12345 -> 172.26.137.130:8000 seq:54243195, ack:1983242894, flags:A
[4912185.648679] [tcp_filter ] TCP: 172.26.137.131:12345 -> 172.26.137.130:8000 seq:54243195, ack:1983242894, flags:A
[4912185.648681] [tcp_v4_do_rcv ] TCP: 172.26.137.131:12345 -> 172.26.137.130:8000 seq:54243195, ack:1983242894, flags:A
[4912185.648685] [tcp_rcv_state_process] TCP: 172.26.137.131:12345 -> 172.26.137.130:8000 seq:54243195, ack:1983242894, flags:A *TCP socket state has changed*
[4912185.648689] [tcp_v4_send_reset ] TCP: 172.26.137.131:12345 -> 172.26.137.130:8000 seq:54243195, ack:1983242894, flags:A
[4912185.648739] [kfree_skb ] TCP: 172.26.137.131:12345 -> 172.26.137.130:8000 seq:54243195, ack:1983242894, flags:A *tcp_v4_do_rcv+0x6c* *packet is dropped by kernel*
[4912185.648741] [packet_rcv ] TCP: 172.26.137.131:12345 -> 172.26.137.130:8000 seq:54243195, ack:1983242894, flags:A
[4912185.648743] [consume_skb ] TCP: 172.26.137.131:12345 -> 172.26.137.130:8000 seq:54243195, ack:1983242894, flags:A *packet is freed (normally)*

上面三个场景都没能重现问题,所以继续构造场景 4

场景 4 timestamp 不递增

保证 tcp options 里面有 timestamp,且不递增,这时终于重现了连接残留:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//这表示有 tcp 连接残留在 8000 端口上,而实际上期望连接要因为有 RST 而被释放
#netstat -ant |grep 8000
tcp 4 0 0.0.0.0:8000 0.0.0.0:* LISTEN
tcp 0 0 172.26.137.130:8000 172.26.137.131:19723 ESTABLISHED


//此时对应的抓包,注意这里 Server 端也没有回复 RST,前面 3 个场景 Server 端 8000 都会回 RST,从而不会残留
//连接残留:ts 为 0,RST 被忽略,导致连接残留
16:09:55.669693 IP 172.26.137.131.19723 > 172.26.137.130.8000: Flags [S], seq 12345, win 8192, options [TS val 1732608595 ecr 0,eol], length 0
16:09:55.669708 IP 172.26.137.130.8000 > 172.26.137.131.19723: Flags [S.], seq 3736478060, ack 12346, win 65160, options [mss 1460,nop,nop,TS val 2982589154 ecr 1732608595], length 0
16:09:55.687943 IP 172.26.137.131.19723 > 172.26.137.130.8000: Flags [R], seq 12346, win 8192, options [TS val 0 ecr 2982589154,eol], length 0
16:09:55.703896 IP 172.26.137.131.19723 > 172.26.137.130.8000: Flags [.], ack 1, win 8192, options [TS val 1732608595 ecr 0,eol], length 0

//连接残留:ts 没递增,RST 被忽略,导致连接残留
17:18:26.739344 IP 172.26.137.131.59541 > 172.26.137.130.8000: Flags [S], seq 12345, win 8192, options [TS val 1732612706 ecr 0,eol], length 0
17:18:26.739358 IP 172.26.137.130.8000 > 172.26.137.131.59541: Flags [S.], seq 3510510105, ack 12346, win 65160, options [mss 1460,nop,nop,TS val 2986700224 ecr 1732612706], length 0
17:18:26.756574 IP 172.26.137.131.59541 > 172.26.137.130.8000: Flags [R], seq 12346, win 8192, options [mss 1460,TS val 1732611916 ecr 0,eol], length 0
17:18:26.870569 IP 172.26.137.131.59541 > 172.26.137.130.8000: Flags [.], ack 1, win 8192, options [TS val 1732612706 ecr 0,eol], length 0

不会导致连接残留的情况:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
//连接不残留, ts 递增
16:24:33.516952 IP 172.26.137.131.19544 > 172.26.137.130.8000: Flags [S], seq 12345, win 8192, options [TS val 1732609473 ecr 0,eol], length 0
16:24:33.516967 IP 172.26.137.130.8000 > 172.26.137.131.19544: Flags [S.], seq 1834771950, ack 12346, win 65160, options [mss 1460,nop,nop,TS val 2983467001 ecr 1732609473], length 0
16:24:33.539178 IP 172.26.137.131.19544 > 172.26.137.130.8000: Flags [R], seq 12346, win 8192, options [TS val 1732609473 ecr 0,eol], length 0
16:24:33.556153 IP 172.26.137.131.19544 > 172.26.137.130.8000: Flags [.], ack 1, win 8192, options [TS val 1732609473 ecr 0,eol], length 0
16:24:33.556164 IP 172.26.137.130.8000 > 172.26.137.131.19544: Flags [R], seq 1834771951, win 0, length 0

//连接不残留, 有 options 但是没有 ts
17:05:16.217333 IP 172.26.137.131.22567 > 172.26.137.130.8000: Flags [S], seq 12345, win 8192, options [TS val 1732611916 ecr 0,eol], length 0
17:05:16.217351 IP 172.26.137.130.8000 > 172.26.137.131.22567: Flags [S.], seq 3503286934, ack 12346, win 65160, options [mss 1460,nop,nop,TS val 2985909702 ecr 1732611916], length 0
17:05:16.229589 IP 172.26.137.131.22567 > 172.26.137.130.8000: Flags [R], seq 12346, win 8192, options [mss 1460], length 0
17:05:16.346564 IP 172.26.137.131.22567 > 172.26.137.130.8000: Flags [.], ack 1, win 8192, options [TS val 1732611916 ecr 0,eol], length 0
17:05:16.346578 IP 172.26.137.130.8000 > 172.26.137.131.22567: Flags [R], seq 3503286935, win 0, length 0

//连接不残留,options 为 null
16:29:38.618811 IP 172.26.137.131.33190 > 172.26.137.130.8000: Flags [S], seq 12345, win 8192, options [TS val 1732609778 ecr 0,eol], length 0
16:29:38.618824 IP 172.26.137.130.8000 > 172.26.137.131.33190: Flags [S.], seq 1867663284, ack 12346, win 65160, options [mss 1460,nop,nop,TS val 2983772103 ecr 1732609778], length 0
16:29:38.647039 IP 172.26.137.131.33190 > 172.26.137.130.8000: Flags [R], seq 12346, win 8192, length 0
16:29:38.670061 IP 172.26.137.131.33190 > 172.26.137.130.8000: Flags [.], ack 1, win 8192, options [TS val 1732609778 ecr 0,eol], length 0
16:29:38.670073 IP 172.26.137.130.8000 > 172.26.137.131.33190: Flags [R], seq 1867663285, win 0, length 0

//连接不残留, 有 options ,但 ts 为 nop
17:37:37.476343 IP 172.26.137.131.12345 > 172.26.137.130.8000: Flags [S], seq 2331525453, win 8192, options [mss 1460,nop,nop,TS val 1732613857 ecr 0], length 0
17:37:37.476460 IP 172.26.137.130.8000 > 172.26.137.131.12345: Flags [S.], seq 230155727, ack 2331525454, win 65160, options [mss 1460,nop,nop,TS val 2987850961 ecr 1732613857], length 0
17:37:37.494579 IP 172.26.137.131.12345 > 172.26.137.130.8000: Flags [R], seq 2331525454, win 8192, options [nop,nop,eol], length 0
17:37:37.511431 IP 172.26.137.131.12345 > 172.26.137.130.8000: Flags [.], ack 1, win 8192, options [nop,nop,TS val 1732613857 ecr 0], length 0
17:37:37.511546 IP 172.26.137.130.8000 > 172.26.137.131.12345: Flags [R], seq 230155728, win 0, length 0
17:37:37.526369 IP 172.26.137.131.12345 > 172.26.137.130.8000: Flags [R], seq 2331525454, win 8192, length 0

结论

最终重现的必要条件:内核在三次握手阶段(TCP_NEW_SYN_RECV),收到的RST 包里有 timestamp 且不递增 就会丢弃 RST

注意:

  • 如果 RST 的 seq 不递增也会导致连接残留,这属于 seq 回绕了 // /proc/net/netstat 中没找到 有哪个指标对应的监控
  • 要区分 timestamp 没有和 timestamp 为 0 的情况,为 0 表示有,大概率回绕了//场景 1-3 都忽略了这个问题
  • options=[(‘NOP’, None), (‘NOP’, None)]) 表示没有 timestamp,也不能重现问题
  • 以上案例 2/3/4 场景下 nettrace 看到的 RST 都被 drop 了,但是不妨碍连接的释放 //这个还需要分析为什么连接 RST 起作用了但是还是会 drop RST 包
  • 如果出现连接残留,也会导致全连接队列增大直到溢出
  • 三次握手成功后的通信阶段(established),此时只校验 RST 的 seq 有没有回绕,不校验 timestamp,这样连接能正确释放
1
2
3
4
5
6
//三次握手成功,如果 RST 带的 timestamp 不递增也会正确触发释放连接,也就是 ESTABLISHED  时只校验 RST 的 seq 有没有回绕,不校验 timestamp
//如下抓包的连接被正确释放了,所以 LVS 会用这个逻辑来释放连接,但是一旦乱序就嗝屁了
12:19:58.588218 IP 172.26.137.131.1406 > 172.26.137.130.8000: Flags [S], seq 2800159571, win 8192, options [mss 1460,nop,nop,TS val 1732681198 ecr 0], length 0
12:19:58.588233 IP 172.26.137.130.8000 > 172.26.137.131.1406: Flags [S.], seq 3011503126, ack 2800159572, win 65160, options [mss 1460,nop,nop,TS val 3055192072 ecr 1732681198], length 0
12:19:58.606594 IP 172.26.137.131.1406 > 172.26.137.130.8000: Flags [.], ack 1, win 8192, options [nop,nop,TS val 1732681198 ecr 0], length 0
12:19:58.624392 IP 172.26.137.131.1406 > 172.26.137.130.8000: Flags [R], seq 2800159572, win 8192, options [nop,nop,TS val 0 ecr 0], length 0

对应的内核 commit

Server 在握手的第三阶段(TCP_NEW_SYN_RECV),等待对端进行握手的第三步回 ACK时候,如果收到RST 内核会对报文进行PAWS校验,如果 RST 带的 timestamp(TVal) 不递增就会因为通不过 PAWS 校验而被扔掉

问题引入:https://github.com/torvalds/linux/commit/7faee5c0d514162853a343d93e4a0b6bb8bfec21 这个 commit 去掉了TCP_SKB_CB(skb)->when = tcp_time_stamp,导致 3.18 的内核版本linger close主动发送的 RST 中 ts_val为0

问题修复:修复的commit在 675ee231d960af2af3606b4480324e26797eb010,直到 4.10 才合并进内核

监控

对应这种握手阶段连接建立如何监控呢?

从内核代码 net/ipv4/tcp_minisocks.c/tcp_check_req 函数会对报文调用 tcp_paws_reject 函数进行 paws_reject 检测,tcp_paws_reject 如果返回值为true,则 tcp_check_req 返回NULL,并且记录 LINUX_MIB_PAWSESTABREJECTED 计数

可以观察 /proc/net/netstat 中的监控指标:PAWSEstab

1
2
3
4
5
//内核中的指标
SNMP_MIB_ITEM("PAWSEstab", LINUX_MIB_PAWSESTABREJECTED)

//尝试了 5 次 RST的 timestamp 不递增导致的残留,监控到这个值每次变化累加 1
TcpExt:PAWSEstab 1 -> 1 -> 1 -> 1 -> 1

虽然三次握手没有完成,但是在服务端连接已经是 ESTABLISHED,所以这里的统计指标还是 PAWSEstab,可以通过 netstat -s 来查看:

1
2
#netstat -s |grep -E -i "timestamp|paws"
71 packets rejected in established connections because of timestamp //无论是三次握手阶段的 RST 还是握手成功后的请求只要 timestamp 不递增就会 drop

这个指标对应在 netstat 源码(net-tools) 中的解释:

1
2
{"PAWSEstab", N_("%llu packets rejected in established connections because of timestamp"), opt_number},
{"PAWSPassive", N_("%llu passive connections rejected because of time stamp"), opt_number},

总结

星球里之前也写过 scapy 的入门以及使用案例: scapy 重现网络问题真香

就像学英语的时候要精读,分析 case 也需要深挖,可以挖上一到两周,不要每天假学习(似乎啥都看了,当时啥都懂,过几个月啥都不懂)

掌握技能比掌握知识点和问题的原因更重要

nettrace 也真的很好用/很好玩,可以帮你学到很多内核知识

参考资料

https://cloud.tencent.com/developer/article/2210423

为什么你的 SYN 包被丢 net.ipv4.tcp_tw_recycle

从一个fin 卡顿问题到 scapy 的使用

tcp会偶尔3秒timeout的分析以及如何用php规避这个问题

这是一篇好文章,随着蘑菇街的完蛋,蘑菇街技术博客也没了,所以特意备份一下这篇

  • 作者:蚩尤

  • 时间:May 27, 2014

2年前做一个cache中间件调用的时候,发现很多通过php的curl调用一个的服务会出现偶尔的connect_time超时, 表现为get_curlinfo的connect_time在3秒左右, 本来没怎么注意, 因为客户端的curl_timeout设置的就是3秒, 某天, 我把这个timeout改到了5秒后, 发现了一个奇怪的现象, 很多慢请求依旧表现为connect_time在3秒左右..看来这个3秒并不是因为客户端设置的timeout引起的.于是开始查找这个原因.


首先, 凭借经验调整了linux内核关于tcp的几个参数

1
2
net.core.netdev_max_backlog = 862144
net.core.somaxconn = 262144

经过观察发现依旧会有3秒超时, 而且数量并没有减少.

第二步, 排除是大并发导致的问题, 在一台空闲机器上也部署同样的服务, 仅让线上一台机器跑空闲机器的服务, 结果发现依旧会有报错.排除并发导致的问题.

最后, 通过查了大量的资料才发现并不是我们才遇到过这个问题, 而且这个问题并不是curl的问题, 它影响到所有tcp的调用, 网上各种说法, 但结论都指向linux内核对于tcp的实现.(某些版本会出现这些问题), 有兴趣的可以看下下面这两个资料.
资料1
资料2

一看深入到linux内核..不管怎样修改的成本一定很大..于是乎, 发挥我们手中的php来规避这个问题的时间到了.

原本的代码, 简单实现,常规curl调用:

1
2
3
4
5
6
7
8
9
10
function curl_call($p1, $p2 ...) {
$ch = curl_init();
curl_setopt($ch, CURLOPT_TIMEOUT, 5);
curl_setopt($ch, CURLOPT_URL, 'http://demon.at');
$res = curl_exec($ch);
if (false === $res) {
//失败..抛异常..
}
return $res;
}

可以看出, 如果用上面的代码, 无法避免3秒connect_time的问题..这种实现对curl版本会有要求(CURLOPT_CONNECTTIMEOUT_MS),主要的思路是,通过对链接时间进行毫秒级的控制(因为超时往往发生在connect的时候),加上失败重试机制,来最大限度保证调用的正确性。所以,下面的代码就诞生了:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
function curl_call($p1, $p2, $times = 1) {
$ch = curl_init();
curl_setopt($ch, CURLOPT_TIMEOUT, 5);
curl_setopt($ch, CURLOPT_URL, 'http://demon.at');
$curl_version = curl_version();
if ($curl_version['version_number'] >= 462850) {
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT_MS, 20);
curl_setopt($ch, CURLOPT_NOSIGNAL, 1);
} else {
throw new Exception('this curl version is too low, version_num : '
. $curl_version['version']);
}
$res = curl_exec($ch);
curl_close($ch);
if (false === $res) {
if (curl_errno($ch) == CURLE_OPERATION_TIMEOUTED
and $times != 最大重试阀值 ) {
$times += 1;
return curl_call($p1, $p2, $times);
}
}

return $res;
}

上面这段代码只是一个规避的简单实例, 一些小细节并没有可以完善..比如抛出异常常以后curl资源的手动释放等等..这里不做讨论..当然还漏了一点要说的是,对重试次数最好加上限制 :)

说明一下上面几个数字值的含义:

1
2
462850 //因为php的CURLOPT_CONNECTTIMEOUT_MS需要 curl_version 7.16.2,这个值就是这个版本的数字版本号,还需要注意的是, php版本要大于5.2.3
20 //连接超时的时间, 单位:ms

这样这个问题就这样通过php的代码来规避开了.
如果有对这个问题有更好的解决方法,欢迎指教.


总结

tcp connect 的流程是这样的:
1、tcp发出SYN建链报文后,报文到ip层需要进行路由查询
2、路由查询完成后,报文到arp层查询下一跳mac地址
3、如果本地没有对应网关的arp缓存,就需要缓存住这个报文,发起arp请求
4、arp层收到arp回应报文之后,从缓存中取出SYN报文,完成mac头填写并发送给驱动。

问题在于,arp层缓存队列长度默认为3。如果你运气不好,刚好赶上缓存已满,这个报文就会被丢弃。

TCP层发现SYN报文发出去3s(默认值)还没有回应,就会重发一个SYN。这就是为什么少数连接会3s后才能建链。

幸运的是,arp层缓存队列长度是可配置的,用 sysctl -a | grep unres_qlen 就能看到,默认值为3。

tcpdump 抓包卡顿分析

背景

从 192.168.104.1 上执行 ping 192.168.104.4 -c 1 ping 命令很快通了, 同时在ubuntu 机(192.168.104.4) 上抓包

在192.168.104.4 上的 tcpdump 要卡很久(几十秒)后才输出几十秒前抓到的包 :(,最一开始以为是自己通过 lima 虚拟化的 ubuntu 机器慢 or tcpdump 初始化慢导致的,但是发现等了几十秒后能看到几十秒前抓到的包,感觉有点诡异,所以分析了一下原因。

既然几十秒后能看到几十秒前的包,说明抓包正常,只是哪里卡了,所以用 strace 看看卡在了哪里。

下文用到的主要的 Debug 命令:

1
2
3
//-r 打印相对时间
//-s 256 表示--string-limit,设置 limit 为 256,可以显示 sendto(下图黄底) 系统调用完整的 DNS 查询字符串(下图绿线)
strace -r -s 256 tcpdump -i eth0 icmp

分析步骤如下:

步骤 1

如下图是 strace -r -s 256 tcpdump -i eth0 icmp 命令的输出 ,发现抓到包后对 IP 192.168.104.4 去做了 DNS 解析,而这个解析发给 127.0.0.53 后长时间没有响应,5 秒超时后并重试(下图红框),导致多次 5 秒超时卡顿:

image-20241008144023596

于是在 /etc/hosts 添加 192.168.104.4 localhost 后不再对 192.168.104.4 进行解析,但是仍然会对对端的 IP 192.168.104.1 进行解析:

image-20241008144145663

上图说明:

  • 上图最后一个绿线表示 tcpdump 抓到了 ping 包(ICMP 协议包)
  • \0011\003104\003168\003192 表示:192.168.104.1 ,\0011 前面的 \001 表示 1 位,1 表示 ip 地址值的最后一个 //把整个双引号内容丢给 GPT 会给你一个很好的解释

步骤 2

从上面两个图中的 connect 内核函数可以看到每次都把 ip 丢给了 127.0.0.53 这个特殊 IP 来解析,下面是 GPT 给出的解释,我试了下将 DNSStubListener=no(修改配置文件:/etc/systemd/resolved.conf 后执行 systemctl restart systemd-resolved) 后 tcpdump 完全不卡了:

systemd-resolved:

  1. systemd-resolved 是一个系统服务,负责为本地应用程序提供网络名称解析。
  2. 它作为一个本地 DNS 解析器和缓存,可以提高 DNS 查询的效率。
  3. systemd-resolved 支持多种 DNS 协议,如 DNSSEC、DNS over TLS 等。
  4. 它可以管理多个网络接口的 DNS 设置,适合复杂的网络环境。

DNSStubListener 参数:

  1. DNSStubListener 是 systemd-resolved 的一个功能,默认情况下是启用的(yes)。
  2. 当启用时,systemd-resolved 会在本地 127.0.0.53 地址上运行一个 DNS 存根监听器。
  3. 这个存根监听器会接收本地应用程序的 DNS 查询请求,然后转发给实际的 DNS 服务器。
  4. 当设置 DNSStubListener=no 时:
    • 存根监听器被禁用。
    • 本地应用程序的 DNS 查询将直接发送到配置的 DNS 服务器,而不经过 systemd-resolved

现在 tcpdump 虽然不卡了,但是抓包的时候通过 strace 看到还是会走 DNS 解析流程,这个时候的 DNS 解析都发给了 192.168.104.2:53 (配置在 /etc/resolv.conf 中),也就是 systemd-resolved 的 127.0.0.53:53 udp 端口虽然在监听,但是不响应任何查询导致了超时,而 192.168.104.2:53 服务正常

这个时候的 strace 日志:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
     0.000308 socket(AF_INET, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, IPPROTO_IP) = 5 //SOCK_DGRAM UDP 模式
0.000134 setsockopt(5, SOL_IP, IP_RECVERR, [1], 4) = 0
0.000414 connect(5, {sa_family=AF_INET, sin_port=htons(53), sin_addr=inet_addr("192.168.104.2")}, 16) = 0 //目标主机 192.168.104.2
0.000373 ppoll([{fd=5, events=POLLOUT}], 1, {tv_sec=0, tv_nsec=0}, NULL, 0) = 1 ([{fd=5, revents=POLLOUT}], left {tv_sec=0, tv_nsec=0})
0.000348 sendto(5, "e\323\1\0\0\1\0\0\0\0\0\0\0014\003104\003168\003192\7in-addr\4arpa\0\0\f\0\1", 44, MSG_NOSIGNAL, NULL, 0) = 44 //发送 DNS 查询,这里可能会超时等待
0.000610 ppoll([{fd=5, events=POLLIN}], 1, {tv_sec=5, tv_nsec=0}, NULL, 0) = 1 ([{fd=5, revents=POLLIN}], left {tv_sec=4, tv_nsec=999999042})
0.000203 ioctl(5, FIONREAD, [44]) = 0
//这次 0.000136 秒后收到了响应
0.000136 recvfrom(5, "e\323\201\200\0\1\0\0\0\0\0\0\0014\003104\003168\003192\7in-addr\4arpa\0\0\f\0\1", 1024, 0, {sa_family=AF_INET, sin_port=htons(53), sin_addr=inet_addr("192.168.104.2")}, [28 => 16]) = 44
0.000462 close(5) = 0
0.000249 write(1, "17:01:20.316738 IP 192.168.104.1 > 192.168.104.4: ICMP echo request, id 31, seq 1, length 64\n", 9317:01:20.316738 IP 192.168.104.1 > 192.168.104.4: ICMP echo request, id 31, seq 1, length 64
) = 93
0.000306 newfstatat(AT_FDCWD, "/etc/localtime", {st_mode=S_IFREG|0644, st_size=561, ...}, 0) = 0
0.000269 write(1, "17:01:20.316795 IP 192.168.104.4 > 192.168.104.1: ICMP echo reply, id 31, seq 1, length 64\n", 9117:01:20.316795 IP 192.168.104.4 > 192.168.104.1: ICMP echo reply, id 31, seq 1, length 64

步骤 3

到这里大概理解这是 tcpdump 引入的 DNS 反查,看了下 tcpdump 帮助完全可以用 -n 参数彻底关闭 DNS 反查 IP:

tcpdump 命令可以关闭 DNS 反查功能。要禁用 DNS 反查,你可以使用 -n 选项;// 我用 tcpdump -n 这么久真没留意这个 -n 具体干啥的,每次都是条件反射写上去的 :(

小结

其实很多应用中会偶尔卡顿,网络操作超时就是典型的导致这种卡顿的原因,从 CPU 资源使用率上还发现不了。比如日常 ssh 连服务器有时候就会卡 30 秒

关于 GSSAPIAuthentication 解释如下,一看也是需要走网络进行授权认证,如果没有配置 kerberos 服务就会卡在网络等待上:

[!TIP]

SSH 中的 GSSAPIAuthentication(Generic Security Services Application Program Interface Authentication)是一种身份验证机制,主要用于实现单点登录(Single Sign-On, SSO)功能。它允许用户在已经通过 Kerberos 认证的环境中,无需再次输入密码就可以登录到支持 GSSAPI 的 SSH 服务器。

类似的网络卡顿/DNS 解析卡顿是很常见的,大家掌握好 Debug 手段。

实际生产中可能没这么好重现也不太好分析,比如我就碰到过 Java 程序都卡在 DNS 解析的问题,Java 中这个 DNS 解析是串行的,所以一般可以通过 jstack 看看堆栈,多个锁窜行等待肯定不正常;多次抓到 DNS 解析肯定也不正常

比如下面这个 jstack 堆栈正常是不应该出现的,如果频繁出现就说明在走 DNS 查机器名啥的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
"Diagnose@diagnose-2-61" #616 daemon prio=5 os_prio=0 tid=0x00007f7668ba6000 nid=0x2fc runnable [0x00007f75dbea8000]
java.lang.Thread.State: RUNNABLE
at java.net.Inet4AddressImpl.lookupAllHostAddr(Native Method)
at java.net.InetAddress$2.lookupAllHostAddr(InetAddress.java:870)
at java.net.InetAddress.getAddressesFromNameService(InetAddress.java:1312)
at java.net.InetAddress$NameServiceAddresses.get(InetAddress.java:818)
- locked <0x0000000500340c10> (a java.net.InetAddress$NameServiceAddresses)
at java.net.InetAddress.getAllByName0(InetAddress.java:1301)
at java.net.InetAddress.getAllByName0(InetAddress.java:1221)
at java.net.InetAddress.getHostFromNameService(InetAddress.java:640)
at java.net.InetAddress.getHostName(InetAddress.java:565)
at java.net.InetAddress.getHostName(InetAddress.java:537)
at java.net.InetSocketAddress$InetSocketAddressHolder.getHostName(InetSocketAddress.java:82)
at java.net.InetSocketAddress$InetSocketAddressHolder.access$600(InetSocketAddress.java:56)
at java.net.InetSocketAddress.getHostName(InetSocketAddress.java:345)
at io.grpc.internal.ProxyDetectorImpl.detectProxy(ProxyDetectorImpl.java:127)
at io.grpc.internal.ProxyDetectorImpl.proxyFor(ProxyDetectorImpl.java:118)
at io.grpc.internal.InternalSubchannel.startNewTransport(InternalSubchannel.java:207)
at io.grpc.internal.InternalSubchannel.obtainActiveTransport(InternalSubchannel.java:188)
- locked <0x0000000500344d38> (a java.lang.Object)
at io.grpc.internal.ManagedChannelImpl$SubchannelImpl.requestConnection(ManagedChannelImpl.java:1130)
at io.grpc.PickFirstBalancerFactory$PickFirstBalancer.handleResolvedAddressGroups(PickFirstBalancerFactory.java:79)
at io.grpc.internal.ManagedChannelImpl$NameResolverListenerImpl$1NamesResolved.run(ManagedChannelImpl.java:1032)
at io.grpc.internal.ChannelExecutor.drain(ChannelExecutor.java:73)
at io.grpc.internal.ManagedChannelImpl$4.get(ManagedChannelImpl.java:403)
at io.grpc.internal.ClientCallImpl.start(ClientCallImpl.java:238)

"Check@diagnose-1-107" #849 daemon prio=5 os_prio=0 tid=0x00007f600ee44200 nid=0x3e5 runnable [0x00007f5f12545000]
java.lang.Thread.State: RUNNABLE
at java.net.Inet4AddressImpl.lookupAllHostAddr(Native Method)
at java.net.InetAddress$2.lookupAllHostAddr(InetAddress.java:870)
at java.net.InetAddress.getAddressesFromNameService(InetAddress.java:1312)
at java.net.InetAddress$NameServiceAddresses.get(InetAddress.java:818)
- locked <0x000000063ee00098> (a java.net.InetAddress$NameServiceAddresses)
at java.net.InetAddress.getAllByName0(InetAddress.java:1301)
at java.net.InetAddress.getAllByName(InetAddress.java:1154)
at java.net.InetAddress.getAllByName(InetAddress.java:1075)
at java.net.InetAddress.getByName(InetAddress.java:1025)
at *.*.*.*.*.check.Utils.isIPv6(Utils.java:59)
at *.*.*.*.*.check.checker.AbstractCustinsChecker.getVipCheckPoint(AbstractCustinsChecker.java:189)
at *.*.*.*.*.*.*.MySQLCustinsChecker.getVipCheckPoint(MySQLCustinsChecker.java:160)
at *.*.*.*.*.*.*.MySQLCustinsChecker.getCheckPoints(MySQLCustinsChecker.java:133)
at *.*.*.*.*.check.checker.AbstractCustinsChecker.checkNormal(AbstractCustinsChecker.java:314)
at *.*.*.*.*.check.checker.CheckExecutorImpl.check(CheckExecutorImpl.java:186)
at *.*.*.*.*.check.checker.CheckExecutorImpl.lambda$0(CheckExecutorImpl.java:118)
at *.*.*.*.*.check.checker.CheckExecutorImpl$$Lambda$302/130696248.call(Unknown Source)
at com.google.common.util.concurrent.TrustedListenableFutureTask$TrustedFutureInterruptibleTask.runInterruptibly(TrustedListenableFutureTask.java:111)
at com.google.common.util.concurrent.InterruptibleTask.run(InterruptibleTask.java:58)
at com.google.common.util.concurrent.TrustedListenableFutureTask.run(TrustedListenableFutureTask.java:75)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:879)

这里以后可以加更多的 DNS 解析卡顿/网络卡顿导致的问题案例……

0%