-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathatom.xml
More file actions
502 lines (266 loc) · 484 KB
/
Copy pathatom.xml
File metadata and controls
502 lines (266 loc) · 484 KB
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
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title>祁迪的博客</title>
<subtitle>企业级客户端技术探索:架构设计·性能优化·效能提升</subtitle>
<link href="https://ustcqidi.github.io/atom.xml" rel="self"/>
<link href="https://ustcqidi.github.io/"/>
<updated>2025-05-03T01:33:23.226Z</updated>
<id>https://ustcqidi.github.io/</id>
<author>
<name>祁迪</name>
</author>
<generator uri="https://hexo.io/">Hexo</generator>
<entry>
<title>管理精力,而不是管理时间</title>
<link href="https://ustcqidi.github.io/2025/05/03/energy/"/>
<id>https://ustcqidi.github.io/2025/05/03/energy/</id>
<published>2025-05-03T01:30:39.000Z</published>
<updated>2025-05-03T01:33:23.226Z</updated>
<content type="html"><![CDATA[<p>决定我们生活质量、工作效率和幸福感的,不是表面上的时间,而是我们每天的精力和情绪。</p><p>我们真正该管理的,不是时间表,而是我们的<strong>精力和情绪。</strong></p><span id="more"></span><p>我常常陷入这样的循环:</p><ul><li>明明昨天能量满满地立下flag,今天却瘫在沙发上对着天花板发呆;</li><li>一次晨跑、一次深度对话,像开了挂;</li><li>一次喝酒、一次糟心的沟通,又会让人emo整晚。</li></ul><p>久而久之我意识到,这种<strong>能量起伏与情绪波动并不是我一个人的问题</strong>,而是大多数人的真实写照。我们每个人都像一个电池,在不同的状态中循环:</p><table><thead><tr><th>状态</th><th>描述</th><th>表现</th></tr></thead><tbody><tr><td>🟥 <strong>低能崩溃区</strong></td><td>精力耗尽</td><td>情绪失控、否定自我、逃避现实</td></tr><tr><td>🟨 <strong>低能预警区</strong></td><td>持续下滑</td><td>情绪低落、推迟任务、回避社交</td></tr><tr><td>🟩 <strong>平衡运行区</strong></td><td>稳定自然</td><td>心情平和、节奏有序、允许松弛</td></tr><tr><td>🟦 <strong>高能爆发区</strong></td><td>创意爆棚</td><td>行动力强、目标清晰、乐于创造</td></tr></tbody></table><p>我们真正要做的,是<strong>缩短低谷期、延长高能时段,搭建属于自己的能量调节系统</strong>。</p><h2 id="增加精力的方式,建议每天来一两个"><a href="#增加精力的方式,建议每天来一两个" class="headerlink" title="增加精力的方式,建议每天来一两个"></a>增加精力的方式,建议每天来一两个</h2><p>我经常反思:<strong>什么事情会让我效率变高、情绪更好?什么事情又在悄悄耗尽我?</strong></p><p>以我自己为例,喝酒是目前<strong>最明显消耗我情绪和精力的习惯</strong>,它让我失控、拖延、情绪低落,这是需要克制的。</p><p>而对我积极影响最大的行为包括:</p><ul><li><strong>早上出去散步</strong>:这是我目前觉得能量最充沛的时刻,很多有价值的想法,都是在自然中散步时冒出来的。</li><li><strong>看一部喜欢的电影,静静躺在沙发上放松</strong>。</li><li><strong>打扫卫生、整理房间、梳理计划</strong>,会带来一种秩序感。</li><li><strong>听音乐、专注读一本书</strong>,让我精神沉静下来。</li><li><strong>冥想</strong>,是我在思绪混乱、精神杂乱时的重要修复方式。</li></ul><p>这些事情组成了我的“个人充电菜单”:</p><h3 id="精神充电"><a href="#精神充电" class="headerlink" title="精神充电"></a>精神充电</h3><ul><li>冥想、阅读启发性的书</li><li>写日记、复盘当下</li><li>看一些让你“想做点事”的视频</li><li>每天完成一个小目标</li></ul><h3 id="身体充电"><a href="#身体充电" class="headerlink" title="身体充电"></a>身体充电</h3><ul><li>清晨散步或慢跑</li><li>保持饮食清淡,避免油腻辛辣以免肠胃不适</li><li>保证充足睡眠,是真正恢复能量的基础</li></ul><h3 id="情绪调节"><a href="#情绪调节" class="headerlink" title="情绪调节"></a>情绪调节</h3><ul><li>听熟悉的轻音乐</li><li>与喜欢的人交谈</li><li>整理生活空间,让内心变得更清爽</li></ul><h2 id="这些事情在悄悄消耗你的能量"><a href="#这些事情在悄悄消耗你的能量" class="headerlink" title="这些事情在悄悄消耗你的能量"></a>这些事情在悄悄消耗你的能量</h2><p>我们也要<strong>警觉地识别消耗自己能量和情绪的人与事</strong>,并尽量远离:</p><ul><li>情绪抱怨、怨天尤人、负面自我对话</li><li>沟通不畅、彼此猜忌、没有目标感</li><li>琐事缠身、频繁被打断</li><li>无效社交、刷短视频、被动接受负面信息</li><li>熬夜、暴饮暴食、喝酒</li><li>拖延任务导致焦虑、陷入内耗</li></ul><p>它们可能短期“爽”,但会让你迅速跌入<strong>低能量区</strong>,并且难以自拔。</p><h2 id="低谷时的恢复流程"><a href="#低谷时的恢复流程" class="headerlink" title="低谷时的恢复流程"></a>低谷时的恢复流程</h2><p>低谷不可怕,重要的是我们有能力拉自己一把。可以试试下面这个“低谷恢复流程”:</p><ol><li><strong>允许自己躺一会儿,不自责</strong></li><li><strong>切断能量流失源(关社交、停酒、不内耗)</strong></li><li><strong>做一个低门槛的小动作(下楼走走、写个todo)</strong></li><li><strong>完成一个小目标(洗衣、收拾桌子)</strong></li><li><strong>慢慢进入状态,找回掌控感</strong></li></ol><h2 id="关键在于持续调节,而非追求完美"><a href="#关键在于持续调节,而非追求完美" class="headerlink" title="关键在于持续调节,而非追求完美"></a>关键在于持续调节,而非追求完美</h2><p>我们追求的从不是永远处于巅峰状态,而是<strong>在情绪和精力的起伏中学会自我调节</strong>,找回节奏。</p><p>你不需要一天完成所有的目标,只需要在状态好的时候<strong>积蓄能量,在状态差的时候不再跌到底</strong>,这就是最大的成长。</p>]]></content>
<summary type="html"><p>决定我们生活质量、工作效率和幸福感的,不是表面上的时间,而是我们每天的精力和情绪。</p>
<p>我们真正该管理的,不是时间表,而是我们的<strong>精力和情绪。</strong></p></summary>
<category term="情绪管理" scheme="https://ustcqidi.github.io/tags/%E6%83%85%E7%BB%AA%E7%AE%A1%E7%90%86/"/>
</entry>
<entry>
<title>为什么我们总是把最坏的情绪留给最亲的人?</title>
<link href="https://ustcqidi.github.io/2025/04/26/family/"/>
<id>https://ustcqidi.github.io/2025/04/26/family/</id>
<published>2025-04-26T02:01:15.000Z</published>
<updated>2025-04-26T02:07:10.821Z</updated>
<content type="html"><![CDATA[<p>最好的家庭教育,是夫妻关系的稳固,是父母内在情绪的稳定,是家庭系统的健康有序</p><span id="more"></span><p>我最近常常在想一个问题:</p><p><strong>“为什么我们在外人面前能保持冷静,一回到家却像变了一个人?”</strong></p><p>拿我自己来说,每天下班已经筋疲力尽,情绪也处在低谷。这时候如果看到孩子考试成绩不理想、作业敷衍了事、还不怎么听话,家里的气氛一下子就绷紧了。</p><p>孩子妈妈在辅导作业时越来越控制不住情绪,说话的语气也越来越冲,孩子也开始哭闹。她一边唠叨一边发脾气,甚至忍不住骂孩子、打孩子,孩子哭得更厉害了。</p><p>我也好不到哪去。一天的压力已经快让我崩溃,再面对这样的场面,说实话,真的很难保持冷静。有时候我会试着劝一句:“别这样,会吓着孩子的。”可往往不仅没用,反而火上浇油,让气氛更糟。</p><p>看到家里一团乱:没人做家务,碗没洗,衣服乱扔……我憋了一天的火也炸了出来。结果妻子也被我点燃,我们开始吵架,孩子哭得更凶,整个家就像被点燃的炸药桶,一触即发。</p><p>这样的场景,最近在我们家屡见不鲜。最严重的一次,我们仨轮番发脾气、歇斯底里地大吵,甚至动手打了孩子。事后我特别后悔,自责情绪管理失败,也花了很久才安抚好孩子、修补关系。</p><p>那段时间,我的心情几乎一直处于一种<strong>焦虑和混乱</strong>的状态。工作压力、育儿焦虑、夫妻争执、孩子的哭声,层层叠加,让人几乎喘不过气。</p><p>可也正是在这种混乱中,我意识到:</p><p><strong>我们不能再这样下去了,必须要改变。</strong></p><p>我们在外人面前维持理性,却把最真实的情绪倾倒在最亲近的人身上。看似是“孩子不听话”,其实是我们大人早就失去了对情绪的掌控,和对家庭关系的经营。</p><p>这让我重新想起那句古老却从未过时的话:</p><p><strong>“修身、齐家、治国、平天下。”</strong></p><p>“齐家”,并不是成功人士才谈的远大目标,而是我们每一个普通人都必须面对的现实问题。</p><p>也正是在情绪的最低谷,我读到了《热锅上的家庭》这本书。书中布莱斯一家的故事,像极了我们家,也让我看清了问题更深层的根源:很多时候,表面上看是“孩子的问题”,实际上却是<strong>夫妻关系紧张、家庭结构混乱</strong>在作祟。</p><h2 id="案例剖析:布莱斯一家"><a href="#案例剖析:布莱斯一家" class="headerlink" title="案例剖析:布莱斯一家"></a>案例剖析:布莱斯一家</h2><h3 id="家庭成员"><a href="#家庭成员" class="headerlink" title="家庭成员"></a>家庭成员</h3><p>布莱斯家有做律师的爸爸大卫、全职妈妈卡罗琳,还有三个孩子。大女儿克劳迪娅16岁,被贴上了「问题少女」的标签,经常逃课、跟妈妈吵架、离家出走,甚至写一些想要自我伤害的诗。</p><h3 id="日常冲突"><a href="#日常冲突" class="headerlink" title="日常冲突"></a>日常冲突</h3><ul><li><strong>母女总是吵架</strong>:卡罗琳因为家务或者女儿房间乱而责骂她,克劳迪娅就会顶嘴,有时候甚至动手打架或者离家出走。</li><li><strong>夫妻关系冷淡</strong>:大卫总是忙工作,不怎么跟妻子沟通感情。他们偶尔会因为「一起管教女儿」而短暂合作,但很快又因为教育观念不同而吵起来。</li></ul><h3 id="「替罪羊」现象"><a href="#「替罪羊」现象" class="headerlink" title="「替罪羊」现象"></a>「替罪羊」现象</h3><p>克劳迪娅的叛逆行为其实成了家庭矛盾的发泄口,掩盖了夫妻关系中的冷漠和对抗。她的「问题」无形中迫使父母暂时联手,从而缓解他们之间的矛盾。</p><h2 id="深层原因分析"><a href="#深层原因分析" class="headerlink" title="深层原因分析"></a>深层原因分析</h2><h3 id="婚姻中的感情空缺"><a href="#婚姻中的感情空缺" class="headerlink" title="婚姻中的感情空缺"></a>婚姻中的感情空缺</h3><p>卡罗琳把所有感情都放在孩子身上,想通过控制女儿来填补自己内心的孤独,而大卫则用工作来逃避家庭责任和感情交流。</p><h3 id="原生家庭影响的延续"><a href="#原生家庭影响的延续" class="headerlink" title="原生家庭影响的延续"></a>原生家庭影响的延续</h3><p>卡罗琳复制了她妈妈那种控制欲很强的教育方式,不自觉地把自己的童年经历强加在女儿身上。大卫因为小时候缺少父爱,特别想成为孩子的「朋友」,反而削弱了父亲的权威,搞乱了家庭角色。</p><h3 id="三角关系的混乱"><a href="#三角关系的混乱" class="headerlink" title="三角关系的混乱"></a>三角关系的混乱</h3><p>克劳迪娅被夹在父母中间,成了情感「缓冲垫」。爸爸有时候站妈妈这边,有时候又偏向女儿,关系特别乱,让母女之间的矛盾更严重。</p><h2 id="家庭治疗的方法和启示"><a href="#家庭治疗的方法和启示" class="headerlink" title="家庭治疗的方法和启示"></a>家庭治疗的方法和启示</h2><p>在对布莱斯一家的治疗过程中,治疗师帮助他们深入剖析了家庭中的根本问题,并提出了一些实际的治疗策略,目的是改善家庭成员之间的互动与沟通。</p><p>以下是几个关键的治疗方法和启示:</p><h3 id="打破「替罪羊」模式"><a href="#打破「替罪羊」模式" class="headerlink" title="打破「替罪羊」模式"></a>打破「替罪羊」模式</h3><p>治疗师指出,克劳迪娅的叛逆行为并非问题的根源,而是夫妻关系不和谐的表面反应。因此,治疗的重点不仅仅是处理「问题孩子」,而是将视角扩大到整个家庭系统。全家人共同面对问题,让每个成员都承担起责任,从而减轻个别成员所承受的压力。</p><h3 id="改变沟通方式"><a href="#改变沟通方式" class="headerlink" title="改变沟通方式"></a>改变沟通方式</h3><p>治疗过程中,家长们学会了如何通过表达自己的感受,而非指责和抱怨来改善沟通。例如,当感到疲惫时,妈妈可以说「我现在很累,你能帮我一下吗?」而不是指责孩子「你怎么这么懒」。同时,家庭会议成为重新分工的有效工具,帮助减轻妈妈的家务负担,也让爸爸和孩子参与到家庭事务中,促进彼此的合作。</p><h3 id="正视婚姻问题"><a href="#正视婚姻问题" class="headerlink" title="正视婚姻问题"></a>正视婚姻问题</h3><p>治疗师还鼓励夫妻双方更真诚地倾听彼此的感受。大卫通过减少加班时间来关注家庭,而卡罗琳则努力恢复自我价值,例如重新回到工作岗位。通过这种方式,夫妻之间逐渐恢复了感情与信任,这为家庭其他关系的修复提供了重要支持。</p><h3 id="调整亲子关系界限"><a href="#调整亲子关系界限" class="headerlink" title="调整亲子关系界限"></a>调整亲子关系界限</h3><p>在亲子关系方面,治疗师建议卡罗琳减少对克劳迪娅的过度干涉,给予她更多的自主空间。同时,大卫通过与孩子们一起玩游戏或运动,逐步建立父亲的权威,重新界定了亲子关系的边界,恢复了家庭的秩序与和谐。</p><p>通过这些治疗方法,布莱斯一家逐步走出了困境,重新找到了家庭成员之间的平衡与和谐。</p><h2 id="我家和布莱斯一家的对比分析"><a href="#我家和布莱斯一家的对比分析" class="headerlink" title="我家和布莱斯一家的对比分析"></a>我家和布莱斯一家的对比分析</h2><h3 id="冲突表现的相似之处"><a href="#冲突表现的相似之处" class="headerlink" title="冲突表现的相似之处"></a>冲突表现的相似之处</h3><p><strong>爆发时机</strong></p><ul><li>我家:工作回家后精疲力竭、孩子作业不配合、家务堆积</li><li>布莱斯一家:妈妈在厨房忙碌、孩子态度叛逆、爸爸忽视家庭责任</li></ul><p><strong>冲突模式</strong></p><ul><li>我家:妻子对孩子发火→我插嘴却适得其反→三方大吵</li><li>布莱斯一家:妈妈责骂女儿→女儿反击→爸爸介入引发夫妻争吵</li></ul><p><strong>家庭气氛</strong></p><ul><li>我家:经常争吵、互相指责、容易混乱</li><li>布莱斯一家:家人彼此对抗,交流被情绪主导</li></ul><p><strong>事后反应</strong></p><ul><li>我家:后悔、自责、疲惫但又陷入下一次循环</li><li>布莱斯一家:家人情绪低落但没有系统性解决方法</li></ul><p><img src="/2025/04/26/family/1.png" alt="conflict-comparison-academic.svg"></p><h3 id="深层根源的对比"><a href="#深层根源的对比" class="headerlink" title="深层根源的对比"></a>深层根源的对比</h3><p><strong>情绪转移</strong></p><ul><li>我家:工作压力没有好好释放,转嫁到家庭中</li><li>布莱斯一家:妈妈把孤独感和不被理解转嫁到对女儿的控制上</li></ul><p><strong>情感连接缺失</strong></p><ul><li>我家:夫妻沟通少,容易形成对立局面</li><li>布莱斯一家:夫妻感情疏远,只能通过”一起管教”短暂合作</li></ul><p><strong>角色不清</strong></p><ul><li>我家:父母教育分工不明确,互相指责</li><li>布莱斯一家:父母身份与婚姻角色混淆,亲子界限模糊</li></ul><p><strong>情绪表达方式</strong></p><ul><li>我家:习惯用命令、批评、控制而非倾听理解</li><li>布莱斯一家:习惯用责骂和控制替代真诚沟通</li></ul><p><strong>系统失衡</strong></p><ul><li>我家:孩子的”问题”成为全家压力的出口</li><li>布莱斯一家:女儿成为”替罪羊”,掩盖了婚姻问题</li></ul><p><img src="/2025/04/26/family/2.png" alt="roots-comparison-academic.svg"></p><h3 id="解决策略的借鉴"><a href="#解决策略的借鉴" class="headerlink" title="解决策略的借鉴"></a>解决策略的借鉴</h3><p><strong>情绪管理</strong></p><ul><li>我家现状:情绪压抑后爆发,事后懊悔</li><li>布莱斯家的治疗方法:通过家庭会谈引导表达感受</li><li>对我家的启示:学习识别并主动表达情绪,而不是指责别人</li></ul><p><strong>沟通方式</strong></p><ul><li>我家现状:尝试”讲道理”但引发反感</li><li>布莱斯家的治疗方法:用”我感到…”代替”你总是…”</li><li>对我家的启示:试试非暴力沟通方式,减少指责性语言</li></ul><p><strong>家务分担</strong></p><ul><li>我家现状:分工不明确,妻子负担重</li><li>布莱斯家的治疗方法:家庭会议重新分配责任</li><li>对我家的启示:每个家庭成员都要承担家务,特别是我这个做父亲的要更”在场”</li></ul><p><strong>教育观念</strong></p><ul><li>我家现状:情绪化反应,缺乏一致性</li><li>布莱斯家的治疗方法:教育方式统一、界限清晰</li><li>对我家的启示:父母需要达成基本共识,给孩子稳定一致的回应</li></ul><p><strong>夫妻关系</strong></p><ul><li>我家现状:被孩子和生活事务消耗,缺乏情感联结</li><li>布莱斯家的治疗方法:鼓励夫妻直面情感需求、重建亲密</li><li>对我家的启示:重新建立二人世界,哪怕每周只有一次安静交流的时间</li></ul><p><img src="/2025/04/26/family/3.png" alt="solutions-comparison-academic.svg"></p><h2 id="反思和改进"><a href="#反思和改进" class="headerlink" title="反思和改进"></a>反思和改进</h2><p>我家和布莱斯一家的共同点,不仅是冲突频繁,更是这些冲突的情绪模式和互动方式特别相似。关键不是消除冲突,而是重建家人之间的联系方式,就像书里说的:「家庭的问题,必须由整个家庭一起治愈」</p><p>通过阅读和反思,我开始尝试一些实际行动。比如,每个周末学着做几道菜,减轻妻子的负担,也给她一些属于自己的自由时间。更重要的是,孩子会看到:<strong>家务不是妈妈一个人的责任</strong>。</p><p>我还给自己定了一个小目标:每天的自我反思里,加上一条「感恩妻子和孩子的小事」。慢慢地,我发现这不仅改变了我的情绪状态,也让家庭氛围变得柔和了许多。</p><p>我也开始主动参与孩子的学习,比如晚上或周末陪他做数学思维题,教他画图、打草稿,理清解题思路。在这个过程中,不但更理解妻子在教育中的辛劳,也和孩子建立了新的共同语言,换个角度陪他看待问题。</p><p>我不是做不到这些事情,只是过去把大部分精力都放在了工作上。下班之后,只想躺平。但逐渐地,我意识到:「修身齐家」不是虚词,而是我们普通人实实在在要用心去做的事。我们总以为,养好一个孩子就是拼命投入,事无巨细;但其实,<strong>最好的家庭教育,是夫妻关系的稳固,是父母内在情绪的稳定,是家庭系统的健康有序。</strong></p>]]></content>
<summary type="html"><p>最好的家庭教育,是夫妻关系的稳固,是父母内在情绪的稳定,是家庭系统的健康有序</p></summary>
<category term="情绪管理" scheme="https://ustcqidi.github.io/tags/%E6%83%85%E7%BB%AA%E7%AE%A1%E7%90%86/"/>
<category term="家庭教育" scheme="https://ustcqidi.github.io/tags/%E5%AE%B6%E5%BA%AD%E6%95%99%E8%82%B2/"/>
</entry>
<entry>
<title>学会和不确定性和平共处</title>
<link href="https://ustcqidi.github.io/2025/04/04/uncertain/"/>
<id>https://ustcqidi.github.io/2025/04/04/uncertain/</id>
<published>2025-04-04T11:56:56.000Z</published>
<updated>2025-04-04T12:04:14.785Z</updated>
<content type="html"><![CDATA[<p>我自己有一个很明显的思维误区,就是非常不愿意接受不确定性,尤其是那些比较糟糕的事情的不确定性。会过度高估小概率事件的影响,灾难化思维。</p><span id="more"></span><h2 id="习惯性焦虑的表现"><a href="#习惯性焦虑的表现" class="headerlink" title="习惯性焦虑的表现"></a>习惯性焦虑的表现</h2><p>我发现自己有个很典型的思维习惯:每次遇到一点点不确定的情况,大脑就会自动跳到最坏的结果,并且开始无限循环地担心各种可能发生的糟糕情况。但现实往往是,绝大多数让我焦虑的事情最后都没发生,或者即使发生了,也远没有我想象的那么严重。</p><p>这种习惯性焦虑在日常生活中有多种表现:</p><h3 id="过度健康担忧"><a href="#过度健康担忧" class="headerlink" title="过度健康担忧"></a>过度健康担忧</h3><p>体检报告里某个指标稍微偏高一点,我也会立刻去查各种资料,甚至会不由自主地想到最坏的可能性。这种对健康状况的过度解读让我陷入不必要的担忧中。</p><h3 id="旅行安全焦虑"><a href="#旅行安全焦虑" class="headerlink" title="旅行安全焦虑"></a>旅行安全焦虑</h3><p>坐飞机的时候,我会忍不住去想「万一失事了怎么办」,尽管我知道飞机事故的概率极低,但就是控制不住这种念头。这种对低概率风险的过度关注严重影响了我的旅行体验。</p><h3 id="对不确定性的恐惧"><a href="#对不确定性的恐惧" class="headerlink" title="对不确定性的恐惧"></a>对不确定性的恐惧</h3><p>我还挺害怕各种生活中的小概率事件——记得之前孩子产检时,医生给了个「几率很低,但不能排除的风险」,我就开始疯狂查论文,想找到一个确切的答案,结果越查越焦虑。</p><p>其实,这些担忧大部分时候都是自己吓自己,事情根本没发生,但它们却占据了我大量的精力,让我每天都处于紧绷状态。</p><h2 id="焦虑背后的机制"><a href="#焦虑背后的机制" class="headerlink" title="焦虑背后的机制"></a>焦虑背后的机制</h2><p>总结了一下,我的焦虑模式大概有几个核心原因:</p><h3 id="认知偏差与大脑结构:灾难化思维的神经科学解释"><a href="#认知偏差与大脑结构:灾难化思维的神经科学解释" class="headerlink" title="认知偏差与大脑结构:灾难化思维的神经科学解释"></a>认知偏差与大脑结构:灾难化思维的神经科学解释</h3><p>在探讨灾难化思维时,我们需要理解其神经科学基础。我们的大脑结构对灾难化思维形成有着深刻影响。</p><p><img src="/2025/04/04/uncertain/brain.png" alt="image.png"></p><h3 id="大脑中的两个关键区域"><a href="#大脑中的两个关键区域" class="headerlink" title="大脑中的两个关键区域"></a>大脑中的两个关键区域</h3><p><strong>杏仁核</strong>是大脑中负责情绪处理的关键区域,特别是恐惧反应。当我们面临不确定性时,杏仁核会迅速激活,引发”战或逃”反应。这是一种古老的生存机制,在人类进化过程中有其生存价值,但在现代生活中却常常导致过度反应。</p><p><strong>前额叶皮层</strong>则负责高级认知功能,包括理性思考、决策判断和冲动控制。它是我们应对不确定性的”理性大脑”,能够分析情况、权衡利弊并做出基于事实的判断。</p><h3 id="灾难化思维的神经机制"><a href="#灾难化思维的神经机制" class="headerlink" title="灾难化思维的神经机制"></a>灾难化思维的神经机制</h3><p>当我遇到不确定事件(如产检结果或飞行颠簸)时,我的杏仁核会立即激活并发出警报信号。这种快速反应往往导致我产生灾难化想象,因为杏仁核偏向于「宁可信其有」的保守策略。毕竟,在原始环境中,错误地忽视威胁可能致命,而错误地警惕则只是浪费一些能量。</p><p>然而,在现代社会,这种反应机制常常适得其反。当我收到体检报告上轻微异常的信息时,杏仁核立即激活,促使我思考可能的最坏情况。如果我无法有效激活前额叶皮层的理性思考功能,这种灾难化思维就会持续并加剧,形成恶性循环。</p><h3 id="害怕不确定性"><a href="#害怕不确定性" class="headerlink" title="害怕不确定性"></a>害怕不确定性</h3><p>我特别喜欢「确定的答案」,但现实世界本来就是充满不确定性的。很多事情就是没办法有个100%明确的结论,但我总是忍不住想要找到那个「确定性」,一旦找不到,就会开始焦虑。这种对确定性的渴求和对不确定性的恐惧成为我焦虑的主要来源之一。</p><h3 id="灾难化思维"><a href="#灾难化思维" class="headerlink" title="灾难化思维"></a>灾难化思维</h3><p>每次遇到问题,大脑都会自动假设最坏的情况,并且放大它的影响。比如,体检报告某个数值超了,我第一反应是「是不是得了什么大病」,而不会去想「可能只是最近熬夜太多」。又或者,孩子生病了,哪怕只是普通感冒,我的脑子里都会忍不住冒出各种可怕的并发症。这种思维模式让我在面对问题时总是先看到最黑暗的一面。</p><h3 id="负面过滤"><a href="#负面过滤" class="headerlink" title="负面过滤"></a>负面过滤</h3><p>我发现自己在面对一件事的时候,特别容易关注负面信息,而忽略那些积极的部分。比如体检报告里,99%指标正常,但我眼里只有那1%的异常值,根本不去考虑整体健康状况。再比如,做决策的时候,我总是盯着「可能出错的地方」,而不是去想「如果成功了会怎么样」。这种选择性注意让我的判断失去了平衡。</p><h3 id="控制欲作祟"><a href="#控制欲作祟" class="headerlink" title="控制欲作祟"></a>控制欲作祟</h3><p>我总觉得只要想得够多、够仔细,就能避免糟糕的事情发生。但事实是,很多事情根本不受我的控制,过度思虑不仅不能带来安全感,反而让我更加焦虑。这种控制幻觉让我陷入了徒劳的担忧循环中。</p><h2 id="调整思维模式的策略"><a href="#调整思维模式的策略" class="headerlink" title="调整思维模式的策略"></a>调整思维模式的策略</h2><h3 id="挑战自己的灾难化想象"><a href="#挑战自己的灾难化想象" class="headerlink" title="挑战自己的灾难化想象"></a>挑战自己的灾难化想象</h3><p>每次焦虑发作时,我开始训练自己用几个问题来挑战自己的想法:</p><ul><li>这个事情真的有那么大的概率发生吗?</li><li>就算发生了,真的会糟糕到不可挽回吗?</li><li>过去有多少次,我担心的事情最后并没有发生?</li></ul><p>很多时候,我发现自己其实是被自己的假设吓到了,而不是现实真的有那么可怕。通过这种理性挑战,我能逐渐识别并纠正自己的灾难化思维。</p><h3 id="学会接受「不确定性」"><a href="#学会接受「不确定性」" class="headerlink" title="学会接受「不确定性」"></a>学会接受「不确定性」</h3><p>世界上有很多事情是没有标准答案的,强行追求确定性,反而会让自己更痛苦。我开始尝试接受「事情就是这样,我没办法掌控一切」,这样反而会轻松很多。学习与不确定性和平共处,是减轻焦虑的重要一步。</p><h3 id="用概率思维对抗焦虑"><a href="#用概率思维对抗焦虑" class="headerlink" title="用概率思维对抗焦虑"></a>用概率思维对抗焦虑</h3><p>过去我听到某个风险,第一反应是「这太可怕了」,但现在我开始习惯性地问自己:「这个事情发生的概率有多大?」</p><ul><li>体检报告里的某个指标偏高一点,可能只是因为昨晚吃太油腻。</li><li>飞机失事的概率低到几乎可以忽略,远低于开车的风险。</li><li>医生说「1%的可能性,那意味着99%都不会发生」</li></ul><p>这种基于数据和概率的思考方式,帮助我更客观地评估风险,避免陷入不必要的担忧。</p><h3 id="减少过度检查和反复确认"><a href="#减少过度检查和反复确认" class="headerlink" title="减少过度检查和反复确认"></a>减少过度检查和反复确认</h3><p>以前,我会反复检查门锁、邮件是否发送成功、东西有没有带齐,现在我试着减少这些重复行为,比如限定自己检查一两次就不再管了。这种行为限制帮助我打破了强迫性思维的循环。</p><h3 id="设定「焦虑时间」"><a href="#设定「焦虑时间」" class="headerlink" title="设定「焦虑时间」"></a>设定「焦虑时间」</h3><p>我发现有时候强行让自己「不去想」反而会更焦虑,所以我试着每天留10-15分钟专门给自己”担心”,时间到了就不再想这件事,效果比强行压抑要好。这种「计划性担忧」策略,既承认了焦虑的存在,又避免了它占据整天的思绪。</p><h3 id="重新平衡大脑活动"><a href="#重新平衡大脑活动" class="headerlink" title="重新平衡大脑活动"></a>重新平衡大脑活动</h3><p>理解神经机制后,我开始有意识地激活前额叶皮层,抑制杏仁核的过度反应:</p><ol><li><strong>深呼吸</strong>:缓慢、深长的呼吸可以降低交感神经系统活跃度,减弱杏仁核反应</li><li><strong>事实分析</strong>:列出具体数据,促进前额叶皮层活动</li><li><strong>认知距离</strong>:通过正念观察自己的想法,创造情绪与认知的距离</li></ol><p>每次成功应用这些策略,都在重塑大脑连接,使平静应对不确定性逐渐成为自然反应。</p><h2 id="目标与实践"><a href="#目标与实践" class="headerlink" title="目标与实践"></a>目标与实践</h2><p>我的目标是:慢慢改变这种习惯性灾难化思维,学会更理性地看待事情,而不是被自己制造的焦虑拖垮。这不是一蹴而就的过程,需要持续的练习和反思。</p><p><img src="/2025/04/04/uncertain/change.png" alt="image.png"></p><p>过去,我经常在一些其实并不值得担心的事情上消耗了太多精力,现在我希望能逐步调整自己的思维方式,把注意力放在更重要、更有意义的事情上,让自己活得更轻松一点。通过理解焦虑的本质、认识大脑的工作机制,并运用有效的策略,我相信可以逐渐突破这种限制,建立更健康的思维习惯。</p><p>最重要的是,我认识到担忧本身不能改变结果,但改变思维方式却能改变我的体验。虽然不确定性永远存在,但我们应对它的方式可以不断优化。</p>]]></content>
<summary type="html"><p>我自己有一个很明显的思维误区,就是非常不愿意接受不确定性,尤其是那些比较糟糕的事情的不确定性。会过度高估小概率事件的影响,灾难化思维。</p></summary>
<category term="心理成长" scheme="https://ustcqidi.github.io/tags/%E5%BF%83%E7%90%86%E6%88%90%E9%95%BF/"/>
</entry>
<entry>
<title>企业级网络请求库开发实践与挑战</title>
<link href="https://ustcqidi.github.io/2025/03/14/net/"/>
<id>https://ustcqidi.github.io/2025/03/14/net/</id>
<published>2025-03-14T09:41:30.000Z</published>
<updated>2025-03-14T09:51:41.721Z</updated>
<content type="html"><![CDATA[<p>详细探讨了在企业级环境下开发网络请求库的复杂性和挑战。从多个维度阐述了看似简单的网络请求功能在大规模企业应用中的复杂性。</p><span id="more"></span><h2 id="1-引言"><a href="#1-引言" class="headerlink" title="1. 引言"></a>1. 引言</h2><p>这篇文章主要介绍在面向B端和企业用户的场景下,开发一个网络请求库会面临多么复杂的挑战。尤其在跨平台、多业务背景支持、效率、安全性、稳定性、可维护性等方面,需要考虑的因素非常多。</p><h3 id="1-1-问题背景:企业级网络请求的复杂性"><a href="#1-1-问题背景:企业级网络请求的复杂性" class="headerlink" title="1.1 问题背景:企业级网络请求的复杂性"></a>1.1 问题背景:企业级网络请求的复杂性</h3><p>表面上看,我们的核心任务似乎只是支持发送HTTPS请求,这是一个看起来相对简单的需求。市面上也确实有许多成熟的开源网络库可供选择。然而,在大型企业级应用的业务场景下,要真正做好这件事情,其复杂度是指数级增长的。</p><p><strong>为什么看似简单的事情会变得如此复杂?</strong></p><p>因为当一个系统达到一定规模后,原本简单的问题也会变得异常棘手。在我们的场景中,这种”规模”体现在以下几个方面:</p><h3 id="1-2-挑战概述:平台广泛性、用户环境复杂性、业务场景多样性"><a href="#1-2-挑战概述:平台广泛性、用户环境复杂性、业务场景多样性" class="headerlink" title="1.2 挑战概述:平台广泛性、用户环境复杂性、业务场景多样性"></a>1.2 挑战概述:平台广泛性、用户环境复杂性、业务场景多样性</h3><ol><li><strong>平台的广泛性</strong>:我们需要确保该网络库能够在各种操作系统和硬件平台上稳定运行,包括 Windows、Linux、Mac、iOS、Android,甚至是定制设备。</li><li><strong>用户环境的复杂性</strong>:我们的客户遍布全球,IT 环境千差万别。特别是大型企业和金融机构,通常拥有复杂的网络配置、严格的安全策略以及各种防火墙设置。还需要支持代理、VPN、运营商限制,以及多样化的使用场景,比如机场、酒店、飞机等网络条件受限的环境。如何保证 App 网络连接的稳定性,是我们面临的重要问题。</li><li><strong>业务场景复杂且广泛</strong>:我们的应用中涵盖了各类业务场景,例如会议、登录、第三方应用 API 调用、文件下载等。各业务模块由不同团队独立开发,且具有自身特点。有些场景可能会在短时间内发起大量网络请求,例如会议中的头像加载、资源动态下载、大文件传输、安装包下载、AI 模型加载等。</li></ol><h3 id="1-3-现有方案的局限:开源库的不足与定制需求"><a href="#1-3-现有方案的局限:开源库的不足与定制需求" class="headerlink" title="1.3 现有方案的局限:开源库的不足与定制需求"></a>1.3 现有方案的局限:开源库的不足与定制需求</h3><p>目前市面上有不少开源网络库,如 OkHttp、腾讯 Mars、Chrome 的网络库、libcurl 等。</p><p>但这些网络库在跨平台支持、业务场景定制、性能调优、稳定性、安全性等方面,往往还无法完全满足我们的需求。例如 OkHttp 仅适用于 Android 平台,更像是一个纯粹的网络传输层库,缺乏对业务层的封装与定制,如线程模型、代理获取与解析等。因此,我们仍需构建一个满足自身业务需求、可定制、跨平台的网络库。我们可能会参考,甚至复用部分开源库的模块,但绝大多数的工作仍需围绕我们的业务场景进行定制开发。</p><p>我们的目标是打造一个高性能、跨平台、可扩展、支持各种复杂业务场景的网络库,能够应对复杂多变的网络环境,服务于不同产品的核心网络需求,满足安全性、稳定性、可信性等要求,同时具备良好的问题排查能力。</p><p>本文将围绕架构设计、稳定性、安全性、性能优化以及故障排查等方面展开介绍。文中所述只是概览性内容,实际上每一个部分都可以单独成文。后续我们也将发布系列文章,进一步系统性地呈现这项工作的全貌。</p><h2 id="2-架构设计"><a href="#2-架构设计" class="headerlink" title="2. 架构设计"></a>2. 架构设计</h2><h3 id="2-1-模块化分层:Vendor层、核心封装层、基础业务封装层、业务接入层"><a href="#2-1-模块化分层:Vendor层、核心封装层、基础业务封装层、业务接入层" class="headerlink" title="2.1 模块化分层:Vendor层、核心封装层、基础业务封装层、业务接入层"></a>2.1 模块化分层:Vendor层、核心封装层、基础业务封装层、业务接入层</h3><p>架构层面需要解决的问题包括合理的技术选型、功能模块划分,以满足跨平台、多业务场景的需求,并支持在公司内部多个 App 中使用。在接口设计时,需要兼顾易用性和可扩展性,使业务代码能够方便地定制自身的网络请求逻辑。业务方无需了解底层网络连接的细节,如安全规则、复杂网络环境适配等,这些都应被封装在核心模块内部。</p><p>我们也会借助部分开源网络组件来实现网络连接过程中的一些关键功能,例如使用 OpenSSL 完成 TLS 认证,借助第三方方案管理 HTTP 连接层,如 DNS 解析、Socket 连接、连接复用、HTTP 认证、代理支持、TLS 握手等协议生命周期的处理。</p><p>根据前述的需求和业务场景,我们将网络库分为以下几个模块:</p><ol><li><strong>Vendor 层</strong>:封装底层网络连接组件,如 libcurl、OpenSSL、c-ares 等开源库,实现网络连接中的核心协议功能。某些平台(如 watchOS)也可能直接使用系统 API。对上层用户来说,这一层是完全透明的。</li><li><strong>核心封装层</strong>:包含线程模型设计、网络请求实体抽象、请求生命周期管理、状态驱动逻辑、代理解析与设置、通用网络策略和安全策略(如同源策略、重定向次数限制、URL 合法性校验等),以及网络性能监控、诊断与埋点等基础能力。该层不依赖具体业务,但对稳定性、安全性和性能至关重要。</li><li><strong>基础业务封装层</strong>:面向公司内部业务通用需求的封装逻辑,如服务端授权、签名生成、Session 管理、Token 刷新、通用文件下载等。这部分代码虽服务于业务,但具有高度复用性,不属于单一业务逻辑。</li><li><strong>业务接入层</strong>:直接面向业务方开放接口,业务模块可通过构建网络请求参数、使用统一的请求实体对象进行网络调用,定义自己的处理逻辑。</li></ol><h3 id="2-2-请求实体抽象与封装:HttpRequestEntity设计"><a href="#2-2-请求实体抽象与封装:HttpRequestEntity设计" class="headerlink" title="2.2 请求实体抽象与封装:HttpRequestEntity设计"></a>2.2 请求实体抽象与封装:HttpRequestEntity设计</h3><p>我们将业务方的每一个网络请求抽象为一个 <code>HttpRequestEntity</code> 对象。该对象封装了请求的所有关键信息,包括:</p><ul><li>请求参数(Headers、Body、Cookie、Method、URL 等)</li><li>网络策略(是否允许重定向、最大重定向次数等)</li><li>安全策略(是否启用同源策略、URL 校验等)</li><li>响应处理逻辑(以函数指针或 Lambda 表达式形式传入)</li></ul><p>借助这个统一的抽象,业务方只需遵循协议填写参数和响应处理逻辑即可。网络库核心模块则负责统一管理这些请求实体的生命周期。</p><p>在实现层面,这种标准化请求封装也便于自动代码生成。我们已经提供了相关工具,只需提供简单的请求参数描述,即可自动生成请求构建逻辑和胶水代码,极大提升业务开发效率。</p><h3 id="2-3-请求生命周期管理"><a href="#2-3-请求生命周期管理" class="headerlink" title="2.3 请求生命周期管理"></a>2.3 请求生命周期管理</h3><p>接下来要介绍的概念是 <strong>请求实体的生命周期</strong>。我们将一个完整的网络请求实体的生命周期划分为以下几个阶段:<strong>创建阶段、等待执行、处理中、等待认证授权、需要重试、执行完成</strong>以及<strong>回调成功</strong>。这些阶段描述了一个网络请求从业务侧发起到最终返回响应的整个过程所经历的状态。每个状态都对应一个独立的队列,由核心网络层进行统一管理和调度,确保请求实体在各个阶段的状态转换和处理流程的有序进行。</p><h3 id="2-4-线程模型设计:单线程模型与多线程扩展"><a href="#2-4-线程模型设计:单线程模型与多线程扩展" class="headerlink" title="2.4 线程模型设计:单线程模型与多线程扩展"></a>2.4 线程模型设计:单线程模型与多线程扩展</h3><p><strong>线程模型</strong>也是架构设计中需要重点考虑的部分。如何处理网络 I/O 事件、如何设计调用与回调线程模型,用户发起请求后的整个生命周期管理,以及如何定义和抽象一个网络请求实体,都是必须明确的问题。</p><p>在<strong>线程模型</strong>方面,目前我们的网络请求架构采用<strong>单线程模型</strong>。也就是说,所有请求的执行、网络事件的处理都在一个专用的<strong>网络线程</strong>中完成。网络线程通过状态机驱动,从不同状态队列中取出对应的请求实体,推动其进入下一个处理阶段。</p><p>与此同时,业务层的网络请求一般都是从<strong>UI线程</strong>发起的。如果某个请求并非在 UI 线程中发起,我们也内置了检测机制,会自动将其切换至 UI 线程进行处理。当某个请求处理完成后,网络线程会将该请求实体移入”请求完成队列”。UI线程则通过<strong>定期轮询机制</strong>,从完成队列中取出请求实体,执行相应的响应解析和回调逻辑,通知对应的业务方。</p><p>值得一提的是,虽然当前实现采用的是单网络线程模型,<strong>未来也可以扩展为多网络线程</strong>以提升性能,这也是后续优化的一个方向。</p><p>此外,我们还引入了一个<strong>代理解析线程</strong>。由于 Windows 和 macOS 平台支持多种类型的代理,一些代理的解析过程可能非常耗时,因此我们设计了独立的线程来专门处理代理解析任务。该线程会负责解析目标 URL 所对应的代理配置,并与 UI 线程及网络线程协同工作,共同完成一个请求在其生命周期中的所有流程。</p><h2 id="3-稳定性保障"><a href="#3-稳定性保障" class="headerlink" title="3. 稳定性保障"></a>3. 稳定性保障</h2><h3 id="3-1-网络连通性的三类竞速策略:域名竞速、代理与直连竞速、热点与Wi-Fi竞速"><a href="#3-1-网络连通性的三类竞速策略:域名竞速、代理与直连竞速、热点与Wi-Fi竞速" class="headerlink" title="3.1 网络连通性的三类竞速策略:域名竞速、代理与直连竞速、热点与Wi-Fi竞速"></a>3.1 网络连通性的三类竞速策略:域名竞速、代理与直连竞速、热点与Wi-Fi竞速</h3><p>网络库在稳定性上的核心目标是提升<strong>鲁棒性</strong>,确保在各种复杂、多变的网络环境下都能保持良好的连接表现。比如在弱网环境、VPN、代理、Wi-Fi 热点、公共场所、办公网络、酒店、甚至飞机上的网络环境下,我们都能实现较为稳定、可靠的连接。</p><p>为了应对不同网络环境下的连接问题,我们引入了 <strong>竞速连接机制</strong>,主要包括三种类型:</p><ol><li><p><strong>域名竞速</strong></p><p> 主要解决某些主域名连接速度慢、或被运营商屏蔽的情况。例如在某些特定场景(如飞机上)主域名可能无法连接。我们会同时配置主域名和备用域名,请求发起后优先尝试主域名连接;若在设定时间内(如几秒钟)未成功,则自动切换到备用域名。同时也支持并发发起两个域名请求,优先使用连接速度更快的那个,以此保障域名层的连通性。</p></li><li><p><strong>代理与直连的竞速</strong></p><p> 针对企业电脑配置代理的场景:在公司内网中,系统会通过 IT 配置的代理连接网络。但当用户离开公司网络(如下班回家或出差)后,原先的代理可能无法使用。如果始终优先走代理,会导致连接失败。我们采用代理与直连同时发起的竞速策略,哪个先连接成功就使用哪个,保证网络环境切换过程中的<strong>无缝连通性</strong>。</p></li><li><p><strong>热点与 Wi-Fi 的竞速</strong></p><p> 常见于移动端用户在公共场景下连接的 Wi-Fi 实际上是”伪连接”,即虽然连接成功,但无法真正访问外网。系统默认优先使用 Wi-Fi,但此时可能热点网络才是可用的。我们会同时发起 Wi-Fi 与热点的连接请求,通过指定使用的网络接口(网卡),选择能成功访问外网的那一个,从而提升连接的稳定性与成功率。</p></li></ol><p>这三种竞速机制有效解决了连接时的各种连通性问题,使得我们的网络库在不同网络质量、带宽条件下都能<strong>透明、稳定、高效地完成请求发起</strong>。</p><h3 id="3-2-高并发请求控制:频率监测、治理反馈、匀速处理"><a href="#3-2-高并发请求控制:频率监测、治理反馈、匀速处理" class="headerlink" title="3.2 高并发请求控制:频率监测、治理反馈、匀速处理"></a>3.2 高并发请求控制:频率监测、治理反馈、匀速处理</h3><p>我们的网络库同时服务于多个业务模块,不同业务在请求频率、下载行为、文件大小等方面差异巨大。例如,有些业务可能在短时间内发起大量高频请求,或进行大文件下载。</p><p>在实际场景中,我们曾遇到某些业务模块在极短时间内发出几十甚至上百个请求,而这些业务并不会关注底层网络连接的限制。但需要注意的是,尤其在移动端或特定平台上,<strong>网络连接句柄是有限的资源</strong>,频繁的高并发请求可能导致网络层压力激增,甚至出现连接失败或资源耗尽的问题。</p><p>为此,我们设计并引入了<strong>请求限速机制</strong>,用于平滑控制请求节奏:</p><ul><li><strong>频率监测与告警</strong>:我们会对同一业务在固定时间窗口内的请求频率进行监控,若超过设定阈值,会自动弹出警告提示或上报告警事件,帮助我们发现潜在的高频问题业务。</li><li><strong>治理与反馈机制</strong>:通过数据统计,我们可以定位具体业务,并与业务团队沟通,建议他们降低请求频率或优化请求策略。</li><li><strong>高频请求缓冲与匀速处理</strong>:在框架层,我们还引入了<strong>自动匀速发送机制</strong>。当检测到高频请求时,不会立即全部发出,而是将其缓存在等待队列中,并以平滑的方式逐步发送,减少网络抖动,提高系统整体稳定性。</li></ul><p>通过上述机制,我们不仅提升了在多变网络环境下的连通性,还增强了网络库在面对多业务并发请求时的稳定承载能力,为整个客户端系统提供了坚实的网络基础保障。</p><h3 id="3-3-网络线程卡顿检测与恢复机制"><a href="#3-3-网络线程卡顿检测与恢复机制" class="headerlink" title="3.3 网络线程卡顿检测与恢复机制"></a>3.3 网络线程卡顿检测与恢复机制</h3><p>此外,我们还关注网络线程可能出现的卡顿问题。由于某些网络请求的特殊处理过程(如 DNS 解析、代理解析或 TLS 握手)较为耗时,容易导致线程阻塞,从而影响整体网络响应。但这种局部请求的延迟,不应影响全局的网络调度和其他请求的处理。</p><p>为此,我们设计并实现了一套<strong>网络线程卡顿检测机制</strong>。该机制在第一阶段主要用于识别可能导致卡顿的网络请求生命周期,并通过数据上报来帮助我们定位潜在问题点,便于后续统一治理和优化。例如,我们通过监测发现,部分请求在等待代理解析时经常出现阻塞,因此我们引入了<strong>代理异步解析机制</strong>,有效缓解了该类卡顿问题。</p><p>然而,也存在一些客户端无法控制的卡顿场景,比如 TLS 握手过程中的 OCSP 请求,这类问题我们无法从根本上加速。因此,在卡顿检测的基础上,我们进一步引入了<strong>卡顿恢复机制与网络线程调度机制</strong>。当系统检测到某个请求已导致当前网络线程长时间阻塞,我们会主动<strong>重启网络线程</strong>,将被卡住的请求丢弃,防止其阻塞其他排队中的正常请求,从而保障整体网络库的稳定性与响应性。</p><h2 id="4-性能优化"><a href="#4-性能优化" class="headerlink" title="4. 性能优化"></a>4. 性能优化</h2><h3 id="4-1-全链路性能监控体系"><a href="#4-1-全链路性能监控体系" class="headerlink" title="4.1 全链路性能监控体系"></a>4.1 全链路性能监控体系</h3><p>我们持续关注当前网络模块在处理网络请求方面的性能表现。无论是同时发起大量请求,还是进行大文件下载,我们都希望网络模块能够高效、稳定地完成任务。</p><p>性能优化的首要前提是可观测性。因此我们对<strong>网络请求的全生命周期</strong>进行了性能埋点与监控覆盖,包括:</p><ul><li>请求对象的创建与调度时间</li><li>网络连接建立过程(如 DNS 解析、代理解析、TLS 握手等)</li><li>数据传输耗时</li><li>响应结果到达后的 UI 层处理时间</li></ul><p>通过这些监控点,我们能够定位当前网络模块的性能瓶颈,并为后续的优化提供数据支撑与对比分析。同时,我们还收集了如下信息:</p><ul><li>各阶段耗时的统计分布</li><li>代理配置及解析情况</li><li>高频请求的 URL 及其触发次数</li><li>网络线程卡顿点</li><li>违规或触发安全策略的网络请求</li></ul><p>这些数据为我们明确优化方向提供了有力支持。</p><h3 id="4-2-网络线程模型优化:从定时器驱动到事件驱动"><a href="#4-2-网络线程模型优化:从定时器驱动到事件驱动" class="headerlink" title="4.2 网络线程模型优化:从定时器驱动到事件驱动"></a>4.2 网络线程模型优化:从定时器驱动到事件驱动</h3><p>在最初的实现中,我们采用了<strong>基于定时器驱动的线程模型</strong>。网络线程每 10~100 毫秒被唤醒一次,统一处理请求调度与响应。这种模型在早期业务规模较小时并无问题,但随着业务增长,网络请求量显著上升,该模型的瓶颈开始暴露:<strong>线程调度滞后,导致请求堆积,无法及时处理</strong>,而非带宽不足造成的问题。</p><p>为了解决这一问题,我们引入了<strong>事件驱动型线程模型</strong>。具体做法是:</p><ul><li>并行启用两类网络线程:一个维持定时器驱动,另一个采用网络 I/O 事件驱动;</li><li>新增业务默认使用事件驱动线程处理,兼顾风险控制与性能优化。</li></ul><p>在落地过程中,我们又发现新的挑战:<strong>移动端系统(如 iOS)对后台频繁 I/O 唤醒行为非常敏感</strong>,可能会将应用直接强制杀掉。因此,我们进一步优化模型,使其能根据<strong>App前后台状态、电量状态、当前业务类型等上下文信息</strong>,<strong>动态切换线程模型</strong>,在性能和系统策略之间找到平衡点。</p><h3 id="4-3-请求完成回调机制优化"><a href="#4-3-请求完成回调机制优化" class="headerlink" title="4.3 请求完成回调机制优化"></a>4.3 请求完成回调机制优化</h3><p>网络请求完成后,我们会将请求对象放入”完成队列”中,原先依赖主线程定期轮询完成队列,将结果回调给业务方。然而,在 UI 主线程卡顿时,这种轮询机制可能导致请求处理延迟,影响业务体验。</p><p>为此,我们引入了优化方案:<strong>在网络线程中直接将完成事件抛入 UI 的消息队列</strong>,绕过主线程轮询,确保请求结果能够更快、更稳定地回调到业务方。</p><h3 id="4-4-备用网络线程与请求优先级机制"><a href="#4-4-备用网络线程与请求优先级机制" class="headerlink" title="4.4 备用网络线程与请求优先级机制"></a>4.4 备用网络线程与请求优先级机制</h3><p>在实际运行中,我们还引入了<strong>多线程备份调度机制</strong>。当检测到某个网络线程处理能力不足,或因个别请求导致线程阻塞时,系统会<strong>自动启用备用网络线程</strong>接管调度任务。该机制能够有效隔离问题线程,避免全局性能受影响,提升整体网络模块的吞吐能力与稳定性。</p><p>在实际业务场景中,不同网络请求的紧急程度往往存在显著差异。如果一律按照<strong>先进先出(FIFO)的方式处理,会导致某些核心业务请求被延迟,影响用户体验。因此,我们引入了请求优先级机制</strong>,对所有业务请求按重要程度进行分类:</p><ul><li><strong>核心请求</strong>将被优先调度,确保关键路径的响应速度;</li><li><strong>次要请求</strong>则在资源充足时按序处理。</li></ul><p>通过合理的优先级管理,我们能更高效地利用系统资源,保障用户操作的关键路径体验。</p><h3 id="4-5-代理解析优化:预检测、缓存、并行解析"><a href="#4-5-代理解析优化:预检测、缓存、并行解析" class="headerlink" title="4.5 代理解析优化:预检测、缓存、并行解析"></a>4.5 代理解析优化:预检测、缓存、并行解析</h3><p><strong>代理解析</strong>是网络连接中一个较为耗时的步骤,特别是当用户启用了基于 PAC(Proxy Auto-Config)脚本的代理配置时。该脚本中定义了多个匹配规则,需要客户端完成以下操作:</p><ol><li>下载 PAC 脚本;</li><li>启动 JS 引擎进行解析;</li><li>匹配请求 URL;</li><li>获取对应的代理地址。</li></ol><p>该过程可能导致<strong>请求前阻塞</strong>,尤其在并发网络请求较多时,代理解析容易成为性能瓶颈。为此我们进行了多项优化:</p><h3 id="1-域名预检测(Pre-Detect)"><a href="#1-域名预检测(Pre-Detect)" class="headerlink" title="1. 域名预检测(Pre-Detect)"></a>1. 域名预检测(Pre-Detect)</h3><p>对于常见域名,我们在请求前预先解析代理规则,减少实时阻塞。</p><h3 id="2-代理配置缓存"><a href="#2-代理配置缓存" class="headerlink" title="2. 代理配置缓存"></a>2. 代理配置缓存</h3><p>如果 App 未重启且网络配置无变化,相同域名的代理规则基本保持不变。因此我们引入了<strong>代理结果缓存机制</strong>,避免重复解析,显著降低开销。</p><h3 id="3-并行解析机制"><a href="#3-并行解析机制" class="headerlink" title="3. 并行解析机制"></a>3. 并行解析机制</h3><p>支持并发输入多个 URL,使用多线程并行进行代理检测,减少排队等待时间,提升整体解析效率。</p><p><strong>代理解析问题</strong>是我们遇到用户反馈最多的网络连接问题之一。不同平台下系统对代理的支持也存在差异,进一步加剧了处理的复杂性。因此,我们投入了大量精力进行适配和优化。后续我们计划将代理相关问题整理为专题内容,深入分析其机制与应对策略。</p><h3 id="4-6-文件传输优化:大文件下载与高频小文件处理"><a href="#4-6-文件传输优化:大文件下载与高频小文件处理" class="headerlink" title="4.6 文件传输优化:大文件下载与高频小文件处理"></a>4.6 文件传输优化:大文件下载与高频小文件处理</h3><p>在我们的应用场景中,<strong>文件下载</strong>是非常常见的需求。例如:</p><ul><li>各类业务模块可能会下载动态资源;</li><li>表情包、头像等 UI 资源;</li><li>IM 或会议聊天中的文件附件;</li><li>AI 大模型所需的模型或推理资源等。</li></ul><p>这些文件下载请求各具特点:有些是<strong>单个大文件</strong>(如 AI 模型包),有些则是<strong>短时间内密集触发的小文件请求</strong>(如头像、表情包等)。</p><p>综合来看,我们主要面临以下两个性能挑战:</p><h3 id="1-大文件下载的效率问题"><a href="#1-大文件下载的效率问题" class="headerlink" title="1. 大文件下载的效率问题"></a>1. 大文件下载的效率问题</h3><p>对于大文件下载,我们需要<strong>支持多线程分片下载与断点续传机制</strong>,以避免以下问题:</p><ul><li>下载速度缓慢,影响用户体验;</li><li>下载中断后无法恢复,需重新下载,浪费用户流量;</li><li>CDN 带宽资源被反复占用,影响整体服务效率。</li></ul><p>在<strong>移动端弱网环境</strong>下,如果缺乏断点续传机制,问题尤为严重,因此这是我们重点优化的方向之一。</p><h3 id="2-高频小文件下载的系统资源消耗问题"><a href="#2-高频小文件下载的系统资源消耗问题" class="headerlink" title="2. 高频小文件下载的系统资源消耗问题"></a>2. 高频小文件下载的系统资源消耗问题</h3><p>在短时间内触发大量小文件请求(例如用户进入会议时批量拉取头像、表情包),容易导致系统资源压力:</p><ul><li>Socket / IO 句柄迅速被占满;</li><li>线程调度频繁,增加系统负担;</li><li>网络调度失衡,影响其他请求的稳定性。</li></ul><h3 id="4-7-下载管理模块设计"><a href="#4-7-下载管理模块设计" class="headerlink" title="4.7 下载管理模块设计"></a>4.7 下载管理模块设计</h3><p>为解决上述问题,我们设计并实现了一个<strong>专用的下载管理组件</strong>,专门负责文件下载请求的调度与资源管理。</p><p>在设计过程中,我们参考了 <strong>Chrome</strong> 和 <strong>Aria2</strong> 等成熟方案,重点吸收了以下核心能力:</p><ul><li>下载协议与状态管理;</li><li>分片下载与并发调度;</li><li>断点续传支持;</li><li>下载任务合并与去重。</li></ul><p>该模块目前已经在部分业务场景中落地,效果良好。后续我们计划撰写专门的技术文章,对此模块的架构设计与实现细节进行深入介绍。</p><h2 id="5-安全保障"><a href="#5-安全保障" class="headerlink" title="5. 安全保障"></a>5. 安全保障</h2><p>网络模块需要考虑和解决的安全问题其实挺多的。我们依赖的一些基础库,比如 OpenSSL、libcurl,经常会被披露出一些公开的安全漏洞,这些我们都需要关注。一旦发现跟我们相关、可能造成影响的漏洞,就需要及时进行更新和升级。</p><h3 id="5-1-请求合规性与敏感信息保护"><a href="#5-1-请求合规性与敏感信息保护" class="headerlink" title="5.1 请求合规性与敏感信息保护"></a>5.1 请求合规性与敏感信息保护</h3><p>我们要确保从客户端发出去的网络请求是合法、合规的。比如 URL 要符合 W3C 的标准,像 URL 的长度不能超过最大限制;又比如 URL 里面不能带有用户的敏感信息,比如 token。</p><p>另外我们发现,在访问我们自有网站的时候,请求中默认会带上一些和用户或 session 相关的 token。但如果这个请求是发给第三方网站的,就不能再带这些敏感信息了。否则就有泄露风险。</p><p>还有一种情况是重定向。比如请求本来是发给我们自己的业务服务器的,但它可能会被重定向到第三方服务。在这种情况下,我们也需要检查整个重定向过程,看看是否有敏感信息被带出去了。</p><h3 id="5-2-下载文件安全处理"><a href="#5-2-下载文件安全处理" class="headerlink" title="5.2 下载文件安全处理"></a>5.2 下载文件安全处理</h3><p>再比如从网络上下载的文件,尤其是可执行程序,我们需要给它加上一些标志位,说明这个文件是从网络下载的。在用户运行的时候,系统可以给出提示。这个在 Windows 和 Mac 系统上,都有对应的文件属性可以设置。</p><h3 id="5-3-请求签名机制"><a href="#5-3-请求签名机制" class="headerlink" title="5.3 请求签名机制"></a>5.3 请求签名机制</h3><p>另外,我们还需要引入签名机制,来确保请求参数没有被篡改,或者说这个请求确实是从客户端发出的。</p><p>比如有些黑客或者第三方用户,可能会伪造客户端发起请求。这时候我们就需要有一套机制,让对方能够识别这个请求确实是从我们客户端发出来的。</p><p>这个时候,我们就可以用签名机制,比如在请求参数里加上签名,或者带上 session key。通俗点讲,就是客户端和服务端之间约定好一个公钥和私钥,客户端发请求时用公钥做签名,服务端再用私钥去验证这个签名,从而确保请求是可信的、没有被篡改的。</p><h3 id="5-4-TLS-连接与证书验证机制优化"><a href="#5-4-TLS-连接与证书验证机制优化" class="headerlink" title="5.4 TLS 连接与证书验证机制优化"></a>5.4 TLS 连接与证书验证机制优化</h3><p>HTTPS 连接建立过程中,TLS 连接的证书认证环节至关重要。这个环节涉及诸多细节,值得单独撰文深入探讨。在此,我们简要介绍几个关键点:</p><p>在移动端应用中,我们通常会将自有网站的证书添加到特定信任列表中,仅信任该列表内的证书而非系统证书。一般情况下,证书验证是通过系统证书链进行的,但系统证书链存在风险——用户可能会将不可信或中间人证书强制添加到系统信任链中,从而绕过 TLS 证书认证检测。</p><p>TLS 认证失败是常见问题,尤其在某些特殊网络环境中。例如,一些企业网络环境配置了 SSL Inspector,需要企业用户将其 SSL Inspect 证书批量添加到系统信任存储中,以确保绕过 TLS 证书检测。然而,当用户在公共网络环境或使用抓包软件时,证书验证可能会失败。此时,我们会向用户显示警告提示,并将验证失败的详细信息提供给用户,让用户自行决定是否强制连接这些站点。这是我们处理网络问题中最常见的情况之一。</p><p>另一个重要环节是证书状态验证,即确认证书是否被吊销。类似于驾驶证或行驶证,证书表面上可能是合法的,但可能因某些原因已被吊销。因此,我们需要在系统中查询证书的吊销状态。这一过程与证书认证并非同一流程,证书认证完成后,我们还需要额外步骤验证证书的吊销状态。</p><p>常见做法是启用 SSL 的 OCSP Stapling 选项。默认情况下,证书吊销状态需要从 CA 站点查询,这意味着在验证证书后还需发送额外请求查询吊销状态,延长了整个验证过程。启用 OCSP Stapling 后,服务器在 TLS 握手阶段会直接提供证书吊销状态,无需额外请求。</p><p>然而,并非所有服务都启用了该选项。若未启用,请求可能会在吊销验证阶段阻塞,因为这是耗时操作。这会导致当前网络请求一直占用网络线程,影响其他请求的处理。因此,我们的重要工作之一是监测这类情况——当发现 TLS 握手因 OCSP 请求导致的卡顿时间过长时,我们会记录相关服务并与服务提供方沟通,探讨启用该选项的可能性。</p><h3 id="5-5-企业级身份认证协议支持"><a href="#5-5-企业级身份认证协议支持" class="headerlink" title="5.5 企业级身份认证协议支持"></a>5.5 企业级身份认证协议支持</h3><p>在身份认证方面,我们支持多种认证协议。通常情况下,一些企业用户会配置 IT 验证策略,采用域用户身份验证协议,如 Kerberos 或 NTLM。这两种认证协议相对复杂,我们所使用的网络库一般会支持这些协议。但当出现问题时,我们需要排查出故障环节。因此,我们通常会积累相关协议的故障排查经验,以便及时解决问题。</p><h3 id="5-6-reCAPTCHA机制"><a href="#5-6-reCAPTCHA机制" class="headerlink" title="5.6 reCAPTCHA机制"></a>5.6 reCAPTCHA机制</h3><p>reCAPTCHA是客户端与Web服务器之间建立的一种安全验证机制。当客户端频繁变更IP地址或发送大量网络请求时,可能被Web服务器标记为异常请求。此时,客户端需要显示验证界面,要求用户输入验证码,以防止自动化程序模拟客户端发送请求,有效避免了服务器遭受过多请求的冲击。</p><h2 id="6-网络故障排查与性能监控"><a href="#6-网络故障排查与性能监控" class="headerlink" title="6. 网络故障排查与性能监控"></a>6. 网络故障排查与性能监控</h2><h3 id="6-1-网络环境适配经验"><a href="#6-1-网络环境适配经验" class="headerlink" title="6.1 网络环境适配经验"></a>6.1 网络环境适配经验</h3><p>我们收到了大量来自复杂网络环境的用户反馈,尤其是大型企业客户。这促使我们深入研究SSL Inspector、证书验证、代理设置等复杂问题,不断完善我们的解决方案以适应各种网络环境。</p><h3 id="6-2-诊断工具开发"><a href="#6-2-诊断工具开发" class="headerlink" title="6.2 诊断工具开发"></a>6.2 诊断工具开发</h3><p>为了帮助用户更有效地诊断问题,我们开发了全面的网络诊断工具套件,并建立了系统化的日志分析知识库。这些工具包括MTR路由检测、PING测试、DNS解析检查、证书验证等多种功能,为用户提供了强大的自助问题排查能力。</p><h3 id="6-3-结构化日志系统"><a href="#6-3-结构化日志系统" class="headerlink" title="6.3 结构化日志系统"></a>6.3 结构化日志系统</h3><p>如我在另一篇文章:<a href="https://ustcqidi.github.io/2025/02/22/net-log/">Chromium NetLog 学习与客户端结构化日志的思考</a> 中所介绍的,我们通过结构化日志将网络请求的整个生命周期关联起来,这大大简化了后续的自动化分析过程,便于持续关联监控。若没有这种机制,排查网络问题将需要大量人工介入,极为繁琐。</p><p>我们参考Chrome Net Log机制,通过结构化日志方式关联请求生命周期的所有事件,利用唯一ID将整个时序逻辑串联起来。这在排查未完成请求导致的野指针问题、WebSocket状态跟踪以及代理解析过程中发挥了重要作用。同时,这种结构也使大模型和AI工具能够轻松追踪特定请求的完整生命周期,并将其转化为更易理解的形式。</p><h3 id="6-4-性能监控体系"><a href="#6-4-性能监控体系" class="headerlink" title="6.4 性能监控体系"></a>6.4 性能监控体系</h3><p>我们建立了完善的性能监控机制,用于检测网络链路中的异常情况。当生产环境中出现各类异常——如请求耗时过长、特定错误频繁发生或请求频率异常高等——系统会自动记录Telemetry事件并上报至后台监控系统。</p><p>这些性能数据不仅为我们的优化工作提供了基准参考,便于比较优化前后的改善情况,还帮助我们准确识别性能瓶颈并确定优化方向,使网络库的性能持续提升。</p><h2 id="7-总结"><a href="#7-总结" class="headerlink" title="7. 总结"></a>7. 总结</h2><p>构建一个高质量的跨平台网络库远不止是实现基本功能那么简单。它需要我们深入理解整个网络请求生命周期,适应各种复杂的使用环境,不断优化性能,并提供全面的支持工具。这是一个持续发展的过程,我们还有很长的路要走。</p><p>我们的目标是打造一个不仅功能强大,而且易于使用、高度可靠、性能卓越的网络库。这要求我们在开发阶段就考虑到模块结构的合理性、代码框架的灵活性和API的易用性。在生产环境中,我们还需要确保整个网络链路的稳定性,这意味着必须深入理解网络协议的每一个细节,并能够快速响应和解决各种复杂的网络问题。</p><p>本文仅是对我们工作的简要概述,内容相对零散。实际上,许多工作都是系统化的,需要完整的篇幅来详细介绍。如果有机会,我们可以针对特定方向深入探讨,或分享一些实际问题的解决案例。</p>]]></content>
<summary type="html"><p>详细探讨了在企业级环境下开发网络请求库的复杂性和挑战。从多个维度阐述了看似简单的网络请求功能在大规模企业应用中的复杂性。</p></summary>
<category term="网络" scheme="https://ustcqidi.github.io/tags/%E7%BD%91%E7%BB%9C/"/>
<category term="Troubleshooting" scheme="https://ustcqidi.github.io/tags/Troubleshooting/"/>
<category term="架构" scheme="https://ustcqidi.github.io/tags/%E6%9E%B6%E6%9E%84/"/>
<category term="性能" scheme="https://ustcqidi.github.io/tags/%E6%80%A7%E8%83%BD/"/>
<category term="稳定性" scheme="https://ustcqidi.github.io/tags/%E7%A8%B3%E5%AE%9A%E6%80%A7/"/>
<category term="企业级网络库" scheme="https://ustcqidi.github.io/tags/%E4%BC%81%E4%B8%9A%E7%BA%A7%E7%BD%91%E7%BB%9C%E5%BA%93/"/>
</entry>
<entry>
<title>记录、Review、联结:我的 flomo 笔记使用心得</title>
<link href="https://ustcqidi.github.io/2025/03/02/flomo/"/>
<id>https://ustcqidi.github.io/2025/03/02/flomo/</id>
<published>2025-03-02T02:11:55.000Z</published>
<updated>2025-03-02T02:18:50.115Z</updated>
<content type="html"><![CDATA[<p>持续不断记录,意义自然浮现</p><span id="more"></span><p>我是 flomo 笔记的深度用户,也是我第一个付费订阅的生产力类 App。</p><p>起初,我一直使用 Notion 作为我的个人 All-in-One 生产力系统,涵盖事务管理、笔记整理、计划制定以及素材收集等各种用途。然而,在日常浏览网页或查阅资料时,我希望能更方便地将零散的灵感和素材快速同步到 Notion。正是这个需求,促使我找到了当时还相对小众的 flomo 笔记。</p><p>然而,安装后我便爱不释手。flomo 的交互设计、配色方案和用户体验都极为清新雅致,且轻量级的特性让记录变得毫无压力,就像发朋友圈般随手可记。我也被 Flomo 的 Slogan——「<strong>持续不断记录,意义自然浮现</strong>」所深深吸引。</p><p>后来,我加入了 flomo 的用户微信群,在那里遇到了来自各个领域的高手,大家分享如何利用 flomo 提升个人生产力。我学到了「<strong>十字标签法</strong>」来管理笔记,也掌握了如何用 flomo 记录和整理<strong>闪念</strong>。</p><p><img src="/2025/03/02/flomo/tag.png" alt="image.png"></p><p>经过几年的使用,我已经积累了超过 1000 条笔记,其中大多数是生活、工作或学习中突然冒出的想法。这些闪念往往出现在洗澡、跑步、聊天,甚至睡觉时,只需随手一记,就能将它们捕捉下来。</p><p>记录闪念,毫无疑问是我在 flomo 上最大的收获。许多重要的想法,如果不及时记录,可能在脑海中停留几秒就消失了。而正是这些当初随手记录的灵感,后来演变成了一篇文章、一个团队项目,甚至成为解决某个问题的关键思路。</p><p>然而,随着时间推移,我也遇到了一个问题——笔记虽然记录了大量想法,但它们仿佛静静躺在库里,<strong>未能被有效利用</strong>。这让我想起我们在学习或工作中常遇到的情况:明明记录了很多有价值的内容,最终却散落在各个角落,<strong>难以检索和复用</strong>。</p><p>直到我发现 flomo 的「<strong>Daily Review</strong>」功能,它每天会从笔记库中随机抽取几条笔记,帮助我回顾过去的想法、灵感和行动项。这个功能仅限付费用户使用,看到它的那一刻,我毫不犹豫地充值了会员。从此,每天早晨上班路上或吃早餐时,我都会打开 flomo,回顾几条笔记,这已经成为我的固定习惯。</p><p><img src="/2025/03/02/flomo/review.png" alt="image.png"></p><p>随着使用的深入,我又发现了一个更强大的功能——「<strong>Relevant MEMOs</strong>」。flomo 能够基于关键词或语义分析,自动找到与某条笔记相关的其他内容。比如,我可能在不同时间用不同的表达方式记录过相似的情绪、认知或感受,而「<strong>Relevant MEMOs</strong>」能将它们串联起来,形成有机的关联。找到<strong>联结</strong>是学习和记忆的重要方式,而这个功能正是帮助我梳理思维、深化理解的利器。现在,我的每日回顾不仅仅是单条笔记,而是回顾它的「<strong>Relevant MEMOs</strong>」,每天三四条,逐步形成更完整的认知体系。</p><p><img src="/2025/03/02/flomo/relevant.png" alt="image.png"></p><p>另一个让我惊喜的功能是「<strong>AI Finding</strong>」。只需输入几个关键词或一个想法,flomo 就能通过语义分析,从我的笔记库中智能匹配出相关内容。这对于做研究、整理调研资料,甚至是回顾过往经验都极其高效。过去,我们常常担心记录的内容会被遗忘,但 flomo 让一切资料变得<strong>可检索、可复用</strong>,真正成为个人知识库,甚至可以说是“思考的沉淀”,并且还能让它们动态流动起来。</p><p><img src="/2025/03/02/flomo/finding.png" alt="image.png"></p><p>现在,我甚至会把读书笔记也记录到 flomo,每读完一本书,都会把核心笔记整理进去,等待未来在「<strong>Daily Review</strong>」中重新邂逅它们。flomo 已成为我日常生活中不可或缺的工具,我每天都在用它记录闪念、想法、行动项,以及各种随手的思考。</p><p>它不仅仅是一个笔记工具,更是一种帮助我更好思考、更高效成长的方式。</p>]]></content>
<summary type="html"><p>持续不断记录,意义自然浮现</p></summary>
<category term="生产力" scheme="https://ustcqidi.github.io/tags/%E7%94%9F%E4%BA%A7%E5%8A%9B/"/>
<category term="flomo" scheme="https://ustcqidi.github.io/tags/flomo/"/>
<category term="联结" scheme="https://ustcqidi.github.io/tags/%E8%81%94%E7%BB%93/"/>
<category term="知识体系" scheme="https://ustcqidi.github.io/tags/%E7%9F%A5%E8%AF%86%E4%BD%93%E7%B3%BB/"/>
<category term="闪念" scheme="https://ustcqidi.github.io/tags/%E9%97%AA%E5%BF%B5/"/>
</entry>
<entry>
<title>Chromium NetLog 学习与客户端结构化日志的思考</title>
<link href="https://ustcqidi.github.io/2025/02/22/net-log/"/>
<id>https://ustcqidi.github.io/2025/02/22/net-log/</id>
<published>2025-02-22T08:09:43.000Z</published>
<updated>2025-02-22T08:38:23.375Z</updated>
<content type="html"><![CDATA[<p>思考当前客户端日志存在的问题,并深入学习 Chromium NetLog 的设计与实现细节,从中汲取灵感,打造一套适用于我们客户端产品的结构化事件日志系统。</p><span id="more"></span><h1 id="一、背景与现状"><a href="#一、背景与现状" class="headerlink" title="一、背景与现状"></a>一、背景与现状</h1><p>日志为 <code>Troubleshooting</code> 提供了关键信息,记录了 App 运行期间的各项上下文数据。理想的日志能够反映 App 运行时的所有状态信息,凭借这些数据,我们可以自信地定位和解决各种问题。</p><p>专业的诊断工具展示了理想日志的标准:Wireshark 能捕获并记录网络请求的每一个细节;而 Linux 与 Windows 的内存转储(Dump)文件则保存了进程运行期间的内存布局、寄存器状态等底层数据。凭借这些详尽的信息,我们可以精确还原应用程序在特定时刻的运行状态。</p><p>然而,实际情况往往并非如此。我们平时常遇到客户反馈问题时,最终提供的信息不足,迫使开发者不得不新增日志后,再要求客户复现问题以采集日志数据。</p><h1 id="二、问题与痛点"><a href="#二、问题与痛点" class="headerlink" title="二、问题与痛点"></a>二、问题与痛点</h1><h2 id="1-日志格式不统一"><a href="#1-日志格式不统一" class="headerlink" title="1. 日志格式不统一"></a>1. 日志格式不统一</h2><ul><li>每个开发者都有自己的日志书写习惯——关键字、日志信息密度、时序记录等各不相同,导致日志分散且格式参差不齐。</li><li>在 Troubleshooting 时,查找所有关键日志项、理清时序及关联关系变得十分困难。</li><li>例如,分析某个请求的代理解析状态时,需要先搜索代理配置相关日志,再查看解析过程中的异常信息。若对 Proxy 解析流程不熟悉,问题排查便异常艰巨。</li></ul><h2 id="2-全局状态信息丢失"><a href="#2-全局状态信息丢失" class="headerlink" title="2. 全局状态信息丢失"></a>2. 全局状态信息丢失</h2><ul><li>某些问题仅靠现有日志难以还原现场。</li><li>例如,我们将每个网络请求封装成一个 Request 对象,并配备对应的 Handler 用于处理响应。</li><li>业务方提前卸载或删除 Handler 导致的野指针 Crash,仅依靠堆栈信息难以确定具体是哪一个 Handler 出现了问题。</li><li>因此,在 Crash 时若能 dump 当前网络模块正在处理的所有请求状态,就能大大简化问题定位。</li></ul><h2 id="3-分散日志与复杂时序"><a href="#3-分散日志与复杂时序" class="headerlink" title="3. 分散日志与复杂时序"></a>3. 分散日志与复杂时序</h2><ul><li>例如,App 使用 WebSocket 实现 Push Notification 时,经常有用户反馈未能及时收到关键数据包。</li><li>单靠记录 WebSocket 关键时间点的日志,这些记录可能被淹没在海量日志中,难以构成完整的连接状态变迁。</li><li>如果能维护一个全局的 WebSocket 连接状态上下文,在关键时刻 dump 出完整信息,就能快速排查此类问题。</li></ul><h2 id="4-自动化解析难度大"><a href="#4-自动化解析难度大" class="headerlink" title="4. 自动化解析难度大"></a>4. 自动化解析难度大</h2><ul><li>现阶段,仅依赖正则表达式对日志进行机械搜索,往往无法捕获需要跨多条日志关键字比对和关联的时序信息。</li><li>这使得利用大模型或 AI 进行日志自动化分析变得极其困难。</li></ul><h2 id="5-日志存储方式不理想"><a href="#5-日志存储方式不理想" class="headerlink" title="5. 日志存储方式不理想"></a>5. 日志存储方式不理想</h2><ul><li>当前所有模块的日志都写入一个“<strong>大而全</strong>”的日志文件中。</li><li>为了提取出与特定模块相关的日志,我们只能打开多个过滤结果的小窗口,手动对比时序和特征。</li><li>缺乏统一的关键字过滤机制,使得 Troubleshooting 工作变得繁琐低效。</li></ul><h1 id="三、Chromium-NetLog"><a href="#三、Chromium-NetLog" class="headerlink" title="三、Chromium NetLog"></a>三、Chromium NetLog</h1><p>Chromium 的 NetLog 提供了很好的参考。NetLog 是一个专门用于记录网络堆栈行为和事件的内置日志系统,其设计思路值得借鉴。</p><h3 id="1-目的与范围"><a href="#1-目的与范围" class="headerlink" title="1. 目的与范围"></a>1. 目的与范围</h3><p>NetLog 记录了网络库(如 URLRequest、Socket 连接、HTTP/2 流、TLS 握手以及代理解析等)中的关键事件和状态变化,为调试和性能分析提供详尽的数据支持。</p><h3 id="2-数据采集与事件记录"><a href="#2-数据采集与事件记录" class="headerlink" title="2. 数据采集与事件记录"></a>2. 数据采集与事件记录</h3><p>NetLog 通过在各个网络模块内部嵌入日志点(logging points)来捕获网络活动。每个日志事件通常包含以下几个部分:</p><ul><li><strong>时间戳</strong>:标识事件发生的精确时刻</li><li><strong>事件类型</strong>:例如连接建立、请求开始、响应接收、TLS 握手失败等</li><li><strong>附加参数</strong>:详细描述事件的上下文,比如请求 URL、HTTP 状态码、错误码、延时统计、网络配置参数以及代理信息等</li><li><strong>对象关联</strong>:许多事件都会与特定的对象 ID 关联(如一个请求或连接),使得后续分析时能重建整个请求的生命周期</li></ul><p>这种设计允许 NetLog 支持基于时序的调试,例如将 DNS 解析、TCP 握手、TLS 握手、请求发送和响应接收等事件串联起来,呈现完整的请求轨迹。</p><h3 id="3-模块化与分层设计"><a href="#3-模块化与分层设计" class="headerlink" title="3. 模块化与分层设计"></a>3. 模块化与分层设计</h3><p>Chromium 的网络栈设计为多个独立模块,NetLog 则以模块化的方式进行记录。每个模块(例如 Socket、HTTP、代理解析等)都负责产生日志信息,而 NetLog 系统提供统一的接口聚合、格式化和导出这些信息。这种分层设计带来的好处包括:</p><ul><li><strong>灵活性</strong>:可以对不同模块进行单独调试或关闭日志采集,降低性能开销</li><li><strong>扩展性</strong>:新的网络功能模块可以方便地集成日志记录功能,而不必修改 NetLog 的核心代码</li></ul><h3 id="4-数据格式与导出"><a href="#4-数据格式与导出" class="headerlink" title="4. 数据格式与导出"></a>4. 数据格式与导出</h3><p>NetLog 生成的日志采用结构化数据格式(通常为 JSON),便于后续通过工具进行过滤、搜索和可视化。Chrome 内置了一个 NetLog Viewer,可以将这些 JSON 日志解析成图形化的时序视图,帮助用户直观地看到请求的各个阶段和各模块之间的交互。这样的设计支持:</p><ul><li><strong>跨平台调试</strong>:JSON 格式简单而标准,方便在各种平台上进行数据交换与分析</li><li><strong>自动化分析</strong>:借助结构化数据,可以利用脚本或大数据分析工具对大规模日志进行聚合、统计和性能瓶颈分析</li></ul><p>导出的日志文件默认名称是 <code>chrome-net-export-log.json</code> , 这个 json 文件主要包含三个结构 <code>constants</code>, <code>events</code>, <code>pooledData</code></p><p><img src="/2025/02/22/net-log/net-log.png" alt="image.png"></p><p>其中 <code>constants</code> 是常量定义,包含了 NetLog 系统中使用的各种枚举类型和错误码:</p><ol><li><strong>activeFieldTrialGroups</strong>:<ul><li>记录了当前启用的实验特性和配置组</li><li>例如 “ALPSNewCodepoint:EnabledLaunch”、”AdsP4:Default” 等</li><li>这些实验特性用于 A/B 测试和功能渐进式发布</li></ul></li><li><p><strong>事件类型(logEventTypes)</strong>:</p><ul><li>定义了所有可以记录的网络事件类型</li><li>包括:<ul><li>HTTP/HTTPS 相关事件</li><li>WebSocket 事件</li><li>DNS 解析事件</li><li>SSL/TLS 事件</li><li>Cache 操作事件</li><li>Proxy 相关事件</li><li>QUIC 协议事件</li></ul></li><li><p>以 WebSocket 为例:</p> <figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">{</span><br><span class="line"> <span class="string">"WEBSOCKET_HANDSHAKE_MESSAGE_SENT"</span>: <span class="number">73</span>,</span><br><span class="line"> <span class="string">"WEBSOCKET_READ_BUFFER_SIZE_CHANGED"</span>: <span class="number">574</span>,</span><br><span class="line"> <span class="string">"WEBSOCKET_RECV_FRAME_HEADER"</span>: <span class="number">575</span>,</span><br><span class="line"> <span class="string">"WEBSOCKET_SENT_FRAME_HEADER"</span>: <span class="number">576</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><ul><li>WebSocket 连接状态追踪</li><li>数据帧收发记录</li><li>缓冲区管理监控</li></ul></li></ul></li><li><strong>logSourceType(日志来源类型)</strong><ul><li>标识日志生成模块,如:<ul><li><code>HTTP2_SESSION(10)</code>:HTTP/2会话日志</li><li><code>QUIC_SESSION(12)</code>:QUIC协议会话日志</li><li><code>CERT_VERIFIER_JOB(25)</code>:证书验证任务日志</li></ul></li></ul></li><li><p><strong>事件阶段(logEventPhase)</strong></p><p> NetLog 使用三种基本的事件阶段来追踪操作的生命周期:</p> <figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">logEventPhase: {</span><br><span class="line"> <span class="string">"PHASE_NONE"</span>: <span class="number">0</span>,<span class="comment">// 独立事件</span></span><br><span class="line"> <span class="string">"PHASE_BEGIN"</span>: <span class="number">1</span>,<span class="comment">// 开始事件</span></span><br><span class="line"> <span class="string">"PHASE_END"</span>: <span class="number">2</span><span class="comment">// 结束事件</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><ul><li><strong>应用场景</strong><ul><li>独立事件 (PHASE_NONE)<ul><li>用于记录瞬时状态变化</li><li>例如:配置更新、错误发生</li><li>不需要配对的单一事件</li></ul></li><li>开始事件 (PHASE_BEGIN)<ul><li>标记一个操作或状态的开始</li><li>必须有对应的结束事件</li><li>用于跟踪持续性操作</li></ul></li><li>结束事件 (PHASE_END)<ul><li>对应开始事件的结束标记</li><li>记录操作的完成状态</li><li>可以包含操作结果信息</li></ul></li></ul></li><li><strong>事件配对</strong><ul><li>生命周期追踪<ul><li>BEGIN/END 配对可以准确计算操作耗时</li><li>有助于发现未完成的操作</li><li>便于分析性能瓶颈</li></ul></li><li>状态一致性<ul><li>确保状态转换的完整性</li><li>帮助发现状态机错误</li><li>支持异步操作的追踪</li></ul></li></ul></li></ul></li><li><strong>错误码(netError)</strong>:<ul><li>定义了网络栈中所有可能的错误类型,例如:<ul><li><code>ERR_CERT_AUTHORITY_INVALID(-202)</code>:证书颁发机构无效</li><li><code>ERR_QUIC_HANDSHAKE_FAILED(-358)</code>:QUIC握手失败</li><li><code>ERR_SSL_VERSION_OR_CIPHER_MISMATCH(-113)</code>:SSL版本或密码套件不匹配</li></ul></li><li>按照不同模块分类:<ul><li>常规网络错误(如连接超时、DNS失败)</li><li>SSL/证书错误</li><li>HTTP错误</li><li>Cache错误</li><li>Proxy错误</li><li>QUIC协议错误</li></ul></li></ul></li></ol><h3 id="5-性能考量"><a href="#5-性能考量" class="headerlink" title="5. 性能考量"></a>5. 性能考量</h3><p>日志系统在设计时需平衡详细信息采集和性能开销。NetLog 的设计提供了动态启用/禁用日志记录的机制,只有在开启详细调试时才会记录大量事件,平时保持较低的性能消耗。此外,日志采集的粒度和记录策略是可配置的,开发者可以根据实际需要选择采样率、日志级别(例如 DEBUG、INFO、ERROR)等参数,以减少对生产环境的影响。</p><h3 id="6-调试与-Troubleshooting"><a href="#6-调试与-Troubleshooting" class="headerlink" title="6. 调试与 Troubleshooting"></a>6. 调试与 Troubleshooting</h3><p>NetLog 的丰富数据帮助开发者在面对疑难网络问题时进行”<strong>时间旅行式</strong>“的调试。例如:</p><ul><li><strong>事件关联</strong>:通过关联不同事件(如请求开始与结束),可以重建出请求的完整生命周期,识别异常延时和错误点</li><li><strong>细粒度诊断</strong>:记录下每个阶段的详细参数(如 DNS 解析时间、TCP 连接时间、TLS 握手详情)使得开发者能快速定位瓶颈或错误原因</li></ul><p><img src="/2025/02/22/net-log/net-viewer.jpg" alt="image.png"></p><h3 id="7-安全与隐私"><a href="#7-安全与隐私" class="headerlink" title="7. 安全与隐私"></a>7. 安全与隐私</h3><p>由于 NetLog 记录了大量敏感网络交互信息,其设计中也包含了对数据的脱敏处理。某些敏感字段(如认证令牌、用户隐私信息等)会在日志中进行屏蔽或模糊化处理,以确保在调试时不会暴露过多用户数据。</p><h1 id="四、多层次、结构化日志的思考"><a href="#四、多层次、结构化日志的思考" class="headerlink" title="四、多层次、结构化日志的思考"></a>四、多层次、结构化日志的思考</h1><p>Zoom 客户端现有的日志大多是在开发阶段由开发者手动添加的文本日志,但日志的形式完全可以多样化。日志的主要目的是记录应用程序的运行状态,以便后续能够还原当时的场景。</p><p>日志既可以采用纯文本格式,也可以采用二进制格式,甚至可以在关键事件发生时自动触发日志记录,比如 Crash、关键业务流程失败或用户主动触发的问题反馈时。</p><p>成熟的系统通常采用多样化、多级别的日志记录策略。根据不同的触发条件,日志记录会侧重于不同类型的信息,例如:</p><ul><li>Android 系统在发生 ANR(应用无响应)时,会生成专门的 ANR 日志,详细记录线程状态和资源占用;</li><li>Crash 时会产生 Tombstone 文件,保存崩溃时的完整调用栈和内存状态;</li><li>Windows 平台发生 Crash 时,会生成 Dump 文件;</li><li><strong>航空领域</strong>的”黑匣子”则代表了最极致的日志系统,在飞行全程记录关键参数,并在紧急情况下保护这些宝贵数据。</li></ul><p>Chromium NetLog 是一个专为网络模块设计的结构化日志系统,其设计理念可以推广应用到其他场景,比如数据库操作或核心业务流程。我们可以将这种模式扩展到通用的结构化日志模块中。</p><h2 id="1-应用场景"><a href="#1-应用场景" class="headerlink" title="1. 应用场景"></a>1. 应用场景</h2><h3 id="数据库操作"><a href="#数据库操作" class="headerlink" title="数据库操作"></a>数据库操作</h3><p>我们的客户端采用基于 SQLite 封装的统一数据库存取模块,通过结构化日志记录对数据库操作的详细过程、耗时和异常信息,从而便于进行性能分析和问题排查。结构化日志不仅可以记录 SQL 指令、事务状态和耗时指标,还能捕获锁竞争、IO 异常和连接池状态等细节,为后续诊断提供精确依据。</p><h3 id="核心业务流程:登录与加会"><a href="#核心业务流程:登录与加会" class="headerlink" title="核心业务流程:登录与加会"></a>核心业务流程:登录与加会</h3><p>另一个例子是核心业务流程,如登录和加会。这两个流程往往涉及多个步骤,目前相关日志散落在庞大的日志文件中,甚至分布在不同的文件里。由于加会过程涉及多个模块,排查单个加会问题或分析性能时需要在多个文件间频繁切换、收集关键信息并进行时序关联,因此采用通用结构化日志方案在这些场景下显得尤为适用。</p><h2 id="2-需要解决的关键问题"><a href="#2-需要解决的关键问题" class="headerlink" title="2. 需要解决的关键问题"></a>2. 需要解决的关键问题</h2><h3 id="Debug-Log-的保留与淘汰"><a href="#Debug-Log-的保留与淘汰" class="headerlink" title="Debug Log 的保留与淘汰"></a><strong>Debug Log 的保留与淘汰</strong></h3><p>我们需要明确:原有的 Debug Log 是否还应继续保留,还是逐步淘汰?同时要设计出一个日志系统,既能满足日常开发调试需求,又能有效支持生产环境问题诊断。建议在开发阶段保留 Debug Log 以便快速定位问题,而在生产环境中则采用结构化日志记录关键业务和底层模块的信息,通过动态配置和日志级别控制,实现详略得当的日志策略。</p><h3 id="通用且可扩展的数据类型与接口设计"><a href="#通用且可扩展的数据类型与接口设计" class="headerlink" title="通用且可扩展的数据类型与接口设计"></a><strong>通用且可扩展的数据类型与接口设计</strong></h3><p>设计一套通用的数据类型和接口定义非常关键。每个日志事件应包含:</p><ul><li><strong>时间戳</strong>:记录事件发生的精确时刻;</li><li><strong>事件类型或步骤标识</strong>:如数据库操作类型、业务流程步骤、网络请求阶段等;</li><li><strong>附加参数</strong>:包括操作细节、耗时、错误码和异常信息等;</li><li><strong>关联 ID</strong>:例如 RequestId,用于关联同一操作的多个日志事件。</li></ul><p>这些日志数据可根据不同业务或使用场景序列化为 JSON、二进制或其它格式,并分别写入相应的 payload 文件,便于后续解析和分析。</p><h3 id="与现有日志系统的整合"><a href="#与现有日志系统的整合" class="headerlink" title="与现有日志系统的整合"></a><strong>与现有日志系统的整合</strong></h3><p>考虑到目前的日志系统已相当稳定,支持异步写入、加密等基础功能,新系统必须与现有系统兼容,充分利用现有优势。具体措施包括:</p><ul><li>提供兼容现有日志系统的接口,实现异步写入和加密传输;</li><li>渐进式集成,即在关键模块中先引入结构化日志记录,同时保留现有 Debug Log,待验证新系统稳定后再逐步推广;</li><li>通过配置统一管理日志级别、格式和存储路径,实现新旧日志系统的无缝衔接。</li></ul><h2 id="3-日志类型及-Troubleshooting-应用场景"><a href="#3-日志类型及-Troubleshooting-应用场景" class="headerlink" title="3. 日志类型及 Troubleshooting 应用场景"></a>3. 日志类型及 Troubleshooting 应用场景</h2><h3 id="Log-类型"><a href="#Log-类型" class="headerlink" title="Log 类型"></a><strong>Log 类型</strong></h3><ul><li><strong>Debug Log</strong>:以纯文本方式记录,便于阅读和快速定位问题。</li><li><strong>Event Log</strong>:采用结构化形式记录日志,便于解析与分析。具体包括:<ul><li><strong>网络相关</strong><ul><li><strong>WebSocket 全局连接状态</strong>:记录建立连接、重连、收发数据包统计及断开连接的各个事件和时间戳。</li><li><strong>未完成请求</strong>:记录仍在请求队列中、生命期未完成的请求信息,便于排查因野指针引起的问题。</li><li><strong>请求生命期</strong>:详细记录每个请求的连接状态变迁、结果、耗时等信息,涵盖各种错误码和 payload(如 DNS 解析失败、TCP 握手异常、TLS 证书验证结果、代理信息等)。</li><li><strong>性能统计</strong>:记录应用存活期间发送请求的总数、各阶段连接耗时统计、DNS 解析信息等数据。</li></ul></li><li><strong>数据库相关</strong><ul><li>记录SQL指令、事务状态、耗时指标</li><li>捕获锁竞争、IO异常、连接池状态</li></ul></li></ul></li></ul><h3 id="Troubleshooting-场景"><a href="#Troubleshooting-场景" class="headerlink" title="Troubleshooting 场景"></a><strong>Troubleshooting 场景</strong></h3><ul><li><strong>手动日志分析</strong><ul><li>在开发阶段用于问题排查。</li><li>针对线上产品中用户反馈问题时附带的日志进行分析。</li><li>对于关键事件(如加会失败、登录失败、Crash 等)自动收集相关日志。</li></ul></li><li><strong>诊断与统计功能</strong><ul><li>在应用运行期间,定期 dump 关键状态信息(如 CPU、内存、网络请求等)。</li><li>针对网络请求,记录所有发出的 HTTPS 请求,包括请求是否成功、每条请求的耗时分布以及 DNS、TCP、TLS 等各阶段的耗时统计。</li></ul></li><li><strong>自动化日志分析</strong><ul><li>目前主要通过正则表达式提取关键字,但若需要跨多条日志进行比对和关联分析,则通常需要多次扫描整个日志文件。</li></ul></li></ul><h2 id="4-具体实例与-NetLog-借鉴"><a href="#4-具体实例与-NetLog-借鉴" class="headerlink" title="4. 具体实例与 NetLog 借鉴"></a>4. 具体实例与 NetLog 借鉴</h2><p>假设用户在使用 Chromium 浏览器时,遇到某个网站加载异常慢。启用 NetLog 后,系统会记录下整个网络请求的事件序列,包括 DNS 解析、TCP 连接、TLS 握手、HTTP 请求和响应等阶段。通过 NetLog Viewer,我们可以直观地看到请求各阶段的时序图,如果发现 DNS 解析阶段耗时异常长,则很可能是这一环节出现了问题,从而指导开发者针对 DNS 环节进行进一步调查和优化。</p><p>此外,通过结合时序数据与各环节之间的关联关系,我们可以进一步探索自动化根因分析的可能性。比如,通过分析历史日志数据,当检测到 DNS 解析延迟超过正常阈值时,系统可自动判断可能存在 DNS 服务器异常或网络故障问题。这种数据驱动的方法将大大提高问题诊断的效率和准确性。</p><p><strong>回到我们的应用场景,如何关联一个请求生命周期的所有 LOG?</strong></p><p>在 Chromium 的 NetLog 设计中,所有与同一请求相关的日志都通过 <code>source id</code> 进行关联。而在 <code>zNet</code> 中,目前涉及到三个 ID:</p><ul><li><code>RequestId</code>:在创建 zNet 请求实例时生成,具有全局唯一性,适用于关联请求的生命周期及时序信息。</li><li><code>curl handle</code>:可能会被多个请求复用,无法唯一标识单个请求。</li><li><code>Web 返回的 trackingId</code>:仅在请求成功时可获取,不适用于完整的生命周期追踪。</li></ul><p><strong>现有问题</strong></p><p>当前的 <strong>troubleshooting</strong> 方式主要依赖 <strong>AfterEmitRequest</strong> 过滤所有请求,然后通过 <strong>RequestId</strong> 或 <strong>请求 URL</strong> 定位对应的请求状态日志,再结合 <strong>curl handle</strong> 追踪网络层日志(如连接状态、Request、Response 详情),最后再查找 <strong>Web 返回的 trackingId</strong> 进行进一步分析。这种方式逻辑机械、繁琐且冗余,增加了排查成本。</p><p><strong>NetLog 方案借鉴</strong></p><p>NetLog 通过 <strong>source id</strong> 关联 HTTP 请求的多个阶段(Phase),确保整个请求的所有事件都可以被统一追踪。</p><p>这里有个例子,NetLog 如何通过 source id 管理一个 HTTP 请求 Phase 的多个 events</p><figure class="highlight jsx"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 1. 开始 HTTP Stream Job Controller</span></span><br><span class="line">{</span><br><span class="line"> <span class="string">"params"</span>: {</span><br><span class="line"> <span class="string">"url"</span>: <span class="string">"<https://github.com/>"</span>,</span><br><span class="line"> <span class="string">"is_preconnect"</span>: <span class="literal">true</span>, <span class="comment">// 这是一个预连接请求</span></span><br><span class="line"> <span class="string">"privacy_mode"</span>: <span class="string">"disabled"</span></span><br><span class="line"> },</span><br><span class="line"> <span class="string">"phase"</span>: <span class="number">1</span>, <span class="comment">// BEGIN</span></span><br><span class="line"> <span class="string">"source"</span>: {</span><br><span class="line"> <span class="string">"id"</span>: <span class="number">21989</span>, <span class="comment">// source id</span></span><br><span class="line"> <span class="string">"start_time"</span>: <span class="string">"223771674"</span>,</span><br><span class="line"> <span class="string">"type"</span>: <span class="number">30</span> <span class="comment">// HTTP_STREAM_JOB_CONTROLLER</span></span><br><span class="line"> },</span><br><span class="line"> <span class="string">"time"</span>: <span class="string">"223771674"</span>,</span><br><span class="line"> <span class="string">"type"</span>: <span class="number">177</span> <span class="comment">// HTTP_STREAM_JOB_CONTROLLER</span></span><br><span class="line">},</span><br><span class="line"></span><br><span class="line"><span class="comment">// 2. 开始代理解析</span></span><br><span class="line">{</span><br><span class="line"> <span class="string">"phase"</span>: <span class="number">1</span>, <span class="comment">// BEGIN</span></span><br><span class="line"> <span class="string">"source"</span>: {</span><br><span class="line"> <span class="string">"id"</span>: <span class="number">21989</span>, <span class="comment">// 复用了同一个 source id</span></span><br><span class="line"> <span class="string">"start_time"</span>: <span class="string">"223771674"</span>,</span><br><span class="line"> <span class="string">"type"</span>: <span class="number">30</span></span><br><span class="line"> },</span><br><span class="line"> <span class="string">"time"</span>: <span class="string">"223771674"</span>,</span><br><span class="line"> <span class="string">"type"</span>: <span class="number">29</span> <span class="comment">// PROXY_RESOLUTION_SERVICE</span></span><br><span class="line">},</span><br><span class="line"></span><br><span class="line"><span class="comment">// 3. 代理解析完成</span></span><br><span class="line">{</span><br><span class="line"> <span class="string">"phase"</span>: <span class="number">2</span>, <span class="comment">// END</span></span><br><span class="line"> <span class="string">"source"</span>: {</span><br><span class="line"> <span class="string">"id"</span>: <span class="number">21989</span>, <span class="comment">// 仍然是同一个 source id</span></span><br><span class="line"> <span class="string">"start_time"</span>: <span class="string">"223771674"</span>,</span><br><span class="line"> <span class="string">"type"</span>: <span class="number">30</span></span><br><span class="line"> },</span><br><span class="line"> <span class="string">"time"</span>: <span class="string">"223771674"</span>,</span><br><span class="line"> <span class="string">"type"</span>: <span class="number">29</span> <span class="comment">// 与开始事件类型相同</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>如果 zNet 也采用类似的方式,将 <strong>RequestId</strong> 作为主关联 ID,不仅能在请求生命周期内确保唯一性,还能减少日志筛选的复杂度,使问题分析更加直观高效。</p><p>因此,对于应用程序来说,借鉴 Chromium NetLog 的设计理念,我们可以构建一个多层次、结构化且统一格式的日志系统,有效解决现有日志中格式不统一、全局状态缺失、关键事件分散、自动化解析难度大和存储混乱等问题,从而为生产环境问题诊断、性能统计和系统优化提供坚实的数据支撑</p><h1 id="五、通用结构化事件日志模块-EventLogger"><a href="#五、通用结构化事件日志模块-EventLogger" class="headerlink" title="五、通用结构化事件日志模块 : EventLogger"></a>五、通用结构化事件日志模块 : EventLogger</h1><h2 id="1-事件类型"><a href="#1-事件类型" class="headerlink" title="1. 事件类型"></a>1. 事件类型</h2><figure class="highlight jsx"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// event_types.h</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 网络事件类型</span></span><br><span class="line">enum <span class="keyword">class</span> <span class="title class_">NetworkEventType</span> {</span><br><span class="line"> <span class="comment">// DNS 相关</span></span><br><span class="line"> <span class="variable constant_">DNS_RESOLUTION_START</span> = <span class="number">1000</span>,</span><br><span class="line"> <span class="variable constant_">DNS_TRANSACTION</span> = <span class="number">1001</span>,</span><br><span class="line"> <span class="variable constant_">DNS_CACHE_LOOKUP</span> = <span class="number">1002</span>,</span><br><span class="line"> <span class="variable constant_">DNS_CONFIG_CHANGED</span> = <span class="number">1003</span>,</span><br><span class="line"> <span class="variable constant_">DNS_RESOLUTION_END</span> = <span class="number">1004</span>,</span><br><span class="line"></span><br><span class="line"> <span class="comment">// TCP 相关</span></span><br><span class="line"> <span class="variable constant_">TCP_CONNECT_START</span> = <span class="number">1100</span>,</span><br><span class="line"> <span class="variable constant_">TCP_CONNECT_ATTEMPT</span> = <span class="number">1101</span>,</span><br><span class="line"> <span class="variable constant_">TCP_CONNECT_END</span> = <span class="number">1102</span>,</span><br><span class="line"></span><br><span class="line"> <span class="comment">// Socket 相关</span></span><br><span class="line"> <span class="variable constant_">SOCKET_ALIVE</span> = <span class="number">1200</span>,</span><br><span class="line"> <span class="variable constant_">SOCKET_OPEN</span> = <span class="number">1201</span>,</span><br><span class="line"> <span class="variable constant_">SOCKET_READ</span> = <span class="number">1202</span>,</span><br><span class="line"> <span class="variable constant_">SOCKET_WRITE</span> = <span class="number">1203</span>,</span><br><span class="line"> <span class="variable constant_">SOCKET_CLOSE</span> = <span class="number">1204</span>,</span><br><span class="line"></span><br><span class="line"> <span class="comment">// SSL/TLS 相关</span></span><br><span class="line"> <span class="variable constant_">SSL_CONNECT_START</span> = <span class="number">1300</span>,</span><br><span class="line"> <span class="variable constant_">SSL_HANDSHAKE</span> = <span class="number">1301</span>,</span><br><span class="line"> <span class="variable constant_">SSL_CERT_VERIFICATION</span> = <span class="number">1302</span>,</span><br><span class="line"> <span class="variable constant_">SSL_CONNECT_END</span> = <span class="number">1303</span>,</span><br><span class="line"></span><br><span class="line"> <span class="comment">// HTTP 相关</span></span><br><span class="line"> <span class="variable constant_">HTTP_TRANSACTION_START</span> = <span class="number">1400</span>,</span><br><span class="line"> <span class="variable constant_">HTTP_SEND_REQUEST_HEADERS</span> = <span class="number">1401</span>,</span><br><span class="line"> <span class="variable constant_">HTTP_SEND_REQUEST_BODY</span> = <span class="number">1402</span>,</span><br><span class="line"> <span class="variable constant_">HTTP_READ_RESPONSE_HEADERS</span> = <span class="number">1403</span>,</span><br><span class="line"> <span class="variable constant_">HTTP_READ_RESPONSE_BODY</span> = <span class="number">1404</span>,</span><br><span class="line"> <span class="variable constant_">HTTP_TRANSACTION_END</span> = <span class="number">1405</span>,</span><br><span class="line"></span><br><span class="line"> <span class="comment">// WebSocket 相关</span></span><br><span class="line"> <span class="variable constant_">WS_CONNECT_START</span> = <span class="number">1500</span>,</span><br><span class="line"> <span class="variable constant_">WS_HANDSHAKE</span> = <span class="number">1501</span>,</span><br><span class="line"> <span class="variable constant_">WS_FRAME_SENT</span> = <span class="number">1502</span>,</span><br><span class="line"> <span class="variable constant_">WS_FRAME_RECEIVED</span> = <span class="number">1503</span>,</span><br><span class="line"> <span class="variable constant_">WS_CONNECTION_CLOSED</span> = <span class="number">1504</span>,</span><br><span class="line"> <span class="variable constant_">WS_ERROR</span> = <span class="number">1505</span>,</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 网络事件</span></span><br><span class="line"> <span class="variable constant_">NETWORK_CHANGED</span> = <span class="number">1600</span>,</span><br><span class="line"> <span class="variable constant_">CONNECTION_POOL_CREATED</span> = <span class="number">1601</span>,</span><br><span class="line"> <span class="variable constant_">PROXY_CONFIG_CHANGED</span> = <span class="number">1602</span>,</span><br><span class="line"> <span class="variable constant_">NETWORK_QUALITY_CHANGED</span> = <span class="number">1603</span></span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="comment">// 数据库事件类型</span></span><br><span class="line">enum <span class="keyword">class</span> <span class="title class_">DatabaseEventType</span> {</span><br><span class="line"> <span class="comment">// 连接管理</span></span><br><span class="line"> <span class="variable constant_">DB_OPEN</span> = <span class="number">2000</span>,</span><br><span class="line"> <span class="variable constant_">DB_CLOSE</span> = <span class="number">2001</span>,</span><br><span class="line"> <span class="variable constant_">DB_CONFIG</span> = <span class="number">2002</span>,</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 事务管理</span></span><br><span class="line"> <span class="variable constant_">TRANSACTION_BEGIN</span> = <span class="number">2100</span>,</span><br><span class="line"> <span class="variable constant_">TRANSACTION_COMMIT</span> = <span class="number">2101</span>,</span><br><span class="line"> <span class="variable constant_">TRANSACTION_ROLLBACK</span> = <span class="number">2102</span>,</span><br><span class="line"> <span class="variable constant_">TRANSACTION_SAVEPOINT</span> = <span class="number">2103</span>,</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 查询执行</span></span><br><span class="line"> <span class="variable constant_">QUERY_PREPARE</span> = <span class="number">2200</span>,</span><br><span class="line"> <span class="variable constant_">QUERY_EXECUTE</span> = <span class="number">2201</span>,</span><br><span class="line"> <span class="variable constant_">QUERY_RESET</span> = <span class="number">2202</span>,</span><br><span class="line"> <span class="variable constant_">QUERY_FINALIZE</span> = <span class="number">2203</span>,</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 语句类型</span></span><br><span class="line"> <span class="variable constant_">STMT_SELECT</span> = <span class="number">2300</span>,</span><br><span class="line"> <span class="variable constant_">STMT_INSERT</span> = <span class="number">2301</span>,</span><br><span class="line"> <span class="variable constant_">STMT_UPDATE</span> = <span class="number">2302</span>,</span><br><span class="line"> <span class="variable constant_">STMT_DELETE</span> = <span class="number">2303</span>,</span><br><span class="line"> <span class="variable constant_">STMT_CREATE</span> = <span class="number">2304</span>,</span><br><span class="line"> <span class="variable constant_">STMT_DROP</span> = <span class="number">2305</span>,</span><br><span class="line"> <span class="variable constant_">STMT_ALTER</span> = <span class="number">2306</span>,</span><br><span class="line"> <span class="variable constant_">STMT_PRAGMA</span> = <span class="number">2307</span>,</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 索引和优化</span></span><br><span class="line"> <span class="variable constant_">QUERY_PLAN</span> = <span class="number">2400</span>,</span><br><span class="line"> <span class="variable constant_">INDEX_USED</span> = <span class="number">2401</span>,</span><br><span class="line"> <span class="variable constant_">TABLE_LOCK</span> = <span class="number">2402</span>,</span><br><span class="line"> <span class="variable constant_">TABLE_SCAN</span> = <span class="number">2403</span>,</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 缓存管理</span></span><br><span class="line"> <span class="variable constant_">CACHE_HIT</span> = <span class="number">2500</span>,</span><br><span class="line"> <span class="variable constant_">CACHE_MISS</span> = <span class="number">2501</span>,</span><br><span class="line"> <span class="variable constant_">CACHE_FLUSH</span> = <span class="number">2502</span>,</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 错误处理</span></span><br><span class="line"> <span class="variable constant_">CONSTRAINT_VIOLATION</span> = <span class="number">2600</span>,</span><br><span class="line"> <span class="variable constant_">LOCKED_ERROR</span> = <span class="number">2601</span>,</span><br><span class="line"> <span class="variable constant_">IO_ERROR</span> = <span class="number">2602</span>,</span><br><span class="line"> <span class="variable constant_">CORRUPT_ERROR</span> = <span class="number">2603</span>,</span><br><span class="line"> <span class="variable constant_">SYNTAX_ERROR</span> = <span class="number">2604</span></span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="comment">// 事件相位</span></span><br><span class="line">enum <span class="keyword">class</span> <span class="title class_">EventPhase</span> {</span><br><span class="line"> <span class="variable constant_">NONE</span> = <span class="number">0</span>,</span><br><span class="line"> <span class="variable constant_">BEGIN</span> = <span class="number">1</span>,</span><br><span class="line"> <span class="variable constant_">END</span> = <span class="number">2</span></span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="comment">// 日志源类型</span></span><br><span class="line">enum <span class="keyword">class</span> <span class="title class_">LogSourceType</span> {</span><br><span class="line"> <span class="variable constant_">NONE</span> = <span class="number">0</span>,</span><br><span class="line"> <span class="variable constant_">NETWORK</span> = <span class="number">1</span>,</span><br><span class="line"> <span class="variable constant_">DATABASE</span> = <span class="number">2</span>,</span><br><span class="line"><span class="comment">// ... 其他类型</span></span><br><span class="line">};</span><br></pre></td></tr></table></figure><h2 id="2-数据结构"><a href="#2-数据结构" class="headerlink" title="2. 数据结构"></a>2. 数据结构</h2><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// event_logger.h</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 事件源</span></span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">EventSource</span> {</span><br><span class="line"> <span class="type">uint32_t</span> id = <span class="number">0</span>;</span><br><span class="line"> LogSourceType type = LogSourceType::NONE;</span><br><span class="line"> std::string start_time;</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="type">bool</span> <span class="title">IsValid</span><span class="params">()</span> <span class="type">const</span> </span>{ <span class="keyword">return</span> id != <span class="number">0</span>; }</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="comment">// 事件条目</span></span><br><span class="line"><span class="keyword">template</span> <<span class="keyword">typename</span> EventTypeEnum></span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">EventEntry</span> {</span><br><span class="line"> <span class="type">uint32_t</span> id = <span class="number">0</span>;</span><br><span class="line"> EventTypeEnum type;</span><br><span class="line"> EventPhase phase = EventPhase::NONE;</span><br><span class="line"> EventSource source;</span><br><span class="line"> std::string time;</span><br><span class="line"> nlohmann::json params;</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="comment">// 事件日志记录器</span></span><br><span class="line"><span class="keyword">template</span> <<span class="keyword">typename</span> EventTypeEnum></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">EventLogger</span> {</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line"> <span class="function"><span class="keyword">explicit</span> <span class="title">EventLogger</span><span class="params">(std::shared_ptr<LegacyLogger> legacy_logger)</span></span></span><br><span class="line"><span class="function"> : legacy_logger_(std::move(legacy_logger)) {</span>}</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 创建事件源</span></span><br><span class="line"> <span class="function">EventSource <span class="title">CreateSource</span><span class="params">(LogSourceType type)</span> </span>{</span><br><span class="line"> EventSource source;</span><br><span class="line"> source.id = next_source_id_++;</span><br><span class="line"> source.type = type;</span><br><span class="line"> source.start_time = <span class="built_in">GetCurrentTimeStr</span>();</span><br><span class="line"> <span class="keyword">return</span> source;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 添加事件</span></span><br><span class="line"> <span class="function"><span class="type">void</span> <span class="title">AddEvent</span><span class="params">(EventTypeEnum type,</span></span></span><br><span class="line"><span class="params"><span class="function"> <span class="type">const</span> EventSource& source,</span></span></span><br><span class="line"><span class="params"><span class="function"> EventPhase phase,</span></span></span><br><span class="line"><span class="params"><span class="function"> <span class="type">const</span> nlohmann::json& params = {})</span> </span>{</span><br><span class="line"> EventEntry<EventTypeEnum> entry;</span><br><span class="line"> entry.id = next_event_id_++;</span><br><span class="line"> entry.type = type;</span><br><span class="line"> entry.phase = phase;</span><br><span class="line"> entry.source = source;</span><br><span class="line"> entry.time = <span class="built_in">GetCurrentTimeStr</span>();</span><br><span class="line"> entry.params = params;</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 记录到传统日志系统</span></span><br><span class="line"> <span class="built_in">WriteToLegacyLog</span>(entry);</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 存储事件</span></span><br><span class="line"> <span class="function">std::lock_guard<std::mutex> <span class="title">lock</span><span class="params">(mutex_)</span></span>;</span><br><span class="line"> entries_.<span class="built_in">push_back</span>(entry);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 导出为JSON</span></span><br><span class="line"> <span class="function">std::string <span class="title">ExportToJson</span><span class="params">()</span> <span class="type">const</span> </span>{</span><br><span class="line"> <span class="function">std::lock_guard<std::mutex> <span class="title">lock</span><span class="params">(mutex_)</span></span>;</span><br><span class="line"></span><br><span class="line"> nlohmann::json root;</span><br><span class="line"> nlohmann::json events = nlohmann::json::<span class="built_in">array</span>();</span><br><span class="line"></span><br><span class="line"> <span class="keyword">for</span> (<span class="type">const</span> <span class="keyword">auto</span>& entry : entries_) {</span><br><span class="line"> events.<span class="built_in">push_back</span>({</span><br><span class="line"> {<span class="string">"id"</span>, entry.id},</span><br><span class="line"> {<span class="string">"type"</span>, <span class="built_in">static_cast</span><<span class="type">int</span>>(entry.type)},</span><br><span class="line"> {<span class="string">"phase"</span>, <span class="built_in">static_cast</span><<span class="type">int</span>>(entry.phase)},</span><br><span class="line"> {<span class="string">"source"</span>, {</span><br><span class="line"> {<span class="string">"id"</span>, entry.source.id},</span><br><span class="line"> {<span class="string">"type"</span>, <span class="built_in">static_cast</span><<span class="type">int</span>>(entry.source.type)},</span><br><span class="line"> {<span class="string">"start_time"</span>, entry.source.start_time}</span><br><span class="line"> }},</span><br><span class="line"> {<span class="string">"time"</span>, entry.time},</span><br><span class="line"> {<span class="string">"params"</span>, entry.params}</span><br><span class="line"> });</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> root[<span class="string">"events"</span>] = std::<span class="built_in">move</span>(events);</span><br><span class="line"> <span class="keyword">return</span> root.<span class="built_in">dump</span>();</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line"> std::shared_ptr<LegacyLogger> legacy_logger_;</span><br><span class="line"> std::atomic<<span class="type">uint32_t</span>> next_event_id_{<span class="number">1</span>};</span><br><span class="line"> std::atomic<<span class="type">uint32_t</span>> next_source_id_{<span class="number">1</span>};</span><br><span class="line"> <span class="keyword">mutable</span> std::mutex mutex_;</span><br><span class="line"> std::vector<EventEntry<EventTypeEnum>> entries_;</span><br><span class="line"></span><br><span class="line"> <span class="function">std::string <span class="title">GetCurrentTimeStr</span><span class="params">()</span> <span class="type">const</span> </span>{</span><br><span class="line"> <span class="keyword">auto</span> now = std::chrono::system_clock::<span class="built_in">now</span>();</span><br><span class="line"> <span class="keyword">auto</span> micros = std::chrono::<span class="built_in">duration_cast</span><std::chrono::microseconds>(</span><br><span class="line"> now.<span class="built_in">time_since_epoch</span>()).<span class="built_in">count</span>();</span><br><span class="line"> <span class="keyword">return</span> std::<span class="built_in">to_string</span>(micros);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="type">void</span> <span class="title">WriteToLegacyLog</span><span class="params">(<span class="type">const</span> EventEntry<EventTypeEnum>& entry)</span> </span>{</span><br><span class="line"> <span class="keyword">if</span> (legacy_logger_) {</span><br><span class="line"> std::string message = <span class="built_in">FormatLogMessage</span>(entry);</span><br><span class="line"> legacy_logger_-><span class="built_in">Log</span>(<span class="built_in">ConvertToLegacyLevel</span>(entry), message);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="function">std::string <span class="title">FormatLogMessage</span><span class="params">(<span class="type">const</span> EventEntry<EventTypeEnum>& entry)</span> <span class="type">const</span></span>;</span><br><span class="line"> <span class="function">LegacyLogLevel <span class="title">ConvertToLegacyLevel</span><span class="params">(<span class="type">const</span> EventEntry<EventTypeEnum>& entry)</span> <span class="type">const</span></span>;</span><br><span class="line">};</span><br></pre></td></tr></table></figure><h2 id="3-用-HTTPRequestEntity-管理-Source"><a href="#3-用-HTTPRequestEntity-管理-Source" class="headerlink" title="3. 用 HTTPRequestEntity 管理 Source"></a>3. 用 HTTPRequestEntity 管理 Source</h2><p>在我们的网络库中,每个请求都被抽象为一个 <code>HTTPRequestEntity</code>,该对象全面 encapsulates(封装)请求的整个生命周期,涵盖从创建到完成的所有关键信息。</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">HTTPRequestEntity</span> {</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line"> <span class="built_in">HTTPRequestEntity</span>(<span class="type">const</span> std::string& url, <span class="type">const</span> std::string& method, NetworkLogger& logger)</span><br><span class="line"> : <span class="built_in">url_</span>(url),</span><br><span class="line"> <span class="built_in">method_</span>(method),</span><br><span class="line"> <span class="built_in">logger_</span>(logger) {</span><br><span class="line"></span><br><span class="line"> source_ = logger_.<span class="built_in">CreateSource</span>(LogSourceType::NETWORK);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> ~<span class="built_in">HTTPRequestEntity</span>() {</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 获取源,用于日志关联</span></span><br><span class="line"> <span class="function"><span class="type">const</span> EventSource& <span class="title">GetSource</span><span class="params">()</span> <span class="type">const</span> </span>{ <span class="keyword">return</span> source_; }</span><br><span class="line"></span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line"></span><br><span class="line"> NetworkLogger& logger_;</span><br><span class="line"> EventSource source_;</span><br><span class="line">};</span><br><span class="line"></span><br></pre></td></tr></table></figure><h2 id="4-将-libCURL-日志转换为结构化事件日志"><a href="#4-将-libCURL-日志转换为结构化事件日志" class="headerlink" title="4. 将 libCURL 日志转换为结构化事件日志"></a>4. 将 libCURL 日志转换为结构化事件日志</h2><p>解析 CURL Debug Callback 输出,并将其转换为结构化的 Event Log,以便更好地分析和追踪请求过程。</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br><span class="line">178</span><br><span class="line">179</span><br><span class="line">180</span><br><span class="line">181</span><br><span class="line">182</span><br><span class="line">183</span><br><span class="line">184</span><br><span class="line">185</span><br><span class="line">186</span><br><span class="line">187</span><br><span class="line">188</span><br><span class="line">189</span><br><span class="line">190</span><br><span class="line">191</span><br><span class="line">192</span><br><span class="line">193</span><br><span class="line">194</span><br><span class="line">195</span><br><span class="line">196</span><br><span class="line">197</span><br><span class="line">198</span><br><span class="line">199</span><br><span class="line">200</span><br><span class="line">201</span><br><span class="line">202</span><br><span class="line">203</span><br><span class="line">204</span><br><span class="line">205</span><br><span class="line">206</span><br><span class="line">207</span><br><span class="line">208</span><br><span class="line">209</span><br><span class="line">210</span><br><span class="line">211</span><br><span class="line">212</span><br><span class="line">213</span><br><span class="line">214</span><br><span class="line">215</span><br><span class="line">216</span><br><span class="line">217</span><br><span class="line">218</span><br><span class="line">219</span><br><span class="line">220</span><br><span class="line">221</span><br><span class="line">222</span><br><span class="line">223</span><br><span class="line">224</span><br><span class="line">225</span><br><span class="line">226</span><br><span class="line">227</span><br><span class="line">228</span><br><span class="line">229</span><br><span class="line">230</span><br><span class="line">231</span><br><span class="line">232</span><br><span class="line">233</span><br><span class="line">234</span><br><span class="line">235</span><br><span class="line">236</span><br><span class="line">237</span><br><span class="line">238</span><br><span class="line">239</span><br><span class="line">240</span><br><span class="line">241</span><br><span class="line">242</span><br><span class="line">243</span><br><span class="line">244</span><br><span class="line">245</span><br><span class="line">246</span><br><span class="line">247</span><br><span class="line">248</span><br><span class="line">249</span><br><span class="line">250</span><br><span class="line">251</span><br><span class="line">252</span><br><span class="line">253</span><br><span class="line">254</span><br><span class="line">255</span><br><span class="line">256</span><br><span class="line">257</span><br><span class="line">258</span><br><span class="line">259</span><br><span class="line">260</span><br><span class="line">261</span><br><span class="line">262</span><br><span class="line">263</span><br><span class="line">264</span><br><span class="line">265</span><br><span class="line">266</span><br><span class="line">267</span><br><span class="line">268</span><br><span class="line">269</span><br><span class="line">270</span><br><span class="line">271</span><br><span class="line">272</span><br><span class="line">273</span><br><span class="line">274</span><br><span class="line">275</span><br><span class="line">276</span><br><span class="line">277</span><br><span class="line">278</span><br><span class="line">279</span><br><span class="line">280</span><br><span class="line">281</span><br><span class="line">282</span><br><span class="line">283</span><br><span class="line">284</span><br><span class="line">285</span><br><span class="line">286</span><br><span class="line">287</span><br><span class="line">288</span><br><span class="line">289</span><br><span class="line">290</span><br><span class="line">291</span><br><span class="line">292</span><br><span class="line">293</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">CurlEventLogger</span> {</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line"> <span class="function"><span class="keyword">explicit</span> <span class="title">CurlEventLogger</span><span class="params">(NetworkLogger& logger)</span></span></span><br><span class="line"><span class="function"> : logger_(logger) {</span>}</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 基于CURL调试信息记录网络事件</span></span><br><span class="line"> <span class="function"><span class="type">void</span> <span class="title">LogCurlDebugInfo</span><span class="params">(<span class="type">const</span> EventSource& source, curl_infotype type,</span></span></span><br><span class="line"><span class="params"><span class="function"> <span class="type">const</span> <span class="type">char</span>* data, <span class="type">size_t</span> size)</span> </span>{</span><br><span class="line"> <span class="keyword">if</span> (data == <span class="literal">nullptr</span> || size == <span class="number">0</span>) {</span><br><span class="line"> <span class="keyword">return</span>;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">switch</span> (type) {</span><br><span class="line"> <span class="keyword">case</span> CURLINFO_TEXT:</span><br><span class="line"> <span class="built_in">LogInfoEvent</span>(source, std::<span class="built_in">string</span>(data, size));</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">case</span> CURLINFO_HEADER_OUT:</span><br><span class="line"> <span class="built_in">LogHeaderOut</span>(source, std::<span class="built_in">string</span>(data, size));</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">case</span> CURLINFO_DATA_OUT:</span><br><span class="line"> <span class="built_in">LogDataOut</span>(source, size);</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">case</span> CURLINFO_SSL_DATA_OUT:</span><br><span class="line"> <span class="built_in">LogSslDataOut</span>(source, size);</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">case</span> CURLINFO_HEADER_IN:</span><br><span class="line"> <span class="built_in">LogHeaderIn</span>(source, std::<span class="built_in">string</span>(data, size));</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">case</span> CURLINFO_DATA_IN:</span><br><span class="line"> <span class="built_in">LogDataIn</span>(source, size);</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">case</span> CURLINFO_SSL_DATA_IN:</span><br><span class="line"> <span class="built_in">LogSslDataIn</span>(source, size);</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">default</span>:</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line"> NetworkLogger& logger_;</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="type">void</span> <span class="title">LogInfoEvent</span><span class="params">(<span class="type">const</span> EventSource& source, <span class="type">const</span> std::string& info)</span> </span>{</span><br><span class="line"> <span class="keyword">if</span> (info.<span class="built_in">find</span>(<span class="string">"Trying "</span>) != std::string::npos) {</span><br><span class="line"> <span class="comment">// DNS解析已完成,开始连接</span></span><br><span class="line"> logger_.<span class="built_in">AddEvent</span>(</span><br><span class="line"> NetworkEventType::TCP_CONNECT_START,</span><br><span class="line"> source,</span><br><span class="line"> EventPhase::BEGIN,</span><br><span class="line"> {{<span class="string">"info"</span>, <span class="built_in">TrimString</span>(info)}}</span><br><span class="line"> );</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">else</span> <span class="keyword">if</span> (info.<span class="built_in">find</span>(<span class="string">"Connected to "</span>) != std::string::npos) {</span><br><span class="line"> <span class="comment">// TCP连接已建立</span></span><br><span class="line"> logger_.<span class="built_in">AddEvent</span>(</span><br><span class="line"> NetworkEventType::TCP_CONNECT_END,</span><br><span class="line"> source,</span><br><span class="line"> EventPhase::END,</span><br><span class="line"> {</span><br><span class="line"> {<span class="string">"info"</span>, <span class="built_in">TrimString</span>(info)},</span><br><span class="line"> {<span class="string">"success"</span>, <span class="literal">true</span>}</span><br><span class="line"> }</span><br><span class="line"> );</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">else</span> <span class="keyword">if</span> (info.<span class="built_in">find</span>(<span class="string">"connect to "</span>) != std::string::npos && info.<span class="built_in">find</span>(<span class="string">"failed:"</span>) != std::string::npos) {</span><br><span class="line"> <span class="comment">// TCP连接失败</span></span><br><span class="line"> logger_.<span class="built_in">AddEvent</span>(</span><br><span class="line"> NetworkEventType::TCP_CONNECT_END,</span><br><span class="line"> source,</span><br><span class="line"> EventPhase::END,</span><br><span class="line"> {</span><br><span class="line"> {<span class="string">"info"</span>, <span class="built_in">TrimString</span>(info)},</span><br><span class="line"> {<span class="string">"success"</span>, <span class="literal">false</span>}</span><br><span class="line"> }</span><br><span class="line"> );</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">else</span> <span class="keyword">if</span> (info.<span class="built_in">find</span>(<span class="string">"SSL connection"</span>) != std::string::npos) {</span><br><span class="line"> <span class="keyword">if</span> (info.<span class="built_in">find</span>(<span class="string">"SSL connection using "</span>) != std::string::npos) {</span><br><span class="line"> <span class="comment">// SSL握手开始</span></span><br><span class="line"> logger_.<span class="built_in">AddEvent</span>(</span><br><span class="line"> NetworkEventType::SSL_CONNECT_START,</span><br><span class="line"> source,</span><br><span class="line"> EventPhase::BEGIN,</span><br><span class="line"> {{<span class="string">"info"</span>, <span class="built_in">TrimString</span>(info)}}</span><br><span class="line"> );</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">else</span> <span class="keyword">if</span> (info.<span class="built_in">find</span>(<span class="string">" established"</span>) != std::string::npos) {</span><br><span class="line"> <span class="comment">// SSL连接建立</span></span><br><span class="line"> logger_.<span class="built_in">AddEvent</span>(</span><br><span class="line"> NetworkEventType::SSL_CONNECT_END,</span><br><span class="line"> source,</span><br><span class="line"> EventPhase::END,</span><br><span class="line"> {</span><br><span class="line"> {<span class="string">"info"</span>, <span class="built_in">TrimString</span>(info)},</span><br><span class="line"> {<span class="string">"success"</span>, <span class="literal">true</span>}</span><br><span class="line"> }</span><br><span class="line"> );</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">else</span> <span class="keyword">if</span> (info.<span class="built_in">find</span>(<span class="string">"Hostname "</span>) != std::string::npos) {</span><br><span class="line"> <span class="keyword">if</span> (info.<span class="built_in">find</span>(<span class="string">"was found in DNS cache"</span>) != std::string::npos) {</span><br><span class="line"> <span class="comment">// DNS缓存命中</span></span><br><span class="line"> logger_.<span class="built_in">AddEvent</span>(</span><br><span class="line"> NetworkEventType::DNS_CACHE_LOOKUP,</span><br><span class="line"> source,</span><br><span class="line"> EventPhase::NONE,</span><br><span class="line"> {</span><br><span class="line"> {<span class="string">"info"</span>, <span class="built_in">TrimString</span>(info)},</span><br><span class="line"> {<span class="string">"cache_hit"</span>, <span class="literal">true</span>}</span><br><span class="line"> }</span><br><span class="line"> );</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">else</span> <span class="keyword">if</span> (info.<span class="built_in">find</span>(<span class="string">"Resolving "</span>) != std::string::npos) {</span><br><span class="line"> <span class="comment">// 开始DNS解析</span></span><br><span class="line"> logger_.<span class="built_in">AddEvent</span>(</span><br><span class="line"> NetworkEventType::DNS_RESOLUTION_START,</span><br><span class="line"> source,</span><br><span class="line"> EventPhase::BEGIN,</span><br><span class="line"> {{<span class="string">"info"</span>, <span class="built_in">TrimString</span>(info)}}</span><br><span class="line"> );</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">else</span> <span class="keyword">if</span> (info.<span class="built_in">find</span>(<span class="string">"Address "</span>) != std::string::npos && info.<span class="built_in">find</span>(<span class="string">" resolved to "</span>) != std::string::npos) {</span><br><span class="line"> <span class="comment">// DNS解析完成</span></span><br><span class="line"> logger_.<span class="built_in">AddEvent</span>(</span><br><span class="line"> NetworkEventType::DNS_RESOLUTION_END,</span><br><span class="line"> source,</span><br><span class="line"> EventPhase::END,</span><br><span class="line"> {</span><br><span class="line"> {<span class="string">"info"</span>, <span class="built_in">TrimString</span>(info)},</span><br><span class="line"> {<span class="string">"success"</span>, <span class="literal">true</span>}</span><br><span class="line"> }</span><br><span class="line"> );</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="type">void</span> <span class="title">LogHeaderOut</span><span class="params">(<span class="type">const</span> EventSource& source, <span class="type">const</span> std::string& headers)</span> </span>{</span><br><span class="line"> <span class="keyword">auto</span> parsed_headers = <span class="built_in">ParseHeaders</span>(headers);</span><br><span class="line"> std::string request_line;</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 提取请求行</span></span><br><span class="line"> <span class="type">size_t</span> pos = headers.<span class="built_in">find</span>(<span class="string">"\\r\\n"</span>);</span><br><span class="line"> <span class="keyword">if</span> (pos != std::string::npos) {</span><br><span class="line"> request_line = headers.<span class="built_in">substr</span>(<span class="number">0</span>, pos);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> logger_.<span class="built_in">AddEvent</span>(</span><br><span class="line"> NetworkEventType::HTTP_SEND_REQUEST_HEADERS,</span><br><span class="line"> source,</span><br><span class="line"> EventPhase::NONE,</span><br><span class="line"> {</span><br><span class="line"> {<span class="string">"headers"</span>, parsed_headers},</span><br><span class="line"> {<span class="string">"request_line"</span>, request_line},</span><br><span class="line"> {<span class="string">"raw_size"</span>, headers.<span class="built_in">size</span>()}</span><br><span class="line"> }</span><br><span class="line"> );</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="type">void</span> <span class="title">LogDataOut</span><span class="params">(<span class="type">const</span> EventSource& source, <span class="type">size_t</span> size)</span> </span>{</span><br><span class="line"> logger_.<span class="built_in">AddEvent</span>(</span><br><span class="line"> NetworkEventType::HTTP_SEND_REQUEST_BODY,</span><br><span class="line"> source,</span><br><span class="line"> EventPhase::NONE,</span><br><span class="line"> {</span><br><span class="line"> {<span class="string">"size"</span>, size}</span><br><span class="line"> }</span><br><span class="line"> );</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="type">void</span> <span class="title">LogSslDataOut</span><span class="params">(<span class="type">const</span> EventSource& source, <span class="type">size_t</span> size)</span> </span>{</span><br><span class="line"> logger_.<span class="built_in">AddEvent</span>(</span><br><span class="line"> NetworkEventType::SSL_DATA_SENT,</span><br><span class="line"> source,</span><br><span class="line"> EventPhase::NONE,</span><br><span class="line"> {</span><br><span class="line"> {<span class="string">"size"</span>, size}</span><br><span class="line"> }</span><br><span class="line"> );</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="type">void</span> <span class="title">LogHeaderIn</span><span class="params">(<span class="type">const</span> EventSource& source, <span class="type">const</span> std::string& headers)</span> </span>{</span><br><span class="line"> <span class="keyword">auto</span> parsed_headers = <span class="built_in">ParseHeaders</span>(headers);</span><br><span class="line"> <span class="type">long</span> status_code = <span class="built_in">ParseStatusCode</span>(headers);</span><br><span class="line"> std::string status_line;</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 提取状态行</span></span><br><span class="line"> <span class="type">size_t</span> pos = headers.<span class="built_in">find</span>(<span class="string">"\\r\\n"</span>);</span><br><span class="line"> <span class="keyword">if</span> (pos != std::string::npos) {</span><br><span class="line"> status_line = headers.<span class="built_in">substr</span>(<span class="number">0</span>, pos);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> logger_.<span class="built_in">AddEvent</span>(</span><br><span class="line"> NetworkEventType::HTTP_READ_RESPONSE_HEADERS,</span><br><span class="line"> source,</span><br><span class="line"> EventPhase::NONE,</span><br><span class="line"> {</span><br><span class="line"> {<span class="string">"headers"</span>, parsed_headers},</span><br><span class="line"> {<span class="string">"status_code"</span>, status_code},</span><br><span class="line"> {<span class="string">"status_line"</span>, status_line},</span><br><span class="line"> {<span class="string">"raw_size"</span>, headers.<span class="built_in">size</span>()}</span><br><span class="line"> }</span><br><span class="line"> );</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="type">void</span> <span class="title">LogDataIn</span><span class="params">(<span class="type">const</span> EventSource& source, <span class="type">size_t</span> size)</span> </span>{</span><br><span class="line"> logger_.<span class="built_in">AddEvent</span>(</span><br><span class="line"> NetworkEventType::HTTP_READ_RESPONSE_BODY,</span><br><span class="line"> source,</span><br><span class="line"> EventPhase::NONE,</span><br><span class="line"> {</span><br><span class="line"> {<span class="string">"size"</span>, size}</span><br><span class="line"> }</span><br><span class="line"> );</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="type">void</span> <span class="title">LogSslDataIn</span><span class="params">(<span class="type">const</span> EventSource& source, <span class="type">size_t</span> size)</span> </span>{</span><br><span class="line"> logger_.<span class="built_in">AddEvent</span>(</span><br><span class="line"> NetworkEventType::SSL_DATA_RECEIVED,</span><br><span class="line"> source,</span><br><span class="line"> EventPhase::NONE,</span><br><span class="line"> {</span><br><span class="line"> {<span class="string">"size"</span>, size}</span><br><span class="line"> }</span><br><span class="line"> );</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 工具函数</span></span><br><span class="line"> <span class="function">nlohmann::json <span class="title">ParseHeaders</span><span class="params">(<span class="type">const</span> std::string& header_text)</span> </span>{</span><br><span class="line"> nlohmann::json headers;</span><br><span class="line"> <span class="function">std::istringstream <span class="title">stream</span><span class="params">(header_text)</span></span>;</span><br><span class="line"> std::string line;</span><br><span class="line"> <span class="type">bool</span> first_line = <span class="literal">true</span>;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">while</span> (std::<span class="built_in">getline</span>(stream, line)) {</span><br><span class="line"> <span class="comment">// 跳过第一行(请求行或状态行)和空行</span></span><br><span class="line"> <span class="keyword">if</span> (first_line) {</span><br><span class="line"> first_line = <span class="literal">false</span>;</span><br><span class="line"> <span class="keyword">continue</span>;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 移除行尾的 \\r</span></span><br><span class="line"> <span class="keyword">if</span> (!line.<span class="built_in">empty</span>() && line.<span class="built_in">back</span>() == <span class="string">'\\r'</span>) {</span><br><span class="line"> line.<span class="built_in">pop_back</span>();</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (line.<span class="built_in">empty</span>()) {</span><br><span class="line"> <span class="keyword">continue</span>;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="type">size_t</span> pos = line.<span class="built_in">find</span>(<span class="string">':'</span>);</span><br><span class="line"> <span class="keyword">if</span> (pos != std::string::npos) {</span><br><span class="line"> std::string key = line.<span class="built_in">substr</span>(<span class="number">0</span>, pos);</span><br><span class="line"> std::string value = line.<span class="built_in">substr</span>(pos + <span class="number">1</span>);</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 移除前后空格</span></span><br><span class="line"> key = <span class="built_in">TrimString</span>(key);</span><br><span class="line"> value = <span class="built_in">TrimString</span>(value);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (!key.<span class="built_in">empty</span>()) {</span><br><span class="line"> headers[key] = value;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> headers;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="type">long</span> <span class="title">ParseStatusCode</span><span class="params">(<span class="type">const</span> std::string& header_line)</span> </span>{</span><br><span class="line"> <span class="function">std::regex <span class="title">status_regex</span><span class="params">(<span class="string">"HTTP/[0-9.]+ ([0-9]+)"</span>)</span></span>;</span><br><span class="line"> std::smatch match;</span><br><span class="line"> <span class="keyword">if</span> (std::<span class="built_in">regex_search</span>(header_line, match, status_regex) && match.<span class="built_in">size</span>() > <span class="number">1</span>) {</span><br><span class="line"> <span class="keyword">return</span> std::<span class="built_in">stol</span>(match[<span class="number">1</span>].<span class="built_in">str</span>());</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="function">std::string <span class="title">TrimString</span><span class="params">(<span class="type">const</span> std::string& str)</span> </span>{</span><br><span class="line"> <span class="type">size_t</span> start = str.<span class="built_in">find_first_not_of</span>(<span class="string">" \\t\\r\\n"</span>);</span><br><span class="line"> <span class="keyword">if</span> (start == std::string::npos) {</span><br><span class="line"> <span class="keyword">return</span> <span class="string">""</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="type">size_t</span> end = str.<span class="built_in">find_last_not_of</span>(<span class="string">" \\t\\r\\n"</span>);</span><br><span class="line"> <span class="keyword">return</span> str.<span class="built_in">substr</span>(start, end - start + <span class="number">1</span>);</span><br><span class="line"> }</span><br><span class="line">};</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>处理 CURL 请求的 Debug Callback</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">SBCurlRequest</span> {</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line"> <span class="built_in">SBCurlRequest</span>(<span class="type">const</span> std::string& url, <span class="type">const</span> std::string& method)</span><br><span class="line"> : <span class="built_in">event_logger_</span>(logger_) {</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 初始化 curl</span></span><br><span class="line"> curl_ = <span class="built_in">curl_easy_init</span>();</span><br><span class="line"></span><br><span class="line"> <span class="comment">// .....</span></span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> ~<span class="built_in">SBCurlRequest</span>() {</span><br><span class="line"> <span class="keyword">if</span> (curl_) {</span><br><span class="line"> <span class="built_in">curl_easy_cleanup</span>(curl_);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line"> CURL* curl_ = <span class="literal">nullptr</span>;</span><br><span class="line"> NetworkLogger logger_;</span><br><span class="line"> CurlEventLogger event_logger_;</span><br><span class="line"></span><br><span class="line"> <span class="comment">// CURL调试回调</span></span><br><span class="line"> <span class="function"><span class="type">static</span> <span class="type">int</span> <span class="title">DebugCallback</span><span class="params">(CURL* handle, curl_infotype type, <span class="type">char</span>* data, <span class="type">size_t</span> size, <span class="type">void</span>* userptr)</span> </span>{</span><br><span class="line"> SBCurlRequest* request = <span class="built_in">static_cast</span><SBCurlRequest*>(userptr);</span><br><span class="line"> <span class="keyword">if</span> (!request || !request->request_entity_) {</span><br><span class="line"> <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 将调试信息传递给事件日志记录器</span></span><br><span class="line"> request->event_logger_.<span class="built_in">LogCurlDebugInfo</span>(</span><br><span class="line"> request->request_entity_-><span class="built_in">GetSource</span>(),</span><br><span class="line"> type,</span><br><span class="line"> data,</span><br><span class="line"> size</span><br><span class="line"> );</span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line">};</span><br></pre></td></tr></table></figure><h2 id="5-结构化的网络事件-Log-示例"><a href="#5-结构化的网络事件-Log-示例" class="headerlink" title="5. 结构化的网络事件 Log 示例"></a>5. 结构化的网络事件 Log 示例</h2><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br><span class="line">178</span><br><span class="line">179</span><br><span class="line">180</span><br><span class="line">181</span><br><span class="line">182</span><br><span class="line">183</span><br><span class="line">184</span><br><span class="line">185</span><br><span class="line">186</span><br><span class="line">187</span><br><span class="line">188</span><br><span class="line">189</span><br><span class="line">190</span><br><span class="line">191</span><br><span class="line">192</span><br><span class="line">193</span><br><span class="line">194</span><br><span class="line">195</span><br><span class="line">196</span><br><span class="line">197</span><br><span class="line">198</span><br><span class="line">199</span><br><span class="line">200</span><br><span class="line">201</span><br><span class="line">202</span><br><span class="line">203</span><br><span class="line">204</span><br><span class="line">205</span><br><span class="line">206</span><br><span class="line">207</span><br><span class="line">208</span><br><span class="line">209</span><br><span class="line">210</span><br><span class="line">211</span><br><span class="line">212</span><br><span class="line">213</span><br><span class="line">214</span><br><span class="line">215</span><br><span class="line">216</span><br><span class="line">217</span><br><span class="line">218</span><br><span class="line">219</span><br><span class="line">220</span><br><span class="line">221</span><br><span class="line">222</span><br><span class="line">223</span><br><span class="line">224</span><br><span class="line">225</span><br><span class="line">226</span><br><span class="line">227</span><br><span class="line">228</span><br><span class="line">229</span><br><span class="line">230</span><br><span class="line">231</span><br><span class="line">232</span><br><span class="line">233</span><br><span class="line">234</span><br><span class="line">235</span><br><span class="line">236</span><br><span class="line">237</span><br><span class="line">238</span><br><span class="line">239</span><br><span class="line">240</span><br><span class="line">241</span><br><span class="line">242</span><br><span class="line">243</span><br><span class="line">244</span><br><span class="line">245</span><br><span class="line">246</span><br><span class="line">247</span><br><span class="line">248</span><br><span class="line">249</span><br><span class="line">250</span><br><span class="line">251</span><br><span class="line">252</span><br><span class="line">253</span><br><span class="line">254</span><br><span class="line">255</span><br><span class="line">256</span><br><span class="line">257</span><br><span class="line">258</span><br><span class="line">259</span><br><span class="line">260</span><br><span class="line">261</span><br><span class="line">262</span><br><span class="line">263</span><br><span class="line">264</span><br><span class="line">265</span><br><span class="line">266</span><br><span class="line">267</span><br><span class="line">268</span><br><span class="line">269</span><br><span class="line">270</span><br><span class="line">271</span><br><span class="line">272</span><br><span class="line">273</span><br><span class="line">274</span><br><span class="line">275</span><br><span class="line">276</span><br><span class="line">277</span><br><span class="line">278</span><br><span class="line">279</span><br><span class="line">280</span><br><span class="line">281</span><br><span class="line">282</span><br><span class="line">283</span><br><span class="line">284</span><br><span class="line">285</span><br><span class="line">286</span><br><span class="line">287</span><br><span class="line">288</span><br><span class="line">289</span><br><span class="line">290</span><br><span class="line">291</span><br><span class="line">292</span><br><span class="line">293</span><br><span class="line">294</span><br><span class="line">295</span><br><span class="line">296</span><br><span class="line">297</span><br><span class="line">298</span><br><span class="line">299</span><br><span class="line">300</span><br><span class="line">301</span><br><span class="line">302</span><br><span class="line">303</span><br><span class="line">304</span><br><span class="line">305</span><br><span class="line">306</span><br><span class="line">307</span><br><span class="line">308</span><br><span class="line">309</span><br><span class="line">310</span><br><span class="line">311</span><br><span class="line">312</span><br><span class="line">313</span><br><span class="line">314</span><br><span class="line">315</span><br><span class="line">316</span><br><span class="line">317</span><br><span class="line">318</span><br><span class="line">319</span><br><span class="line">320</span><br><span class="line">321</span><br><span class="line">322</span><br><span class="line">323</span><br><span class="line">324</span><br><span class="line">325</span><br><span class="line">326</span><br><span class="line">327</span><br><span class="line">328</span><br><span class="line">329</span><br><span class="line">330</span><br></pre></td><td class="code"><pre><span class="line"><span class="punctuation">{</span></span><br><span class="line"> <span class="attr">"constants"</span><span class="punctuation">:</span> <span class="punctuation">{</span></span><br><span class="line"> <span class="attr">"logEventPhase"</span><span class="punctuation">:</span> <span class="punctuation">{</span></span><br><span class="line"> <span class="attr">"BEGIN"</span><span class="punctuation">:</span> <span class="number">1</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"END"</span><span class="punctuation">:</span> <span class="number">2</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"NONE"</span><span class="punctuation">:</span> <span class="number">0</span></span><br><span class="line"> <span class="punctuation">}</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"logEventTypes"</span><span class="punctuation">:</span> <span class="punctuation">{</span></span><br><span class="line"> <span class="attr">"DNS_CACHE_LOOKUP"</span><span class="punctuation">:</span> <span class="number">1002</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"DNS_CONFIG_CHANGED"</span><span class="punctuation">:</span> <span class="number">1003</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"DNS_RESOLUTION_END"</span><span class="punctuation">:</span> <span class="number">1004</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"DNS_RESOLUTION_START"</span><span class="punctuation">:</span> <span class="number">1000</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"DNS_TRANSACTION"</span><span class="punctuation">:</span> <span class="number">1001</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"HTTP_READ_RESPONSE_BODY"</span><span class="punctuation">:</span> <span class="number">1404</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"HTTP_READ_RESPONSE_HEADERS"</span><span class="punctuation">:</span> <span class="number">1403</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"HTTP_SEND_REQUEST_BODY"</span><span class="punctuation">:</span> <span class="number">1402</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"HTTP_SEND_REQUEST_HEADERS"</span><span class="punctuation">:</span> <span class="number">1401</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"HTTP_TRANSACTION_END"</span><span class="punctuation">:</span> <span class="number">1405</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"HTTP_TRANSACTION_START"</span><span class="punctuation">:</span> <span class="number">1400</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"NETWORK_ERROR"</span><span class="punctuation">:</span> <span class="number">1900</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"SOCKET_ALIVE"</span><span class="punctuation">:</span> <span class="number">1200</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"SOCKET_CLOSE"</span><span class="punctuation">:</span> <span class="number">1204</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"SOCKET_OPEN"</span><span class="punctuation">:</span> <span class="number">1201</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"SOCKET_READ"</span><span class="punctuation">:</span> <span class="number">1202</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"SOCKET_WRITE"</span><span class="punctuation">:</span> <span class="number">1203</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"SSL_CERT_VERIFICATION"</span><span class="punctuation">:</span> <span class="number">1302</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"SSL_CONNECT_END"</span><span class="punctuation">:</span> <span class="number">1303</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"SSL_CONNECT_START"</span><span class="punctuation">:</span> <span class="number">1300</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"SSL_DATA_RECEIVED"</span><span class="punctuation">:</span> <span class="number">1305</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"SSL_DATA_SENT"</span><span class="punctuation">:</span> <span class="number">1304</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"SSL_HANDSHAKE"</span><span class="punctuation">:</span> <span class="number">1301</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"TCP_CONNECT_ATTEMPT"</span><span class="punctuation">:</span> <span class="number">1101</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"TCP_CONNECT_END"</span><span class="punctuation">:</span> <span class="number">1102</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"TCP_CONNECT_START"</span><span class="punctuation">:</span> <span class="number">1100</span></span><br><span class="line"> <span class="punctuation">}</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"logSourceType"</span><span class="punctuation">:</span> <span class="punctuation">{</span></span><br><span class="line"> <span class="attr">"DATABASE"</span><span class="punctuation">:</span> <span class="number">2</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"MEETING"</span><span class="punctuation">:</span> <span class="number">3</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"NETWORK"</span><span class="punctuation">:</span> <span class="number">1</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"NONE"</span><span class="punctuation">:</span> <span class="number">0</span></span><br><span class="line"> <span class="punctuation">}</span></span><br><span class="line"> <span class="punctuation">}</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"events"</span><span class="punctuation">:</span> <span class="punctuation">[</span></span><br><span class="line"> <span class="punctuation">{</span></span><br><span class="line"> <span class="attr">"id"</span><span class="punctuation">:</span> <span class="number">1</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"params"</span><span class="punctuation">:</span> <span class="punctuation">{</span></span><br><span class="line"> <span class="attr">"method"</span><span class="punctuation">:</span> <span class="string">"GET"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"request_id"</span><span class="punctuation">:</span> <span class="string">"1"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"timestamp"</span><span class="punctuation">:</span> <span class="string">"1708053600000000"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"url"</span><span class="punctuation">:</span> <span class="string">"<https://api.github.com/users/octocat>"</span></span><br><span class="line"> <span class="punctuation">}</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"phase"</span><span class="punctuation">:</span> <span class="number">1</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"source"</span><span class="punctuation">:</span> <span class="punctuation">{</span></span><br><span class="line"> <span class="attr">"id"</span><span class="punctuation">:</span> <span class="number">1</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"start_time"</span><span class="punctuation">:</span> <span class="string">"1708053600000000"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"type"</span><span class="punctuation">:</span> <span class="number">1</span></span><br><span class="line"> <span class="punctuation">}</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"time"</span><span class="punctuation">:</span> <span class="string">"1708053600000000"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"type"</span><span class="punctuation">:</span> <span class="number">1400</span></span><br><span class="line"> <span class="punctuation">}</span><span class="punctuation">,</span></span><br><span class="line"> <span class="punctuation">{</span></span><br><span class="line"> <span class="attr">"id"</span><span class="punctuation">:</span> <span class="number">2</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"params"</span><span class="punctuation">:</span> <span class="punctuation">{</span></span><br><span class="line"> <span class="attr">"info"</span><span class="punctuation">:</span> <span class="string">"Hostname api.github.com was found in DNS cache"</span></span><br><span class="line"> <span class="punctuation">}</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"phase"</span><span class="punctuation">:</span> <span class="number">0</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"source"</span><span class="punctuation">:</span> <span class="punctuation">{</span></span><br><span class="line"> <span class="attr">"id"</span><span class="punctuation">:</span> <span class="number">1</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"start_time"</span><span class="punctuation">:</span> <span class="string">"1708053600000000"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"type"</span><span class="punctuation">:</span> <span class="number">1</span></span><br><span class="line"> <span class="punctuation">}</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"time"</span><span class="punctuation">:</span> <span class="string">"1708053600050000"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"type"</span><span class="punctuation">:</span> <span class="number">1002</span></span><br><span class="line"> <span class="punctuation">}</span><span class="punctuation">,</span></span><br><span class="line"> <span class="punctuation">{</span></span><br><span class="line"> <span class="attr">"id"</span><span class="punctuation">:</span> <span class="number">3</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"params"</span><span class="punctuation">:</span> <span class="punctuation">{</span></span><br><span class="line"> <span class="attr">"info"</span><span class="punctuation">:</span> <span class="string">"Trying 140.82.121.6:443..."</span></span><br><span class="line"> <span class="punctuation">}</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"phase"</span><span class="punctuation">:</span> <span class="number">1</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"source"</span><span class="punctuation">:</span> <span class="punctuation">{</span></span><br><span class="line"> <span class="attr">"id"</span><span class="punctuation">:</span> <span class="number">1</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"start_time"</span><span class="punctuation">:</span> <span class="string">"1708053600000000"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"type"</span><span class="punctuation">:</span> <span class="number">1</span></span><br><span class="line"> <span class="punctuation">}</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"time"</span><span class="punctuation">:</span> <span class="string">"1708053600100000"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"type"</span><span class="punctuation">:</span> <span class="number">1100</span></span><br><span class="line"> <span class="punctuation">}</span><span class="punctuation">,</span></span><br><span class="line"> <span class="punctuation">{</span></span><br><span class="line"> <span class="attr">"id"</span><span class="punctuation">:</span> <span class="number">4</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"params"</span><span class="punctuation">:</span> <span class="punctuation">{</span></span><br><span class="line"> <span class="attr">"info"</span><span class="punctuation">:</span> <span class="string">"Connected to api.github.com (140.82.121.6) port 443"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"success"</span><span class="punctuation">:</span> <span class="literal"><span class="keyword">true</span></span></span><br><span class="line"> <span class="punctuation">}</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"phase"</span><span class="punctuation">:</span> <span class="number">2</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"source"</span><span class="punctuation">:</span> <span class="punctuation">{</span></span><br><span class="line"> <span class="attr">"id"</span><span class="punctuation">:</span> <span class="number">1</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"start_time"</span><span class="punctuation">:</span> <span class="string">"1708053600000000"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"type"</span><span class="punctuation">:</span> <span class="number">1</span></span><br><span class="line"> <span class="punctuation">}</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"time"</span><span class="punctuation">:</span> <span class="string">"1708053600250000"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"type"</span><span class="punctuation">:</span> <span class="number">1102</span></span><br><span class="line"> <span class="punctuation">}</span><span class="punctuation">,</span></span><br><span class="line"> <span class="punctuation">{</span></span><br><span class="line"> <span class="attr">"id"</span><span class="punctuation">:</span> <span class="number">5</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"params"</span><span class="punctuation">:</span> <span class="punctuation">{</span></span><br><span class="line"> <span class="attr">"info"</span><span class="punctuation">:</span> <span class="string">"SSL connection using TLSv1.3 / TLS_AES_128_GCM_SHA256"</span></span><br><span class="line"> <span class="punctuation">}</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"phase"</span><span class="punctuation">:</span> <span class="number">1</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"source"</span><span class="punctuation">:</span> <span class="punctuation">{</span></span><br><span class="line"> <span class="attr">"id"</span><span class="punctuation">:</span> <span class="number">1</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"start_time"</span><span class="punctuation">:</span> <span class="string">"1708053600000000"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"type"</span><span class="punctuation">:</span> <span class="number">1</span></span><br><span class="line"> <span class="punctuation">}</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"time"</span><span class="punctuation">:</span> <span class="string">"1708053600300000"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"type"</span><span class="punctuation">:</span> <span class="number">1300</span></span><br><span class="line"> <span class="punctuation">}</span><span class="punctuation">,</span></span><br><span class="line"> <span class="punctuation">{</span></span><br><span class="line"> <span class="attr">"id"</span><span class="punctuation">:</span> <span class="number">6</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"params"</span><span class="punctuation">:</span> <span class="punctuation">{</span></span><br><span class="line"> <span class="attr">"size"</span><span class="punctuation">:</span> <span class="number">324</span></span><br><span class="line"> <span class="punctuation">}</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"phase"</span><span class="punctuation">:</span> <span class="number">0</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"source"</span><span class="punctuation">:</span> <span class="punctuation">{</span></span><br><span class="line"> <span class="attr">"id"</span><span class="punctuation">:</span> <span class="number">1</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"start_time"</span><span class="punctuation">:</span> <span class="string">"1708053600000000"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"type"</span><span class="punctuation">:</span> <span class="number">1</span></span><br><span class="line"> <span class="punctuation">}</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"time"</span><span class="punctuation">:</span> <span class="string">"1708053600350000"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"type"</span><span class="punctuation">:</span> <span class="number">1304</span></span><br><span class="line"> <span class="punctuation">}</span><span class="punctuation">,</span></span><br><span class="line"> <span class="punctuation">{</span></span><br><span class="line"> <span class="attr">"id"</span><span class="punctuation">:</span> <span class="number">7</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"params"</span><span class="punctuation">:</span> <span class="punctuation">{</span></span><br><span class="line"> <span class="attr">"size"</span><span class="punctuation">:</span> <span class="number">512</span></span><br><span class="line"> <span class="punctuation">}</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"phase"</span><span class="punctuation">:</span> <span class="number">0</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"source"</span><span class="punctuation">:</span> <span class="punctuation">{</span></span><br><span class="line"> <span class="attr">"id"</span><span class="punctuation">:</span> <span class="number">1</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"start_time"</span><span class="punctuation">:</span> <span class="string">"1708053600000000"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"type"</span><span class="punctuation">:</span> <span class="number">1</span></span><br><span class="line"> <span class="punctuation">}</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"time"</span><span class="punctuation">:</span> <span class="string">"1708053600400000"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"type"</span><span class="punctuation">:</span> <span class="number">1305</span></span><br><span class="line"> <span class="punctuation">}</span><span class="punctuation">,</span></span><br><span class="line"> <span class="punctuation">{</span></span><br><span class="line"> <span class="attr">"id"</span><span class="punctuation">:</span> <span class="number">8</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"params"</span><span class="punctuation">:</span> <span class="punctuation">{</span></span><br><span class="line"> <span class="attr">"info"</span><span class="punctuation">:</span> <span class="string">"SSL connection established, cipher: TLS_AES_128_GCM_SHA256"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"success"</span><span class="punctuation">:</span> <span class="literal"><span class="keyword">true</span></span></span><br><span class="line"> <span class="punctuation">}</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"phase"</span><span class="punctuation">:</span> <span class="number">2</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"source"</span><span class="punctuation">:</span> <span class="punctuation">{</span></span><br><span class="line"> <span class="attr">"id"</span><span class="punctuation">:</span> <span class="number">1</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"start_time"</span><span class="punctuation">:</span> <span class="string">"1708053600000000"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"type"</span><span class="punctuation">:</span> <span class="number">1</span></span><br><span class="line"> <span class="punctuation">}</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"time"</span><span class="punctuation">:</span> <span class="string">"1708053600500000"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"type"</span><span class="punctuation">:</span> <span class="number">1303</span></span><br><span class="line"> <span class="punctuation">}</span><span class="punctuation">,</span></span><br><span class="line"> <span class="punctuation">{</span></span><br><span class="line"> <span class="attr">"id"</span><span class="punctuation">:</span> <span class="number">9</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"params"</span><span class="punctuation">:</span> <span class="punctuation">{</span></span><br><span class="line"> <span class="attr">"headers"</span><span class="punctuation">:</span> <span class="punctuation">{</span></span><br><span class="line"> <span class="attr">"Accept"</span><span class="punctuation">:</span> <span class="string">"application/json"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"Host"</span><span class="punctuation">:</span> <span class="string">"api.github.com"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"User-Agent"</span><span class="punctuation">:</span> <span class="string">"EventLogger/1.0"</span></span><br><span class="line"> <span class="punctuation">}</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"raw_size"</span><span class="punctuation">:</span> <span class="number">127</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"request_line"</span><span class="punctuation">:</span> <span class="string">"GET /users/octocat HTTP/1.1"</span></span><br><span class="line"> <span class="punctuation">}</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"phase"</span><span class="punctuation">:</span> <span class="number">0</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"source"</span><span class="punctuation">:</span> <span class="punctuation">{</span></span><br><span class="line"> <span class="attr">"id"</span><span class="punctuation">:</span> <span class="number">1</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"start_time"</span><span class="punctuation">:</span> <span class="string">"1708053600000000"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"type"</span><span class="punctuation">:</span> <span class="number">1</span></span><br><span class="line"> <span class="punctuation">}</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"time"</span><span class="punctuation">:</span> <span class="string">"1708053600550000"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"type"</span><span class="punctuation">:</span> <span class="number">1401</span></span><br><span class="line"> <span class="punctuation">}</span><span class="punctuation">,</span></span><br><span class="line"> <span class="punctuation">{</span></span><br><span class="line"> <span class="attr">"id"</span><span class="punctuation">:</span> <span class="number">10</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"params"</span><span class="punctuation">:</span> <span class="punctuation">{</span></span><br><span class="line"> <span class="attr">"headers"</span><span class="punctuation">:</span> <span class="punctuation">{</span></span><br><span class="line"> <span class="attr">"Content-Type"</span><span class="punctuation">:</span> <span class="string">"application/json; charset=utf-8"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"Date"</span><span class="punctuation">:</span> <span class="string">"Wed, 16 Feb 2024 10:00:01 GMT"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"ETag"</span><span class="punctuation">:</span> <span class="string">"W/\\"</span><span class="number">12345</span>abcdef\\<span class="string">""</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"Server"</span><span class="punctuation">:</span> <span class="string">"GitHub.com"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"Status"</span><span class="punctuation">:</span> <span class="string">"200 OK"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"X-RateLimit-Limit"</span><span class="punctuation">:</span> <span class="string">"60"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"X-RateLimit-Remaining"</span><span class="punctuation">:</span> <span class="string">"59"</span></span><br><span class="line"> <span class="punctuation">}</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"raw_size"</span><span class="punctuation">:</span> <span class="number">532</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"status_code"</span><span class="punctuation">:</span> <span class="number">200</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"status_line"</span><span class="punctuation">:</span> <span class="string">"HTTP/1.1 200 OK"</span></span><br><span class="line"> <span class="punctuation">}</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"phase"</span><span class="punctuation">:</span> <span class="number">0</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"source"</span><span class="punctuation">:</span> <span class="punctuation">{</span></span><br><span class="line"> <span class="attr">"id"</span><span class="punctuation">:</span> <span class="number">1</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"start_time"</span><span class="punctuation">:</span> <span class="string">"1708053600000000"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"type"</span><span class="punctuation">:</span> <span class="number">1</span></span><br><span class="line"> <span class="punctuation">}</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"time"</span><span class="punctuation">:</span> <span class="string">"1708053600750000"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"type"</span><span class="punctuation">:</span> <span class="number">1403</span></span><br><span class="line"> <span class="punctuation">}</span><span class="punctuation">,</span></span><br><span class="line"> <span class="punctuation">{</span></span><br><span class="line"> <span class="attr">"id"</span><span class="punctuation">:</span> <span class="number">11</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"params"</span><span class="punctuation">:</span> <span class="punctuation">{</span></span><br><span class="line"> <span class="attr">"size"</span><span class="punctuation">:</span> <span class="number">1024</span></span><br><span class="line"> <span class="punctuation">}</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"phase"</span><span class="punctuation">:</span> <span class="number">0</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"source"</span><span class="punctuation">:</span> <span class="punctuation">{</span></span><br><span class="line"> <span class="attr">"id"</span><span class="punctuation">:</span> <span class="number">1</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"start_time"</span><span class="punctuation">:</span> <span class="string">"1708053600000000"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"type"</span><span class="punctuation">:</span> <span class="number">1</span></span><br><span class="line"> <span class="punctuation">}</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"time"</span><span class="punctuation">:</span> <span class="string">"1708053600800000"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"type"</span><span class="punctuation">:</span> <span class="number">1404</span></span><br><span class="line"> <span class="punctuation">}</span><span class="punctuation">,</span></span><br><span class="line"> <span class="punctuation">{</span></span><br><span class="line"> <span class="attr">"id"</span><span class="punctuation">:</span> <span class="number">12</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"params"</span><span class="punctuation">:</span> <span class="punctuation">{</span></span><br><span class="line"> <span class="attr">"size"</span><span class="punctuation">:</span> <span class="number">1256</span></span><br><span class="line"> <span class="punctuation">}</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"phase"</span><span class="punctuation">:</span> <span class="number">0</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"source"</span><span class="punctuation">:</span> <span class="punctuation">{</span></span><br><span class="line"> <span class="attr">"id"</span><span class="punctuation">:</span> <span class="number">1</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"start_time"</span><span class="punctuation">:</span> <span class="string">"1708053600000000"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"type"</span><span class="punctuation">:</span> <span class="number">1</span></span><br><span class="line"> <span class="punctuation">}</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"time"</span><span class="punctuation">:</span> <span class="string">"1708053600850000"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"type"</span><span class="punctuation">:</span> <span class="number">1404</span></span><br><span class="line"> <span class="punctuation">}</span><span class="punctuation">,</span></span><br><span class="line"> <span class="punctuation">{</span></span><br><span class="line"> <span class="attr">"id"</span><span class="punctuation">:</span> <span class="number">13</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"params"</span><span class="punctuation">:</span> <span class="punctuation">{</span></span><br><span class="line"> <span class="attr">"size_download"</span><span class="punctuation">:</span> <span class="number">2280</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"status_code"</span><span class="punctuation">:</span> <span class="number">200</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"success"</span><span class="punctuation">:</span> <span class="literal"><span class="keyword">true</span></span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"total_time_ms"</span><span class="punctuation">:</span> <span class="number">875</span></span><br><span class="line"> <span class="punctuation">}</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"phase"</span><span class="punctuation">:</span> <span class="number">2</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"source"</span><span class="punctuation">:</span> <span class="punctuation">{</span></span><br><span class="line"> <span class="attr">"id"</span><span class="punctuation">:</span> <span class="number">1</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"start_time"</span><span class="punctuation">:</span> <span class="string">"1708053600000000"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"type"</span><span class="punctuation">:</span> <span class="number">1</span></span><br><span class="line"> <span class="punctuation">}</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"time"</span><span class="punctuation">:</span> <span class="string">"1708053600875000"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"type"</span><span class="punctuation">:</span> <span class="number">1405</span></span><br><span class="line"> <span class="punctuation">}</span><span class="punctuation">,</span></span><br><span class="line"> <span class="punctuation">{</span></span><br><span class="line"> <span class="attr">"id"</span><span class="punctuation">:</span> <span class="number">14</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"params"</span><span class="punctuation">:</span> <span class="punctuation">{</span></span><br><span class="line"> <span class="attr">"method"</span><span class="punctuation">:</span> <span class="string">"GET"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"request_id"</span><span class="punctuation">:</span> <span class="string">"2"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"timestamp"</span><span class="punctuation">:</span> <span class="string">"1708053601000000"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"url"</span><span class="punctuation">:</span> <span class="string">"<https://nonexistent.example.com/api>"</span></span><br><span class="line"> <span class="punctuation">}</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"phase"</span><span class="punctuation">:</span> <span class="number">1</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"source"</span><span class="punctuation">:</span> <span class="punctuation">{</span></span><br><span class="line"> <span class="attr">"id"</span><span class="punctuation">:</span> <span class="number">2</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"start_time"</span><span class="punctuation">:</span> <span class="string">"1708053601000000"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"type"</span><span class="punctuation">:</span> <span class="number">1</span></span><br><span class="line"> <span class="punctuation">}</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"time"</span><span class="punctuation">:</span> <span class="string">"1708053601000000"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"type"</span><span class="punctuation">:</span> <span class="number">1400</span></span><br><span class="line"> <span class="punctuation">}</span><span class="punctuation">,</span></span><br><span class="line"> <span class="punctuation">{</span></span><br><span class="line"> <span class="attr">"id"</span><span class="punctuation">:</span> <span class="number">15</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"params"</span><span class="punctuation">:</span> <span class="punctuation">{</span></span><br><span class="line"> <span class="attr">"info"</span><span class="punctuation">:</span> <span class="string">"Resolving nonexistent.example.com..."</span></span><br><span class="line"> <span class="punctuation">}</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"phase"</span><span class="punctuation">:</span> <span class="number">1</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"source"</span><span class="punctuation">:</span> <span class="punctuation">{</span></span><br><span class="line"> <span class="attr">"id"</span><span class="punctuation">:</span> <span class="number">2</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"start_time"</span><span class="punctuation">:</span> <span class="string">"1708053601000000"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"type"</span><span class="punctuation">:</span> <span class="number">1</span></span><br><span class="line"> <span class="punctuation">}</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"time"</span><span class="punctuation">:</span> <span class="string">"1708053601050000"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"type"</span><span class="punctuation">:</span> <span class="number">1000</span></span><br><span class="line"> <span class="punctuation">}</span><span class="punctuation">,</span></span><br><span class="line"> <span class="punctuation">{</span></span><br><span class="line"> <span class="attr">"id"</span><span class="punctuation">:</span> <span class="number">16</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"params"</span><span class="punctuation">:</span> <span class="punctuation">{</span></span><br><span class="line"> <span class="attr">"info"</span><span class="punctuation">:</span> <span class="string">"Host nonexistent.example.com not found: 3(NXDOMAIN)"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"success"</span><span class="punctuation">:</span> <span class="literal"><span class="keyword">false</span></span></span><br><span class="line"> <span class="punctuation">}</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"phase"</span><span class="punctuation">:</span> <span class="number">2</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"source"</span><span class="punctuation">:</span> <span class="punctuation">{</span></span><br><span class="line"> <span class="attr">"id"</span><span class="punctuation">:</span> <span class="number">2</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"start_time"</span><span class="punctuation">:</span> <span class="string">"1708053601000000"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"type"</span><span class="punctuation">:</span> <span class="number">1</span></span><br><span class="line"> <span class="punctuation">}</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"time"</span><span class="punctuation">:</span> <span class="string">"1708053607050000"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"type"</span><span class="punctuation">:</span> <span class="number">1004</span></span><br><span class="line"> <span class="punctuation">}</span><span class="punctuation">,</span></span><br><span class="line"> <span class="punctuation">{</span></span><br><span class="line"> <span class="attr">"id"</span><span class="punctuation">:</span> <span class="number">17</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"params"</span><span class="punctuation">:</span> <span class="punctuation">{</span></span><br><span class="line"> <span class="attr">"curl_code"</span><span class="punctuation">:</span> <span class="number">6</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"error"</span><span class="punctuation">:</span> <span class="literal"><span class="keyword">true</span></span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"error_message"</span><span class="punctuation">:</span> <span class="string">"Couldn't resolve host name"</span></span><br><span class="line"> <span class="punctuation">}</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"phase"</span><span class="punctuation">:</span> <span class="number">2</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"source"</span><span class="punctuation">:</span> <span class="punctuation">{</span></span><br><span class="line"> <span class="attr">"id"</span><span class="punctuation">:</span> <span class="number">2</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"start_time"</span><span class="punctuation">:</span> <span class="string">"1708053601000000"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"type"</span><span class="punctuation">:</span> <span class="number">1</span></span><br><span class="line"> <span class="punctuation">}</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"time"</span><span class="punctuation">:</span> <span class="string">"1708053607055000"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"type"</span><span class="punctuation">:</span> <span class="number">1405</span></span><br><span class="line"> <span class="punctuation">}</span><span class="punctuation">,</span></span><br><span class="line"> <span class="punctuation">{</span></span><br><span class="line"> <span class="attr">"id"</span><span class="punctuation">:</span> <span class="number">18</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"params"</span><span class="punctuation">:</span> <span class="punctuation">{</span></span><br><span class="line"> <span class="attr">"curl_code"</span><span class="punctuation">:</span> <span class="number">6</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"error_message"</span><span class="punctuation">:</span> <span class="string">"Couldn't resolve host name"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"url"</span><span class="punctuation">:</span> <span class="string">"<https://nonexistent.example.com/api>"</span></span><br><span class="line"> <span class="punctuation">}</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"phase"</span><span class="punctuation">:</span> <span class="number">0</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"source"</span><span class="punctuation">:</span> <span class="punctuation">{</span></span><br><span class="line"> <span class="attr">"id"</span><span class="punctuation">:</span> <span class="number">2</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"start_time"</span><span class="punctuation">:</span> <span class="string">"1708053601000000"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"type"</span><span class="punctuation">:</span> <span class="number">1</span></span><br><span class="line"> <span class="punctuation">}</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"time"</span><span class="punctuation">:</span> <span class="string">"1708053607056000"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"type"</span><span class="punctuation">:</span> <span class="number">1900</span></span><br><span class="line"> <span class="punctuation">}</span></span><br><span class="line"> <span class="punctuation">]</span></span><br><span class="line"><span class="punctuation">}</span></span><br></pre></td></tr></table></figure><h2 id="6-应用场景"><a href="#6-应用场景" class="headerlink" title="6. 应用场景"></a>6. 应用场景</h2><ul><li>代替 MEMLOG,用于线上问题 Troubleshooting</li><li>诊断窗口可以导入分析,产生可视化报表</li><li><p>自动化分析、用户 AI 训练</p><p> 其实并不需要额外的训练。大语言模型本身已经具备出色的分析能力。我将上述 Log 示例作为输入,让大模型进行解析,得到的输出结果如下,可以看出其表现已经相当优秀。</p><p> <strong>Claude 的分析输出结果:</strong></p> <figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br></pre></td><td class="code"><pre><span class="line">第一个请求 (request_id<span class="punctuation">:</span> <span class="string">"1"</span>)<span class="punctuation">:</span></span><br><span class="line"></span><br><span class="line"><span class="number">1.</span> 时间<span class="punctuation">:</span> 从 <span class="number">1708053600000000</span> 开始 (约<span class="number">2024</span>年<span class="number">2</span>月<span class="number">16</span>日<span class="number">10</span><span class="punctuation">:</span><span class="number">00</span><span class="punctuation">:</span><span class="number">00</span>)</span><br><span class="line"><span class="number">2.</span> 目标<span class="punctuation">:</span> GET https<span class="punctuation">:</span><span class="comment">//api.github.com/users/octocat</span></span><br><span class="line"><span class="number">3.</span> 请求过程<span class="punctuation">:</span></span><br><span class="line"> - DNS查询<span class="punctuation">:</span> 发现 <span class="punctuation">[</span>api.github.com<span class="punctuation">]</span>(https<span class="punctuation">:</span><span class="comment">//api.github.com/) 在DNS缓存中</span></span><br><span class="line"> - TCP连接<span class="punctuation">:</span> 连接到 <span class="number">140.82</span><span class="number">.121</span><span class="number">.6</span><span class="punctuation">:</span><span class="number">443</span> 端口,连接成功</span><br><span class="line"> - SSL/TLS握手<span class="punctuation">:</span> 使用 TLSv1<span class="number">.3</span> 和 TLS_AES_128_GCM_SHA256 加密套件</span><br><span class="line"> - HTTP请求<span class="punctuation">:</span> 发送带有基本headers的GET请求</span><br><span class="line"> - 服务器响应<span class="punctuation">:</span></span><br><span class="line"> - 状态码<span class="punctuation">:</span> <span class="number">200</span> OK</span><br><span class="line"> - 收到总计<span class="number">2280</span>字节的响应数据</span><br><span class="line"> - 完整请求耗时<span class="punctuation">:</span> <span class="number">875</span>毫秒</span><br><span class="line"> - 整个请求成功完成</span><br><span class="line"></span><br><span class="line">第二个请求 (request_id<span class="punctuation">:</span> <span class="string">"2"</span>)<span class="punctuation">:</span></span><br><span class="line"></span><br><span class="line"><span class="number">1.</span> 时间<span class="punctuation">:</span> 从 <span class="number">1708053601000000</span> 开始 (比第一个请求晚<span class="number">1</span>秒)</span><br><span class="line"><span class="number">2.</span> 目标<span class="punctuation">:</span> GET https<span class="punctuation">:</span><span class="comment">//nonexistent.example.com/api</span></span><br><span class="line"><span class="number">3.</span> 请求过程<span class="punctuation">:</span></span><br><span class="line"> - DNS解析<span class="punctuation">:</span> 尝试解析 <span class="punctuation">[</span>nonexistent.example.com<span class="punctuation">]</span>(https<span class="punctuation">:</span><span class="comment">//nonexistent.example.com/)</span></span><br><span class="line"> - 失败<span class="punctuation">:</span> 收到 NXDOMAIN 错误(域名不存在)</span><br><span class="line"> - 请求终止<span class="punctuation">:</span></span><br><span class="line"> - 错误代码<span class="punctuation">:</span> <span class="number">6</span></span><br><span class="line"> - 错误信息<span class="punctuation">:</span> <span class="string">"Couldn't resolve host name"</span></span><br><span class="line"> - 整个请求失败,耗时约<span class="number">6</span>秒</span><br><span class="line"></span><br><span class="line">这个日志系统使用了不同的事件类型来追踪请求的各个阶段:</span><br><span class="line"></span><br><span class="line">- <span class="number">1000</span>系列<span class="punctuation">:</span> DNS相关事件</span><br><span class="line">- <span class="number">1100</span>系列<span class="punctuation">:</span> TCP连接事件</span><br><span class="line">- <span class="number">1300</span>系列<span class="punctuation">:</span> SSL/TLS事件</span><br><span class="line">- <span class="number">1400</span>系列<span class="punctuation">:</span> HTTP事件</span><br><span class="line">- <span class="number">1900</span>系列<span class="punctuation">:</span> 错误事件</span><br><span class="line"></span><br><span class="line">每个事件都包含了详细的时间戳、事件类型、阶段(开始/结束/中间)以及相关参数,这对于调试网络问题和性能分析非常有帮助。</span><br></pre></td></tr></table></figure></li></ul><h1 id="六、总结"><a href="#六、总结" class="headerlink" title="六、总结"></a>六、总结</h1><p>通过分析当前日志系统的问题并结合 Chromium NetLog 的设计经验,我们可以得出以下几点启示:</p><ol><li>日志系统应该采用统一的格式和标准,便于工具化处理和自动化分析</li><li>需要建立完整的上下文跟踪机制,特别是在处理复杂的异步操作时</li><li>应该支持多级别、多类型的日志记录,以适应不同的调试场景</li><li>日志的存储和检索机制需要优化,支持高效的过滤和关联分析</li><li>要在详细程度和性能开销之间找到平衡点:既能满足日常开发调试需求,又能有效支持生产环境问题诊断的日志系统</li></ol>]]></content>
<summary type="html"><p>思考当前客户端日志存在的问题,并深入学习 Chromium NetLog 的设计与实现细节,从中汲取灵感,打造一套适用于我们客户端产品的结构化事件日志系统。</p></summary>
<category term="Chromium" scheme="https://ustcqidi.github.io/tags/Chromium/"/>
<category term="Troubleshooting" scheme="https://ustcqidi.github.io/tags/Troubleshooting/"/>
<category term="关联分析" scheme="https://ustcqidi.github.io/tags/%E5%85%B3%E8%81%94%E5%88%86%E6%9E%90/"/>
<category term="自动化分析" scheme="https://ustcqidi.github.io/tags/%E8%87%AA%E5%8A%A8%E5%8C%96%E5%88%86%E6%9E%90/"/>
</entry>
<entry>
<title>这短短几十年,我们仿佛穿越了好几个时代</title>
<link href="https://ustcqidi.github.io/2025/02/20/future/"/>
<id>https://ustcqidi.github.io/2025/02/20/future/</id>
<published>2025-02-20T13:39:58.000Z</published>
<updated>2025-02-20T13:41:56.530Z</updated>
<content type="html"><![CDATA[<p>我们经常在想,如果能穿越回某个时间点,我们就知道该如何抓住财富机遇,如何让自己财务自由。但其实,我们现在就处在一个巨变的时间点——AI、大模型、机器人、低空飞行……这些技术正重新塑造世界,我们的生活方式、职业形态、商业格局都将在未来几年发生深刻变化。</p><span id="more"></span><h3 id="科技进步的速度,超乎想象"><a href="#科技进步的速度,超乎想象" class="headerlink" title="科技进步的速度,超乎想象"></a><strong>科技进步的速度,超乎想象</strong></h3><p>今天下午,我和马老师以及一位同事在外面散步聊天,感慨最近科技的进步实在是太快了,日新月异,让人应接不暇。从春晚上的机器人,到AI大模型DeepSeek的突破,我们这一代人正见证着一个前所未有的时代变革。</p><p>宇数科技的机器人,它的视频演示如今的动作流畅度已经远超春晚时的水平,让我们畅想未来机器人技术的无限可能。我们感叹,作为这一代人,我们真的赶上了好时候。未来,机器人的应用场景将无处不在,也许等我们老了,医护机器人、情感陪护机器人都会成为生活的一部分。而DeepSeek的出现,更是直接颠覆了整个大模型领域,应用场景的想象空间变得更加广阔。试想一下,如果宇数科技的机器人能结合DeepSeek的模型,那将会带来怎样的震撼?</p><p>另外,现象级的国产动画电影《哪吒》的票房和成绩也是过去难以想象的。这一切的背后,离不开科技的进步,生产力的提升以及数字化内容制作能力的飞跃。</p><h3 id="日常生活中的科技变革"><a href="#日常生活中的科技变革" class="headerlink" title="日常生活中的科技变革"></a><strong>日常生活中的科技变革</strong></h3><p>散步期间,马老师正在关注华为汽车发布会,对智能驾驶和新能源汽车技术的快速发展深感震撼。我们在园区和快递站点已经常见到无人车执行快递调度任务。科技进步的速度确实超出预期,日新月异。我们这代人有幸将目睹科幻电影中的场景逐一实现:街道上各种自动化设备、机器人、自动驾驶、低空飞行、无人车调度、无人机等众多科幻元素。</p><p>作为变革最剧烈时代的亲历者,我们是幸运的一代。曾经的生活与现在相比几乎割裂:从物质匮乏时代到生活逐渐改善,每隔五到十年就会有截然不同的体验。</p><p>从2010年左右开始,我们见证了移动互联网浪潮的兴起,经历了许多革命性变革。最初实习时接触的安卓2.0系统与今天的版本判若两物。我们经历了通讯工具从固定电话、大哥大、传呼机、小灵通到诺基亚,再到智能手机的演变;从网吧排队通宵打CS,到人人拥有笔记本电脑和智能终端。</p><p>我们还经历了从一条短信1毛钱的时代,到飞信、QQ、微信彻底改变人们的沟通方式;经历了百团大战、共享单车大战、滴滴快滴的混战,以及外卖行业的兴起;经历了互联网创业潮,曾经的编程培训班里,只要会写个”Hello World”的人都能拿到高薪。</p><p>我们见证了网购支付方式从繁琐的银联支付、U盾验证,到如今便捷的移动支付;从乘坐通宵绿皮车听列车员吆喝”啤酒饮料矿泉水,瓜子泡面火腿肠”,到现在的高铁时代;房价从一两千到几万的攀升;茅台、比特币、特斯拉等各类投资机会的出现。</p><p>短短几十年的人生经历仿佛被割裂成不同时空。</p><h3 id="面对变革,我们如何保持竞争力?"><a href="#面对变革,我们如何保持竞争力?" class="headerlink" title="面对变革,我们如何保持竞争力?"></a><strong>面对变革,我们如何保持竞争力?</strong></h3><p>许多行业和岗位正在被颠覆,比如线上问诊、法律咨询、文案撰写、初级数据处理等,AI已经可以轻松取代大量基础工作。</p><p>在这个充满不确定性的时代,我们如何保持竞争力,不被淘汰?</p><ol><li><p><strong>拥抱变化,持续学习</strong></p><p> 未来世界最大的变量就是变化本身,我们不能固守过去的经验,而是要不断学习新的知识,适应新的技术。AI时代,学习如何与智能工具协作,比单纯掌握一门技能更重要。</p></li><li><p><strong>提升创造力和独立思考能力</strong></p><p> 机械化的工作终究会被AI取代,而人类的创造力、批判性思维、情感共鸣是目前机器无法替代的。我们需要培养自己的创新能力,拓展思维边界,才能在新时代立于不败之地。</p></li><li><p><strong>跨界融合,拓展认知边界</strong></p><p> 未来的竞争,不只是单一技能的比拼,而是跨领域能力的竞争。懂编程的设计师、会产品思维的工程师、具备商业洞察力的技术人员,都会在AI时代拥有更强的生存能力。</p></li><li><p><strong>关注行业趋势,把握风口</strong></p><p> 在科技变革的洪流中,有些行业会消失,但也有新的机会诞生。AI、机器人、低空飞行等领域都是未来的风口,我们要学会捕捉趋势,找到自己的定位。就如马老师所说,我们没法再去一线参与这些行业的研发,但也许可以投资这些方向的股票和基金…</p></li><li><p><strong>培养不可替代的人际沟通和管理能力</strong></p><p> AI可以代替很多重复性的工作,但人与人之间的深度交流、团队协作、组织管理仍然是无可替代的软实力。懂得如何高效沟通,如何激励团队,如何管理复杂的项目,依然是未来职场的核心竞争力。</p></li></ol><h3 id="未来,充满未知,但也充满机会"><a href="#未来,充满未知,但也充满机会" class="headerlink" title="未来,充满未知,但也充满机会"></a><strong>未来,充满未知,但也充满机会</strong></h3><p>我们经常在想,如果能穿越回某个时间点,我们就知道该如何抓住财富机遇,如何让自己财务自由。但其实,我们现在就处在一个巨变的时间点——AI、大模型、机器人、低空飞行……这些技术正重新塑造世界,我们的生活方式、职业形态、商业格局都将在未来几年发生深刻变化。</p><p>科技进步带来的冲击是不可避免的,但面对变革,我们能做的不是抗拒,而是主动拥抱。那些有远见、敢于思考、不断提升自我的人,终将在这个时代中找到属于自己的机会。</p><p><strong>未来已来,我们准备好了吗?</strong></p>]]></content>
<summary type="html"><p>我们经常在想,如果能穿越回某个时间点,我们就知道该如何抓住财富机遇,如何让自己财务自由。但其实,我们现在就处在一个巨变的时间点——AI、大模型、机器人、低空飞行……这些技术正重新塑造世界,我们的生活方式、职业形态、商业格局都将在未来几年发生深刻变化。</p></summary>
<category term="大模型" scheme="https://ustcqidi.github.io/tags/%E5%A4%A7%E6%A8%A1%E5%9E%8B/"/>
<category term="低空飞行" scheme="https://ustcqidi.github.io/tags/%E4%BD%8E%E7%A9%BA%E9%A3%9E%E8%A1%8C/"/>
<category term="机器人" scheme="https://ustcqidi.github.io/tags/%E6%9C%BA%E5%99%A8%E4%BA%BA/"/>
<category term="AI" scheme="https://ustcqidi.github.io/tags/AI/"/>
<category term="科技变革" scheme="https://ustcqidi.github.io/tags/%E7%A7%91%E6%8A%80%E5%8F%98%E9%9D%A9/"/>
</entry>
<entry>
<title>《老后破产》的警示</title>
<link href="https://ustcqidi.github.io/2025/02/07/fi/"/>
<id>https://ustcqidi.github.io/2025/02/07/fi/</id>
<published>2025-02-07T13:45:57.000Z</published>
<updated>2025-02-07T13:49:30.104Z</updated>
<content type="html"><![CDATA[<p>建立被动收入体系对普通人(尤其是程序员)至关重要。虽然初期效果可能不明显,但随着时间推移,稳固的被动收入将成为实现财务自由和应对裁员、行业波动及人工智能冲击风险的关键。因此,越早开始规划和布局,被动收入体系将使未来的风险更易掌控。</p><span id="more"></span><p>今天散步时,突然想起之前看过的一部日本纪录片,名叫<strong>《老后破产》</strong>。它讲述了一群日本老人的境遇:他们到了退休年龄,却仍然不得不工作。尽管这些老人有退休金,但由于上有老、下有小,退休金难以支撑最基本的生活开销,因此只能继续打工。更令人唏嘘的是,他们年轻时的工作和收入状况都相当不错,只是缺乏未雨绸缪的财务规划,过于乐观地认为当下的收入可以持续,直到真正退休后才意识到现实的残酷。有空的话,不妨找来这部纪录片看看,它的教育意义不言而喻。</p><p><img src="/2025/02/07/fi/old.png" alt="image.png"></p><p>之前偶然看到一幅关于“财富蓄水池”的图,觉得非常有趣,第一时间就分享给了马老师。这幅图的内容让我深有感触,尤其是对于我们这些正处于人生关键阶段的中年人。它提醒我们,不能只是埋头苦干、拿着一成不变的工资,而是要提前规划,思考如何让自己的财务更加稳健,朝着更长远的目标努力。</p><p><img src="/2025/02/07/fi/money.png" alt="image.png"></p><h2 id="财务健康与自由的核心"><a href="#财务健康与自由的核心" class="headerlink" title="财务健康与自由的核心"></a>财务健康与自由的核心</h2><p>这幅图清晰地展示了<strong>财务健康的关键</strong>:我们的财务由<strong>主动收入</strong>(如工作薪资)和<strong>被动收入</strong>(如投资收益)构成,同时支出包括<strong>日常开销</strong>和<strong>应急开销</strong>。<strong>实现财务自由的核心</strong>,在于构建一个足够稳健的“财富蓄水池”,并且拥有<strong>持续增长的被动收入</strong>,确保它能够覆盖所有生活支出。</p><p>在尚未实现财务自由之前,我们唯一能做的,就是<strong>开源节流</strong>。</p><h2 id="节流:控制开支,精简消费"><a href="#节流:控制开支,精简消费" class="headerlink" title="节流:控制开支,精简消费"></a><strong>节流:控制开支,精简消费</strong></h2><p><strong>减少不必要的支出</strong>,降低物质欲望,把时间和精力投入到学习、运动和工作中,从而提升自我价值。具体方法包括:</p><ul><li><strong>消费降级</strong>:<ul><li>手机、汽车等非必需品,不必追求最新款,更不必频繁更换。</li><li>生活必需品(如衣物、家居用品等)可批量采购,或在双11等促销期间囤货。</li></ul></li><li><strong>定期审视消费</strong>:<ul><li>每月或每半个月检查一次支出,砍掉不必要的开销。</li></ul></li><li><strong>投资健康,减少隐性成本</strong>:<ul><li>规律锻炼、保持良好作息,减少疾病带来的额外支出和焦虑。</li><li>维持稳定的情绪和心理状态,避免冲动消费和不必要的财务风险。</li></ul></li></ul><h2 id="开源:提升主动收入,探索被动收入"><a href="#开源:提升主动收入,探索被动收入" class="headerlink" title="开源:提升主动收入,探索被动收入"></a><strong>开源:提升主动收入,探索被动收入</strong></h2><p>收入来源分为<strong>主动收入</strong>(通过时间或劳动力换取)和<strong>被动收入</strong>(无需持续投入精力即可获得)。想要加速财务自由的进程,必须同时提升<strong>主动收入</strong>和<strong>被动收入</strong>。</p><h3 id="提高主动收入"><a href="#提高主动收入" class="headerlink" title="提高主动收入"></a><strong>提高主动收入</strong></h3><p>在工作中,不要满足于“躺平”或拿着死工资,而是要不断提升<strong>专业技能、管理能力和综合素质</strong>,从而拓宽职业发展路径,提高收入上限。除了完成日常工作,还要有意识地:</p><ul><li><strong>主动学习</strong>:总结经验、提升技能,积累核心竞争力。</li><li><strong>走出舒适区</strong>:承担更具挑战性的任务,拓展职业成长空间。</li><li><strong>提升议价能力</strong>:让自己的技能和影响力匹配更高的薪资水平。</li></ul><h3 id="构建被动收入"><a href="#构建被动收入" class="headerlink" title="构建被动收入"></a><strong>构建被动收入</strong></h3><p><strong>被动收入是财务自由的关键</strong>,即便你的主动收入再高、财富积累再多,如果没有稳定的被动收入,仍然有坐吃山空的那一天。</p><p>纪录片中有个令人印象深刻的案例:一位老年出租车司机,年轻时曾是企业高管,收入丰厚。然而,他从未考虑过退休后的财务安排,过于依赖当时的薪资,认为自己的收入可以持续。结果退休后才发现,主动收入无法稳定输出,而他的退休金远不足以支撑家庭开销,因此不得不重新投入工作。</p><p>这个故事给我们的启示是:<strong>被动收入的建立,必须趁早规划,越早投入,长期收益越可观</strong>。即使最初的被动收入占比不高,也要投入足够的时间和精力去构建它。</p><p><strong>如何构建被动收入?</strong></p><ol><li><strong>投资理财与资产配置</strong>:<ul><li>选择适合自己的投资方向,如基金、股票、房地产等,但前提是<strong>具备足够的学习能力和市场认知</strong>,并保持长期坚持的耐心。</li></ul></li><li><strong>培养可持续变现的技能</strong>:<ul><li>除了本职工作,还可以探索副业、内容创作、知识付费等方式,实现多元化收入。</li></ul></li><li><strong>建立可复用的资产</strong>:<ul><li>例如开发软件、写书、制作课程,或构建具有长期收益的业务模式。</li></ul></li></ol><h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a><strong>总结</strong></h2><p>对于普通人来说,构建被动收入需要耐心和持续投入。尽管前期收益可能微乎其微,但一旦建立起稳固的收入体系,它将成为通往财务自由的基石。</p><p>特别是我们程序员群体,30岁后更应该将时间向被动收入构建倾斜。当前行业存在诸多不确定性:突如其来的裁员、行业周期性波动,更不用说 DeepSeek 等AI大模型的迅猛发展,可能会重塑整个就业市场。</p><p>因此,<strong>尽早开始规划和布局被动收入,才能更从容地应对未来的不确定性</strong>。早期的积累和尝试,会在未来的风险来临时成为最可靠的保障。</p>]]></content>
<summary type="html"><p>建立被动收入体系对普通人(尤其是程序员)至关重要。虽然初期效果可能不明显,但随着时间推移,稳固的被动收入将成为实现财务自由和应对裁员、行业波动及人工智能冲击风险的关键。因此,越早开始规划和布局,被动收入体系将使未来的风险更易掌控。</p></summary>
<category term="被动收入" scheme="https://ustcqidi.github.io/tags/%E8%A2%AB%E5%8A%A8%E6%94%B6%E5%85%A5/"/>
<category term="资产配置" scheme="https://ustcqidi.github.io/tags/%E8%B5%84%E4%BA%A7%E9%85%8D%E7%BD%AE/"/>
<category term="长期积累" scheme="https://ustcqidi.github.io/tags/%E9%95%BF%E6%9C%9F%E7%A7%AF%E7%B4%AF/"/>
<category term="消费降级" scheme="https://ustcqidi.github.io/tags/%E6%B6%88%E8%B4%B9%E9%99%8D%E7%BA%A7/"/>
<category term="风险管理" scheme="https://ustcqidi.github.io/tags/%E9%A3%8E%E9%99%A9%E7%AE%A1%E7%90%86/"/>
</entry>
<entry>
<title>利用大语言模型分析 Wireshark 抓包数据</title>
<link href="https://ustcqidi.github.io/2025/02/06/llm-pcap/"/>
<id>https://ustcqidi.github.io/2025/02/06/llm-pcap/</id>
<published>2025-02-06T12:29:34.000Z</published>
<updated>2025-02-06T13:53:02.731Z</updated>
<content type="html"><![CDATA[<p>利用大语言模型来辅助分析 Wireshark 网络数据包,从而加深对典型网络协议的理解,提升分析常见网络问题的能力,并进一步建立标准化的分析流程 Prompt,以提高问题排查的效率。</p><span id="more"></span><h1 id="背景"><a href="#背景" class="headerlink" title="背景"></a>背景</h1><p>Wireshark 作为一款功能强大的网络数据包捕获与分析工具,在实际问题分析过程中,我主要遇到两个挑战:一是对数据包中异常信息或特定字段的含义理解不够深入,二是对过滤器(filter)的使用不够熟练,导致难以快速定位关键数据。 </p><p>我希望能够利用大语言模型来辅助分析 Wireshark 网络数据包,从而加深对典型网络协议的理解,提升分析常见网络问题的能力,并进一步建立标准化的分析流程 Prompt,以提高问题排查的效率。</p><h1 id="如何开始"><a href="#如何开始" class="headerlink" title="如何开始"></a>如何开始</h1><p>Wireshark 导出的数据包格式是 .pcap,它是一种二进制格式,ChatGPT、Claude 和 DeepSeek 等大语言模型无法直接解析。但如果将 .pcap 转换为可读的结构化文本,它们就能进行分析并提供有价值的见解。</p><p>以下是 ChatGPT 给出的一些思路</p><blockquote><p>你想要 LLM 直接解析 Wireshark 捕获的 <code>.pcap</code> 文件,并提取有价值的信息,比如网络错误、丢包、时延等,然后结合 Prompt 进行总结,对吧?你打算怎么使用这个分析工具?是本地跑 LLM 解析,还是想要一个在线交互式的分析环境? </p><p>从你的目标来看,可以有几种方式: </p><ol><li><p><strong>自动化日志提取与分析</strong>: </p><ul><li>解析 <code>.pcap</code> 文件,提取关键数据(如 TCP 三次握手失败、DNS 解析错误等)。 </li><li>生成结构化的日志,用 LLM 进行摘要和问题分析。 </li></ul></li><li><p><strong>沉淀 Prompt 以优化 LLM 分析能力</strong>: </p><ul><li>针对不同的网络问题(丢包、高时延、握手失败),设计一系列 Prompt,引导 LLM 输出更准确的诊断。 </li><li>建立一个 Prompt 库,方便后续类似问题的分析。 </li></ul></li><li><p><strong>构建一个 Project(可能是 Python 脚本 + LLM)</strong>: </p><ul><li>用 <code>pyshark</code> 解析 <code>.pcap</code> 文件,将流量数据转换成 LLM 适合处理的格式(比如 JSON)。 </li><li>结合 LLM 进行分析,并生成易读的报告。 </li></ul></li></ol><p>你倾向于哪种方式?或者你希望从哪一步开始?</p></blockquote><p>简单来说,有两种思路: </p><ol><li>使用 <strong>pyshark</strong> 解析 <code>.pcap</code> 文件,直接提取关键数据。 </li><li>将 <code>.pcap</code> 转换为可读的结构化文本,再交给大语言模型进行分析。 </li></ol><p>我更倾向于第二种方式,这样可以提升数据的可读性,并更灵活地利用大语言模型进行问题诊断和总结。</p><h1 id="导出数据包解析结果"><a href="#导出数据包解析结果" class="headerlink" title="导出数据包解析结果"></a>导出数据包解析结果</h1><p>利用 Wireshark 的 “Export packet dissections” 功能,可以以结构化的方式导出解析后的数据包信息,便于后续分析、共享或存档。</p><p>常见的导出格式包括:</p><ul><li>Plain Text (.txt):以纯文本形式呈现解析数据,适合快速浏览。</li><li>CSV (.csv):以表格化方式组织数据,便于统计和筛选分析。</li><li>JSON (.json):包含完整的层级结构,适合机器读取和进一步处理。</li><li>XML (.xml):适用于结构化存储和解析。</li><li>PSML/PDML:Wireshark 专用的 XML 格式,提供详细的协议解析信息。</li></ul><p><img src="/2025/02/06/llm-pcap/export.jpg" alt="export"></p><p>我分别导出了 CSV 和 JSON 格式,并进行对比后发现:JSON 格式包含所有数据包的详细信息,更适合大语言模型进行深入分析。</p><p>以下是一个数据包的 JSON 样本,可以看出它完整记录了网络包的所有字段和细节。</p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br></pre></td><td class="code"><pre><span class="line"><span class="punctuation">{</span></span><br><span class="line"> <span class="attr">"_index"</span><span class="punctuation">:</span> <span class="string">"packets-2005-03-30"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"_type"</span><span class="punctuation">:</span> <span class="string">"doc"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"_score"</span><span class="punctuation">:</span> <span class="literal"><span class="keyword">null</span></span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"_source"</span><span class="punctuation">:</span> <span class="punctuation">{</span></span><br><span class="line"> <span class="attr">"layers"</span><span class="punctuation">:</span> <span class="punctuation">{</span></span><br><span class="line"> <span class="attr">"frame"</span><span class="punctuation">:</span> <span class="punctuation">{</span></span><br><span class="line"> <span class="attr">"frame.encap_type"</span><span class="punctuation">:</span> <span class="string">"1"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"frame.time"</span><span class="punctuation">:</span> <span class="string">"Mar 30, 2005 16:52:17.755930000 CST"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"frame.time_utc"</span><span class="punctuation">:</span> <span class="string">"Mar 30, 2005 08:52:17.755930000 UTC"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"frame.time_epoch"</span><span class="punctuation">:</span> <span class="string">"1112172737.755930000"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"frame.offset_shift"</span><span class="punctuation">:</span> <span class="string">"0.000000000"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"frame.time_delta"</span><span class="punctuation">:</span> <span class="string">"0.015764000"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"frame.time_delta_displayed"</span><span class="punctuation">:</span> <span class="string">"0.015764000"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"frame.time_relative"</span><span class="punctuation">:</span> <span class="string">"271.259884000"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"frame.number"</span><span class="punctuation">:</span> <span class="string">"28"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"frame.len"</span><span class="punctuation">:</span> <span class="string">"129"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"frame.cap_len"</span><span class="punctuation">:</span> <span class="string">"129"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"frame.marked"</span><span class="punctuation">:</span> <span class="string">"0"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"frame.ignored"</span><span class="punctuation">:</span> <span class="string">"0"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"frame.protocols"</span><span class="punctuation">:</span> <span class="string">"eth:ethertype:ip:udp:dns"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"frame.coloring_rule.name"</span><span class="punctuation">:</span> <span class="string">"UDP"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"frame.coloring_rule.string"</span><span class="punctuation">:</span> <span class="string">"udp"</span></span><br><span class="line"> <span class="punctuation">}</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"eth"</span><span class="punctuation">:</span> <span class="punctuation">{</span></span><br><span class="line"> <span class="attr">"eth.dst"</span><span class="punctuation">:</span> <span class="string">"00:12:a9:00:32:23"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"eth.dst_tree"</span><span class="punctuation">:</span> <span class="punctuation">{</span></span><br><span class="line"> <span class="attr">"eth.dst_resolved"</span><span class="punctuation">:</span> <span class="string">"3Com_00:32:23"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"eth.dst.oui"</span><span class="punctuation">:</span> <span class="string">"4777"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"eth.dst.oui_resolved"</span><span class="punctuation">:</span> <span class="string">"3Com Ltd"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"eth.dst.lg"</span><span class="punctuation">:</span> <span class="string">"0"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"eth.dst.ig"</span><span class="punctuation">:</span> <span class="string">"0"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"eth.addr"</span><span class="punctuation">:</span> <span class="string">"00:12:a9:00:32:23"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"eth.addr_resolved"</span><span class="punctuation">:</span> <span class="string">"3Com_00:32:23"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"eth.addr.oui"</span><span class="punctuation">:</span> <span class="string">"4777"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"eth.addr.oui_resolved"</span><span class="punctuation">:</span> <span class="string">"3Com Ltd"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"eth.lg"</span><span class="punctuation">:</span> <span class="string">"0"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"eth.ig"</span><span class="punctuation">:</span> <span class="string">"0"</span></span><br><span class="line"> <span class="punctuation">}</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"eth.src"</span><span class="punctuation">:</span> <span class="string">"00:60:08:45:e4:55"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"eth.src_tree"</span><span class="punctuation">:</span> <span class="punctuation">{</span></span><br><span class="line"> <span class="attr">"eth.src_resolved"</span><span class="punctuation">:</span> <span class="string">"3Com_45:e4:55"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"eth.src.oui"</span><span class="punctuation">:</span> <span class="string">"24584"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"eth.src.oui_resolved"</span><span class="punctuation">:</span> <span class="string">"3Com"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"eth.src.lg"</span><span class="punctuation">:</span> <span class="string">"0"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"eth.src.ig"</span><span class="punctuation">:</span> <span class="string">"0"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"eth.addr"</span><span class="punctuation">:</span> <span class="string">"00:60:08:45:e4:55"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"eth.addr_resolved"</span><span class="punctuation">:</span> <span class="string">"3Com_45:e4:55"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"eth.addr.oui"</span><span class="punctuation">:</span> <span class="string">"24584"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"eth.addr.oui_resolved"</span><span class="punctuation">:</span> <span class="string">"3Com"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"eth.lg"</span><span class="punctuation">:</span> <span class="string">"0"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"eth.ig"</span><span class="punctuation">:</span> <span class="string">"0"</span></span><br><span class="line"> <span class="punctuation">}</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"eth.type"</span><span class="punctuation">:</span> <span class="string">"0x0800"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"eth.stream"</span><span class="punctuation">:</span> <span class="string">"1"</span></span><br><span class="line"> <span class="punctuation">}</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"ip"</span><span class="punctuation">:</span> <span class="punctuation">{</span></span><br><span class="line"> <span class="attr">"ip.version"</span><span class="punctuation">:</span> <span class="string">"4"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"ip.hdr_len"</span><span class="punctuation">:</span> <span class="string">"20"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"ip.dsfield"</span><span class="punctuation">:</span> <span class="string">"0x00"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"ip.dsfield_tree"</span><span class="punctuation">:</span> <span class="punctuation">{</span></span><br><span class="line"> <span class="attr">"ip.dsfield.dscp"</span><span class="punctuation">:</span> <span class="string">"0"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"ip.dsfield.ecn"</span><span class="punctuation">:</span> <span class="string">"0"</span></span><br><span class="line"> <span class="punctuation">}</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"ip.len"</span><span class="punctuation">:</span> <span class="string">"115"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"ip.id"</span><span class="punctuation">:</span> <span class="string">"0x87de"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"ip.flags"</span><span class="punctuation">:</span> <span class="string">"0x00"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"ip.flags_tree"</span><span class="punctuation">:</span> <span class="punctuation">{</span></span><br><span class="line"> <span class="attr">"ip.flags.rb"</span><span class="punctuation">:</span> <span class="string">"0"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"ip.flags.df"</span><span class="punctuation">:</span> <span class="string">"0"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"ip.flags.mf"</span><span class="punctuation">:</span> <span class="string">"0"</span></span><br><span class="line"> <span class="punctuation">}</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"ip.frag_offset"</span><span class="punctuation">:</span> <span class="string">"0"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"ip.ttl"</span><span class="punctuation">:</span> <span class="string">"128"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"ip.proto"</span><span class="punctuation">:</span> <span class="string">"17"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"ip.checksum"</span><span class="punctuation">:</span> <span class="string">"0x6a95"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"ip.checksum.status"</span><span class="punctuation">:</span> <span class="string">"2"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"ip.src"</span><span class="punctuation">:</span> <span class="string">"192.168.170.56"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"ip.addr"</span><span class="punctuation">:</span> <span class="string">"192.168.170.56"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"ip.src_host"</span><span class="punctuation">:</span> <span class="string">"192.168.170.56"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"ip.host"</span><span class="punctuation">:</span> <span class="string">"192.168.170.56"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"ip.dst"</span><span class="punctuation">:</span> <span class="string">"217.13.4.24"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"ip.addr"</span><span class="punctuation">:</span> <span class="string">"217.13.4.24"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"ip.dst_host"</span><span class="punctuation">:</span> <span class="string">"217.13.4.24"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"ip.host"</span><span class="punctuation">:</span> <span class="string">"217.13.4.24"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"ip.stream"</span><span class="punctuation">:</span> <span class="string">"1"</span></span><br><span class="line"> <span class="punctuation">}</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"udp"</span><span class="punctuation">:</span> <span class="punctuation">{</span></span><br><span class="line"> <span class="attr">"udp.srcport"</span><span class="punctuation">:</span> <span class="string">"1707"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"udp.dstport"</span><span class="punctuation">:</span> <span class="string">"53"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"udp.port"</span><span class="punctuation">:</span> <span class="string">"1707"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"udp.port"</span><span class="punctuation">:</span> <span class="string">"53"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"udp.length"</span><span class="punctuation">:</span> <span class="string">"95"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"udp.checksum"</span><span class="punctuation">:</span> <span class="string">"0x39f0"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"udp.checksum.status"</span><span class="punctuation">:</span> <span class="string">"2"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"udp.stream"</span><span class="punctuation">:</span> <span class="string">"3"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"udp.stream.pnum"</span><span class="punctuation">:</span> <span class="string">"1"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"Timestamps"</span><span class="punctuation">:</span> <span class="punctuation">{</span></span><br><span class="line"> <span class="attr">"udp.time_relative"</span><span class="punctuation">:</span> <span class="string">"0.000000000"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"udp.time_delta"</span><span class="punctuation">:</span> <span class="string">"0.000000000"</span></span><br><span class="line"> <span class="punctuation">}</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"udp.payload"</span><span class="punctuation">:</span> <span class="string">"32:6e:01:00:00:01:00:00:00:00:00:00:05:5f:6c:64:61:70:04:5f:74:63:70:17:44:65:66:61:75:6c:74:2d:46:69:72:73:74:2d:53:69:74:65:2d:4e:61:6d:65:06:5f:73:69:74:65:73:02:64:63:06:5f:6d:73:64:63:73:0b:75:74:65:6c:73:79:73:74:65:6d:73:05:6c:6f:63:61:6c:00:00:21:00:01"</span></span><br><span class="line"> <span class="punctuation">}</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"dns"</span><span class="punctuation">:</span> <span class="punctuation">{</span></span><br><span class="line"> <span class="attr">"dns.id"</span><span class="punctuation">:</span> <span class="string">"0x326e"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"dns.flags"</span><span class="punctuation">:</span> <span class="string">"0x0100"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"dns.flags_tree"</span><span class="punctuation">:</span> <span class="punctuation">{</span></span><br><span class="line"> <span class="attr">"dns.flags.response"</span><span class="punctuation">:</span> <span class="string">"0"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"dns.flags.opcode"</span><span class="punctuation">:</span> <span class="string">"0"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"dns.flags.truncated"</span><span class="punctuation">:</span> <span class="string">"0"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"dns.flags.recdesired"</span><span class="punctuation">:</span> <span class="string">"1"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"dns.flags.z"</span><span class="punctuation">:</span> <span class="string">"0"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"dns.flags.checkdisable"</span><span class="punctuation">:</span> <span class="string">"0"</span></span><br><span class="line"> <span class="punctuation">}</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"dns.count.queries"</span><span class="punctuation">:</span> <span class="string">"1"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"dns.count.answers"</span><span class="punctuation">:</span> <span class="string">"0"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"dns.count.auth_rr"</span><span class="punctuation">:</span> <span class="string">"0"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"dns.count.add_rr"</span><span class="punctuation">:</span> <span class="string">"0"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"Queries"</span><span class="punctuation">:</span> <span class="punctuation">{</span></span><br><span class="line"> <span class="attr">"_ldap._tcp.Default-First-Site-Name._sites.dc._msdcs.utelsystems.local: type SRV, class IN"</span><span class="punctuation">:</span> <span class="punctuation">{</span></span><br><span class="line"> <span class="attr">"dns.qry.name"</span><span class="punctuation">:</span> <span class="string">"_ldap._tcp.Default-First-Site-Name._sites.dc._msdcs.utelsystems.local"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"dns.qry.name.len"</span><span class="punctuation">:</span> <span class="string">"69"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"dns.count.labels"</span><span class="punctuation">:</span> <span class="string">"8"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"dns.qry.type"</span><span class="punctuation">:</span> <span class="string">"33"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"dns.qry.class"</span><span class="punctuation">:</span> <span class="string">"0x0001"</span></span><br><span class="line"> <span class="punctuation">}</span></span><br><span class="line"> <span class="punctuation">}</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"dns.response_in"</span><span class="punctuation">:</span> <span class="string">"30"</span></span><br><span class="line"> <span class="punctuation">}</span></span><br><span class="line"> <span class="punctuation">}</span></span><br><span class="line"> <span class="punctuation">}</span></span><br><span class="line"><span class="punctuation">}</span></span><br></pre></td></tr></table></figure><h1 id="初步探索"><a href="#初步探索" class="headerlink" title="初步探索"></a>初步探索</h1><h2 id="抓包样本"><a href="#抓包样本" class="headerlink" title="抓包样本"></a>抓包样本</h2><p>网上有许多开源的 Wireshark 抓包样本可供学习,例如 <strong>Wireshark Sample Captures 官方库</strong>,该库收录了涵盖各种网络协议和应用场景的 <code>.pcap</code> 文件,便于研究和测试。地址:<a href="https://wiki.wireshark.org/SampleCaptures">Wireshark Sample Captures</a>。 </p><h2 id="实验"><a href="#实验" class="headerlink" title="实验"></a>实验</h2><p>我的目标是<strong>利用大语言模型分析和理解 Wireshark 抓取的网络数据包</strong>,主要聚焦于两个方向: </p><ol><li><strong>学习典型的网络协议</strong>,加深对不同协议结构和行为的理解。 </li><li><strong>分析常见网络问题</strong>,并总结出一套标准化的 Prompt ,以提升问题排查效率。 </li></ol><p>在实际分析中,以下问题较为常见: </p><ul><li><strong>TCP 连接问题</strong>(如三次握手失败、连接被重置) </li><li><strong>HTTP/HTTPS 错误</strong>(如 4xx、5xx 状态码) </li><li><strong>DNS 解析异常</strong>(如解析失败、响应超时) </li><li><strong>网络延迟/性能问题</strong>(如高 RTT、丢包) </li><li><strong>应用层协议异常</strong>(如 WebSocket 断连、TLS 握手失败) </li></ul><p>作为初步探索,我选取了一个 <strong>DNS 查询包</strong> 作为示例,并尝试用大语言模型进行分析,以验证其对网络数据的理解和诊断能力。</p><h2 id="Prompt"><a href="#Prompt" class="headerlink" title="Prompt"></a>Prompt</h2><p>在使用大语言模型解析 Wireshark 抓包数据时,我设计了一系列 Prompt,以提取关键信息、诊断网络问题,并加深对协议的理解。</p><h3 id="基础信息提取"><a href="#基础信息提取" class="headerlink" title="基础信息提取"></a>基础信息提取</h3><p>用于获取抓包数据的基本概况,包括通信协议、IP 地址、时间信息等:</p><blockquote><p>请分析这个 Wireshark 抓包数据文件并提供以下信息:</p><ol><li>涉及的主要协议</li><li>通信双方的 IP 地址</li><li>关键的时间节点</li><li>异常数据包的特征</li><li>基本的统计信息(包数量、流量等)</li></ol></blockquote><h3 id="问题诊断"><a href="#问题诊断" class="headerlink" title="问题诊断"></a>问题诊断</h3><p>用于识别潜在的网络问题,并提供分析和解决方案:</p><blockquote><p>基于上述基础信息,请:</p><ol><li>识别存在的网络问题</li><li>分析问题的可能原因</li><li>提供问题的解决建议</li><li>列出类似问题的预防措施</li></ol></blockquote><h3 id="协议学习"><a href="#协议学习" class="headerlink" title="协议学习"></a>协议学习</h3><p>用于深入学习抓包数据涉及的具体协议,加深对协议的理解:</p><blockquote><p>请详细解释这个抓包中的 [具体协议] :</p><ol><li>协议的基本工作流程</li><li>关键字段的含义</li><li>正常vs异常的特征</li><li>该协议的最佳实践</li></ol></blockquote><p>这套 Prompt 既能帮助快速提取抓包的核心信息,也能用于深入分析问题和学习协议规范,从而提升网络数据分析的效率和准确性。</p><h2 id="大模型给出的结果"><a href="#大模型给出的结果" class="headerlink" title="大模型给出的结果"></a>大模型给出的结果</h2><h3 id="基础信息提取-1"><a href="#基础信息提取-1" class="headerlink" title="基础信息提取"></a>基础信息提取</h3><blockquote><p>我来分析这个DNS相关的抓包文件:</p><ol><li>涉及的主要协议</li></ol><ul><li><strong>底层</strong>: Ethernet </li><li><strong>网络层</strong>: IPv4 </li><li><strong>传输层</strong>: UDP </li><li><strong>应用层</strong>: DNS (端口53) </li></ul><ol start="2"><li>通信双方的IP地址<br><strong>主要的通信方</strong>: </li></ol><ul><li>内部客户端: <code>192.168.170.8</code>, <code>192.168.170.56</code> </li><li>DNS服务器: <code>192.168.170.20</code>, <code>217.13.4.24</code> </li></ul><ol start="3"><li>关键时间节点</li></ol><ul><li><strong>开始时间</strong>: <code>2005-03-30 16:47:46.496</code> </li><li><strong>结束时间</strong>: <code>2005-03-30 16:52:25.375</code> </li><li><strong>总时长</strong>: 约<code>4分39秒</code> </li></ul><ol start="4"><li>异常数据包的特征<br>检测到多个DNS查询失败 (<code>RCODE=3 NXDOMAIN</code>): </li></ol><ul><li><code>GRIMM.utelsystems.local</code> 的A记录查询 </li><li><code>_ldap._tcp.dc._msdcs.utelsystems.local</code> 的SRV记录查询 </li><li><code>_ldap._tcp.Default-First-Site-Name._sites.dc._msdcs.utelsystems.local</code> 的SRV记录查询 </li></ul><ol start="5"><li>基本统计信息</li></ol><ul><li><strong>总包数</strong>: <code>38个数据包</code> </li><li><strong>DNS查询类型分布</strong>: <ul><li>A记录查询 </li><li>AAAA记录查询 </li><li>MX记录查询 </li><li>TXT记录查询 </li><li>SRV记录查询 </li><li>PTR记录查询 </li><li>ANY记录查询 </li></ul></li><li><strong>主要查询域名</strong>: <ul><li><code>google.com</code> </li><li><code>www.netbsd.org</code> </li><li><code>www.isc.org</code> </li><li><code>*.utelsystems.local</code> </li></ul></li><li><strong>DNS响应情况</strong>: <ul><li>成功响应包含A记录、AAAA记录、MX记录等 </li><li>多个NXDOMAIN失败响应 </li><li>平均DNS响应时间在<strong>毫秒级别</strong> </li></ul></li></ul><p>这些DNS查询数据显示系统在进行<strong>常规的域名解析</strong>,同时也在尝试解析一些<strong>内部Active Directory相关的域名(但失败了)</strong>,这可能表明是一个<strong>Windows域环境中的客户端行为</strong>。</p></blockquote><h3 id="分析指定编号或者ID的报文"><a href="#分析指定编号或者ID的报文" class="headerlink" title="分析指定编号或者ID的报文"></a>分析指定编号或者ID的报文</h3><blockquote><p>在Wireshark数据包中, DNS查询ID在DNS头部, 你可以在报文的dns层看到:</p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">"dns"</span><span class="punctuation">:</span> <span class="punctuation">{</span></span><br><span class="line"> <span class="attr">"dns.id"</span><span class="punctuation">:</span> <span class="string">"0x326e"</span><span class="punctuation">,</span> <span class="comment">// 这里就是DNS查询ID</span></span><br><span class="line"> <span class="attr">"dns.flags"</span><span class="punctuation">:</span> <span class="string">"0x0100"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"dns.flags_tree"</span><span class="punctuation">:</span> <span class="punctuation">{</span></span><br><span class="line"> <span class="attr">"dns.flags.response"</span><span class="punctuation">:</span> <span class="string">"0"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"dns.flags.opcode"</span><span class="punctuation">:</span> <span class="string">"0"</span><span class="punctuation">,</span> </span><br><span class="line"> ...</span><br><span class="line"> <span class="punctuation">}</span></span><br><span class="line"><span class="punctuation">}</span></span><br></pre></td></tr></table></figure><p>我们以报文28为例,完整路径是:<br><code>frame->layers->dns->dns.id : "0x326e"</code></p><p>这个ID的作用是:</p><ol><li>用于匹配DNS请求和响应 </li><li>是一个16位的标识符 </li><li>响应包会携带相同的ID, 使客户端能够匹配对应的请求 </li><li>在报文30(响应包)中可以看到相同的ID <code>"0x326e"</code> </li></ol><p><strong>建议在Wireshark中:</strong></p><ol><li>使用显示过滤器 <code>dns.id == 0x326e</code> 可以快速找到请求-响应对 </li><li>选择DNS协议的数据包, 展开DNS部分就能看到ID字段 </li><li>也可以在列表视图中添加一列显示 <code>dns.id</code> </li></ol><p>这是DNS协议中请求-响应匹配机制的关键字段。</p></blockquote><h3 id="详细解释每个-flag-的含义"><a href="#详细解释每个-flag-的含义" class="headerlink" title="详细解释每个 flag 的含义"></a>详细解释每个 flag 的含义</h3><p><img src="/2025/02/06/llm-pcap/flag.png" alt="flag"></p><blockquote><p>让我详细解释这个DNS响应中Flags字段(0x8583)的每个比特位的含义:</p><p>这个16位的Flags值 <code>0x8583</code> 转换成二进制是: <code>1000 0101 1000 0011</code></p><p>从左到右每个位的含义是:</p><ol><li>QR(位15): 1 = 表示这是一个响应消息(Response) </li><li>Opcode(位11-14): 0000 = 标准查询(Standard query) </li><li>AA(位10): 1 = 权威应答(Authoritative Answer),表示响应来自权威DNS服务器 </li><li>TC(位9): 0 = 消息没有被截断(Not truncated) </li><li>RD(位8): 1 = 期望递归(Recursion Desired) </li><li>RA(位7): 1 = 服务器支持递归查询(Recursion Available) </li><li>Z(位6): 0 = 保留位,必须为0 </li><li>AD(位5): 0 = 响应未经过DNSSEC认证 </li><li>CD(位4): 0 = 不接受未认证的数据 </li><li>Rcode(位0-3): 0011 = 3,表示域名不存在(NXDOMAIN) </li></ol><p>特别注意两点:</p><ol><li>AA(Authoritative Answer)位被设置,说明这是一个权威DNS服务器的响应 </li><li>Rcode为3(NXDOMAIN),这是一个否定性响应,表示请求的域名不存在 </li></ol><p>这个Flags组合表明:</p><ul><li>这是一个来自权威服务器的响应 </li><li>服务器支持递归查询 </li><li>请求的域名不存在 </li><li>响应未经过DNSSEC认证 </li><li>消息完整(未截断) </li></ul><p>这个特定的响应是一个典型的”域名不存在”错误响应,来自一个权威DNS服务器。</p></blockquote><h1 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h1><p>利用大语言模型分析 Wireshark 抓包数据可以显著提升网络协议学习和问题诊断效率,特别适合帮助工程师快速理解复杂协议细节和异常模式。</p>]]></content>
<summary type="html"><p>利用大语言模型来辅助分析 Wireshark 网络数据包,从而加深对典型网络协议的理解,提升分析常见网络问题的能力,并进一步建立标准化的分析流程 Prompt,以提高问题排查的效率。</p></summary>
<category term="网络" scheme="https://ustcqidi.github.io/tags/%E7%BD%91%E7%BB%9C/"/>
<category term="Troubleshooting" scheme="https://ustcqidi.github.io/tags/Troubleshooting/"/>
<category term="大语言模型" scheme="https://ustcqidi.github.io/tags/%E5%A4%A7%E8%AF%AD%E8%A8%80%E6%A8%A1%E5%9E%8B/"/>
<category term="ChatGPT" scheme="https://ustcqidi.github.io/tags/ChatGPT/"/>
<category term="Claude" scheme="https://ustcqidi.github.io/tags/Claude/"/>
<category term="Wireshark" scheme="https://ustcqidi.github.io/tags/Wireshark/"/>
</entry>
<entry>
<title>优化老代码的渐进式方法:以网络模块治理为例</title>
<link href="https://ustcqidi.github.io/2025/02/02/webservice-refactor/"/>
<id>https://ustcqidi.github.io/2025/02/02/webservice-refactor/</id>
<published>2025-02-02T13:37:56.000Z</published>
<updated>2025-02-02T14:08:49.376Z</updated>
<content type="html"><![CDATA[<p>在软件开发过程中,随着业务的不断演进,老模块往往会面临代码膨胀、耦合严重、维护成本高等问题。本文以网络模块的治理为例,探讨如何通过解耦、抽象和自动化手段优化架构,提高可维护性和扩展性。我们从网络层与业务层的分离入手,分析当前架构的问题,并提出动态库拆分、API 设计优化、自动化代码生成等解决方案。最终,我们通过实践验证了这些改进措施的有效性,为老模块的治理提供了一套可行的方法论。</p><span id="more"></span><p>在这篇文章中,我想分享一些关于治理老模块代码的经验和想法。 </p><p>我们负责的几个业务模块中,一个是与网络相关的,另一个是所有业务的中转站,类似于业务的“容器”,还有一个是数据库相关的模块。这篇文章主要聚焦在网络模块的治理。 </p><h3 id="早期设计的问题"><a href="#早期设计的问题" class="headerlink" title="早期设计的问题"></a>早期设计的问题</h3><p>最初设计这个网络模块时,用户量较小,业务场景也相对单一。当时的实现方式是将业务代码与网络层代码混在一起,每次新增一个业务请求,就需要修改大量接口、添加中转代码,并且网络请求的构造逻辑也都写在这个模块里。最开始这样做没有太大问题,但随着应用程序的发展,现在已有十几个业务,每个版本都会新增大量接口,导致这个模块逐渐演变成一个“公共模块”,甚至被我们戏称为“公交车”——所有人都在这里修改代码。 </p><p>由此引发了几个问题: </p><ol><li><strong>频繁的构建错误</strong>:由于许多团队同时修改该模块,导致 build error 的情况时有发生。 </li><li><strong>代码膨胀</strong>:模块代码不断增长,大量业务逻辑与网络通信代码交织在一起,使得维护变得困难。 </li><li><strong>稳定性隐患</strong>:业务代码与核心网络代码混杂,任何改动都有可能影响其他业务,引发潜在的风险。 </li></ol><p>面对这些问题,我们意识到,首先需要做的就是<strong>分离业务代码和核心网络代码</strong>。其次,我们需要重新设计更合理的接口,使业务代码的编写更加轻松,同时在<strong>保持业务逻辑不变、尽量降低回归风险的前提下,完成模块重构</strong>。 </p><h3 id="分析问题与挑战"><a href="#分析问题与挑战" class="headerlink" title="分析问题与挑战"></a>分析问题与挑战</h3><p>这个网络模块最早的设计并没有考虑到今天的业务规模扩展,因此所有业务代码和网络核心库的代码都混合在一个公共模块里,并对外提供了一个庞大的“全量接口”。每当有新的业务API需要增加时,所有团队都必须修改这个公共接口文件。 </p><p>问题逐渐显现: </p><ol><li><strong>代码耦合严重</strong>:业务代码与网络通信逻辑混在一起,影响了整体的稳定性。某些业务团队在新增请求API时,不慎修改了核心网络库代码,带来了安全隐患和回归风险。 </li><li><strong>包体积急剧增长</strong>:模块中充斥着重复的业务代码,比如网络请求的组装、解析等逻辑,导致整体代码量迅速膨胀。 </li><li><strong>维护困难</strong>:所有人都在修改同一个大接口文件,导致代码冲突频发,构建错误不断增加,甚至还可能引发资源冲突等问题。 </li></ol><h3 id="解决方案:逐步拆分与重构"><a href="#解决方案:逐步拆分与重构" class="headerlink" title="解决方案:逐步拆分与重构"></a>解决方案:逐步拆分与重构</h3><p>我们的目标很明确: </p><ul><li><strong>解耦核心网络库代码与业务代码</strong>,让业务代码能够独立管理,避免相互干扰。 </li><li><strong>减少对公共模块的依赖</strong>,业务团队可以在自己的代码仓库中管理自己的网络请求逻辑。 </li><li><strong>优化模块复用方式</strong>,减少不必要的代码编译,降低包大小。 </li></ul><p>但直接从公共模块剥离业务代码的方式难度极大,因为<strong>对外的公共接口已经广泛被使用</strong>,如果直接修改,可能会影响多个业务的开发,同时带来巨大的回归风险。我们尝试了几种策略,但最初几次都失败了。 </p><p>最终,我们借助一个新的业务场景找到了突破口:<strong>某个独立 APP 需要一个轻量级的网络库</strong>。于是,我们采取了一种折中方案:<strong>保持现有模块不变,同时抽取核心网络库,形成一个新的独立模块</strong>。 </p><p>这个新的网络库(我们称之为 <code>zNet</code>)<strong>不包含任何业务请求逻辑</strong>,只有最纯粹的网络通信功能,并将其编译为一个独立的动态库供该 APP 使用。这种方式不仅满足了该 APP 的需求,同时也为我们提供了一个“试水”机会——先验证抽取出的网络库是否稳定,再逐步推广到现有模块的底层替换。这样做的好处是: </p><ol><li><strong>不会影响现有业务开发</strong>:原有模块保持不变,业务团队不需要修改代码,避免了回归风险。 </li><li><strong>模块拆分的可行性验证</strong>:先在独立 APP 上验证 <code>zNet</code> 的稳定性,再逐步替换现有模块中的网络代码,实现平滑迁移。 </li></ol><h3 id="进一步优化:业务请求的抽象"><a href="#进一步优化:业务请求的抽象" class="headerlink" title="进一步优化:业务请求的抽象"></a>进一步优化:业务请求的抽象</h3><p>完成核心网络库的拆分后,我们又面临另一个问题:<strong>如何让业务代码更清晰地组织,并减少重复代码?</strong> </p><p>原本的业务请求代码是所有人共用的,构造 HTTP 请求、处理响应的逻辑都写在一个大文件里,每个业务请求都得手写参数构造、请求封装、响应解析,导致大量重复代码。因此,我们对<strong>业务请求的方式进行了抽象</strong>: </p><ul><li><strong>定义请求实体对象</strong>:每个 HTTP 请求都可以抽象为一个 <code>Request</code> 对象,它包含请求的参数(如 Headers、Body、Method 等)以及相关的安全以及连接策略等。 </li><li><strong>统一解析响应</strong>:构造请求和解析响应的逻辑被提取成通用的方法,业务方只需创建一个 <code>Request</code> 实例即可发起请求。 </li></ul><p>有了这个改进,我们可以进一步实现<strong>业务代码的自动生成</strong>: </p><ul><li>与服务端团队定义好请求参数格式后,可以用脚本解析这些参数,<strong>自动生成请求代码</strong>,避免重复手写。 </li><li>代码生成时可以<strong>自动添加安全检查</strong>,例如: <ul><li>域名检测,防止错误请求泄露敏感数据。 </li><li>统一的安全策略校验,减少人为失误。 </li><li>代码风格一致性检查,提升可维护性。 </li></ul></li></ul><p>这套自动化方案不仅提升了开发效率,还提升了安全性和代码质量,因此我们还为此申请了一项专利。 </p><h3 id="结论"><a href="#结论" class="headerlink" title="结论"></a>结论</h3><p>通过这次网络模块的重构,我们总结出了一套较为通用的老模块治理方法: </p><ol><li><strong>先分析痛点,找到业务扩展遇到的核心问题</strong>。 </li><li><strong>制定渐进式的改造方案,避免影响现有业务,降低回归风险</strong>。 </li><li><strong>利用新的业务需求作为突破口,验证新的架构设计</strong>。 </li><li><strong>通过合理的抽象和自动化工具,减少重复代码,提高开发效率</strong>。 </li></ol><p>这次网络模块的拆分和优化,让我们的架构更加清晰、维护成本降低,同时也为未来的业务扩展做好了准备。希望这些经验能对大家有所启发!</p>]]></content>
<summary type="html"><p>在软件开发过程中,随着业务的不断演进,老模块往往会面临代码膨胀、耦合严重、维护成本高等问题。本文以网络模块的治理为例,探讨如何通过解耦、抽象和自动化手段优化架构,提高可维护性和扩展性。我们从网络层与业务层的分离入手,分析当前架构的问题,并提出动态库拆分、API 设计优化、自动化代码生成等解决方案。最终,我们通过实践验证了这些改进措施的有效性,为老模块的治理提供了一套可行的方法论。</p></summary>
<category term="老模块治理" scheme="https://ustcqidi.github.io/tags/%E8%80%81%E6%A8%A1%E5%9D%97%E6%B2%BB%E7%90%86/"/>
<category term="架构优化" scheme="https://ustcqidi.github.io/tags/%E6%9E%B6%E6%9E%84%E4%BC%98%E5%8C%96/"/>
<category term="API 设计" scheme="https://ustcqidi.github.io/tags/API-%E8%AE%BE%E8%AE%A1/"/>
<category term="技术债务治理" scheme="https://ustcqidi.github.io/tags/%E6%8A%80%E6%9C%AF%E5%80%BA%E5%8A%A1%E6%B2%BB%E7%90%86/"/>
</entry>
<entry>
<title>域名污染引发的SSL证书验证问题</title>
<link href="https://ustcqidi.github.io/2025/01/27/dns/"/>
<id>https://ustcqidi.github.io/2025/01/27/dns/</id>
<published>2025-01-27T14:37:07.000Z</published>
<updated>2025-01-27T14:48:44.289Z</updated>
<content type="html"><![CDATA[<p>本文将通过一个真实的域名污染案例,展示什么是域名污染,它的危害有哪些,以及如何识别和处理域名污染问题。文章覆盖了域名污染的形式、后果,并给出了具体的日志分析实例,以及临时解决方案和彻底处理建议。</p><span id="more"></span><h2 id="一、域名污染定义"><a href="#一、域名污染定义" class="headerlink" title="一、域名污染定义"></a><strong>一、域名污染定义</strong></h2><h3 id="1-1-什么是域名污染"><a href="#1-1-什么是域名污染" class="headerlink" title="1.1 什么是域名污染"></a><strong>1.1 什么是域名污染</strong></h3><p>域名污染是一种网络攻击或干扰行为,旨在干扰特定域名或主机名的正常解析过程,从而导致用户无法访问目标网站或资源,或者被重定向到恶意站点。域名污染可以采用多种方式进行,其目的通常是欺骗、劫持、屏蔽或干扰用户的网络连接。</p><h3 id="1-2-域名污染的形式"><a href="#1-2-域名污染的形式" class="headerlink" title="1.2 域名污染的形式"></a><strong>1.2 域名污染的形式</strong></h3><p>以下是一些常见的域名污染方式:</p><ol><li><strong>DNS劫持:</strong> 攻击者修改DNS(Domain Name System)解析请求或响应,将合法域名映射到恶意IP地址上。这样用户访问合法域名时将被重定向到恶意站点。</li><li><strong>DNS投毒:</strong> 攻击者在DNS缓存中插入虚假的DNS记录,以污染DNS解析结果,从而导致用户访问错误的网站或资源。</li><li><strong>HTTP劫持:</strong> 攻击者通过修改HTTP响应头或HTML内容,将用户重定向到不希望访问的站点。这种方法通常被用于广告或欺诈目的。</li><li><strong>DNS屏蔽:</strong> 在某些情况下,政府或互联网服务提供商可能会屏蔽特定域名,以限制用户访问特定网站或服务。</li><li><strong>域名欺诈:</strong> 攻击者创建与合法域名相似的伪造域名,以诱使用户访问恶意站点,泄露个人信息或进行欺诈活动。</li></ol><h3 id="1-3-域名污染的危害"><a href="#1-3-域名污染的危害" class="headerlink" title="1.3 域名污染的危害"></a><strong>1.3 域名污染的危害</strong></h3><ul><li>使网络服务瘫痪,网站和应用无法访问</li><li>窃取用户访问的网站数据,例如账号密码</li><li>返回恶意网站或广告,进行钓鱼攻击和信息欺诈</li><li>执行恶意代码,控制用户设备作为僵尸网络参与DDoS等攻击</li></ul><h2 id="二、企业级应用遭遇域名污染案例"><a href="#二、企业级应用遭遇域名污染案例" class="headerlink" title="二、企业级应用遭遇域名污染案例"></a><strong>二、企业级应用遭遇域名污染案例</strong></h2><h3 id="2-1-问题表象-应用运行异常等"><a href="#2-1-问题表象-应用运行异常等" class="headerlink" title="2.1 问题表象(应用运行异常等)"></a><strong>2.1 问题表象(应用运行异常等)</strong></h3><p>我们的应用程序使用HTTPS通信,所有网络请求设计要进行TLS握手,以建立安全连接。但是域名被污染后,DNS解析结果将连接导向了错误的IP地址。这会导致TLS握手过程中,证书中的域名(DNS name)无法匹配目标服务器的地址(Host name),从而造成TLS握手失败,连接无法建立。</p><p>当TLS握手失败时,为了保证用户体验,我们的应用程序会像浏览器一样,弹出一个证书错误的警告框提示用户。用户可以选择忽略这个错误并强制继续连接,也可以选择中断这个连接。</p><p><img src="/2025/01/27/dns/pop.png" alt="pop"></p><p>如果用户忽略错误强制连接,应用程序会使用不可信的证书完成TLS握手,然后访问被污染的域名。这可能导致:</p><ul><li>用户隐私数据泄露给中间人</li><li>打开钓鱼网站进行诈骗</li><li>访问含恶意代码的网站</li></ul><p>所以域名污染会直接导致我们的应用无法通过TLS安全连接到真实服务器。用户可能在应用中看到各种网络连接错误、证书警告等异常表象。这严重影响了用户体验。</p><h3 id="2-2-相关日志分析"><a href="#2-2-相关日志分析" class="headerlink" title="2.2 相关日志分析"></a><strong>2.2 相关日志分析</strong></h3><figure class="highlight jsx"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">[<span class="title class_">CSBPTApp</span>::VTLS_NotifyCertItemVerifyFailed] <span class="title class_">Cert</span> verified <span class="attr">failed</span>: </span><br><span class="line"><span class="attr">SN</span>:<span class="number">02</span> <span class="number">72</span> <span class="variable constant_">BA</span> ...............</span><br><span class="line"><span class="attr">ISSUER</span>:<span class="title class_">DigiCert</span> <span class="title class_">SHA2</span> <span class="title class_">High</span> <span class="title class_">Assurance</span> <span class="title class_">Server</span> <span class="variable constant_">CA</span> </span><br><span class="line"><span class="attr">DNS</span>:*.<span class="property">cdninstagram</span>.<span class="property">com</span>,*.<span class="property">igcdn</span>.<span class="property">com</span>,*.<span class="property">igsonar</span>.<span class="property">com</span>,*.<span class="property">instagram</span>.<span class="property">com</span>,cdninstagram.<span class="property">com</span>,igcdn.<span class="property">com</span>,igsonar.<span class="property">com</span>,instagram.<span class="property">com</span> </span><br><span class="line"><span class="attr">CAFP</span>:<span class="variable constant_">A0</span> <span class="number">31</span> <span class="variable constant_">C4</span> <span class="number">67</span> ...............</span><br><span class="line"><span class="attr">FP</span>:8F <span class="number">83</span> 6C <span class="number">81</span> <span class="variable constant_">A2</span> ...............</span><br><span class="line"><span class="attr">host</span>:secure.<span class="property">gravatar</span>.<span class="property">com</span></span><br></pre></td></tr></table></figure><p>通过分析应用程序的日志,可以识别域名污染事件。关键是要解析日志中记录的主机名和域名信息,并与实际域名解析结果进行比对。</p><p>例如上述日志显示:</p><ul><li>连接的主机名(host):secure.gravatar.com</li><li>证书包含的域名(DNS):<em>.cdninstagram.com,</em>.igcdn.com,<em>.igsonar.com,</em>.instagram.com,cdninstagram.com,igcdn.com,igsonar.com,instagram.com</li></ul><p>通过对比可以发现,日志记录的连接主机名secure.gravatar.com并不在证书授权的域名列表内。这说明该连接主机名并不被该证书信任,因此出现了证书验证失败的错误。</p><p>这很可能是域名污染所导致的,证书与实际服务器不匹配。进一步查询证书信息和与公网DNS解析的比较,可以确认域名污染事件。</p><h2 id="三、域名污染的处理"><a href="#三、域名污染的处理" class="headerlink" title="三、域名污染的处理"></a><strong>三、域名污染的处理</strong></h2><h3 id="3-1-临时解决方案"><a href="#3-1-临时解决方案" class="headerlink" title="3.1 临时解决方案"></a><strong>3.1 临时解决方案</strong></h3><ul><li>在本地Hosts文件中添加域名与正确IP地址的映射,强制正确解析结果,覆盖污染解析记录。</li><li>通过VPN访问可信任域名服务器,绕过本地DNS污染结果。</li><li>浏览器插件修正本地DNS,如DNS Jumper等。</li><li>应用程序代码层面指定可信DNS服务器,不直接调用系统API,例如使用c-ares第三方库实现DNS解析。</li></ul><h3 id="3-2-彻底处理方法"><a href="#3-2-彻底处理方法" class="headerlink" title="3.2 彻底处理方法"></a><strong>3.2 彻底处理方法</strong></h3><ul><li>联系网络服务商,通过ACL规则过滤掉污染流量,阻止攻击链路。</li><li>清洗本地DNS服务器缓存,删除污染记录,并修复DNS服务器软件漏洞。</li><li>对注册商和注册中心报告域名遭劫持,进行域名Whois记录更正。</li><li>根据证书特征,在浏览器及系统级加入证书拦截策略,拒绝访问。</li></ul><h3 id="3-3-防范措施"><a href="#3-3-防范措施" class="headerlink" title="3.3 防范措施"></a><strong>3.3 防范措施</strong></h3><ul><li>开启DNSSEC验证,避免DNS查询结果被劫持污染。</li><li>启用HTTP Strict Transport Security (HSTS),强制HTTPS访问。</li><li>加强域名注册管理,设置注册锁避免被转移。</li><li>监控和检测域名解析情况,发现污染行为。</li><li>employee网络隔离,避免全部服务受影响。</li></ul>]]></content>
<summary type="html"><p>本文将通过一个真实的域名污染案例,展示什么是域名污染,它的危害有哪些,以及如何识别和处理域名污染问题。文章覆盖了域名污染的形式、后果,并给出了具体的日志分析实例,以及临时解决方案和彻底处理建议。</p></summary>
<category term="DNS" scheme="https://ustcqidi.github.io/tags/DNS/"/>
<category term="网络故障排查" scheme="https://ustcqidi.github.io/tags/%E7%BD%91%E7%BB%9C%E6%95%85%E9%9A%9C%E6%8E%92%E6%9F%A5/"/>
<category term="Troubleshooting" scheme="https://ustcqidi.github.io/tags/Troubleshooting/"/>
<category term="企业级网络" scheme="https://ustcqidi.github.io/tags/%E4%BC%81%E4%B8%9A%E7%BA%A7%E7%BD%91%E7%BB%9C/"/>
</entry>
<entry>
<title>从问题出发:VPN的核心原理与日志分析方法</title>
<link href="https://ustcqidi.github.io/2025/01/25/vpn/"/>
<id>https://ustcqidi.github.io/2025/01/25/vpn/</id>
<published>2025-01-25T14:50:39.000Z</published>
<updated>2025-03-14T09:59:45.225Z</updated>
<content type="html"><![CDATA[<p>通过一则VPN断开引发DNS请求异常的案例,本文探讨了VPN的核心技术,包括隧道概念、网络层定位、应用层透明性,以及网络流量分割的实现原理。同时,结合 GlobalProtect 软件的实践,探索日志分析的具体方法与思路。</p><span id="more"></span><p>VPN、代理、防火墙是我们日常生活中经常听到的名词,尤其是VPN。在远程办公时,我们经常需要通过它来访问公司内部的一些资源。对于我们来说,VPN已经是一个非常熟悉、常见的工具。然而,说到它的技术细节,我其实了解得并不多,甚至有些模糊。</p><p>前段时间,我们的客户遇到了一些与VPN相关的问题。我们的软件主要应用于远程办公的场景,而这个问题比较特殊:在Mac系统上,有部分用户在使用VPN时,如果VPN处于连接状态,一切正常。但一旦断开VPN连接,我们的软件所有DNS请求都无法发出。而奇怪的是,系统中其他软件,包括浏览器,访问相同的域名却完全正常。唯一的解决方法是重启电脑。从现象上看,这个问题似乎与用户电脑上的VPN有关。</p><p>但客户对这种解释并不认可。由于他们并非技术专业人士,无法理解这种关联,反而会质疑:为什么偏偏只有你们的软件会出现这种问题?这使得问题变得非常棘手。我们尝试在应用层增加大量日志,甚至抓包分析,但始终没有找到问题的根本原因。本质上,问题源于我们对VPN工作机制的了解不够深入。为了解决问题,我们还调取了客户VPN的日志,但因为对原理不够熟悉,很多内容看不明白。整个解决过程非常痛苦,花了很长时间也未能找到有效方案。</p><p>于是,我决定花时间系统地查阅资料,研究VPN的工作机制。这篇文章便是在这样的背景下诞生的。我希望通过它,搞清楚几个关键问题:</p><ol><li><strong>什么是VPN?它解决了什么问题?</strong> </li><li><strong>防火墙、代理、VPN这些常见名词之间有什么区别、差异和关联?</strong> </li><li><strong>VPN工作在网络的哪一层?它的连接建立过程是什么?什么是VPN隧道?经常听到“VPN隧道”这个概念,那么隧道到底指的是什么?</strong> </li><li><strong>VPN连接对应用层是否透明?应用层能否感知到当前设备已连接VPN?</strong> </li><li><strong>VPN的网络流量分割是如何实现的?</strong> </li><li><strong>我们常用的VPN软件 <em>GlobalProtect</em> 的工作机制是什么?如何分析它的日志?</strong></li></ol><p>希望通过这篇文章能够解答这些问题,同时为后续处理类似问题积累一些经验。</p><h1 id="VPN技术背景与发展脉络"><a href="#VPN技术背景与发展脉络" class="headerlink" title="VPN技术背景与发展脉络"></a>VPN技术背景与发展脉络</h1><h2 id="起源:安全互联需求的驱动"><a href="#起源:安全互联需求的驱动" class="headerlink" title="起源:安全互联需求的驱动"></a>起源:安全互联需求的驱动</h2><p>VPN(虚拟专用网络)的诞生源于20世纪90年代企业对安全远程通信的迫切需求。早期企业通过租赁专线(如ATM、帧中继)实现分支机构互联,但存在成本高昂(单条线路年费用可达数十万美元)、部署周期长(平均6-12个月)等问题。随着互联网基础设施的成熟,如何在公共网络上构建私有化安全通道成为关键命题。这一需求在军事领域尤为突出——1993年美国国防部提出”安全IP网络”概念,要求实现跨地域军事网络的加密通信,这直接推动了IPsec协议的标准化进程。</p><h2 id="发展时间线:协议迭代与技术突破"><a href="#发展时间线:协议迭代与技术突破" class="headerlink" title="发展时间线:协议迭代与技术突破"></a>发展时间线:协议迭代与技术突破</h2><p><strong>1. 技术萌芽期(1960s-1980s)</strong></p><ul><li><strong>1969年 ARPANET</strong><br>美国国防部高级研究计划局(ARPA)开发首个分组交换网络,虽未直接涉及VPN技术,但为后续互联网协议栈奠定基础。</li><li><strong>1983年 TCP/IP协议标准化</strong><br>正式取代NCP协议,成为互联网通信的核心标准,为VPN所需的跨网络通信提供技术前提。</li></ul><p><strong>2. 技术奠基期(1990s)</strong></p><ul><li><strong>1993年 IPsec协议雏形</strong><br>AT&T实验室提出<a href="https://www.mattblaze.org/papers/swipe.id.txt">swIPe</a>协议,首次实现IP层加密,成为IPsec的前身。</li><li><strong>1996年 PPTP协议发布</strong><br>微软工程师Gurdeep Singh-Pall主导开发,首次实现基于公网的加密隧道,支持Windows 95远程访问企业网络。</li><li><strong>1999年 L2TP协议诞生</strong><br>Cisco与微软合作整合L2F与PPTP,形成更安全的L2TP协议,支持多协议封装与IPsec加密组合。</li></ul><p><strong>3. 技术扩展期(2000s)</strong></p><ul><li><strong>2001年 OpenVPN开源发布</strong><br>基于SSL/TLS的应用层加密方案,突破传统网络层限制,成为跨平台VPN主流协议。</li><li><strong>2005年 SSL-VPN商业化</strong><br>通过浏览器插件实现免客户端接入,推动VPN从企业工具向个人用户渗透。</li><li><strong>2006年 IKEv2/IPsec协议成熟</strong><br>增强移动设备VPN连接的稳定性和抗干扰能力,支持网络切换时无缝重连。</li></ul><p><strong>4. 技术革新期(2010s至今)</strong></p><ul><li><strong>2018年 WireGuard协议发布</strong><br>代码量仅为OpenVPN的1/10,采用ChaCha20加密算法,移动端连接速度提升300%。</li><li><strong>2020年 SD-WAN与VPN融合</strong><br>疫情催生远程办公需求激增,SD-WAN技术结合VPN实现云原生架构,时延降低至20ms以下。</li><li><strong>2022年 零信任架构兴起</strong><br>结合SASE(安全访问服务边缘)模型,逐步替代传统VPN的星型拓扑,实现动态身份验证与最小权限控制。</li></ul><h2 id="技术驱动因素"><a href="#技术驱动因素" class="headerlink" title="技术驱动因素"></a>技术驱动因素</h2><ol><li><p>军事与科研需求(1960s-1990s)<br>ARPANET与早期加密协议(如IPsec)均源于美国国防项目,强调跨地域安全通信</p></li><li><p>企业远程办公(1990s-2000s)<br>PPTP与L2TP协议推动VPN从专线租赁转向公网加密隧道,降低企业70%通信成本</p></li><li><p>移动互联网安全挑战(2010s)<br>OpenVPN与WireGuard应对公共Wi-Fi风险,支持移动设备安全接入</p></li><li><p>云原生与边缘计算(2020s至今)<br>SD-WAN与SASE架构解决传统VPN在云服务中的性能瓶颈,支持多云环境无缝连接</p></li></ol><p>可以用一张图来总结:<br><img src="/2025/01/25/vpn/timeline.svg" alt></p><h2 id="VPN在企业办公的应用场景"><a href="#VPN在企业办公的应用场景" class="headerlink" title="VPN在企业办公的应用场景"></a>VPN在企业办公的应用场景</h2><p>VPN 在企业远程办公领域,核心目标是确保数据传输的安全性,同时实现对企业内部资源的安全访问。 </p><p>例如,在远程办公时,我们可能需要访问企业内部的文档、代码资源,或者一些私有化的服务,而 VPN 可以保障这些访问的安全性。此外,它还能帮助我们突破地理位置的限制。例如,当我们在家庭网络环境下,需要访问企业内部资源时,可以通过 VPN 绕过地理位置的限制。 </p><p>另一个重要的场景是在公共网络环境下,例如在咖啡馆、机场等公共 Wi-Fi 环境中,这些网络可能存在路由器配置问题或其他中间网络的不稳定和安全隐患。VPN 能够通过加密和隧道技术,保护我们的数据隐私,确保通信的安全。这些正是 VPN 在远程办公中的主要应用场景。</p><p>总结来说,VPN 的核心需求是解决数据的<strong>安全传输问题</strong>。以下是关键点: </p><ul><li><strong>技术本质</strong>:通过隧道封装(Tunneling)和加密算法(如 AES-256),构建一个“网络中的网络”。这种方式将明文数据转化为密文传输,从而有效抵御中间人攻击,确保数据的安全性和隐私性。 </li><li><strong>典型方案</strong>:以 IPsec 的 ESP(封装安全载荷)协议为例,它对 IP 包进行端到端加密,确保数据在传输中的完整性(通过 HMAC 验证)与机密性(加密处理),为用户提供了可靠的安全保障。 </li></ul><h1 id="VPN-技术领域的几个问题"><a href="#VPN-技术领域的几个问题" class="headerlink" title="VPN 技术领域的几个问题"></a>VPN 技术领域的几个问题</h1><p>厘清背景信息后,接下来,我们开始深入探讨了 VPN 的核心目标:<strong>保障网络中的数据安全传输</strong>。</p><p>这就引出了技术领域中需要解决的几个关键问题:</p><h2 id="数据传输的安全性"><a href="#数据传输的安全性" class="headerlink" title="数据传输的安全性"></a>数据传输的安全性</h2><p>为确保数据在网络中的传输不会被窃听或拦截,需要对数据进行加密。由于传统网络通道是公共的,明文数据传输存在巨大的安全隐患,因此加密是关键。</p><ul><li>加密方法:主要包括对称加密(如 AES)和非对称加密(如 RSA)。</li><li>工作流程:通常通过非对称加密协商密钥,然后使用对称加密传输数据,这种方式兼顾了安全性和效率。</li><li>关键点:密钥的管理和协商机制至关重要,是整个加密传输体系的基础。</li></ul><h2 id="数据的完整性"><a href="#数据的完整性" class="headerlink" title="数据的完整性"></a>数据的完整性</h2><p>即使数据加密了,也不能排除中间人对数据进行篡改的可能性。为此,需要对数据的完整性进行校验,以确保数据未被篡改或者能检测到篡改的情况。</p><ul><li>实现方法:通常使用 HMAC 或数字签名算法生成数据的摘要,接收方通过校验摘要来确认数据的完整性。</li><li>目标:防止恶意篡改,同时提供篡改检测的能力。</li></ul><h2 id="授权与认证"><a href="#授权与认证" class="headerlink" title="授权与认证"></a>授权与认证</h2><p>当通过 VPN 访问内部资源时,需要确保访问者是合法用户,并且具有相应的权限。</p><ul><li>认证机制:包括用户名密码、双因素认证、证书等方式。</li><li>授权流程:需要精确验证用户身份,并控制其对资源的访问权限,确保最小权限原则的实现。</li></ul><h1 id="VPN-隧道协议解析"><a href="#VPN-隧道协议解析" class="headerlink" title="VPN 隧道协议解析"></a>VPN 隧道协议解析</h1><p>我们了解了VPN的技术背景、发展脉络,以及需要解决的问题。针对这些问题,我们探讨了已有的解决方案。我们将围绕这些技术问题,逐步讨论VPN的协议层及其解决方案,重点介绍VPN的隧道协议 (Tunnel Protocol)。 </p><h2 id="隧道协议的基本概念"><a href="#隧道协议的基本概念" class="headerlink" title="隧道协议的基本概念"></a>隧道协议的基本概念</h2><p>首先引入“隧道协议”的概念(Tunnel)。很多人,尤其是初学者,对“隧道”这一术语容易产生误解,认为隧道是网络中拉了一条“实体专线”,类似于物理存在的专用网络。然而,实际上的“隧道协议”并不是这个含义。 </p><p>隧道协议指的是在网络通信的两端(如Client和Server)之间传输的数据经过加密或重新打包,使得这些数据的格式只有通信双方能够识别。隧道技术通过“重新打包(Repackaging)”的方法,保障了通信数据的安全性。例如,在传输数据时,对其进行加密和封装,这种封装过程就是隧道协议的核心工作之一。隧道技术是VPN实现数据安全的核心技术,也是VPN构建的基础。 </p><p>当前的隧道协议有很多种,接下来我们将重点介绍两个最具代表性的协议:IPSec和WireGuard。 </p><h2 id="IPSec协议"><a href="#IPSec协议" class="headerlink" title="IPSec协议"></a>IPSec协议</h2><p>IPSec是一个公开的标准和框架,也是较早实现隧道协议的具体应用之一。IPSec提供了数据加密、数据完整性验证以及身份认证等功能,主要由以下几部分组成: </p><ol><li><p><strong>认证头部 (Authentication Header, AH)</strong><br>在IP数据包传输之前,IPSec会通过一个认证头部过程对数据进行重新打包。该过程在原始的IP数据包上附加额外的安全信息,包括安全策略 (Security Policy)、加密参数等。这些附加信息确保了数据的完整性和认证可靠性。在接收端,数据包会先通过认证头部进行解密和验证,再处理原始的IP数据。 </p></li><li><p><strong>封装安全载荷 (Encapsulating Security Payload, ESP)</strong><br>ESP进一步加密了IP数据包中的有效载荷(Payload),保护其机密性。这一过程通常结合使用加密算法(如DES、AES)和摘要算法(如MD5、SHA)。虽然一些老旧的算法可能存在安全隐患,但IPSec更多的是提供一个框架,后续可以通过替换算法来满足现代安全需求。 </p></li><li><p><strong>密钥交换 (Internet Key Exchange, IKE)</strong><br>IPSec的加密过程采用对称加密,这需要通信双方共享一个密钥。IKE协议负责协商和交换密钥,分为以下两个阶段: </p><ul><li><strong>阶段一:身份认证和密钥协商</strong><br>使用非对称加密算法(如RSA)进行身份验证并交换初始密钥。 </li><li><p><strong>阶段二:生成对称密钥</strong><br>基于阶段一的协商结果,生成对称加密的密钥,并用于后续的数据加密操作。 </p><p><img src="/2025/01/25/vpn/phase.svg" alt></p></li></ul><p>这一密钥协商过程类似于TLS/SSL建立连接时的机制,也涉及到CA(Certificate Authority)数字签名等基础设施的支持。 </p></li><li><p><strong>工作模式</strong><br>IPSec支持两种主要的工作模式: </p><ul><li><strong>隧道模式 (Tunnel Mode)</strong>:对整个IP数据包(包括头部和载荷)进行加密和重新封装,提供更全面的保护。 </li><li><strong>传输模式 (Transport Mode)</strong>:仅对IP数据包的有效载荷部分进行加密,不对头部信息进行封装。 </li></ul></li></ol><p>隧道模式由于其全面的端到端保护,是当前应用最广泛的一种模式。 </p><p>简单总结IPSec的工作流程: </p><ol><li>数据发送前,进行身份认证和授权,确保通信双方是合法用户; </li><li>通过IKE协议完成密钥的协商与交换; </li><li>使用生成的密钥对数据进行加密、封装,并保证数据的完整性和安全性; </li><li>隧道断开时,对通信过程进行清理,确保安全。</li></ol><p><img src="/2025/01/25/vpn/ike.png" alt></p><h2 id="WireGuard协议"><a href="#WireGuard协议" class="headerlink" title="WireGuard协议"></a>WireGuard协议</h2><p>WireGuard是一个相对较新的隧道协议,其官网介绍称其为“<strong>极其简单、高效且现代化的VPN协议 (Extremely Simple Yet Fast and Modern VPN)</strong>”,采用最先进的密码学技术,目标是比IPSec和OpenVPN更快速、更轻量、更安全。 </p><p><img src="/2025/01/25/vpn/wireguard.svg" alt></p><p>与IPSec相比,WireGuard的代码量少得多,设计也更加简单。然而,IPSec作为一个框架,定义了VPN的基本工作流程和隧道协议的实现方式,而WireGuard则通过现代化的优化,在以下方面表现出色: </p><ol><li><strong>更高效的加密算法</strong><br>WireGuard采用了更现代化的加密算法套件,如ChaCha20和Poly1305,以提高加密效率和安全性。 </li><li><strong>更快的连接建立</strong><br>WireGuard在隧道建立过程中采用了握手协议 (Handshake Protocol),显著减少了隧道建立的耗时,特别适用于移动端应用。 </li><li><strong>轻量级设计</strong><br>WireGuard的代码量少,便于审计和维护,同时降低了部署成本。 </li></ol><p>虽然WireGuard在安全性和性能上优于传统协议,但其本质仍然是对IPSec框架的改进与优化。 </p><h2 id="VPN的层级解析"><a href="#VPN的层级解析" class="headerlink" title="VPN的层级解析"></a>VPN的层级解析</h2><p>VPN的隧道协议有多种实现,例如SSL VPN、OpenVPN、PPTP等,但它们的基本原理相似。我们最初提到的问题是:<strong>VPN的连接建立在哪一层?是否对应用层透明?</strong> </p><p>通过对VPN背景和技术细节的分析,可以得出以下结论: </p><ul><li>VPN的隧道技术是在网络层对IP数据包进行重新打包和封装。</li><li>使用VPN后,数据在应用层产生,经传输层(TCP/UDP)打包形成IP数据包。VPN会在网络层对IP数据包进行加密和重新打包,附加认证头部或安全载荷,实现安全传输。 </li></ul><p>能不能在应用程序中感知到用户是否连接了VPN,从而进行一些优化?答案是:<strong>应用层是无法感知VPN连接的存在的</strong>。 </p><p>那么,VPN软件到底是如何实现的?它又是如何对 IP 数据包进行重新打包的?这就涉及到一个重要的概念——<strong>虚拟网卡</strong>。在 Linux 系统中,有一个叫 <strong>TUN/TAP</strong> 的机制,它可以在设备上模拟一个虚拟网卡,直接拦截实际网卡传输的数据。例如,当我们通过 VPN 建立代理隧道后,虚拟网卡会拦截所有实际网卡的数据流量。接着,通过虚拟网卡获取的数据,VPN 软件会对数据包进行打包、解包,以及加密、解密等一系列操作。这种机制是当前所有 VPN 软件的技术核心,虚拟网卡在其中扮演了至关重要的角色。</p><p>到这里,我们基本上完整介绍了 VPN 的技术细节,包括加密密钥的协商与交换、加密传输的实现、认证授权机制、隧道的建立与关闭,以及 VPN 所处的网络层级、对应用层的影响,以及如何获取网络层数据等问题。</p><h2 id="VPN-与代理、防火墙的关联与区别"><a href="#VPN-与代理、防火墙的关联与区别" class="headerlink" title="VPN 与代理、防火墙的关联与区别"></a>VPN 与代理、防火墙的关联与区别</h2><p>接下来我们讨论一个更基础但常见的问题:<strong>VPN、代理和防火墙三者之间的关系是什么?</strong></p><ol><li><p><strong>代理的基本概念</strong><br>代理是一种用户常见的技术,比如我们可以手动设置代理服务器,或使用正向代理和反向代理。代理的核心功能是转发网络请求。从严格意义上讲,VPN 也可以被看作是一种特殊的代理,因为所有的数据包都会经过 VPN 的网关中转,然后由 VPN 网关访问目标资源。这种模式和代理非常相似,但在实现机制和网络分层上存在显著区别。 </p><ul><li><strong>代理的实现</strong>:代理运行在<strong>应用层</strong>,对应用层的数据包是有感知的,它会直接将应用层的数据原封不动地转发到代理服务器。例如,HTTP 代理或 SOCKS 代理就是运行在应用层的典型例子。</li><li><strong>VPN 的实现</strong>:VPN 是对<strong>应用层无感知</strong>的。它对网络层的数据包进行重新打包,再发送到 VPN 网关进行处理。尽管形式上与代理类似,但 VPN 在实现细节和网络分层上的差别显而易见。</li></ul></li><li><p><strong>反向代理的特点</strong><br>反向代理是用户通常感知不到的,它主要用来做负载均衡。举例来说,当一个请求发到 Web 网关时,反向代理会根据负载情况将请求分发到最合适的网络节点。这种机制常用于分布式系统中的流量管理。</p></li><li><p><strong>防火墙的功能</strong><br>防火墙与 VPN 和代理的功能有本质区别。它主要负责网络流量的过滤,是流量进入系统的第一道防线。防火墙通过预设的规则来判断和拦截“可疑”或“不合规”的数据包,其主要目的在于安全防护,而非流量转发。</p></li></ol><h1 id="GlobalProtect"><a href="#GlobalProtect" class="headerlink" title="GlobalProtect"></a>GlobalProtect</h1><p>GlobalProtect 是 Palo Alto Networks 提供的一种 VPN 解决方案,能够帮助企业和组织提供安全的远程访问。</p><p>下面我们将探讨其工作机制、日志文件以及如何进行 VPN 连接分析。</p><h2 id="工作机制"><a href="#工作机制" class="headerlink" title="工作机制"></a>工作机制</h2><p>GlobalProtect是一种通过加密隧道建立远程访问VPN的技术,通常用于保护企业内部网络免受外部威胁。其工作流程可分为几个阶段:</p><h3 id="连接初始化阶段"><a href="#连接初始化阶段" class="headerlink" title="连接初始化阶段"></a>连接初始化阶段</h3><ul><li><strong>客户端连接尝试</strong>:客户端会尝试连接到预配置的门户服务器。</li><li><strong>身份验证</strong>:客户端通过设备证书或用户凭据进行身份验证,门户服务器使用不同的验证方式(如LDAP、RADIUS、SAML等)来验证用户身份。</li><li><strong>网关分配</strong>:身份验证成功后,门户服务器将选择一个最佳的网关,并将相关配置推送到客户端。</li></ul><h3 id="隧道建立阶段"><a href="#隧道建立阶段" class="headerlink" title="隧道建立阶段"></a>隧道建立阶段</h3><ul><li><strong>连接到网关</strong>:客户端连接到指定的网关,建立TLS加密通道以保证通信的安全。</li><li><strong>IPSec协商</strong>:协商IPSec的相关参数(如加密算法和认证方法),并建立IPSec隧道。</li><li><strong>分配虚拟IP</strong>:网关为客户端分配虚拟IP地址,确保通信的私密性。</li></ul><h3 id="数据传输阶段"><a href="#数据传输阶段" class="headerlink" title="数据传输阶段"></a>数据传输阶段</h3><ul><li><strong>分隧道配置</strong>:根据预设的分隧道规则,决定哪些流量通过VPN隧道。</li><li><strong>精细化路由</strong>:流量路由可以根据目标地址、应用类型和用户身份等条件进行细分。</li><li><strong>加密流量</strong>:所有通过VPN隧道的流量都会进行加密,确保数据传输的安全。</li><li><strong>安全策略执行</strong>:网关会执行安全策略并进行威胁防护。</li></ul><h3 id="会话维护阶段"><a href="#会话维护阶段" class="headerlink" title="会话维护阶段"></a>会话维护阶段</h3><ul><li><strong>心跳包</strong>:客户端定期发送心跳包以维持连接的有效性。</li><li><strong>自动重连</strong>:在网络中断时,客户端能够自动尝试重新连接。</li><li><strong>网络优化</strong>:客户端监控网络质量,并根据情况自动优化连接。</li><li><strong>HIP更新</strong>:客户端定期更新主机信息配置(HIP),确保端点符合安全要求。</li></ul><h3 id="HIP(主机信息配置)检查"><a href="#HIP(主机信息配置)检查" class="headerlink" title="HIP(主机信息配置)检查"></a>HIP(主机信息配置)检查</h3><ul><li><strong>端点安全检查</strong>:GlobalProtect会定期检查设备的安全状态,验证防病毒软件、防火墙及其他安全软件的运行状态。</li><li><strong>系统补丁检查</strong>:检测操作系统和应用程序的补丁级别,确保设备没有已知的漏洞。</li><li><strong>访问控制</strong>:根据安全检查的结果决定是否允许连接。</li></ul><h3 id="断开连接-重新连接处理"><a href="#断开连接-重新连接处理" class="headerlink" title="断开连接/重新连接处理"></a>断开连接/重新连接处理</h3><ul><li><strong>自动重连</strong>:如果检测到网络中断,客户端会自动尝试重新连接。</li><li><strong>故障转移</strong>:支持多个网关之间的故障转移,确保稳定的连接。</li><li><strong>清理操作</strong>:在断开连接后,客户端会清理缓存并恢复初始状态,保持连接会话状态以便快速重新连接。</li></ul><h2 id="日志文件"><a href="#日志文件" class="headerlink" title="日志文件"></a>日志文件</h2><p>GlobalProtect的日志文件对于故障排查和性能分析至关重要。以下是几种常见的日志文件及其作用:</p><h3 id="核心日志文件"><a href="#核心日志文件" class="headerlink" title="核心日志文件"></a>核心日志文件</h3><ol><li><strong><code>PanGPS.log</code></strong>:记录连接状态、身份验证过程、隧道建立及核心信息,是故障排查的主要参考文件。</li><li><strong><code>pan_gp_event.log</code></strong>:记录GlobalProtect的事件日志,如连接、断开、重新连接等事件,用于追踪连接历史。</li><li><strong><code>PanGPA.log</code></strong>:与代理认证相关的日志,记录认证过程和结果,用于排查认证问题。</li><li><strong><code>PanGpHip.log</code> 和 <code>PanGpHipMp.log</code></strong>:与HIP(主机信息配置)相关的日志,记录端点的安全检查信息,用于排查合规性问题。</li></ol><h3 id="诊断与配置文件"><a href="#诊断与配置文件" class="headerlink" title="诊断与配置文件"></a>诊断与配置文件</h3><ol><li><strong><code>debug_drv.log</code></strong>:记录驱动程序调试日志,通常用于分析底层网络接口问题。</li><li><strong><code>pan_gp_hrpt.xml</code></strong>:HIP报告文件,包含详细的端点安全状态信息。</li></ol><h3 id="系统相关文件"><a href="#系统相关文件" class="headerlink" title="系统相关文件"></a>系统相关文件</h3><ol><li><strong><code>system.log</code> 和 <code>system.log.[0-3].gz</code></strong>:系统级日志文件,包含压缩的历史日志文件,适用于深度故障分析。</li><li><strong><code>DNS.txt</code> 和 <code>IfConfig.txt</code></strong>:记录DNS配置信息和网络接口配置信息。</li><li><strong><code>NetStat.txt</code> 和 <code>RoutePrint.txt</code></strong>:记录网络连接状态和路由表信息。</li></ol><h3 id="状态与代理相关文件"><a href="#状态与代理相关文件" class="headerlink" title="状态与代理相关文件"></a>状态与代理相关文件</h3><ol><li><strong><code>MachineState.txt</code></strong>:记录设备的硬件和系统状态。</li><li><strong><code>SystemInfo.txt</code></strong>:系统信息摘要,包含操作系统版本和配置等详细信息。</li><li><strong><code>PanNExt.log</code> 和 <code>Proxy.txt</code></strong>:记录浏览器扩展日志和代理设置配置信息。</li></ol><h2 id="GlobalProtect-VPN连接分析"><a href="#GlobalProtect-VPN连接分析" class="headerlink" title="GlobalProtect VPN连接分析"></a>GlobalProtect VPN连接分析</h2><p>以下是从GlobalProtect日志中提取的一些分析示例,展示了VPN连接过程的不同阶段。</p><h3 id="身份验证阶段"><a href="#身份验证阶段" class="headerlink" title="身份验证阶段"></a>身份验证阶段</h3><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">// 证书验证开始</span><br><span class="line">P1681-T23571 01/15/2025 19:20:23:858 Debug(1027): 收到挑战 NSURLAuthenticationMethodServerTrust</span><br><span class="line"></span><br><span class="line">// 添加信任证书</span><br><span class="line">P1681-T23571 01/15/2025 19:20:23:864 Info (1041): 添加受信锚 (</span><br><span class="line"> "<cert(0x7ff102f08080) s: Encryption Everywhere DV TLS CA - G1 i: DigiCert Global Root CA>"</span><br><span class="line">)</span><br><span class="line"></span><br><span class="line">// 验证成功</span><br><span class="line">P1681-T23571 01/15/2025 19:20:23:868 Info (647): 服务器已信任 hfremote.***.com</span><br></pre></td></tr></table></figure><p><strong>分析:</strong></p><ul><li>证书验证开始,客户端启动与服务器之间的安全认证过程。</li><li>客户端添加受信证书,验证服务器的身份。</li><li>验证成功后,客户端信任服务器并继续连接。</li></ul><h3 id="网关配置获取"><a href="#网关配置获取" class="headerlink" title="网关配置获取"></a>网关配置获取</h3><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">// 获取VPN网关配置</span><br><span class="line">P1681-T27919 01/15/2025 19:20:23:956 Debug(2842): 获取网关 hfremote.****.com:4433 配置: <?xml version="1.0" encoding="UTF-8" ?></span><br><span class="line"></span><br><span class="line">接收到的关键配置:</span><br><span class="line">- 分配IP: 10.100****</span><br><span class="line">- DNS服务器: 10.100****, 10.100****</span><br><span class="line">- MTU设置: 1400</span><br><span class="line">- 会话超时: 10800秒</span><br></pre></td></tr></table></figure><p><strong>分析:</strong></p><ul><li>客户端从网关接收到VPN连接的关键配置信息,包括IP分配、DNS服务器、MTU设置和会话超时。</li></ul><h3 id="隧道建立尝试"><a href="#隧道建立尝试" class="headerlink" title="隧道建立尝试"></a>隧道建立尝试</h3><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">// 初始IPSec尝试</span><br><span class="line">P1681-T27919 01/15/2025 19:20:23:964 Debug(166): 尝试连接IPSec到 220.248.****[4501]</span><br><span class="line"></span><br><span class="line">// IPSec失败并回退至SSL</span><br><span class="line">P1681-T27919 01/15/2025 19:20:28:039 Info (366): 连接到 220.248.**** 失败</span><br><span class="line">P1681-T27919 01/15/2025 19:20:28:039 Debug(819): IPSec回退原因是IPSec连接失败</span><br></pre></td></tr></table></figure><p><strong>分析:</strong></p><ul><li>客户端首先尝试通过IPSec协议建立连接,但由于连接失败,自动回退到SSL协议继续隧道建立。</li></ul><h3 id="SSL隧道建立"><a href="#SSL隧道建立" class="headerlink" title="SSL隧道建立"></a>SSL隧道建立</h3><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">// SSL连接建立</span><br><span class="line">P1681-T27919 01/15/2025 19:20:28:151 Info (119): 成功建立SSL隧道到 220.248.****(4433)</span><br><span class="line">P1681-T27919 01/15/2025 19:20:28:151 Info (376): 与 220.248.**** 建立隧道</span><br></pre></td></tr></table></figure><p><strong>分析:</strong></p><ul><li>在回退到SSL协议后,成功建立了SSL隧道,确保安全的连接。</li></ul><h1 id="总结:始于问题、终于理解"><a href="#总结:始于问题、终于理解" class="headerlink" title="总结:始于问题、终于理解"></a>总结:始于问题、终于理解</h1><p>这段时间我零零散散地了解了一些VPN相关的概念,感觉对以后可能遇到的类似问题不会再那么陌生了。通过这些学习,我不仅掌握了一些基本的方向和概念,也顺便了解了VPN技术的一些发展趋势,比如“零信任网络”和结合人工智能来提高安全性和隐私保护的技术。这些技术的背后都是为了解决现实中的需求,未来遇到具体问题时,我也能更加从容地进行分析和应对。</p><p>另外,在写这篇文章的过程中,我也遇到了一个新的问题。当我在家里连接到公司的VPN时,虽然成功连接,但却无法访问公司的一些内部网络资源。于是,我询问了IT部门,他们建议我尝试用手机热点重新连接WiFi,结果问题解决了。我也好奇为什么只是换了个IP就能正常连接。后来和IT部门进一步沟通后得知,我们家庭网络的IP是动态分配的,而某些IP可能会被封锁,导致无法连接。换个热点后,IP重新分配,一切问题迎刃而解。至于这个IP是否被运营商屏蔽,还是被我们内部网关屏蔽,具体情况还不清楚,但从整体来看,这次经历让我对VPN的技术有了更深的理解。</p><p>通过这个过程,我感受到解决问题的驱动力让我不断学习和探索,不仅能够找到具体的答案,还能通过查阅资料形成自己的总结和沉淀。</p><p>最后,引用爱因斯坦的一段话,与大家共勉:</p><blockquote><p><strong>“提出一个问题往往比解决一个问题更重要。</strong><br><strong>The formulation of a problem is often more essential than its solution.”</strong></p></blockquote>]]></content>
<summary type="html"><p>通过一则VPN断开引发DNS请求异常的案例,本文探讨了VPN的核心技术,包括隧道概念、网络层定位、应用层透明性,以及网络流量分割的实现原理。同时,结合 GlobalProtect 软件的实践,探索日志分析的具体方法与思路。</p></summary>
<category term="VPN技术" scheme="https://ustcqidi.github.io/tags/VPN%E6%8A%80%E6%9C%AF/"/>
<category term="DNS故障" scheme="https://ustcqidi.github.io/tags/DNS%E6%95%85%E9%9A%9C/"/>
<category term="网络安全" scheme="https://ustcqidi.github.io/tags/%E7%BD%91%E7%BB%9C%E5%AE%89%E5%85%A8/"/>
<category term="远程办公" scheme="https://ustcqidi.github.io/tags/%E8%BF%9C%E7%A8%8B%E5%8A%9E%E5%85%AC/"/>
<category term="Global Protect" scheme="https://ustcqidi.github.io/tags/Global-Protect/"/>
<category term="网络分层" scheme="https://ustcqidi.github.io/tags/%E7%BD%91%E7%BB%9C%E5%88%86%E5%B1%82/"/>
<category term="隧道协议" scheme="https://ustcqidi.github.io/tags/%E9%9A%A7%E9%81%93%E5%8D%8F%E8%AE%AE/"/>
<category term="流量切分" scheme="https://ustcqidi.github.io/tags/%E6%B5%81%E9%87%8F%E5%88%87%E5%88%86/"/>
<category term="故障排查" scheme="https://ustcqidi.github.io/tags/%E6%95%85%E9%9A%9C%E6%8E%92%E6%9F%A5/"/>
<category term="网络代理" scheme="https://ustcqidi.github.io/tags/%E7%BD%91%E7%BB%9C%E4%BB%A3%E7%90%86/"/>
<category term="防火墙" scheme="https://ustcqidi.github.io/tags/%E9%98%B2%E7%81%AB%E5%A2%99/"/>
</entry>
<entry>
<title>拥抱AI:从基础框架团队视角探索效率与挑战</title>
<link href="https://ustcqidi.github.io/2025/01/24/ai-brainstorm/"/>
<id>https://ustcqidi.github.io/2025/01/24/ai-brainstorm/</id>
<published>2025-01-24T10:02:03.000Z</published>
<updated>2025-01-24T10:05:47.278Z</updated>
<content type="html"><![CDATA[<p>在这个 <strong>All in AI</strong> 的时代,无论是学习、工作、生活还是娱乐,AI 已经深刻融入了各种场景。大家利用 AI 来赋能,不仅解决问题,还能显著降低工作量、提升工作效率。</p><p>今天下午我们组织了一场非常有趣的 <strong>Brainstorm 会议</strong>,探讨了作为客户端基础框架团队,我们如何应对 AI 带来的变化与冲击,如何利用 AI 解决我们面临的问题,同时规避可能产生的负面影响。</p><span id="more"></span><h3 id="1-AI-对现有业务流程和生态的潜在冲击"><a href="#1-AI-对现有业务流程和生态的潜在冲击" class="headerlink" title="1. AI 对现有业务流程和生态的潜在冲击"></a><strong>1. AI 对现有业务流程和生态的潜在冲击</strong></h3><p>AI 模型及其应用场景是否会对我们现有的视频会议业务流程和生态系统带来颠覆性或破坏性的影响?<br>例如: </p><ul><li><strong>频繁调用 API 的问题</strong><br>AI 助手可能频繁调用我们的接口,生成会议纪要、规划议程或实时生成摘要。这类新的 AI 驱动业务模式会对后台流量、代码框架乃至整体业务流程产生压力。</li><li><strong>业务场景的变化</strong><br>传统会议可能只涉及会议内容的简单记录,但随着 AI 自动生成摘要和行动项的普及,我们的系统是否能高效支持这种业务扩展?如何应对这些流量和性能冲击?</li></ul><h3 id="2-借助-AI-提升团队开发效率"><a href="#2-借助-AI-提升团队开发效率" class="headerlink" title="2. 借助 AI 提升团队开发效率"></a><strong>2. 借助 AI 提升团队开发效率</strong></h3><p>我们主要从以下几个方向进行探讨: </p><h4 id="2-1-AI-驱动的问答机器人"><a href="#2-1-AI-驱动的问答机器人" class="headerlink" title="2.1 AI 驱动的问答机器人"></a><strong>2.1 AI 驱动的问答机器人</strong></h4><p>作为提供客户端基础服务和框架的团队,我们需要频繁解答业务团队的技术问题,例如 API 使用方法、技术支持,甚至客户问题的处理。这些内容虽然已有大量文档和知识沉淀(如文档目录、培训视频、PPT 等),但查找效率较低。<br>我们是否可以开发一个 <strong>面向基础组件的 AI 问答机器人</strong>,帮助团队成员通过对话快速获取答案,从而减轻技术支持的工作量?</p><h4 id="2-2-自动生成单元测试代码"><a href="#2-2-自动生成单元测试代码" class="headerlink" title="2.2 自动生成单元测试代码"></a><strong>2.2 自动生成单元测试代码</strong></h4><p>我们是否能借助 AI 模型自动生成 <strong>单元测试</strong> 或 <strong>TA 测试代码</strong>?<br>具体设想: </p><ul><li>对于基础模块中的常用函数(如字符串操作、加解密方法等),通过提供函数原型,AI 模型可生成基于 GTest 的 C++ 测试代码,或提供测试建议。</li><li>对于端到端测试(E2E),我们目前基于 Lua 封装了接口驱动的测试框架。它支持多角色、多设备的复杂测试场景,但测试用例的设计、胶水代码的组织仍然高度依赖人工。<br>我们是否可以通过 AI 生成测试用例的建议、自动编排测试流程,甚至直接生成 Lua 脚本,从而减少人工投入?</li></ul><h4 id="2-3-AI-辅助日志分析"><a href="#2-3-AI-辅助日志分析" class="headerlink" title="2.3 AI 辅助日志分析"></a><strong>2.3 AI 辅助日志分析</strong></h4><p>目前我们的日志模式较为固定,错误模式和关键字有一定规律,但团队仍需投入大量时间查看日志、解决问题。如果能基于我们业务场景训练一个 <strong>定制化的日志分析模型</strong>,是否可以帮助我们快速定位常见问题、提高问题排查效率?</p><h4 id="2-4-代码审查与-Bug-检查"><a href="#2-4-代码审查与-Bug-检查" class="headerlink" title="2.4 代码审查与 Bug 检查"></a><strong>2.4 代码审查与 Bug 检查</strong></h4><p>现有 AI 模型在代码生成和错误修复方面已表现出强大的能力。我们是否可以利用 AI 帮助团队: </p><ul><li>自动审查代码风格,确保一致性; </li><li>查找常见错误并提供修改建议; </li><li>提高代码质量和开发效率?</li></ul>]]></content>
<summary type="html"><p>在这个 <strong>All in AI</strong> 的时代,无论是学习、工作、生活还是娱乐,AI 已经深刻融入了各种场景。大家利用 AI 来赋能,不仅解决问题,还能显著降低工作量、提升工作效率。</p>
<p>今天下午我们组织了一场非常有趣的 <strong>Brainstorm 会议</strong>,探讨了作为客户端基础框架团队,我们如何应对 AI 带来的变化与冲击,如何利用 AI 解决我们面临的问题,同时规避可能产生的负面影响。</p></summary>
<category term="AI赋能开发效能提升" scheme="https://ustcqidi.github.io/tags/AI%E8%B5%8B%E8%83%BD%E5%BC%80%E5%8F%91%E6%95%88%E8%83%BD%E6%8F%90%E5%8D%87/"/>
<category term="问答机器人" scheme="https://ustcqidi.github.io/tags/%E9%97%AE%E7%AD%94%E6%9C%BA%E5%99%A8%E4%BA%BA/"/>
<category term="日志分析" scheme="https://ustcqidi.github.io/tags/%E6%97%A5%E5%BF%97%E5%88%86%E6%9E%90/"/>
<category term="技术支持效率" scheme="https://ustcqidi.github.io/tags/%E6%8A%80%E6%9C%AF%E6%94%AF%E6%8C%81%E6%95%88%E7%8E%87/"/>
<category term="测试自动化与AI融合" scheme="https://ustcqidi.github.io/tags/%E6%B5%8B%E8%AF%95%E8%87%AA%E5%8A%A8%E5%8C%96%E4%B8%8EAI%E8%9E%8D%E5%90%88/"/>
</entry>
<entry>
<title>构建高效的客户端 Rate Limit 框架:设计与应用场景</title>
<link href="https://ustcqidi.github.io/2025/01/24/rate-limit/"/>
<id>https://ustcqidi.github.io/2025/01/24/rate-limit/</id>
<published>2025-01-24T05:26:47.000Z</published>
<updated>2025-01-24T06:46:12.258Z</updated>
<content type="html"><![CDATA[<p>本文介绍了一个用于客户端的通用限流框架设计与实践。该框架通过多种限流策略(计数器、滑动窗口、令牌桶)解决了HTTP请求并发、reCAPTCHA验证、日志写入等场景的限流需求。框架采用模块化设计,支持灵活配置和扩展,并集成了开发环境实时告警和生产环境Telemetry监控功能,有效提升了系统稳定性和用户体验。</p><span id="more"></span><h1 id="1-问题背景与需求"><a href="#1-问题背景与需求" class="headerlink" title="1. 问题背景与需求"></a><strong>1. 问题背景与需求</strong></h1><p>在现代客户端开发中,资源管理和系统稳定性至关重要。然而,在实际业务中,我们发现了以下问题:</p><ol><li><strong>HTTP 请求无约束</strong>:短时间内可能触发大量并发 HTTP 请求,缺乏全局限制,容易导致服务端压力激增。</li><li><strong>不合理的 reCaptcha 错误处理</strong>:当服务负载较高时,频繁弹出 reCaptcha 提示框,极大地影响用户体验。</li><li><strong>日志系统重复记录</strong>:大量重复 MemLog 日志可能掩盖关键问题,影响诊断效率。</li><li><strong>重复 Telemetry 数据</strong>:Telemetry 系统被重复数据淹没,导致分析困难。</li><li><strong>频繁文件操作</strong>:频繁打开文件可能导致系统资源耗尽。</li><li><strong>频繁数据库读写</strong>:短时间内的高频数据库操作影响性能并降低系统响应速度。</li></ol><p><strong>解决目标</strong>:<br>为了解决以上问题,我们设计了一个通用的 <strong>Rate Limit 框架</strong>,以实现以下目标:</p><ul><li>统一管理各类操作的限流规则(如 HTTP 请求、日志记录、文件操作等)。</li><li>提供灵活可配置的限流策略以适应不同业务场景。</li><li>优化用户体验,减少无效操作对用户的干扰。</li><li>提供扩展性,适配未来可能的需求变化。</li></ul><h1 id="2-框架设计思路"><a href="#2-框架设计思路" class="headerlink" title="2. 框架设计思路"></a><strong>2. 框架设计思路</strong></h1><p>根据需求,我们将 Rate Limit 框架的设计分为以下几个关键模块:</p><ol><li><strong>统一管理模块</strong>:提供统一的限流机制,支持跨业务模块的限流需求。</li><li><strong>策略定义与配置</strong>:允许开发者为不同场景灵活配置限流策略,包括阈值、时间窗口等。</li><li><strong>数据采集与监控</strong>:实时采集操作数据并监控限流执行状态。</li><li><strong>限流执行与反馈</strong>:根据策略结果进行限流操作,同时提供友好的反馈机制,如开发阶段的对话框提示和线上日志上报。</li></ol><h1 id="3-框架结构设计"><a href="#3-框架结构设计" class="headerlink" title="3. 框架结构设计"></a><strong>3. 框架结构设计</strong></h1><h2 id="3-1-核心组件"><a href="#3-1-核心组件" class="headerlink" title="3.1 核心组件"></a><strong>3.1 核心组件</strong></h2><ol><li><strong>RateLimiter</strong>:限流核心模块,负责执行限流策略。</li><li><strong>RateLimitStrategy</strong>:策略配置模块,用于定义场景的限流规则和算法。可以为不同的业务场景(如HTTP请求、文件操作、数据库操作)设置不同的限流策略和配置。</li><li><strong>DataCollector</strong>:数据采集模块,负责实时记录操作数据。通过DataCollector接口,可以实现自定义的数据收集逻辑,适应不同的监控需求。</li><li><strong>RateLimitHandler</strong>:限流处理模块,根据策略执行反馈操作(如弹窗或日志上报)。</li></ol><h2 id="3-2-架构图"><a href="#3-2-架构图" class="headerlink" title="3.2 架构图"></a><strong>3.2 架构图</strong></h2><p>以下是框架的核心架构图:</p><p><img src="/2025/01/24/rate-limit/arch.png" alt="image.png"></p><h2 id="3-3-核心类图"><a href="#3-3-核心类图" class="headerlink" title="3.3 核心类图"></a><strong>3.3 核心类图</strong></h2><p>以下是核心类的 UML 类图:</p><p><img src="/2025/01/24/rate-limit/class.png" alt="image.png"></p><h1 id="4-实现细节"><a href="#4-实现细节" class="headerlink" title="4. 实现细节"></a><strong>4. 实现细节</strong></h1><h2 id="4-1-核心模块代码"><a href="#4-1-核心模块代码" class="headerlink" title="4.1 核心模块代码"></a><strong>4.1 核心模块代码</strong></h2><h3 id="RateLimitTypes-h"><a href="#RateLimitTypes-h" class="headerlink" title="RateLimitTypes.h"></a><strong>RateLimitTypes.h</strong></h3><p>定义限流策略的核心数据结构:</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// RateLimitTypes.h</span></span><br><span class="line"><span class="meta">#<span class="keyword">ifndef</span> RATE_LIMIT_TYPES_H</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> RATE_LIMIT_TYPES_H</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string"><chrono></span></span></span><br><span class="line"></span><br><span class="line"><span class="keyword">namespace</span> RateLimitFramework {</span><br><span class="line"></span><br><span class="line"><span class="keyword">enum class</span> <span class="title class_">RateLimitAlgorithm</span> {</span><br><span class="line"> SIMPLE_COUNTER,</span><br><span class="line"> SLIDING_WINDOW,</span><br><span class="line"> TOKEN_BUCKET</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="keyword">enum class</span> <span class="title class_">StrategyType</span> {</span><br><span class="line">Request,</span><br><span class="line"> Recapcha,</span><br><span class="line"> MEMLOG,</span><br><span class="line"> DB_IO</span><br><span class="line"><span class="comment">// Add more strategy types as needed</span></span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">RateLimitStrategy</span> {</span><br><span class="line"> StrategyType strategyType;</span><br><span class="line"><span class="type">int</span> threshold;</span><br><span class="line">std::chrono::milliseconds timeWindow;</span><br><span class="line">RateLimitAlgorithm algorithm;</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line">} <span class="comment">// namespace RateLimitFramework</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">endif</span> <span class="comment">// RATE_LIMIT_TYPES_H</span></span></span><br></pre></td></tr></table></figure><h3 id="DataCollector"><a href="#DataCollector" class="headerlink" title="DataCollector"></a><strong>DataCollector</strong></h3><p>负责记录和查询操作时间戳:</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">ifndef</span> DATA_COLLECTOR_H</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> DATA_COLLECTOR_H</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"cmmlib/CmmBase.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"RateLimitTypes.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string"><string></span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string"><chrono></span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string"><map></span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string"><deque></span></span></span><br><span class="line"></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std::chrono;</span><br><span class="line"></span><br><span class="line"><span class="keyword">namespace</span> RateLimitFramework {</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">CMM_API</span> DataCollector {</span><br><span class="line"><span class="keyword">protected</span>:</span><br><span class="line"> std::map<std::string, std::deque<std::chrono::steady_clock::time_point>> timestamps;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line"><span class="function"><span class="keyword">virtual</span> <span class="type">void</span> <span class="title">record</span><span class="params">(<span class="type">const</span> std::string& key, <span class="type">const</span> RateLimitStrategy& strategy)</span></span>;</span><br><span class="line"><span class="function"><span class="keyword">virtual</span> <span class="type">int</span> <span class="title">getData</span><span class="params">(<span class="type">const</span> std::string& key, <span class="type">const</span> RateLimitStrategy& strategy)</span> <span class="type">const</span></span>;</span><br><span class="line"> <span class="keyword">virtual</span> ~<span class="built_in">DataCollector</span>() = <span class="keyword">default</span>;</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line">} <span class="comment">// namespace RateLimitFramework</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">endif</span> <span class="comment">// DATA_COLLECTOR_H</span></span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"cmmlib/ratelimit/DataCollector.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string"><algorithm></span></span></span><br><span class="line"></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std::chrono;</span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> RateLimitFramework;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">DataCollector::record</span><span class="params">(<span class="type">const</span> std::string& key, <span class="type">const</span> RateLimitStrategy& strategy)</span> </span>{</span><br><span class="line"><span class="keyword">auto</span> now = std::chrono::steady_clock::<span class="built_in">now</span>();</span><br><span class="line"><span class="keyword">auto</span>& timePoints = timestamps[key];</span><br><span class="line">timePoints.<span class="built_in">push_back</span>(now);</span><br><span class="line"></span><br><span class="line"><span class="keyword">auto</span> startTime = now - strategy.timeWindow;</span><br><span class="line"><span class="keyword">while</span> (!timePoints.<span class="built_in">empty</span>() && timePoints.<span class="built_in">front</span>() < startTime) {</span><br><span class="line">timePoints.<span class="built_in">pop_front</span>();</span><br><span class="line">}</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">DataCollector::getData</span><span class="params">(<span class="type">const</span> std::string& key, <span class="type">const</span> RateLimitStrategy& strategy)</span> <span class="type">const</span> </span>{</span><br><span class="line"><span class="keyword">auto</span> it = timestamps.<span class="built_in">find</span>(key);</span><br><span class="line"><span class="keyword">if</span> (it == timestamps.<span class="built_in">end</span>()) {</span><br><span class="line"><span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br><span class="line"><span class="keyword">auto</span> now = std::chrono::steady_clock::<span class="built_in">now</span>();</span><br><span class="line"><span class="keyword">auto</span> startTime = now - strategy.timeWindow;</span><br><span class="line"><span class="keyword">return</span> std::<span class="built_in">count_if</span>(it->second.<span class="built_in">begin</span>(), it->second.<span class="built_in">end</span>(),</span><br><span class="line">[startTime](<span class="type">const</span> <span class="keyword">auto</span>& tp) { <span class="keyword">return</span> tp >= startTime; });</span><br><span class="line">}</span><br><span class="line"></span><br></pre></td></tr></table></figure><h3 id="RateLimiter"><a href="#RateLimiter" class="headerlink" title="RateLimiter"></a><strong>RateLimiter</strong></h3><p>根据不同的限流算法执行限流逻辑:</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">ifndef</span> RATE_LIMITER_H</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> RATE_LIMITER_H</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"DataCollector.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"cmmlib/CmmBase.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string"><map></span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string"><memory></span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string"><chrono></span></span></span><br><span class="line"></span><br><span class="line"><span class="keyword">namespace</span> RateLimitFramework {</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">CMM_API</span> RateLimitHandler {</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line"> <span class="function"><span class="keyword">virtual</span> <span class="type">void</span> <span class="title">handle</span><span class="params">(<span class="type">const</span> std::string& key, <span class="type">const</span> std::map<std::string, std::string>& context)</span> </span>= <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">virtual</span> ~<span class="built_in">RateLimitHandler</span>() = <span class="keyword">default</span>;</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">CMM_API</span> RateLimiter {</span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line"> <span class="keyword">struct</span> <span class="title class_">Strategy</span> {</span><br><span class="line"> RateLimitStrategy config;</span><br><span class="line"> std::shared_ptr<DataCollector> dataCollector;</span><br><span class="line"> std::shared_ptr<RateLimitHandler> handler;</span><br><span class="line"> std::chrono::steady_clock::time_point lastExecutionTime;</span><br><span class="line"> <span class="type">double</span> tokens;</span><br><span class="line"></span><br><span class="line"><span class="built_in">Strategy</span>(<span class="type">const</span> RateLimitStrategy& cfg, std::shared_ptr<DataCollector> dc, std::shared_ptr<RateLimitHandler> rh)</span><br><span class="line">: <span class="built_in">config</span>(cfg), <span class="built_in">dataCollector</span>(dc), <span class="built_in">handler</span>(rh), <span class="built_in">lastExecutionTime</span>(std::chrono::steady_clock::<span class="built_in">now</span>()), <span class="built_in">tokens</span>(cfg.threshold) {}</span><br><span class="line"></span><br><span class="line"><span class="built_in">Strategy</span>()</span><br><span class="line">: config{}, <span class="built_in">dataCollector</span>(<span class="literal">nullptr</span>), <span class="built_in">handler</span>(<span class="literal">nullptr</span>), <span class="built_in">lastExecutionTime</span>(std::chrono::steady_clock::<span class="built_in">now</span>()), <span class="built_in">tokens</span>(<span class="number">0.0</span>) {}</span><br><span class="line"> };</span><br><span class="line"></span><br><span class="line"> std::map<StrategyType, Strategy> strategies;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line"> <span class="function"><span class="type">void</span> <span class="title">addStrategy</span><span class="params">(<span class="type">const</span> RateLimitStrategy& strategy, std::shared_ptr<DataCollector> dataCollector, std::shared_ptr<RateLimitHandler> handler)</span></span>;</span><br><span class="line"> <span class="function"><span class="type">bool</span> <span class="title">checkLimit</span><span class="params">(StrategyType strategyType, <span class="type">const</span> std::string& key)</span></span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line"><span class="function"><span class="type">bool</span> <span class="title">checkSimpleCounter</span><span class="params">(<span class="type">const</span> Strategy& strategy, <span class="type">const</span> std::string& key)</span></span>;</span><br><span class="line"><span class="function"><span class="type">bool</span> <span class="title">checkSlidingWindow</span><span class="params">(<span class="type">const</span> Strategy& strategy, <span class="type">const</span> std::string& key)</span></span>;</span><br><span class="line"><span class="function"><span class="type">bool</span> <span class="title">checkTokenBucket</span><span class="params">(Strategy& strategy, <span class="type">const</span> std::string& key)</span></span>;</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line">} <span class="comment">// namespace RateLimitFramework</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">endif</span> <span class="comment">// RATE_LIMITER_H</span></span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"cmmlib/ratelimit/RateLimit.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string"><map></span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string"><algorithm></span></span></span><br><span class="line"></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std::chrono;</span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> RateLimitFramework;</span><br><span class="line"></span><br><span class="line"><span class="keyword">namespace</span> RateLimitFramework {</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="type">void</span> <span class="title">RateLimiter::addStrategy</span><span class="params">(<span class="type">const</span> RateLimitStrategy& strategy, shared_ptr<DataCollector> dataCollector, shared_ptr<RateLimitHandler> handler)</span> </span>{</span><br><span class="line"> strategies[strategy.strategyType] = <span class="built_in">Strategy</span>(strategy, dataCollector, handler);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="type">bool</span> <span class="title">RateLimiter::checkLimit</span><span class="params">(StrategyType strategyType, <span class="type">const</span> std::string& key)</span> </span>{</span><br><span class="line"> <span class="keyword">auto</span> strategyIt = strategies.<span class="built_in">find</span>(strategyType);</span><br><span class="line"> <span class="keyword">if</span> (strategyIt == strategies.<span class="built_in">end</span>()) {</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">true</span>; <span class="comment">// If strategy doesn't exist, allow the request</span></span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">auto</span>& strategy = strategyIt->second;</span><br><span class="line"> strategy.dataCollector-><span class="built_in">record</span>(key, strategy.config);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">switch</span> (strategy.config.algorithm) {</span><br><span class="line"> <span class="keyword">case</span> RateLimitAlgorithm::SIMPLE_COUNTER:</span><br><span class="line"> <span class="keyword">return</span> <span class="built_in">checkSimpleCounter</span>(strategy, key);</span><br><span class="line"> <span class="keyword">case</span> RateLimitAlgorithm::SLIDING_WINDOW:</span><br><span class="line"> <span class="keyword">return</span> <span class="built_in">checkSlidingWindow</span>(strategy, key);</span><br><span class="line"> <span class="keyword">case</span> RateLimitAlgorithm::TOKEN_BUCKET:</span><br><span class="line"> <span class="keyword">return</span> <span class="built_in">checkTokenBucket</span>(strategy, key);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">bool</span> <span class="title">RateLimiter::checkSimpleCounter</span><span class="params">(<span class="type">const</span> Strategy& strategy, <span class="type">const</span> std::string& key)</span> </span>{</span><br><span class="line"><span class="type">int</span> count = strategy.dataCollector-><span class="built_in">getData</span>(key, strategy.config);</span><br><span class="line"><span class="keyword">if</span> (count > strategy.config.threshold) {</span><br><span class="line">std::map<std::string, std::string> context;</span><br><span class="line">strategy.handler-><span class="built_in">handle</span>(key, context);</span><br><span class="line"><span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">}</span><br><span class="line"><span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">bool</span> <span class="title">RateLimiter::checkSlidingWindow</span><span class="params">(<span class="type">const</span> Strategy& strategy, <span class="type">const</span> std::string& key)</span> </span>{</span><br><span class="line"><span class="type">int</span> count = strategy.dataCollector-><span class="built_in">getData</span>(key, strategy.config);</span><br><span class="line"><span class="keyword">if</span> (count > strategy.config.threshold) {</span><br><span class="line">std::map<std::string, std::string> context;</span><br><span class="line">strategy.handler-><span class="built_in">handle</span>(key, context);</span><br><span class="line"><span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">}</span><br><span class="line"><span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">bool</span> <span class="title">RateLimiter::checkTokenBucket</span><span class="params">(Strategy& strategy, <span class="type">const</span> std::string& key)</span> </span>{</span><br><span class="line"><span class="keyword">auto</span> now = steady_clock::<span class="built_in">now</span>();</span><br><span class="line"> <span class="keyword">auto</span> timePassed = chrono::<span class="built_in">duration_cast</span><chrono::milliseconds>(now - strategy.lastExecutionTime).<span class="built_in">count</span>();</span><br><span class="line"> strategy.tokens += timePassed * (strategy.config.threshold / <span class="built_in">static_cast</span><<span class="type">double</span>>(strategy.config.timeWindow.<span class="built_in">count</span>()));</span><br><span class="line"> strategy.tokens = (std::min)(strategy.tokens, <span class="built_in">static_cast</span><<span class="type">double</span>>(strategy.config.threshold));</span><br><span class="line"> strategy.lastExecutionTime = now;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (strategy.tokens < <span class="number">1.0</span>) {</span><br><span class="line"> std::map<std::string, std::string> context;</span><br><span class="line"> strategy.handler-><span class="built_in">handle</span>(key, context);</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">else</span> {</span><br><span class="line"> strategy.tokens -= <span class="number">1.0</span>;</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="4-2-测试程序"><a href="#4-2-测试程序" class="headerlink" title="4.2 测试程序"></a><strong>4.2 测试程序</strong></h2><p>示例测试用例展示了简单计数限流算法的应用:</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"cmmlib/ratelimit/RateLimit.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"cmmlib/ratelimit/DataCollector.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"ConsoleHandler.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"EmitRequestDataCollector.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string"><iostream></span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string"><thread></span></span></span><br><span class="line"></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std::chrono;</span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> RateLimitFramework;</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">RateLimitTest</span> : <span class="keyword">public</span> ::testing::Test {</span><br><span class="line"><span class="keyword">protected</span>:</span><br><span class="line">RateLimiter rateLimit;</span><br><span class="line">shared_ptr<DataCollector> collector;</span><br><span class="line">shared_ptr<RateLimitHandler> handler;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">SetUp</span><span class="params">()</span> <span class="keyword">override</span> </span>{</span><br><span class="line">collector = <span class="built_in">make_shared</span><EmitRequestDataCollector>();</span><br><span class="line">handler = <span class="built_in">make_shared</span><ConsoleHandler>();</span><br><span class="line">}</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="built_in">TEST_F</span>(RateLimitTest, SimpleCounterTest) {</span><br><span class="line">RateLimitStrategy strategy = { StrategyType::Request, <span class="number">5</span>, <span class="built_in">milliseconds</span>(<span class="number">1000</span>), RateLimitAlgorithm::SIMPLE_COUNTER };</span><br><span class="line">rateLimit.<span class="built_in">addStrategy</span>(strategy, collector, handler);</span><br><span class="line"></span><br><span class="line">string key = <span class="string">"example.com/api"</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// Test within limit</span></span><br><span class="line"><span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i < <span class="number">5</span>; i++) {</span><br><span class="line"><span class="built_in">EXPECT_TRUE</span>(rateLimit.<span class="built_in">checkLimit</span>(StrategyType::Request, key));</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// Test exceeding limit</span></span><br><span class="line"><span class="built_in">EXPECT_FALSE</span>(rateLimit.<span class="built_in">checkLimit</span>(StrategyType::Request, key));</span><br><span class="line"></span><br><span class="line"><span class="comment">// Wait for the window to pass</span></span><br><span class="line">this_thread::<span class="built_in">sleep_for</span>(<span class="built_in">milliseconds</span>(<span class="number">1100</span>));</span><br><span class="line"></span><br><span class="line"><span class="comment">// Test reset after window</span></span><br><span class="line"><span class="built_in">EXPECT_TRUE</span>(rateLimit.<span class="built_in">checkLimit</span>(StrategyType::Request, key));</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="built_in">TEST_F</span>(RateLimitTest, DifferentKeysTest) {</span><br><span class="line">RateLimitStrategy strategy = { StrategyType::Request, <span class="number">3</span>, <span class="built_in">milliseconds</span>(<span class="number">1000</span>), RateLimitAlgorithm::SIMPLE_COUNTER };</span><br><span class="line">rateLimit.<span class="built_in">addStrategy</span>(strategy, collector, handler);</span><br><span class="line"></span><br><span class="line">string key1 = <span class="string">"example.com/api1"</span>;</span><br><span class="line">string key2 = <span class="string">"example.com/api2"</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// Test key1</span></span><br><span class="line"><span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i < <span class="number">3</span>; i++) {</span><br><span class="line"><span class="built_in">EXPECT_TRUE</span>(rateLimit.<span class="built_in">checkLimit</span>(StrategyType::Request, key1));</span><br><span class="line">}</span><br><span class="line"><span class="built_in">EXPECT_FALSE</span>(rateLimit.<span class="built_in">checkLimit</span>(StrategyType::Request, key1));</span><br><span class="line"></span><br><span class="line"><span class="comment">// Test key2 (should not be affected by key1's limit)</span></span><br><span class="line"><span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i < <span class="number">3</span>; i++) {</span><br><span class="line"><span class="built_in">EXPECT_TRUE</span>(rateLimit.<span class="built_in">checkLimit</span>(StrategyType::Request, key2));</span><br><span class="line">}</span><br><span class="line"><span class="built_in">EXPECT_FALSE</span>(rateLimit.<span class="built_in">checkLimit</span>(StrategyType::Request, key2));</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="built_in">TEST_F</span>(RateLimitTest, NonExistentStrategyTest) {</span><br><span class="line">string key = <span class="string">"test"</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// Should allow requests for non-existent strategies</span></span><br><span class="line"><span class="built_in">EXPECT_TRUE</span>(rateLimit.<span class="built_in">checkLimit</span>(<span class="built_in">static_cast</span><StrategyType>(<span class="number">999</span>), key));</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="built_in">TEST_F</span>(RateLimitTest, MultipleStrategiesTest) {</span><br><span class="line">RateLimitStrategy apiStrategy = { StrategyType::Request, <span class="number">5</span>, <span class="built_in">milliseconds</span>(<span class="number">1000</span>), RateLimitAlgorithm::SIMPLE_COUNTER };</span><br><span class="line">RateLimitStrategy recapchaStrategy = { StrategyType::Recapcha, <span class="number">3</span>, <span class="built_in">milliseconds</span>(<span class="number">2000</span>), RateLimitAlgorithm::SLIDING_WINDOW };</span><br><span class="line"></span><br><span class="line">rateLimit.<span class="built_in">addStrategy</span>(apiStrategy, <span class="built_in">make_shared</span><EmitRequestDataCollector>(), handler);</span><br><span class="line">rateLimit.<span class="built_in">addStrategy</span>(recapchaStrategy, <span class="built_in">make_shared</span><EmitRequestDataCollector>(), handler);</span><br><span class="line"></span><br><span class="line">string request_url = <span class="string">"example.com/api"</span>;</span><br><span class="line">string recapcha_url = <span class="string">"user123"</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// Test Request strategy</span></span><br><span class="line"><span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i < <span class="number">5</span>; i++) {</span><br><span class="line"><span class="built_in">EXPECT_TRUE</span>(rateLimit.<span class="built_in">checkLimit</span>(StrategyType::Request, request_url));</span><br><span class="line">}</span><br><span class="line"><span class="built_in">EXPECT_FALSE</span>(rateLimit.<span class="built_in">checkLimit</span>(StrategyType::Request, request_url));</span><br><span class="line"></span><br><span class="line"><span class="comment">// Test Recapcha strategy</span></span><br><span class="line"><span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i < <span class="number">3</span>; i++) {</span><br><span class="line"><span class="built_in">EXPECT_TRUE</span>(rateLimit.<span class="built_in">checkLimit</span>(StrategyType::Recapcha, recapcha_url));</span><br><span class="line">}</span><br><span class="line"><span class="built_in">EXPECT_FALSE</span>(rateLimit.<span class="built_in">checkLimit</span>(StrategyType::Recapcha, recapcha_url));</span><br><span class="line"></span><br><span class="line"><span class="comment">// Wait for API strategy to reset</span></span><br><span class="line">this_thread::<span class="built_in">sleep_for</span>(<span class="built_in">milliseconds</span>(<span class="number">1100</span>));</span><br><span class="line"></span><br><span class="line"><span class="built_in">EXPECT_TRUE</span>(rateLimit.<span class="built_in">checkLimit</span>(StrategyType::Request, request_url));</span><br><span class="line"><span class="built_in">EXPECT_FALSE</span>(rateLimit.<span class="built_in">checkLimit</span>(StrategyType::Recapcha, recapcha_url)); <span class="comment">// Login strategy should still be blocked</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// Wait for Login strategy to reset</span></span><br><span class="line">this_thread::<span class="built_in">sleep_for</span>(<span class="built_in">milliseconds</span>(<span class="number">1000</span>));</span><br><span class="line"></span><br><span class="line"><span class="built_in">EXPECT_TRUE</span>(rateLimit.<span class="built_in">checkLimit</span>(StrategyType::Recapcha, recapcha_url));</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h1 id="5-实际应用场景"><a href="#5-实际应用场景" class="headerlink" title="5. 实际应用场景"></a><strong>5. 实际应用场景</strong></h1><h2 id="开发环境集成"><a href="#开发环境集成" class="headerlink" title="开发环境集成"></a>开发环境集成</h2><p>在开发阶段,我们实现了实时告警机制:</p><ul><li>当检测到限流事件时,立即弹出对话框提醒开发者</li><li>包含具体的限流原因和相关上下文信息</li><li>帮助开发者及早发现和解决潜在问题</li></ul><p>比如下面的截图就是某个 HTTP Request 触发了 Ratelimit 规则的 Warning 提示</p><p><img src="/2025/01/24/rate-limit/warning.png" alt="image.png"></p><h2 id="生产环境监控"><a href="#生产环境监控" class="headerlink" title="生产环境监控"></a>生产环境监控</h2><p>在生产环境中:</p><ul><li>通过Telemetry系统上报限流事件</li><li>收集用户行为数据进行分析</li><li>持续优化限流策略和阈值</li></ul><p>下图是线上触发 Ratelimit 规则的数据情况</p><p><img src="/2025/01/24/rate-limit/telemetry.png" alt="image.png"></p><h1 id="6-总结"><a href="#6-总结" class="headerlink" title="6. 总结"></a><strong>6. 总结</strong></h1><p>Rate Limit 框架的设计与实现为客户端提供了统一、灵活、可扩展的限流解决方案,不仅提升了系统稳定性,还优化了用户体验。</p>]]></content>
<summary type="html"><p>本文介绍了一个用于客户端的通用限流框架设计与实践。该框架通过多种限流策略(计数器、滑动窗口、令牌桶)解决了HTTP请求并发、reCAPTCHA验证、日志写入等场景的限流需求。框架采用模块化设计,支持灵活配置和扩展,并集成了开发环境实时告警和生产环境Telemetry监控功能,有效提升了系统稳定性和用户体验。</p></summary>
<category term="Rate Limit" scheme="https://ustcqidi.github.io/tags/Rate-Limit/"/>
<category term="Telemetry 监控" scheme="https://ustcqidi.github.io/tags/Telemetry-%E7%9B%91%E6%8E%A7/"/>
<category term="实时告警" scheme="https://ustcqidi.github.io/tags/%E5%AE%9E%E6%97%B6%E5%91%8A%E8%AD%A6/"/>
</entry>
<entry>
<title>WebSocket 数据传输中的大数据帧发送问题</title>
<link href="https://ustcqidi.github.io/2025/01/04/websocket-bigframe/"/>
<id>https://ustcqidi.github.io/2025/01/04/websocket-bigframe/</id>
<published>2025-01-04T04:03:30.000Z</published>
<updated>2025-01-04T04:46:08.244Z</updated>
<content type="html"><![CDATA[<p>详细记录了一个WebSocket发送大数据包被截断的问题排查过程</p><span id="more"></span><h1 id="问题背景"><a href="#问题背景" class="headerlink" title="问题背景"></a>问题背景</h1><h2 id="需求背景"><a href="#需求背景" class="headerlink" title="需求背景"></a>需求背景</h2><p>我们有个业务场景,需要通过 WebSocket 发送图片,根据同事反馈,目前发送一个image数据大概140kb,会出现发送的数据被截断的情况,连接也会被断开</p><h2 id="初步猜测:数据帧过大导致发送失败"><a href="#初步猜测:数据帧过大导致发送失败" class="headerlink" title="初步猜测:数据帧过大导致发送失败"></a>初步猜测:数据帧过大导致发送失败</h2><p>看了一下代码,我们没有对发送数据包的大小做限制。但是从实际的测试表现来看,发送数据大小超过 140kb 的包,就很容易出现这个问题,初步猜测跟包大小有关。还需要进一步通过 Log 或者抓包确定。</p><h2 id="问题复现与初步分析"><a href="#问题复现与初步分析" class="headerlink" title="问题复现与初步分析"></a>问题复现与初步分析</h2><p>这个问题很容易复现,这个对我们 troubleshooting 是很好的事情。我们用 mitmproxy 开启了一个代理服务器,然后在测试机器上配置这个代理服务器复现这个问题,这样做的目的是我们可以在 mitmproxy 上看到完整的 WebSocket 包,便于定位问题。mitmproxy 的 UI 交互有终端和 Web 两种形式,我更习惯使用 Web 方式,只需要在执行 mitmweb 命令,就可以启动服务。</p><p>交互界面如下:</p><p><img src="/2025/01/04/websocket-bigframe/general.jpg" alt></p><p>初步分析验证后,我们确定就是数据帧过大导致的发送失败,具体错误信息从 Log 中可以获取到。基于 Log 的错误信息,我们可以进一步分析 Root Cause。</p><h1 id="分析-Root-Cause"><a href="#分析-Root-Cause" class="headerlink" title="分析 Root Cause"></a>分析 Root Cause</h1><p>以下是我们在 Log 中发现的错误信息</p><h2 id="Socket-not-ready-for-send-recv"><a href="#Socket-not-ready-for-send-recv" class="headerlink" title="Socket not ready for send/recv"></a>Socket not ready for send/recv</h2><p><code>[10164:18664:12-20/17:43:51.370:DEBUG:SBCUrlRequest_WebSocket.cc(480)] [CSBCUrlRequest::WebSocket_CheckSendRecvData] End to post data, size:106830 Sent:0 Return:81 Curl Error String:Socket not ready for send/recv</code></p><p>这里的 <code>Return:81</code> 表示错误码是 81</p><figure class="highlight jsx"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="title function_">CURLE_AGAIN</span> (<span class="number">81</span>)</span><br><span class="line"></span><br><span class="line"><span class="title class_">Socket</span> is not ready <span class="keyword">for</span> send/recv wait till it<span class="string">'s ready and try again. This return code is only returned from curl_easy_recv and curl_easy_send (Added in 7.18.2)</span></span><br></pre></td></tr></table></figure><p>这个部分的错误信息表示套接字(Socket)没有准备好进行发送或接收操作。具体的错误代码是 <strong><code>CURLE_AGAIN (81)</code></strong>,这表示需要等待,直到套接字准备好,然后再次尝试。这通常是由于非阻塞套接字的操作,需要等待套接字变得可用才能进行进一步的发送或接收。</p><h2 id="SSL-routines-bad-length"><a href="#SSL-routines-bad-length" class="headerlink" title="SSL routines::bad length"></a>SSL routines::bad length</h2><p>我们在日志中还观察到以下错误,这些错误出现在发生 <code>CURLE_AGAIN</code> 之后。</p><p><code>[10164:18664:12-20/17:43:51.520:INFO:SBCUrlRequest.cc(415)] [my_curl_debug_callback] This: 100664864 TEXT :SSL_write() error: error:0A00010F:SSL routines::bad length</code></p><p>我们当前是没有处理 <code>CURLE_AGAIN</code> ,出现 CURLE_AGAIN 时也会直接给业务层 <code>OnDataSent</code> callback。所以业务层可能在 socket 没有就绪的状态下持续不断地发送数据。</p><figure class="highlight jsx"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line">size_t n_sent = <span class="number">0</span>;</span><br><span class="line"><span class="title class_">CURLcode</span> ret = <span class="title function_">curl_easy_send</span>(m_hCurl, <span class="title class_">GetBaseOf</span>(total_to_send),total_to_send.<span class="title function_">size</span>(),&n_sent);</span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span>(<span class="attr">std</span>::vector<<span class="title class_">CAString</span>*>::iterator it = bytes_sent.<span class="title function_">begin</span>(); it != bytes_sent.<span class="title function_">end</span>(); ++it)</span><br><span class="line">{</span><br><span class="line">m_webSocketState.<span class="title function_">doCallback</span>([it,ret](<span class="title class_">IWebSocketConnectionCallback</span> * callback){</span><br><span class="line"> callback-><span class="title class_">OnDataSent</span>(*it,ret);</span><br><span class="line">});</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="title function_">ZLOG</span>(<span class="variable constant_">DBG</span>,<span class="string">"[CSBCUrlRequest::WebSocket_CheckSendRecvData] End to post data, size:"</span><<total_to_send.<span class="title function_">size</span>()</span><br><span class="line"><<<span class="string">" Sent:"</span><<n_sent</span><br><span class="line"><<<span class="string">" Return:"</span><<ret</span><br><span class="line"><<<span class="string">" Curl Error String:"</span><<<span class="title function_">curl_easy_strerror</span>(ret));</span><br></pre></td></tr></table></figure><h3 id="为何会出现-SSL-routines-bad-length-错误呢-?"><a href="#为何会出现-SSL-routines-bad-length-错误呢-?" class="headerlink" title="为何会出现 SSL routines::bad length 错误呢 ?"></a><strong>为何会出现 <code>SSL routines::bad length</code> 错误呢 ?</strong></h3><p>如果在套接字不可用的情况下进行 <code>SSL_write</code>,可能会引发 <code>SSL_write()</code> 错误,如 “bad length”。</p><p>SSL/TLS是一个状态机,涉及了复杂的握手/协商、加密/解密等过程。这些状态需要持续地保持同步。具体来说,SSL连接建立时,客户端和服务器端会协商生成“会话密钥”、初始化变量等。这构成了一个持续的SSL “会话”。如果在“会话”过程中,由于套接字不可用中断了通信,那么客户端和服务器端的SSL状态就失去了同步。之后在套接字不可用时强行调用SSL_write写入数据,那么参与后续加密和传输的数据长度,就很可能和服务器端持有的会话状态不匹配,导致解密或者长度校验失败,出现“bad length”错误。</p><h1 id="问题修复与验证"><a href="#问题修复与验证" class="headerlink" title="问题修复与验证"></a>问题修复与验证</h1><h2 id="测试环境"><a href="#测试环境" class="headerlink" title="测试环境"></a>测试环境</h2><p>为了快速验证问题,我尝试搭建一个 WebSocket 测试服务器,在 github 中找到了一个 go 语言的版本的 WebSocket 服务器。但是我们的 WebSocket Client 实现有点问题,连接这个测试服务器时遇到了一些问题。</p><h3 id="Sec-WebSocket-Key-不合法"><a href="#Sec-WebSocket-Key-不合法" class="headerlink" title="Sec-WebSocket-Key 不合法"></a>Sec-WebSocket-Key 不合法</h3><p>Sec-WebSocket-Key是WebSocket协议中的一个重要字段,它用于WebSocket连接的握手过程中。</p><p>其规则及用途可以总结如下:</p><ol><li>格式:Sec-WebSocket-Key字段的值是一个base64编码的随机字符串。</li><li>长度:随机字符串的长度必须是16个字节,即24个base64编码后的字符。所以服务端要验证长度是否合法。</li><li>用途:用于计算Sec-WebSocket-Accept的值,这个值会在服务器端响应首部中返回,这样可以完成WebSocket握手。计算方式是:将Sec-WebSocket-Key的值与一个特定的字符串“258EAFA5-E914-47DA-95CA-C5AB0DC85B11”连接起来,取SHA-1哈希值,然后base64编码。</li><li>目的:这一握手机制的目的是为了防止误连接,确认这确实是一个要升级为WebSocket的HTTP请求,避免被非法连接利用。</li></ol><p>但是我们的 WebSocket Client 格式不规范,所以连接测试服务器时报错。</p><p>以下是具体的错误信息:</p><p><strong>客户端 Log</strong></p><figure class="highlight jsx"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line">[<span class="number">46528</span>:<span class="number">41076</span>:<span class="number">12</span>-<span class="number">21</span>/<span class="number">10</span>:<span class="number">29</span>:<span class="number">59.773</span>:<span class="attr">INFO</span>:<span class="title class_">SBCUrlRequest</span>_WebSocket.<span class="title function_">cc</span>(<span class="number">326</span>)] [<span class="title class_">CSBCUrlRequest</span>::<span class="title class_">WebSocket</span>_NotifyConnectionGood] <span class="title class_">End</span> to post data, <span class="attr">head</span>:</span><br><span class="line"><span class="variable constant_">GET</span> /f <span class="variable constant_">HTTP</span>/<span class="number">1.1</span></span><br><span class="line"><span class="title class_">Host</span>: <span class="number">127.0</span><span class="number">.0</span><span class="number">.1</span></span><br><span class="line"><span class="title class_">Upgrade</span>: websocket</span><br><span class="line"><span class="title class_">Connection</span>: <span class="title class_">Upgrade</span></span><br><span class="line">**<span class="title class_">Sec</span>-<span class="title class_">WebSocket</span>-<span class="title class_">Key</span>: **{15E21AB6-6AE4-<span class="number">4</span>****</span><br><span class="line"><span class="title class_">Sec</span>-<span class="title class_">WebSocket</span>-<span class="title class_">Version</span>: <span class="number">13</span></span><br><span class="line"><span class="variable constant_">ZM</span>-<span class="attr">CAP</span>: <span class="number">8300567970761955255</span>,<span class="number">6445493618999263140</span>,<span class="number">7795585261064146175</span>,<span class="number">7998097959182395391</span>,<span class="number">18587783470514173</span></span><br><span class="line"><span class="variable constant_">ZM</span>-<span class="attr">PROP</span>: <span class="title class_">Win</span>.<span class="property">Zoom</span></span><br><span class="line">X-<span class="title class_">Zoom</span>-<span class="title class_">AccountId</span>: </span><br><span class="line"></span><br><span class="line"> <span class="title class_">Sent</span>:<span class="number">287</span> <span class="title class_">Return</span>:<span class="number">0</span> <span class="title class_">Curl</span> <span class="title class_">Error</span> <span class="title class_">String</span>:<span class="title class_">No</span> error </span><br><span class="line">[<span class="number">46528</span>:<span class="number">41076</span>:<span class="number">12</span>-<span class="number">21</span>/<span class="number">10</span>:<span class="number">29</span>:<span class="number">59.788</span>:<span class="attr">INFO</span>:<span class="title class_">SBCUrlRequest</span>_WebSocket.<span class="title function_">cc</span>(<span class="number">207</span>)] [<span class="title class_">CSBCUrlRequest</span>::<span class="title class_">WebSocket</span>_ParseHeadResponse] <span class="title class_">Heads</span>:</span><br><span class="line">**<span class="variable constant_">HTTP</span>/<span class="number">1.1</span> <span class="number">400</span> <span class="title class_">Bad</span> <span class="title class_">Request</span>**</span><br><span class="line"><span class="title class_">Content</span>-<span class="title class_">Type</span>: text/plain; charset=utf-<span class="number">8</span></span><br><span class="line"><span class="title class_">Sec</span>-<span class="title class_">Websocket</span>-<span class="title class_">Version</span>: <span class="number">13</span></span><br><span class="line">X-<span class="title class_">Content</span>-<span class="title class_">Type</span>-<span class="title class_">Options</span>: nosniff</span><br><span class="line"><span class="title class_">Date</span>: <span class="title class_">Thu</span>, <span class="number">21</span> <span class="title class_">Dec</span> <span class="number">2023</span> <span class="number">02</span>:<span class="number">29</span>:<span class="number">59</span> <span class="variable constant_">GMT</span></span><br><span class="line"><span class="title class_">Content</span>-<span class="title class_">Length</span>: <span class="number">12</span></span><br><span class="line"></span><br><span class="line"><span class="title class_">Bad</span> <span class="title class_">Request</span></span><br></pre></td></tr></table></figure><p><strong>服务端 Log</strong></p><figure class="highlight jsx"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="title class_">Upgrade</span>: <span class="attr">websocket</span>: not a websocket <span class="attr">handshake</span>: <span class="string">'Sec-WebSocket-Key'</span> header must be <span class="title class_">Base64</span> encoded value <span class="keyword">of</span> <span class="number">16</span>-byte <span class="keyword">in</span> length</span><br></pre></td></tr></table></figure><p><strong>修复代码</strong></p><figure class="highlight jsx"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/*Upgrade: websocket: not a websocket handshake: 'Sec-WebSocket-Key'</span></span><br><span class="line"><span class="comment">header must be Base64 encoded value of 16-byte in length*/</span></span><br><span class="line"></span><br><span class="line"><span class="title class_">CAString</span> trimed_client_guild = <span class="title function_">CMM_T2UTF</span>(<span class="title class_">CString</span>(m_clientGUID, <span class="number">16</span>));</span><br><span class="line"></span><br><span class="line"><span class="title class_">CAString</span> secKey = <span class="title function_">base64EncodeA</span>((<span class="title class_">CmmUInt8</span>*)trimed_client_guild.<span class="title function_">c_str</span>(), trimed_client_guild.<span class="title function_">size</span>());</span><br></pre></td></tr></table></figure><h2 id="测试用例"><a href="#测试用例" class="headerlink" title="测试用例"></a>测试用例</h2><p>模拟发送大数据帧,期望能否复现 <code>CURLE_AGAIN</code> 错误,这样就能验证我们是否修复这个问题。</p><figure class="highlight jsx"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line"><span class="title function_">TEST</span>(<span class="params">WebSocketServiceTest, SendBigData</span>)</span><br><span class="line">{</span><br><span class="line"> <span class="title class_">CSBWebService</span> *ws = <span class="title class_">CWebServiceTestInstManager</span>::<span class="title class_">GetInstance</span>().<span class="title class_">SetUp</span>(<span class="title class_">Env</span>_Dev);</span><br><span class="line"><span class="title class_">Cmm</span>::<span class="title class_">CFileName</span> fn;</span><br><span class="line">fn.<span class="title class_">GetModuleFilePath</span>();</span><br><span class="line">fn += <span class="title function_">_T</span>(<span class="string">"\\big_size_file.log"</span>);</span><br><span class="line"><span class="attr">std</span>::ifstream <span class="title function_">file</span>(fn.<span class="title function_">c_str</span>());</span><br><span class="line"><span class="attr">std</span>::stringstream buffer;</span><br><span class="line">buffer << file.<span class="title function_">rdbuf</span>();</span><br><span class="line"><span class="attr">std</span>::string longString = buffer.<span class="title function_">str</span>();</span><br><span class="line"></span><br><span class="line"><span class="title class_">WebSocketTestApp</span> app;</span><br><span class="line"><span class="title class_">CString</span> channel_id;</span><br><span class="line"><span class="title function_">EXPECT_TRUE</span>(app.<span class="title class_">ConnectTo</span>(<span class="title class_">GetTestWebSocketHost</span>(), channel_id));</span><br><span class="line"><span class="title class_">CmmUInt32</span> result = -<span class="number">1</span>;</span><br><span class="line">app.<span class="title class_">CheckConnectState</span>(channel_id, result);</span><br><span class="line"><span class="comment">//EXPECT_EQ(result, SB_ERROR_SUCCESS);</span></span><br><span class="line"></span><br><span class="line"><span class="title class_">CAString</span> <span class="title function_">test_str</span>(longString);</span><br><span class="line"> <span class="keyword">for</span> (int i = <span class="number">0</span>; i < <span class="number">100</span>; i++) {</span><br><span class="line"> <span class="title function_">ZLOG</span>(<span class="variable constant_">INFO</span>, <span class="string">"WebSocketServiceTest SendData test_str length: "</span> << longString.<span class="title function_">size</span>());</span><br><span class="line"> <span class="title function_">EXPECT_TRUE</span>(app.<span class="title class_">SendDataEx</span>(<span class="number">0x2</span>, channel_id, test_str));</span><br><span class="line"> app.<span class="title class_">CheckSendDataEvent</span>(channel_id, result);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line">app.<span class="title class_">Close</span>(channel_id);</span><br><span class="line">app.<span class="title class_">CheckCloseEvent</span>(channel_id, result);</span><br><span class="line"><span class="title function_">EXPECT_EQ</span>(result, <span class="number">0</span>);</span><br><span class="line"></span><br><span class="line"> <span class="title class_">CWebServiceTestInstManager</span>::<span class="title class_">GetInstance</span>().<span class="title class_">TearDown</span>();</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="CURLE-AGAIN-处理和重试逻辑"><a href="#CURLE-AGAIN-处理和重试逻辑" class="headerlink" title="CURLE_AGAIN 处理和重试逻辑"></a><code>CURLE_AGAIN</code> 处理和重试逻辑</h2><p>需要增加 <code>CURLE_AGAIN</code> 等待逻辑,等待 socket 就绪时,再重发数据。</p><p>以下是基于 poll 事件驱动的伪代码:</p><figure class="highlight jsx"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br></pre></td><td class="code"><pre><span class="line">#include <curl/curl.<span class="property">h</span>></span><br><span class="line">#include <string></span><br><span class="line">#include <poll.<span class="property">h</span>></span><br><span class="line"></span><br><span class="line"><span class="attr">std</span>::string curle_again_cache; </span><br><span class="line"></span><br><span class="line">struct pollfd poll_fd;</span><br><span class="line"></span><br><span class="line"><span class="title class_">CURLcode</span> <span class="title function_">try_send_data</span>(<span class="params">CURL* curl, <span class="keyword">const</span> std::string& data</span>) {</span><br><span class="line"></span><br><span class="line"> <span class="title class_">CURLcode</span> res;</span><br><span class="line"> size_t sent = <span class="number">0</span>; </span><br><span class="line"></span><br><span class="line"> res = <span class="title function_">curl_easy_send</span>(curl, data.<span class="title function_">c_str</span>(), data.<span class="title function_">size</span>(), &sent);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span>(res == <span class="variable constant_">CURLE_AGAIN</span>) {</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 缓存数据 </span></span><br><span class="line"> curle_again_cache = data;</span><br><span class="line"> </span><br><span class="line"> <span class="comment">// 注册curl socket</span></span><br><span class="line"> poll_fd.<span class="property">fd</span> = <span class="title function_">curl_easy_getfd</span>(curl);</span><br><span class="line"> poll_fd.<span class="property">events</span> = <span class="variable constant_">POLLOUT</span>;</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 等待socket就绪</span></span><br><span class="line"> <span class="title function_">poll</span>(&poll_fd, <span class="number">1</span>, -<span class="number">1</span>);</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 重试发送</span></span><br><span class="line"> <span class="keyword">return</span> <span class="title function_">try_send_data</span>(curl, curle_again_cache);</span><br><span class="line"> </span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> res;</span><br><span class="line"></span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">int <span class="title function_">main</span>(<span class="params"></span>) {</span><br><span class="line"></span><br><span class="line"> <span class="variable constant_">CURL</span>* curl = <span class="title function_">curl_easy_init</span>();</span><br><span class="line"> <span class="attr">std</span>::string data = <span class="string">"123"</span>; </span><br><span class="line"></span><br><span class="line"> <span class="title class_">CURLcode</span> ret = <span class="title function_">try_send_data</span>(curl, data);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span>(ret != <span class="variable constant_">CURLE_OK</span>) {</span><br><span class="line"> <span class="comment">// 错误处理</span></span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="title function_">curl_easy_cleanup</span>(curl);</span><br><span class="line"></span><br><span class="line">}</span><br></pre></td></tr></table></figure><h1 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h1><h2 id="troubleshooting-过程回顾"><a href="#troubleshooting-过程回顾" class="headerlink" title="troubleshooting 过程回顾"></a>troubleshooting 过程回顾</h2><ol><li>收到问题反馈后,我们随即启动 mitmproxy 代理,让同事连接这个服务器复现问题。期间准备验证所需的 build 折腾了大半天。</li><li>通过 Log 定位到具体的错误原因,Review 分析代码以后确定 Root Cause 是,我们的 WebSocket 发送数据没有处理 again 错误</li><li>讨论了大体的解决方案</li><li>分工并行: 着手修改代码,搭建测试服务器,写测试 Case,模拟复现验证问题</li><li>Fix Patch 基于测试代码联调、优化、验证,单元测试通过</li><li>给业务方提供 Fix Patch 验证,end to end case 验证通过</li><li>19/12/2023 5:10 PM 同事反馈问题,21/12/2023 6:45 PM end to end 解决,耗时 2 天</li></ol><h2 id="两点启发"><a href="#两点启发" class="headerlink" title="两点启发"></a>两点启发</h2><p>可以复现问题,并且能 Debug,基本上离找到 Root Cause 并且解决问题不远了。我们面临的很多问题都有类似的困惑,要么很难复现,要不测试环境非常复杂。有时候解决问题的关键就是能否找到复现问题的路径并模拟出测试环境。这个 Case 中,我们自己搭建了一个测试服务器,模拟发送大文件失败的 Case,我跟同事通过修改 test case 反复修改验证我们的代码,确保没问题以后交给使用方在真是业务场景下验证。</p><p>给我的启发有两个:</p><ol><li>首先无论多么简单的代码调整都需要配上相应的测试代码,这个测试代码有助于我们在后续 toubleshooting 时快速调试、定位以及验证问题。</li><li>其次,测试环境对与模拟和复现问题非常关键,这个测试环境包括了诸如 NTLM & Kerberos 等复杂的认证流程,也包括了测试路径很复杂的业务流程,比如 SSO 登录,IPD,飞机模式代理域名登录等,我们无法保证每次 Regression 测试都能 Cover 所有的点,但是可以通过代码方式模拟测试验证。</li></ol><h2 id="三个收获"><a href="#三个收获" class="headerlink" title="三个收获"></a>三个收获</h2><ol><li>我们一直没有 WebSocket 的测试服务器,虽然有单元测试代码,但也是连接产线的一个数据推送服务器,现有的测试代码只是验证了 WebSocket 的连接建立、数据收发以及连接关闭,无法根据测试需求定制 Case,为了验证解决这个问题,我们也搭建了测试服务器,优化了测试代码,这个对后续验证 WebSocket 的问题提供了便利。</li><li>优化了对 WebSocket 发送大数据的处理,增强了 WebSocket 连接的鲁棒性</li><li>搭建 WebSocket 测试服务器的过程中,我们参考了一些开源项目。在这个过程中,我们了解到了 autobahn test-suite,这是一个专用于验证 WebSocket 实现是否符合规范的测试工具。autobahn test-suite 提供了丰富的测试用例,覆盖了 WebSocket 协议的多个边界情况。通过测试,我们发现了我们的 WebSocket 客户端存在许多不符合规范的地方。因此,我们计划基于这个 test-suite 对我们的 WebSocket 客户端进行进一步的测试和改进。</li></ol>]]></content>
<summary type="html"><p>详细记录了一个WebSocket发送大数据包被截断的问题排查过程</p></summary>
<category term="网络" scheme="https://ustcqidi.github.io/tags/%E7%BD%91%E7%BB%9C/"/>
<category term="WebSocket" scheme="https://ustcqidi.github.io/tags/WebSocket/"/>
<category term="企业级应用程序网络问题" scheme="https://ustcqidi.github.io/tags/%E4%BC%81%E4%B8%9A%E7%BA%A7%E5%BA%94%E7%94%A8%E7%A8%8B%E5%BA%8F%E7%BD%91%E7%BB%9C%E9%97%AE%E9%A2%98/"/>
</entry>
<entry>
<title>2024 年度总结</title>
<link href="https://ustcqidi.github.io/2025/01/01/summary-2024/"/>
<id>https://ustcqidi.github.io/2025/01/01/summary-2024/</id>
<published>2025-01-01T09:27:25.000Z</published>
<updated>2025-01-05T09:38:16.745Z</updated>
<content type="html"><![CDATA[<p>2024总结与2025规划</p><span id="more"></span><h2 id="2024年总结"><a href="#2024年总结" class="headerlink" title="2024年总结"></a><strong>2024年总结</strong></h2><h3 id="个人成长与健身"><a href="#个人成长与健身" class="headerlink" title="个人成长与健身"></a><strong>个人成长与健身</strong></h3><p>今年是我的本命年,经历了许多值得记录的事情,其中最显著的变化来自健身。从年初开始,我正式走进健身房,每周坚持三次左右的训练。从三大项的入门阶段逐步找到训练节奏,同时尝试了跑步和羽毛球。通过这一年的坚持,我的体检指标得到了明显改善,体脂率和饮食相关的指标趋于正常。</p><p>健身不仅带来了身体上的改变,也让我内心更加平静,杂念减少,整个人的状态变得更加积极从容。</p><h3 id="工作与团队"><a href="#工作与团队" class="headerlink" title="工作与团队"></a><strong>工作与团队</strong></h3><p>今年工作上的收获也颇丰:</p><ol><li><strong>专利成果</strong>:成功通过了5项专利,进一步巩固了我的技术积累。</li><li><strong>团队发展</strong>:团队规模和工作范围扩大了近一倍。我的角色更多地转向团队目标与方向的思考,同时关注项目状态。虽然写代码的时间减少,但在团队管理和战略规划方面收获颇多。</li></ol><h3 id="生活与家庭"><a href="#生活与家庭" class="headerlink" title="生活与家庭"></a><strong>生活与家庭</strong></h3><p>生活逐渐变得简单,家庭分工也更加明确。孩子的学习状态步入正轨,周末陪伴他上羽毛球课成为一大乐事。但在家庭生活中,我对孩子和家人的投入仍显不足,精力大多集中在工作上。</p><h3 id="不足与反思"><a href="#不足与反思" class="headerlink" title="不足与反思"></a><strong>不足与反思</strong></h3><ul><li><strong>时间管理</strong>:基础技能的提升时间过少,仍需优化时间分配。</li><li><strong>情绪管理</strong>:今年情绪波动较大,多次因压力过度饮酒,情绪控制有待加强。</li></ul><hr><h2 id="2025年计划"><a href="#2025年计划" class="headerlink" title="2025年计划"></a><strong>2025年计划</strong></h2><h3 id="核心目标"><a href="#核心目标" class="headerlink" title="核心目标"></a><strong>核心目标</strong></h3><ol><li><strong>构建被动收入</strong>:增强抗风险能力,提高财务自由度。</li><li><strong>沉淀知识资产</strong>:将日常工作和技术积累系统化,形成知识库。通过博客总结经验,实现价值最大化。</li></ol><h3 id="具体行动计划"><a href="#具体行动计划" class="headerlink" title="具体行动计划"></a><strong>具体行动计划</strong></h3><ol><li><strong>财务与生活</strong><ul><li>制定系统的财务规划,优化理财方式,减少不必要的开支。</li><li>养成节俭的生活习惯,聚焦必要消费,远离浪费。</li><li>增强抗风险能力,通过投资被动收入来源确保长远的生活稳定。</li></ul></li><li><strong>工作优化</strong><ul><li>减少琐碎事务,将精力集中于关键节点、核心项目和技术决策。</li><li>定期知识分享,参与行业调研,提升个人影响力。</li><li>避免过度情绪投入,聚焦目标与效率,保持专业性。</li></ul></li><li><strong>个人提升</strong><ul><li><strong>健身</strong>:继续保持每周3次以上的锻炼频率,探索更多健身方式,增强体能与健康。</li><li><strong>知识管理</strong>:完善知识资产,记录、总结并分享工作中的经验与教训。</li><li><strong>情绪管理</strong>:控制情绪波动,避免因外界影响而分散注意力,保持内心平衡。</li></ul></li></ol><h3 id="长远愿景"><a href="#长远愿景" class="headerlink" title="长远愿景"></a><strong>长远愿景</strong></h3><p>2025年,我将以降低系统性风险为核心,确保家庭、工作和个人发展方向的平衡:</p><ul><li><strong>工作上</strong>:以提高能力为目标,确保技术深度与管理能力同步发展。</li><li><strong>生活上</strong>:通过有效规划和行动,为家庭创造更稳定的环境。</li><li><strong>个人成长</strong>:专注自我完善,实现从内而外的全面提升。</li></ul>]]></content>
<summary type="html"><p>2024总结与2025规划</p></summary>
<category term="年度总结" scheme="https://ustcqidi.github.io/tags/%E5%B9%B4%E5%BA%A6%E6%80%BB%E7%BB%93/"/>
</entry>
<entry>
<title>2023 年度总结</title>
<link href="https://ustcqidi.github.io/2024/01/01/summary-2023/"/>
<id>https://ustcqidi.github.io/2024/01/01/summary-2023/</id>
<published>2024-01-01T09:27:25.000Z</published>
<updated>2025-01-26T14:47:14.982Z</updated>
<content type="html"><![CDATA[<p>2023总结与2024规划</p><span id="more"></span><p>2023年相较于2022年,我的生活经历了明显的进步和积极的变化。</p><p>在2022年,我处于低迷状态,生活杂乱无章,既没有专心读书也没有参与运动,时间仿佛溜走在无所作为中。工作只是机械的完成任务,缺乏激情。</p><p>然而,2023年是不同的……</p><h2 id="回顾-2023"><a href="#回顾-2023" class="headerlink" title="回顾 2023"></a>回顾 2023</h2><ol><li>工作方面我找到了明确的方向。我开始正式领导一个团队,我们一起完成了一系列有意义的项目,取得了一些显著的成就。团队的氛围很好,还给两位出色的同事升职。整体而言,我对团队的现状感到非常满意。</li><li>在技术方面,我也找到了自己的兴趣点,开始关注管理相关的知识,以及更宏观层面的技术规划和人才培养等,而不再局限于具体的技术细节。</li><li>我的阅读和运动量相较去年有了明显提升。我阅读了20多本书,尽管中间有一段时间没有跑步,但参加了马拉松比赛后重新点燃了我对运动的兴趣,最近一个月我按计划进行了良好的跑步训练。</li><li>2023年,我还参加了软考系统架构师的考试,尽管由于工作繁忙没有充分复习,考试成绩并不理想。但这次经历让我更深刻地认识到了自己的兴趣和发展方向。</li><li>2023年最大的收获之一,就是开始利用 ChatGPT 等大型模型工具来提高我的工作和学习效率。我发现借助这些工具,能够更快速、高效地解决常见问题,明显提升了我的个人效率。</li><li>当然,生活中也遇到了一些波折,家庭关系变得复杂。在家庭关系方面,让父母担心,让孩子感到不安。但是我在年底明确了未来的走向,决心慢慢来改善。</li><li>结清了保利的房贷,提前还了蜀山别院 60W 的房贷。再加上国家统一调整房贷利率,由原来的 5.5% 降到 4.2%,现在每个月还款从 2022 年的 14000+ 降到 6600 左右。</li><li>学会利用四象限管理我的工作及生活事务:紧急且重要的事情亲自做,重要不紧急的事情坚持做,紧急不重要的事情找人做</li><li>提交 & 通过了一项专利</li><li>真正的开始注意自己的形象,学会整理发型,关注穿搭之类的</li></ol><h2 id="展望-2024"><a href="#展望-2024" class="headerlink" title="展望 2024"></a>展望 2024</h2><ol><li>我希望通过<strong>网络规划设计师考试</strong>来追求更符合我的兴趣的方向。我还计划<strong>认真撰写1-2篇专利</strong>,并在团队管理方面取得更大的进步,<strong>帮助新成员成长,维持团队的良好氛围</strong>。</li><li>在个人层面,我将致力于<strong>总结和深入挖掘之前处理过的 tickets 或问题</strong>,进一步提升自己在经验和知识上的水平。</li><li>我将努力<strong>维持家庭关系和家庭氛围的稳定</strong>。我要确保孩子和家人感到平稳和安全,不再有不必要的争吵,而是寻找自己内心的平静。</li><li>我设定了<strong>每月跑步10小时的健康目标</strong>。跑步有助于改善我的身体状态,也对健康有益。此外,我计划进行一次体检,因为已经有2-3年没有检查了。</li><li>在阅读方面,我的目标是<strong>每个月投入10小时来阅读</strong>。我不会设定太多限制,而是看当时的兴趣来选择阅读的内容。</li></ol><p>2023年是更深入了解自己的一年,而2024年将是在新的方向上持续努力和奋斗的一年。</p>]]></content>
<summary type="html"><p>2023总结与2024规划</p></summary>
<category term="年度总结" scheme="https://ustcqidi.github.io/tags/%E5%B9%B4%E5%BA%A6%E6%80%BB%E7%BB%93/"/>
</entry>
<entry>
<title>当程序员遇上ChatGPT:如何提高编码效率</title>
<link href="https://ustcqidi.github.io/2023/04/02/chatgpt/"/>
<id>https://ustcqidi.github.io/2023/04/02/chatgpt/</id>
<published>2023-04-02T01:14:51.000Z</published>
<updated>2025-02-08T01:15:42.875Z</updated>
<content type="html"><![CDATA[<p>使用 ChatGPT 提高工作效率的场景与心得</p><p>自从 3 月份开始使用 ChatGPT,我的工作效率有了大幅提升。本文记录我使用 ChatGPT 的一些场景和心得。</p><span id="more"></span><h3 id="文案生成和润色"><a href="#文案生成和润色" class="headerlink" title="文案生成和润色"></a>文案生成和润色</h3><p>我使用 ChatGPT 润色了部门季度总结会议的 PPT 内容。ChatGPT 提供了不少的修饰建议,使得最终文案更为流畅、易懂。</p><h3 id="代码-Review"><a href="#代码-Review" class="headerlink" title="代码 Review"></a>代码 Review</h3><p>公司禁止直接使用 ChatGPT 生成的代码,但可以使用 ChatGPT 辅助 Review,移除敏感的代码片段并保留部分需要检查的代码段。使用 ChatGPT Review 代码的效果非常好。</p><p>一个具体的例子:</p><p>这是同事提交的原始代码:</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">void</span> <span class="title">foo::bar</span><span class="params">(<span class="type">const</span> Json::Value& jsonData)</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line">eventMap.<span class="built_in">clear</span>();</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> (jsonData.<span class="built_in">isMemberWithValidFormat</span>(<span class="string">"event"</span>)) {</span><br><span class="line">Json::Value event = jsonData[<span class="string">"event"</span>];</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span>(event.<span class="built_in">isMemberWithValidFormat</span>(<span class="string">"licenseTypeOption"</span>)){</span><br><span class="line">Json::Value licenseTypeOption = event[<span class="string">"licenseTypeOption"</span>];</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> (licenseTypeOption.<span class="built_in">isArray</span>()) {</span><br><span class="line">Json::Value::iterator it = licenseTypeOption.<span class="built_in">begin</span>();</span><br><span class="line"><span class="keyword">for</span> (; it != licenseTypeOption.<span class="built_in">end</span>(); ++it) {</span><br><span class="line">Json::Value& item = *it;</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> (item.<span class="built_in">isMemberWithValidFormat</span>(<span class="string">"licenseType"</span>)) {</span><br><span class="line">Json::Value& licenseType = item[<span class="string">"licenseType"</span>];</span><br><span class="line">Json::Value& licenseOption = item[<span class="string">"licenseOption"</span>];</span><br><span class="line"></span><br><span class="line">CAString licenseTypeStr = licenseType.<span class="built_in">isString</span>() ? licenseType.<span class="built_in">asString</span>() : <span class="string">""</span>;</span><br><span class="line">CAString licenseOptionStr = licenseOption.<span class="built_in">isString</span>() ? licenseOption.<span class="built_in">asString</span>() : <span class="string">""</span>;</span><br><span class="line"><span class="keyword">if</span> (!licenseTypeStr.<span class="built_in">IsEmpty</span>() && !licenseOptionStr.<span class="built_in">IsEmpty</span>()) {</span><br><span class="line">eventMap[licenseTypeStr] = licenseOptionStr;</span><br><span class="line">}</span><br><span class="line">}</span><br><span class="line">}</span><br><span class="line">}</span><br><span class="line">}</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// ...</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>这是 ChatGPT 优化过的代码:</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">void</span> <span class="title">foo::bar</span><span class="params">(<span class="type">const</span> Json::Value& jsonData)</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"> eventMap.<span class="built_in">clear</span>();</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (!jsonData.<span class="built_in">isMemberWithValidFormat</span>(<span class="string">"event"</span>)) {</span><br><span class="line"> <span class="keyword">return</span>;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="type">const</span> Json::Value& event = jsonData[<span class="string">"event"</span>];</span><br><span class="line"> <span class="keyword">if</span> (!event.<span class="built_in">isMemberWithValidFormat</span>(<span class="string">"licenseTypeOption"</span>)) {</span><br><span class="line"> <span class="keyword">return</span>;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="type">const</span> Json::Value& licenseTypeOption = event[<span class="string">"licenseTypeOption"</span>];</span><br><span class="line"> <span class="keyword">if</span> (!licenseTypeOption.<span class="built_in">isArray</span>()) {</span><br><span class="line"> <span class="keyword">return</span>;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">for</span> (<span class="type">const</span> <span class="keyword">auto</span>& item : licenseTypeOption) {</span><br><span class="line"> <span class="keyword">if</span> (!item.<span class="built_in">isMemberWithValidFormat</span>(<span class="string">"licenseType"</span>)) {</span><br><span class="line"> <span class="keyword">continue</span>;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="type">const</span> Json::Value& licenseType = item[<span class="string">"licenseType"</span>];</span><br><span class="line"> <span class="type">const</span> Json::Value& licenseOption = item[<span class="string">"licenseOption"</span>];</span><br><span class="line"> <span class="keyword">if</span> (!licenseType.<span class="built_in">isString</span>() || !licenseOption.<span class="built_in">isString</span>()) {</span><br><span class="line"> <span class="keyword">continue</span>;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> eventMap[licenseType.<span class="built_in">asString</span>()] = licenseOption.<span class="built_in">asString</span>();</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>ChatGPT 还给出了相关的说明</p><p>if 语句嵌套太多,可以考虑进行重构,以提高代码的可读性和可维护性。一种常见的方法是使用早返回(early return)的方式,即在满足某些条件时直接返回结果,而不是嵌套多个if语句。以下是一个示例:</p><p>原始代码:</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">if</span> (a) {</span><br><span class="line"> <span class="keyword">if</span> (b) {</span><br><span class="line"> <span class="keyword">if</span> (c) {</span><br><span class="line"> <span class="comment">// do something</span></span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> <span class="comment">// do something else</span></span><br><span class="line"> }</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> <span class="comment">// do something else</span></span><br><span class="line"> }</span><br><span class="line">} <span class="keyword">else</span> {</span><br><span class="line"> <span class="comment">// do something else</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>重构后的代码:</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">if</span> (!a) {</span><br><span class="line"> <span class="comment">// do something else</span></span><br><span class="line"> <span class="keyword">return</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> (!b) {</span><br><span class="line"> <span class="comment">// do something else</span></span><br><span class="line"> <span class="keyword">return</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> (!c) {</span><br><span class="line"> <span class="comment">// do something else</span></span><br><span class="line"> <span class="keyword">return</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// do something</span></span><br></pre></td></tr></table></figure><p>如上所示,通过早返回的方式,可以将原始代码中的多个嵌套if语句转换为多个单独的if语句,使得代码更加清晰和易于阅读。同时,还可以避免出现嵌套层数过深的情况,提高代码的可维护性。</p><h3 id="定义类和接口"><a href="#定义类和接口" class="headerlink" title="定义类和接口"></a>定义类和接口</h3><p>ChatGPT 可以根据需求和变量命名规范要求,生成很不错的接口或类定义。</p><h3 id="生成单元测试用例"><a href="#生成单元测试用例" class="headerlink" title="生成单元测试用例"></a>生成单元测试用例</h3><p>直接提供函数或接口给 ChatGPT,可以生成详细的测试用例和单元测试代码,提高了测试覆盖率。</p><h3 id="搜索引擎"><a href="#搜索引擎" class="headerlink" title="搜索引擎"></a>搜索引擎</h3><p>我已经使用 ChatGPT 替代 Google 和 StackOverflow。ChatGPT 可以根据关键词快速给出相关信息,缩短了搜索的时间。</p><h3 id="翻译"><a href="#翻译" class="headerlink" title="翻译"></a>翻译</h3><p>ChatGPT 在翻译方面也很强大。我用它来翻译了一些文档和文章,翻译质量非常好。</p><h3 id="学习助手"><a href="#学习助手" class="headerlink" title="学习助手"></a>学习助手</h3><p>ChatGPT 可以帮助我总结所学知识,并提供更详细的说明,提高了学习效率。</p><p>总的来说, ChatGPT 为我提供了很多帮助,提高了我的工作效率和学习效率。虽然 ChatGPT 目前还有一些局限性,但随着技术的发展,相信 ChatGPT 将会变得越来越强大,为程序员提供更多的帮助。</p>]]></content>
<summary type="html"><p>使用 ChatGPT 提高工作效率的场景与心得</p>
<p>自从 3 月份开始使用 ChatGPT,我的工作效率有了大幅提升。本文记录我使用 ChatGPT 的一些场景和心得。</p></summary>
<category term="ChatGPT" scheme="https://ustcqidi.github.io/tags/ChatGPT/"/>
</entry>
<entry>
<title>2022 年度总结</title>
<link href="https://ustcqidi.github.io/2023/01/02/summary-2022/"/>
<id>https://ustcqidi.github.io/2023/01/02/summary-2022/</id>
<published>2023-01-02T09:35:56.000Z</published>
<updated>2025-01-05T09:37:41.705Z</updated>
<content type="html"><![CDATA[<p>2022 像是被偷走的一年,整年浑浑噩噩,不知道时间用在了哪里。焦虑、憋屈、愤怒、小心翼翼,各种各样的情绪夹杂在一起。</p><span id="more"></span><p>2022 没有跑步、没有读书、没有娱乐、没有学习、没有旅行、没有去过电影院,工作上也没有啥亮眼的成绩。</p><p>2022 是疫情防控最严格的一年,也是对普通人生活影响最大的一年。孩子从幼儿园升到小学,每天的健康打卡雷打不动。手机相册中,绝大多数都行程码、安康码、核酸截图。</p><p>2022 也是疫情结束的一年。12 月份,疫情防疫政策全面放开,1-2周内全国人民都在躺在家里吃布洛芬。</p><p> 2022 也有一些值得开心的事情:</p><ol><li>家人、朋友都健康平安;</li><li>拔了智齿,以后再也不用担心牙齿疼了;</li><li>搬进新家,住宿条件极大改善;</li><li>升为 Manager,职业生涯有了新的挑战。</li></ol><p>2023 我不再制定虚无缥缈的愿景和目标,所有计划都量化成具体的数字。希望今年能珍惜时间,创造更多精彩的瞬间。</p>]]></content>
<summary type="html"><p>2022 像是被偷走的一年,整年浑浑噩噩,不知道时间用在了哪里。焦虑、憋屈、愤怒、小心翼翼,各种各样的情绪夹杂在一起。</p></summary>
<category term="年度总结" scheme="https://ustcqidi.github.io/tags/%E5%B9%B4%E5%BA%A6%E6%80%BB%E7%BB%93/"/>
</entry>
<entry>
<title>2021 年度总结</title>
<link href="https://ustcqidi.github.io/2021/12/27/summary-2021/"/>
<id>https://ustcqidi.github.io/2021/12/27/summary-2021/</id>
<published>2021-12-27T02:34:28.000Z</published>
<updated>2025-01-05T09:28:57.733Z</updated>
<content type="html"><![CDATA[<p>先看看去年立的 flag 的完成情况</p><span id="more"></span><h2 id="2021-年-flag"><a href="#2021-年-flag" class="headerlink" title="2021 年 flag"></a><strong>2021 年 flag</strong></h2><h3 id="理财-(基金定投)"><a href="#理财-(基金定投)" class="headerlink" title="理财 (基金定投)"></a><strong>理财 (基金定投)</strong></h3><ul><li>计划投入 50W</li><li>预期收益率 20%</li></ul><aside><br>💡 负收益,只能说是一塌糊涂 !保持定投就可以了,不要瞎操作 !!!<br><br></aside><h3 id="装修"><a href="#装修" class="headerlink" title="装修"></a><strong>装修</strong></h3><aside><br>💡 如期按照计划正常进行<br><br></aside><h3 id="跑步,控制体重在-60kg-以内"><a href="#跑步,控制体重在-60kg-以内" class="headerlink" title="跑步,控制体重在 60kg 以内"></a><strong>跑步,控制体重在 60kg 以内</strong></h3><aside><br>💡 跑步次数并不是很多,需要更加自律<br><br></aside><h3 id="减少体检异常指标"><a href="#减少体检异常指标" class="headerlink" title="减少体检异常指标"></a><strong>减少体检异常指标</strong></h3><aside><br>💡 喝酒太多<br>焦虑情绪太多<br>饮食结构 需要优化<br><br></aside><h3 id="陪儿子一起学习钢琴课程"><a href="#陪儿子一起学习钢琴课程" class="headerlink" title="陪儿子一起学习钢琴课程"></a><strong><del>陪儿子一起学习钢琴课程</del></strong></h3><aside><br>💡 儿子实在不喜欢钢琴,计划已经放弃<br><br></aside><h3 id="学完英语流利说:商务英语"><a href="#学完英语流利说:商务英语" class="headerlink" title="学完英语流利说:商务英语"></a><strong><del>学完英语流利说:商务英语</del></strong></h3><aside><br>💡 改成看 Youtube 技术 Channel<br><br></aside><h3 id="学完-CS155"><a href="#学完-CS155" class="headerlink" title="学完 CS155"></a><strong><del>学完 CS155</del></strong></h3><aside><br>💡 Project / Problem Based Learning, 没有完整地跟着课程学习<br><br></aside><h3 id="使用日程管理-App,做好任务清单、计划管理"><a href="#使用日程管理-App,做好任务清单、计划管理" class="headerlink" title="使用日程管理 App,做好任务清单、计划管理"></a><strong>使用日程管理 App,做好任务清单、计划管理</strong></h3><aside><br>💡 用 Notion 做 Worksapce,今年最大的收获<br><br></aside><h3 id="每个月看完两本书"><a href="#每个月看完两本书" class="headerlink" title="每个月看完两本书"></a><strong>每个月看完两本书</strong></h3><ul><li>用思维导图整理读书心得</li></ul><aside><br>💡 用微信读书读了 15 本,整体还是比较满意,开始把微信读书笔记整合同步到 Notion,学习卡片笔记等方法论,专业书籍读的偏少。<br><br></aside><h3 id="每周-每月总结复盘"><a href="#每周-每月总结复盘" class="headerlink" title="每周/每月总结复盘"></a><strong>每周/每月总结复盘</strong></h3><ul><li>计划完成情况</li><li>思考总结</li><li>输出 PPT</li></ul><aside><br>💡 Weekly Review 做的还比较满意<br><br></aside><hr><h2 id="2021-年回顾盘点"><a href="#2021-年回顾盘点" class="headerlink" title="2021 年回顾盘点"></a><strong>2021 年回顾盘点</strong></h2><h3 id="读了-15-本书"><a href="#读了-15-本书" class="headerlink" title="读了 15 本书"></a>读了 15 本书</h3><h3 id="跑步-200-多公里"><a href="#跑步-200-多公里" class="headerlink" title="跑步 200 多公里"></a>跑步 200 多公里</h3><h3 id="每周陪儿子打羽毛球,下象棋,教他用-Scratch-编程"><a href="#每周陪儿子打羽毛球,下象棋,教他用-Scratch-编程" class="headerlink" title="每周陪儿子打羽毛球,下象棋,教他用 Scratch 编程"></a>每周陪儿子打羽毛球,下象棋,教他用 Scratch 编程</h3><h3 id="用-Notion-做日程管理、文档、计划,整合-amp-优化了-Workflow"><a href="#用-Notion-做日程管理、文档、计划,整合-amp-优化了-Workflow" class="headerlink" title="用 Notion 做日程管理、文档、计划,整合 & 优化了 Workflow"></a>用 Notion 做日程管理、文档、计划,整合 & 优化了 Workflow</h3><h3 id="装修-1"><a href="#装修-1" class="headerlink" title="装修"></a>装修</h3><h3 id="924-剁手"><a href="#924-剁手" class="headerlink" title="924 剁手"></a>924 剁手</h3><p>太轻易相信别人,血的教训</p><h3 id="身体健康"><a href="#身体健康" class="headerlink" title="身体健康"></a><strong>身体健康</strong></h3><ul><li>体检情况比 2020 年严重,各种亚健康问题凸显</li></ul><h3 id="家庭关系"><a href="#家庭关系" class="headerlink" title="家庭关系"></a>家庭关系</h3><ul><li>跟父母关系处理的比较糟糕,沟通太少</li><li>更多的亲子陪伴时间</li><li>跟老婆的沟通太少</li></ul><h3 id="Side-Project"><a href="#Side-Project" class="headerlink" title="Side Project"></a>Side Project</h3><ul><li>完整上线了 Google Slide 插件</li></ul><hr><h2 id="2022-年度目标"><a href="#2022-年度目标" class="headerlink" title="2022 年度目标"></a>2022 年度目标</h2><h3 id="保持健康"><a href="#保持健康" class="headerlink" title="保持健康"></a>保持健康</h3><ul><li>不酗酒、少熬夜</li><li>午休</li><li>保持积极、愉悦的心情</li><li>少吃腌制食品,减少外出就餐</li><li>保持运动</li></ul><h3 id="家庭关系-1"><a href="#家庭关系-1" class="headerlink" title="家庭关系"></a>家庭关系</h3><ul><li>多跟家人聊天、沟通</li><li>保持和谐的家庭氛围,减少抱怨,不要轻易发脾气</li><li>亲子陪伴:羽毛球、象棋、Scratch 编程</li></ul><h3 id="个人成长"><a href="#个人成长" class="headerlink" title="个人成长"></a>个人成长</h3><ul><li>学习提高认知以及专业能力<ul><li>Blog</li><li>YouTube Channel</li></ul></li><li>戒骄戒躁,每天晚上读读书</li><li>戒掉不良习惯,争取摆脱最近几年的 “魔咒”</li><li>高质量完成工作</li><li>控制情绪,减少内耗</li></ul><h3 id="财务"><a href="#财务" class="headerlink" title="财务"></a>财务</h3><ul><li>保持定投,不要瞎操作。尽量不亏钱。</li></ul>]]></content>
<summary type="html"><p>先看看去年立的 flag 的完成情况</p></summary>
<category term="年度总结" scheme="https://ustcqidi.github.io/tags/%E5%B9%B4%E5%BA%A6%E6%80%BB%E7%BB%93/"/>
</entry>
</feed>