From df63692a1835784ff3ae0e9fe6ebe99081ea8ac3 Mon Sep 17 00:00:00 2001 From: wonder Date: Wed, 2 Sep 2026 21:33:09 +0800 Subject: [PATCH] feat: add 180 questions (90 sc + 90 fb) for networking subtopics Cover 9 subtopics from the computer networking documentation: - http-handshake: TCP/TLS/HTTP2/HTTP3 handshakes - http-connection-cost: connection resource overhead & million concurrency - connection-pooling: pool reuse, HTTP/1.1 vs HTTP/2 - http-keepalive: Keep-Alive principle, head-of-line blocking - keepalive-scenarios: when to enable/disable Keep-Alive - domain-sharding: HTTP/1.1 hack, HTTP/2 obsolescence - cdn: edge nodes, caching, DDoS protection - cors: same-origin policy, preflight requests - go-build-strip: -s -w flags, binary size optimization --- topics/index.json | 126 +++++++++- topics/networking/cdn/fill_blank.json | 201 ++++++++++++++++ topics/networking/cdn/meta.json | 46 ++++ topics/networking/cdn/single_choice.json | 208 +++++++++++++++++ .../connection-pooling/fill_blank.json | 198 ++++++++++++++++ .../networking/connection-pooling/meta.json | 48 ++++ .../connection-pooling/single_choice.json | 209 +++++++++++++++++ topics/networking/cors/fill_blank.json | 197 ++++++++++++++++ topics/networking/cors/meta.json | 43 ++++ topics/networking/cors/single_choice.json | 209 +++++++++++++++++ .../domain-sharding/fill_blank.json | 197 ++++++++++++++++ topics/networking/domain-sharding/meta.json | 44 ++++ .../domain-sharding/single_choice.json | 209 +++++++++++++++++ .../networking/go-build-strip/fill_blank.json | 200 ++++++++++++++++ topics/networking/go-build-strip/meta.json | 42 ++++ .../go-build-strip/single_choice.json | 218 ++++++++++++++++++ .../http-connection-cost/fill_blank.json | 164 +++++++++++++ .../networking/http-connection-cost/meta.json | 38 +++ .../http-connection-cost/single_choice.json | 213 +++++++++++++++++ .../networking/http-handshake/fill_blank.json | 167 ++++++++++++++ topics/networking/http-handshake/meta.json | 34 +++ .../http-handshake/single_choice.json | 211 +++++++++++++++++ .../networking/http-keepalive/fill_blank.json | 201 ++++++++++++++++ topics/networking/http-keepalive/meta.json | 50 ++++ .../http-keepalive/single_choice.json | 210 +++++++++++++++++ .../keepalive-scenarios/fill_blank.json | 202 ++++++++++++++++ .../networking/keepalive-scenarios/meta.json | 50 ++++ .../keepalive-scenarios/single_choice.json | 209 +++++++++++++++++ 28 files changed, 4143 insertions(+), 1 deletion(-) create mode 100644 topics/networking/cdn/fill_blank.json create mode 100644 topics/networking/cdn/meta.json create mode 100644 topics/networking/cdn/single_choice.json create mode 100644 topics/networking/connection-pooling/fill_blank.json create mode 100644 topics/networking/connection-pooling/meta.json create mode 100644 topics/networking/connection-pooling/single_choice.json create mode 100644 topics/networking/cors/fill_blank.json create mode 100644 topics/networking/cors/meta.json create mode 100644 topics/networking/cors/single_choice.json create mode 100644 topics/networking/domain-sharding/fill_blank.json create mode 100644 topics/networking/domain-sharding/meta.json create mode 100644 topics/networking/domain-sharding/single_choice.json create mode 100644 topics/networking/go-build-strip/fill_blank.json create mode 100644 topics/networking/go-build-strip/meta.json create mode 100644 topics/networking/go-build-strip/single_choice.json create mode 100644 topics/networking/http-connection-cost/fill_blank.json create mode 100644 topics/networking/http-connection-cost/meta.json create mode 100644 topics/networking/http-connection-cost/single_choice.json create mode 100644 topics/networking/http-handshake/fill_blank.json create mode 100644 topics/networking/http-handshake/meta.json create mode 100644 topics/networking/http-handshake/single_choice.json create mode 100644 topics/networking/http-keepalive/fill_blank.json create mode 100644 topics/networking/http-keepalive/meta.json create mode 100644 topics/networking/http-keepalive/single_choice.json create mode 100644 topics/networking/keepalive-scenarios/fill_blank.json create mode 100644 topics/networking/keepalive-scenarios/meta.json create mode 100644 topics/networking/keepalive-scenarios/single_choice.json diff --git a/topics/index.json b/topics/index.json index 3bcbea6..054fc5c 100644 --- a/topics/index.json +++ b/topics/index.json @@ -73,6 +73,130 @@ } } ] + }, + { + "slug": "networking", + "name": "计算机网络", + "description": "从 HTTP 协议到底层连接管理,覆盖面试和实战中的核心网络知识", + "subtopics": [ + { + "slug": "http-handshake", + "name": "HTTP 握手", + "description": "TCP 三次握手、TLS 握手、HTTP/2 与 HTTP/3 的连接建立", + "path": "topics/networking/http-handshake", + "stats": { + "total": 20, + "by_type": { + "single_choice": 10, + "fill_blank": 10 + } + } + }, + { + "slug": "http-connection-cost", + "name": "HTTP 连接资源消耗", + "description": "单连接资源开销、百万并发的真实数字与应对策略", + "path": "topics/networking/http-connection-cost", + "stats": { + "total": 20, + "by_type": { + "single_choice": 10, + "fill_blank": 10 + } + } + }, + { + "slug": "connection-pooling", + "name": "连接池复用", + "description": "预建连接、借还机制、HTTP/1.1 与 HTTP/2 的复用差异", + "path": "topics/networking/connection-pooling", + "stats": { + "total": 20, + "by_type": { + "single_choice": 10, + "fill_blank": 10 + } + } + }, + { + "slug": "http-keepalive", + "name": "HTTP Keep-Alive", + "description": "Keep-Alive 原理、队头阻塞、演进路线", + "path": "topics/networking/http-keepalive", + "stats": { + "total": 20, + "by_type": { + "single_choice": 10, + "fill_blank": 10 + } + } + }, + { + "slug": "keepalive-scenarios", + "name": "Keep-Alive 使用场景", + "description": "何时启用、何时关闭、生产环境最佳实践", + "path": "topics/networking/keepalive-scenarios", + "stats": { + "total": 20, + "by_type": { + "single_choice": 10, + "fill_blank": 10 + } + } + }, + { + "slug": "domain-sharding", + "name": "域名分片", + "description": "HTTP/1.1 时代的 hack,HTTP/2 后的淘汰", + "path": "topics/networking/domain-sharding", + "stats": { + "total": 20, + "by_type": { + "single_choice": 10, + "fill_blank": 10 + } + } + }, + { + "slug": "cdn", + "name": "CDN", + "description": "边缘节点、缓存策略、加速原理与配置", + "path": "topics/networking/cdn", + "stats": { + "total": 20, + "by_type": { + "single_choice": 10, + "fill_blank": 10 + } + } + }, + { + "slug": "cors", + "name": "跨域 CORS", + "description": "同源三元组、CORS 响应头、预检请求", + "path": "topics/networking/cors", + "stats": { + "total": 20, + "by_type": { + "single_choice": 10, + "fill_blank": 10 + } + } + }, + { + "slug": "go-build-strip", + "name": "Go 编译标志 -s", + "description": "剥离调试信息、体积优化、生产发布", + "path": "topics/networking/go-build-strip", + "stats": { + "total": 20, + "by_type": { + "single_choice": 10, + "fill_blank": 10 + } + } + } + ] } ] -} +} \ No newline at end of file diff --git a/topics/networking/cdn/fill_blank.json b/topics/networking/cdn/fill_blank.json new file mode 100644 index 0000000..0f3ae25 --- /dev/null +++ b/topics/networking/cdn/fill_blank.json @@ -0,0 +1,201 @@ +{ + "topic": "cdn", + "type": "fill_blank", + "schema_version": "1.0.0", + "generated": "2026-09-02T21:20:00+08:00", + "questions": [ + { + "id": "fb-001", + "type": "fill_blank", + "question": "CDN 的 ____ 是部署在全球各地的缓存服务器,用户访问时会就近获取资源,大幅降低网络延迟。", + "answer": [ + "边缘节点" + ], + "answer_rule": "any", + "difficulty": 1, + "tags": [ + "CDN", + "边缘节点", + "就近访问" + ], + "explanation": "边缘节点(Edge Node)是 CDN 网络中最靠近用户的服务器节点,分布在全球各大城市的数据中心。当用户请求资源时,CDN 会将其引导至地理位置或网络距离最近的边缘节点,从该节点的缓存中直接返回资源,避免了跨地域长距离传输带来的延迟。", + "source": null, + "related": [] + }, + { + "id": "fb-002", + "type": "fill_blank", + "question": "当边缘节点的缓存中没有用户请求的资源时,会向 ____ 拉取源文件,这个过程称为回源。", + "answer": [ + "源站", + "源服务器", + "源站服务器" + ], + "answer_rule": "any", + "difficulty": 1, + "tags": [ + "CDN", + "回源", + "源站" + ], + "explanation": "回源(Origin Pull)是 CDN 的核心机制之一。当边缘节点缓存未命中(Cache Miss)时,节点会代替用户向源站(Origin Server)发起请求获取原始资源,获取后缓存到本地,再返回给用户。后续相同资源的请求就可以直接从边缘节点缓存返回,不再需要回源。", + "source": null, + "related": [] + }, + { + "id": "fb-003", + "type": "fill_blank", + "question": "CDN 使用 ____ 技术,根据用户的来源 IP 地址和地理位置信息,将请求解析到距离最近的边缘节点。", + "answer": [ + "智能DNS", + "智能DNS解析", + "全局负载均衡" + ], + "answer_rule": "any", + "difficulty": 2, + "tags": [ + "CDN", + "智能DNS", + "地理位置解析" + ], + "explanation": "智能 DNS 是 CDN 的流量调度核心。它不同于传统 DNS 只返回固定的 IP 地址,而是根据请求来源 IP 的地理位置、网络运营商、节点负载等多种因素,动态返回最优边缘节点的 IP 地址。这样用户就被引导到最近、最快的节点,实现就近访问。", + "source": null, + "related": [] + }, + { + "id": "fb-004", + "type": "fill_blank", + "question": "CDN 最典型的应用场景是 ____ 加速,即将图片、CSS、JS、视频等不经常变化的文件缓存到边缘节点。", + "answer": [ + "静态资源" + ], + "answer_rule": "any", + "difficulty": 1, + "tags": [ + "CDN", + "静态资源", + "加速" + ], + "explanation": "静态资源加速是 CDN 最基础也是最广泛的应用。图片、样式表、JavaScript 文件、字体、视频等静态资源具有可缓存、不随请求变化的特点,天然适合 CDN 缓存。通过将这些资源分发到全球边缘节点,用户可以从最近的节点获取,显著减少加载时间,提升页面性能。", + "source": null, + "related": [] + }, + { + "id": "fb-005", + "type": "fill_blank", + "question": "CDN 可以提供 ____ 防护能力,将攻击流量分散到全球多个边缘节点,避免源站被压垮。", + "answer": [ + "DDoS" + ], + "answer_rule": "any", + "difficulty": 2, + "tags": [ + "CDN", + "DDoS防护", + "安全" + ], + "explanation": "CDN 天然具有 DDoS 防护能力。由于 CDN 拥有全球分布的大量边缘节点和巨大的带宽容量,当遭受 DDoS 攻击时,攻击流量会被分散到全球各地的边缘节点上,而不是全部涌向源站。每个边缘节点只需承受一小部分攻击流量,加上专业的流量清洗机制,可以有效缓解 DDoS 攻击对源站的冲击。", + "source": null, + "related": [] + }, + { + "id": "fb-006", + "type": "fill_blank", + "question": "CDN 的 HTTPS 终止是指边缘节点负责处理与用户之间的 ____ 握手,而边缘节点到源站之间可以使用 HTTP 协议通信。", + "answer": [ + "TLS", + "SSL" + ], + "answer_rule": "any", + "difficulty": 3, + "tags": [ + "CDN", + "HTTPS终止", + "TLS", + "安全" + ], + "explanation": "HTTPS 终止(TLS Termination)是 CDN 的重要功能。用户与边缘节点之间建立 HTTPS/TLS 加密连接,边缘节点负责完成 TLS 握手和加解密工作。而边缘节点与源站之间可以使用 HTTP 通信,这样既保证了用户端的安全性,又减少了源站处理 TLS 的计算开销,提升了整体性能。", + "source": null, + "related": [] + }, + { + "id": "fb-007", + "type": "fill_blank", + "question": "CDN 缓存策略中,Cache-Control 的 max-age 指令控制资源在浏览器/节点缓存中的 ____ 时间,immutable 标记表示资源永不变形。", + "answer": [ + "有效", + "存活", + "过期" + ], + "answer_rule": "any", + "difficulty": 3, + "tags": [ + "CDN", + "Cache-Control", + "缓存策略" + ], + "explanation": "max-age 指令指定了资源在缓存中的有效期(秒数),在有效期内浏览器或 CDN 节点不会重新向服务器请求该资源。immutable 标记告诉浏览器该资源一旦部署就不会修改(通常配合版本号文件名使用),即使用户手动刷新也不会重新请求,进一步减少不必要的网络请求,提升加载速度。", + "source": null, + "related": [] + }, + { + "id": "fb-008", + "type": "fill_blank", + "question": "CDN 的缓存通常采用分层架构:用户请求先到达 ____ 节点,未命中则到区域节点,最后才回源站。", + "answer": [ + "边缘" + ], + "answer_rule": "any", + "difficulty": 3, + "tags": [ + "CDN", + "缓存分层", + "边缘节点", + "区域节点" + ], + "explanation": "CDN 采用三级缓存分层架构:①边缘节点(Edge)——最靠近用户,缓存热门资源的副本;②区域节点(Regional/Mid-Tier)——覆盖一个地理区域,缓存该区域内多个边缘节点的共享资源;③源站(Origin)——最终的数据源。请求从边缘到区域到源站逐层回溯,只有上层缓存未命中才会访问下层,有效降低了源站压力。", + "source": null, + "related": [] + }, + { + "id": "fb-009", + "type": "fill_blank", + "question": "CDN 保证缓存一致性的常见手段包括:文件名加版本号、主动调用 ____ API 清除缓存、以及设置较短的 max-age。", + "answer": [ + "清除", + "刷新", + "purge" + ], + "answer_rule": "any", + "difficulty": 3, + "tags": [ + "CDN", + "缓存一致性", + "版本号", + "缓存清除" + ], + "explanation": "CDN 缓存一致性是常见挑战,主要通过三种方式保证:①文件名加版本号或内容哈希(如 style.v2.css),新版本 URL 是新资源,天然绕过旧缓存;②主动调用 CDN 提供的 purge/invalidation API,强制清除指定 URL 或目录的缓存;③设置较短的 max-age,让缓存快速过期重新验证。实际项目中通常组合使用这些策略。", + "source": null, + "related": [] + }, + { + "id": "fb-010", + "type": "fill_blank", + "question": "CDN 的缓存命中率通常很高,100 万用户访问时,源站可能只需处理 ____ 次回源请求。", + "answer": [ + "1万", + "10000" + ], + "answer_rule": "any", + "difficulty": 2, + "tags": [ + "CDN", + "缓存命中", + "回源比例" + ], + "explanation": "CDN 的核心价值就是通过缓存大幅减少源站压力。假设 CDN 缓存命中率为 99%,那么 100 万次用户请求中只有 1% 需要回源,即源站只需处理约 1 万次请求。这意味着源站的负载降低了 99%,极大地保护了源站的可用性和性能,同时也能节省源站的带宽和计算资源成本。", + "source": null, + "related": [] + } + ] +} \ No newline at end of file diff --git a/topics/networking/cdn/meta.json b/topics/networking/cdn/meta.json new file mode 100644 index 0000000..f74bcc2 --- /dev/null +++ b/topics/networking/cdn/meta.json @@ -0,0 +1,46 @@ +{ + "slug": "cdn", + "name": "Cdn", + "description": "", + "tags": [ + "CDN", + "Cache-Control", + "DDoS防护", + "HTTPS终止", + "TLS", + "加速", + "区域节点", + "回源", + "回源比例", + "地理位置解析", + "安全", + "就近访问", + "智能DNS", + "源站", + "版本号", + "缓存一致性", + "缓存分层", + "缓存命中", + "缓存清除", + "缓存策略", + "边缘节点", + "静态资源" + ], + "difficulty_range": [ + 1, + 3 + ], + "schema_version": "1.0.0", + "updated": "2026-09-02", + "question_files": [ + "single_choice", + "fill_blank" + ], + "stats": { + "total": 20, + "by_type": { + "single_choice": 10, + "fill_blank": 10 + } + } +} diff --git a/topics/networking/cdn/single_choice.json b/topics/networking/cdn/single_choice.json new file mode 100644 index 0000000..c98c979 --- /dev/null +++ b/topics/networking/cdn/single_choice.json @@ -0,0 +1,208 @@ +{ + "topic": "cdn", + "type": "single_choice", + "schema_version": "1.0.0", + "generated": "2026-09-02T21:20:00+08:00", + "questions": [ + { + "id": "sc-001", + "type": "single_choice", + "difficulty": 2, + "tags": [ + "CDN", + "边缘节点" + ], + "question": "CDN 的核心思想是什么?", + "options": { + "A": "所有用户直接访问源站", + "B": "把资源复制到全球一堆服务器上,用户就近获取", + "C": "使用加密传输", + "D": "增加源站带宽" + }, + "answer": "B", + "explanation": "CDN 的核心思想是把资源复制到全世界的边缘节点上,用户就近访问最近的边缘节点,减少延迟和源站压力。", + "source": null, + "related": [] + }, + { + "id": "sc-002", + "type": "single_choice", + "difficulty": 3, + "tags": [ + "CDN", + "智能DNS" + ], + "question": "CDN 的智能 DNS 根据什么决定返回哪个边缘节点的 IP?", + "options": { + "A": "用户浏览器版本", + "B": "请求来源 IP 的地理位置", + "C": "服务器负载", + "D": "随机选择" + }, + "answer": "B", + "explanation": "CDN 的 DNS 服务器根据请求来源 IP 的地理位置(通过 IP 地理位置数据库)来决定返回哪个边缘节点的 IP,这个过程对用户完全透明。", + "source": null, + "related": [] + }, + { + "id": "sc-003", + "type": "single_choice", + "difficulty": 3, + "tags": [ + "CDN", + "回源" + ], + "question": "CDN 的回源是什么意思?", + "options": { + "A": "用户直接访问源站", + "B": "边缘节点缓存未命中时,去源站拉取文件", + "C": "源站主动推送文件", + "D": "DNS 解析失败" + }, + "answer": "B", + "explanation": "回源是指边缘节点缓存未命中时,去源站拉取文件。第一次请求会慢一点(回源),之后就快了(缓存命中)。", + "source": null, + "related": [] + }, + { + "id": "sc-004", + "type": "single_choice", + "difficulty": 2, + "tags": [ + "CDN", + "适合内容" + ], + "question": "以下哪种内容最适合上 CDN?", + "options": { + "A": "用户个人数据", + "B": "实时交易数据", + "C": "图片、JS、CSS 等静态资源", + "D": "登录接口" + }, + "answer": "C", + "explanation": "CDN 适合缓存内容不怎么变、所有人看到的一样、可以缓存的静态资源。用户个人数据、实时交易数据、登录接口等需要实时性的内容不适合。", + "source": null, + "related": [] + }, + { + "id": "sc-005", + "type": "single_choice", + "difficulty": 4, + "tags": [ + "CDN", + "HTTPS终止" + ], + "question": "CDN 的 HTTPS 终止是什么意思?", + "options": { + "A": "禁止 HTTPS 访问", + "B": "边缘节点处理 TLS 握手,到源站可以用 HTTP", + "C": "源站不支持 HTTPS", + "D": "CDN 自动升级到 HTTP/3" + }, + "answer": "B", + "explanation": "HTTPS 终止是指边缘节点处理 TLS 握手,到源站可以用 HTTP(内网更快)。用户←HTTPS→CDN 边缘节点←HTTP→源站。", + "source": null, + "related": [] + }, + { + "id": "sc-006", + "type": "single_choice", + "difficulty": 3, + "tags": [ + "CDN", + "DDoS" + ], + "question": "CDN 如何防 DDoS?", + "options": { + "A": "直接丢弃流量", + "B": "攻击流量被分散到全球几百个边缘节点吸收", + "C": "使用防火墙", + "D": "关闭源站" + }, + "answer": "B", + "explanation": "CDN 防 DDoS 的原理是攻击流量被分散到全球几百个边缘节点吸收,源站几乎无感。这是 CDN 的核心能力之一。", + "source": null, + "related": [] + }, + { + "id": "sc-007", + "type": "single_choice", + "difficulty": 3, + "tags": [ + "CDN", + "缓存策略" + ], + "question": "Cache-Control: max-age=31536000, immutable 表示什么?", + "options": { + "A": "缓存 1 天", + "B": "缓存 1 年,且不需要向源站发起条件请求", + "C": "不缓存", + "D": "缓存 5 分钟" + }, + "answer": "B", + "explanation": "max-age=31536000 表示缓存 1 年,immutable 告诉浏览器这个资源永远不会改变,不需要向源站发起条件请求(If-Modified-Since/ETag 验证)。适用于文件名带版本号的静态资源。", + "source": null, + "related": [] + }, + { + "id": "sc-008", + "type": "single_choice", + "difficulty": 2, + "tags": [ + "CDN", + "缓存分层" + ], + "question": "CDN 的缓存分层顺序正确的是?", + "options": { + "A": "源站 → 区域节点 → 边缘节点 → 用户", + "B": "用户 → 边缘节点 → 区域节点 → 源站", + "C": "用户 → 源站 → 边缘节点", + "D": "边缘节点 → 用户 → 源站" + }, + "answer": "B", + "explanation": "CDN 缓存分层:用户 → 边缘节点(缓存1)→ 区域节点(缓存2)→ 源站(原始文件)。请求从用户开始,逐层向上查找缓存。", + "source": null, + "related": [] + }, + { + "id": "sc-009", + "type": "single_choice", + "difficulty": 4, + "tags": [ + "CDN", + "缓存一致性" + ], + "question": "更新了 logo.png 但 CDN 还缓存旧的,应该怎么解决?", + "options": { + "A": "等自然过期", + "B": "文件名带版本号、调用 CDN API 强制清除缓存、或设置较短的 max-age", + "C": "重启 CDN 服务器", + "D": "删除源站文件" + }, + "answer": "B", + "explanation": "缓存一致性问题的解决方案:(1)文件名带版本号 /logo.v2.png;(2)调用 CDN API 强制清除缓存;(3)设置较短的 max-age 等自然过期。", + "source": null, + "related": [] + }, + { + "id": "sc-010", + "type": "single_choice", + "difficulty": 3, + "tags": [ + "CDN", + "源站压力" + ], + "question": "100 万用户访问 CDN,源站大约处理多少次请求?", + "options": { + "A": "100 万次", + "B": "50 万次", + "C": "1 万次", + "D": "0 次" + }, + "answer": "C", + "explanation": "CDN 可以消化 99% 的流量,100 万用户 → 99 万次命中边缘节点 → 源站只处理约 1 万次回源。这是 CDN 减轻源站压力的核心能力。", + "source": null, + "related": [] + } + ] +} \ No newline at end of file diff --git a/topics/networking/connection-pooling/fill_blank.json b/topics/networking/connection-pooling/fill_blank.json new file mode 100644 index 0000000..29602fb --- /dev/null +++ b/topics/networking/connection-pooling/fill_blank.json @@ -0,0 +1,198 @@ +{ + "topic": "connection-pooling", + "type": "fill_blank", + "schema_version": "1.0.0", + "generated": "2026-09-02T21:20:00+08:00", + "questions": [ + { + "id": "fb-001", + "type": "fill_blank", + "difficulty": 2, + "tags": [ + "连接池", + "TCP", + "预建连接" + ], + "question": "连接池的核心思想是____,避免每次请求都经历 TCP 三次握手和 TLS 协商的开销。", + "answer": [ + "预建连接复用", + "预先建立连接并复用", + "提前创建连接然后复用" + ], + "answer_rule": "any", + "explanation": "连接池在启动时(或按需)预先创建一批 TCP 连接,客户端发起请求时直接从池中「借」一个已建好的连接,用完后「还」回池中。这样省去了每次新建连接的三次握手和可能的 TLS 握手开销,显著降低延迟。", + "source": null, + "related": [] + }, + { + "id": "fb-002", + "type": "fill_blank", + "difficulty": 3, + "tags": [ + "Keep-Alive", + "HTTP/1.1", + "队头阻塞" + ], + "question": "HTTP/1.1 的 Keep-Alive 虽然允许在一个 TCP 连接上发送多个请求,但请求之间是严格____的,存在____问题。", + "answer": [ + "串行,队头阻塞", + "串行 队头阻塞" + ], + "answer_rule": "any", + "explanation": "HTTP/1.1 的 Keep-Alive 复用连接时,客户端必须等上一个请求的响应回来后才能发下一个请求(串行),这导致「队头阻塞」(head-of-line blocking):一个慢请求会阻塞同一连接上的后续所有请求。这是 HTTP/1.1 连接复用的根本局限。", + "source": null, + "related": [] + }, + { + "id": "fb-003", + "type": "fill_blank", + "difficulty": 4, + "tags": [ + "HTTP/2", + "多路复用", + "帧" + ], + "question": "HTTP/2 通过____机制解决了队头阻塞问题,每个请求的报文被拆分为多个帧,不同请求的帧通过____编号来区分,到达后重新组装。", + "answer": [ + "多路复用,流", + "多路复用 流" + ], + "answer_rule": "any", + "explanation": "HTTP/2 的多路复用(multiplexing)允许在同一个 TCP 连接上同时交错发送多个请求/响应。每个请求对应一个「流」(stream),每个帧都携带流 ID,接收端根据流 ID 将帧归到正确的请求进行重组。这从根本上解决了 HTTP/1.1 的队头阻塞问题。", + "source": null, + "related": [] + }, + { + "id": "fb-004", + "type": "fill_blank", + "difficulty": 3, + "tags": [ + "连接池参数", + "最大连接数", + "空闲回收" + ], + "question": "连接池的四个关键参数分别是:____(池中允许的最大连接数)、____(保持的最少空闲连接数)、连接超时时间、空闲连接____时间。", + "answer": [ + "最大连接数,最小空闲连接数,存活", + "最大连接数 最小空闲连接数 存活" + ], + "answer_rule": "any", + "explanation": "最大连接数(maxTotal)限制池中总连接数上限;最小空闲连接数(minIdle)保证池中始终有预热好的连接可用;连接超时(connectionTimeout)控制获取连接的最大等待时间;空闲连接存活时间(idleTimeout/keepAliveTime)控制超时后回收空闲连接以释放资源。四者共同决定了连接池的吞吐能力、延迟表现和资源消耗。", + "source": null, + "related": [] + }, + { + "id": "fb-005", + "type": "fill_blank", + "difficulty": 3, + "tags": [ + "Python", + "requests", + "连接池" + ], + "question": "Python requests 库底层使用 urllib3 连接池,创建 Session 时可通过 pool_connections 参数设置____,pool_maxsize 参数设置____。", + "answer": [ + "连接池数量(到不同主机的连接池数),每个主机的最大连接数", + "到不同主机的连接池数量 每个主机的最大连接数" + ], + "answer_rule": "any", + "explanation": "pool_connections 控制连接池的个数(即支持多少个不同 host 的独立连接池),默认为 10;pool_maxsize 控制每个连接池的最大连接数,即对同一个 host 最多保持多少个并发连接,默认为 10。调大 pool_maxsize 可以提升对同一服务器的并发请求能力。", + "source": null, + "related": [] + }, + { + "id": "fb-006", + "type": "fill_blank", + "difficulty": 4, + "tags": [ + "Go", + "net/http", + "MaxIdleConns" + ], + "question": "Go 语言 net/http 默认使用连接池复用,通过 Transport 的 MaxIdleConns 参数控制____,MaxIdleConnsPerHost 参数控制____。", + "answer": [ + "所有主机的最大空闲连接总数,每个主机的最大空闲连接数", + "所有主机的最大空闲连接总数 每个主机的最大空闲连接数" + ], + "answer_rule": "any", + "explanation": "MaxIdleConns 是全局所有 host 共享的空闲连接总数上限,默认 100;MaxIdleConnsPerHost 是每个 host 单独的空闲连接上限,默认 2。当单个 host 并发量高时,往往需要调大 MaxIdleConnsPerHost 以避免频繁创建和销毁连接。", + "source": null, + "related": [] + }, + { + "id": "fb-007", + "type": "fill_blank", + "difficulty": 4, + "tags": [ + "Java", + "HttpClient", + "连接池参数" + ], + "question": "Java HttpClient(Apache)中,setMaxTotal() 设置____,setDefaultMaxPerRoute() 设置____。", + "answer": [ + "连接池最大总连接数,每个路由(host)的最大连接数", + "连接池最大总连接数 每个路由的最大连接数" + ], + "answer_rule": "any", + "explanation": "setMaxTotal(int) 控制整个连接池允许的最大连接数,默认 20;setDefaultMaxPerRoute(int) 控制单个目标主机(路由)允许的最大并发连接数,默认 2。实际生产中通常将两者分别调大到数百级别以支持高并发场景。注意 maxPerRoute 不能超过 maxTotal。", + "source": null, + "related": [] + }, + { + "id": "fb-008", + "type": "fill_blank", + "difficulty": 3, + "tags": [ + "僵尸连接", + "连接池" + ], + "question": "服务端单方面关闭了 TCP 连接但客户端连接池并不知道,池中仍持有这条已失效的连接,这种连接被称为____。客户端下次使用时会收到____错误。", + "answer": [ + "僵尸连接,连接被重置", + "僵尸连接 连接被重置" + ], + "answer_rule": "any", + "explanation": "僵尸连接(stale connection)是指服务端或中间网络设备已关闭了连接,但客户端连接池没有收到通知,仍然认为这条连接可用。当客户端尝试在这条连接上发请求时,会收到 connection reset 错误。成熟的连接池实现通常提供连接有效性检测(如 evict idle connections)来缓解此问题。", + "source": null, + "related": [] + }, + { + "id": "fb-009", + "type": "fill_blank", + "difficulty": 3, + "tags": [ + "连接池", + "延迟", + "性能" + ], + "question": "当连接池中的连接被全部借出且无空闲连接时,新请求只能____(等待空闲连接归还 / 立即创建新连接 / 抛出异常),导致请求____显著飙升。", + "answer": [ + "等待空闲连接归还,延迟", + "等待空闲连接归还 延迟" + ], + "answer_rule": "any", + "explanation": "连接池打满时,如果未达到最大连接数限制则可能创建新连接(取决于实现),如果已到上限则新请求排队等待。无论哪种情况,请求延迟都会显著飙升:等待队列变长,排队时间叠加在正常请求时间之上。监控连接池的活跃连接数和等待队列长度是发现此类问题的关键。", + "source": null, + "related": [] + }, + { + "id": "fb-010", + "type": "fill_blank", + "difficulty": 4, + "tags": [ + "连接池", + "容错", + "高可用" + ], + "question": "当后端某个节点出现故障时,连接池可以通过____机制将流量自动转移到健康节点,提供了一定的____能力。", + "answer": [ + "故障检测与连接驱逐,容错", + "故障检测 连接驱逐 容错" + ], + "answer_rule": "any", + "explanation": "成熟的连接池实现支持对连接进行健康检查和空闲驱逐(如 Apache HttpClient 的 evictor、HikariCP 的 connection test query)。当检测到某条连接不通时,将其从池中移除,后续请求会连接到其他健康节点。配合负载均衡使用,连接池为整个调用链路提供了基本的容错和高可用能力。", + "source": null, + "related": [] + } + ] +} \ No newline at end of file diff --git a/topics/networking/connection-pooling/meta.json b/topics/networking/connection-pooling/meta.json new file mode 100644 index 0000000..5a37be2 --- /dev/null +++ b/topics/networking/connection-pooling/meta.json @@ -0,0 +1,48 @@ +{ + "slug": "connection-pooling", + "name": "Connection Pooling", + "description": "", + "tags": [ + "Go", + "HTTP/1.1", + "HTTP/2", + "HttpClient", + "Java", + "Keep-Alive", + "MaxIdleConns", + "Python", + "TCP", + "net/http", + "requests", + "僵尸连接", + "多路复用", + "容错", + "帧", + "延迟", + "性能", + "最大连接数", + "空闲回收", + "连接池", + "连接池参数", + "队头阻塞", + "预建连接", + "高可用" + ], + "difficulty_range": [ + 2, + 4 + ], + "schema_version": "1.0.0", + "updated": "2026-09-02", + "question_files": [ + "single_choice", + "fill_blank" + ], + "stats": { + "total": 20, + "by_type": { + "single_choice": 10, + "fill_blank": 10 + } + } +} diff --git a/topics/networking/connection-pooling/single_choice.json b/topics/networking/connection-pooling/single_choice.json new file mode 100644 index 0000000..81d93ec --- /dev/null +++ b/topics/networking/connection-pooling/single_choice.json @@ -0,0 +1,209 @@ +{ + "topic": "connection-pooling", + "type": "single_choice", + "schema_version": "1.0.0", + "generated": "2026-09-02T21:20:00+08:00", + "questions": [ + { + "id": "sc-001", + "type": "single_choice", + "difficulty": 2, + "tags": [ + "连接池", + "基本概念" + ], + "question": "连接池的核心思想是什么?", + "options": { + "A": "每次请求都新建连接", + "B": "预建一批连接,用时借出,用完归还而非关闭", + "C": "关闭所有空闲连接", + "D": "限制最大请求并发数" + }, + "answer": "B", + "explanation": "连接池是一批预先建好的连接集合,用时借出,用完归还而非关闭。高频场景下最简单有效的优化,避免了每次请求都要建 TCP 连接和 TLS 握手的开销。", + "source": null, + "related": [] + }, + { + "id": "sc-002", + "type": "single_choice", + "difficulty": 3, + "tags": [ + "HTTP/1.1", + "Keep-Alive", + "队头阻塞" + ], + "question": "HTTP/1.1 的 Keep-Alive 复用存在什么限制?", + "options": { + "A": "不能复用连接", + "B": "同一时刻只能有一个请求在跑(队头阻塞)", + "C": "最多复用 3 个请求", + "D": "需要额外付费" + }, + "answer": "B", + "explanation": "HTTP/1.1 的 Keep-Alive 复用虽然解决了频繁握手问题,但同一时刻只能有一个请求在跑(队头阻塞)。浏览器默认开 6 个连接来弥补。", + "source": null, + "related": [] + }, + { + "id": "sc-003", + "type": "single_choice", + "difficulty": 3, + "tags": [ + "HTTP/2", + "多路复用" + ], + "question": "HTTP/2 的多路复用如何处理多个请求?", + "options": { + "A": "每个请求一个 TCP 连接", + "B": "一个 TCP 连接上并行处理多个请求/响应,通过帧编号区分", + "C": "串行处理所有请求", + "D": "使用 UDP 协议" + }, + "answer": "B", + "explanation": "HTTP/2 的多路复用在一个 TCP 连接上同时跑多个请求/响应,通过帧编号区分。不再需要多个 TCP 连接来并行,一根连接全部搞定。", + "source": null, + "related": [] + }, + { + "id": "sc-004", + "type": "single_choice", + "difficulty": 4, + "tags": [ + "连接池", + "配置" + ], + "question": "连接池最大连接数设得过大可能导致什么问题?", + "options": { + "A": "请求处理速度变慢", + "B": "内存和文件描述符浪费", + "C": "连接加密失败", + "D": "DNS 解析变慢" + }, + "answer": "B", + "explanation": "连接池最大连接数过大,每个连接都占内存和 fd,导致资源浪费。应该根据服务端能承受的并发数和实际需求合理配置。", + "source": null, + "related": [] + }, + { + "id": "sc-005", + "type": "single_choice", + "difficulty": 3, + "tags": [ + "僵尸连接", + "健康检查" + ], + "question": "连接池中的僵尸连接是什么?", + "options": { + "A": "正在使用的连接", + "B": "服务器端已关闭但客户端不知道,下次复用时发现连接已死", + "C": "加密失败的连接", + "D": "超时的连接" + }, + "answer": "B", + "explanation": "僵尸连接是指服务器端已经关闭了连接,但客户端不知道,下次复用时发现连接已死。解决方案:开启连接健康检查、设置合理的空闲超时、使用 TCP keepalive。", + "source": null, + "related": [] + }, + { + "id": "sc-006", + "type": "single_choice", + "difficulty": 2, + "tags": [ + "连接池", + "Python" + ], + "question": "Python requests 库中,pool_maxsize 参数控制什么?", + "options": { + "A": "最大请求数", + "B": "每个池子最多保持的连接数", + "C": "请求超时时间", + "D": "重试次数" + }, + "answer": "B", + "explanation": "pool_maxsize 控制每个池子最多保持的连接数。pool_connections 是连接到不同目标服务器的池子数。", + "source": null, + "related": [] + }, + { + "id": "sc-007", + "type": "single_choice", + "difficulty": 4, + "tags": [ + "连接池", + "HTTP/2" + ], + "question": "HTTP/2 的多路复用还需要连接池吗?", + "options": { + "A": "完全不需要", + "B": "仍然需要,连接池提供容错和负载均衡能力", + "C": "只需要 1 个连接", + "D": "连接池会干扰多路复用" + }, + "answer": "B", + "explanation": "连接池仍然有用。HTTP/2 解决了单个连接上的并行问题,但连接池提供了容错(一个连接挂了可以切到另一个)和负载均衡(多连接分散压力)的能力。", + "source": null, + "related": [] + }, + { + "id": "sc-008", + "type": "single_choice", + "difficulty": 2, + "tags": [ + "连接池", + "Go" + ], + "question": "Go 的 http.Transport 中 MaxIdleConnsPerHost 参数控制什么?", + "options": { + "A": "最大空闲连接总数", + "B": "每个主机最多保留的空闲连接数", + "C": "连接超时时间", + "D": "最大并发请求数" + }, + "answer": "B", + "explanation": "MaxIdleConnsPerHost 控制每个主机最多保留的空闲连接数。MaxIdleConns 是最大空闲连接总数,IdleConnTimeout 是空闲连接超时时间。", + "source": null, + "related": [] + }, + { + "id": "sc-009", + "type": "single_choice", + "difficulty": 3, + "tags": [ + "连接池", + "延迟" + ], + "question": "连接池打满时会发生什么?", + "options": { + "A": "自动扩容", + "B": "新请求排队等连接,延迟飙升", + "C": "直接拒绝请求", + "D": "自动建立新连接" + }, + "answer": "B", + "explanation": "连接池打满时,新请求需要排队等连接归还,导致延迟飙升。解决方案:合理调大池子,或用 HTTP/2 多路复用减少对连接数的需求。", + "source": null, + "related": [] + }, + { + "id": "sc-010", + "type": "single_choice", + "difficulty": 4, + "tags": [ + "连接池", + "Java" + ], + "question": "Java Apache HttpClient 中 setDefaultMaxPerRoute(20) 的含义是?", + "options": { + "A": "总连接池大小为 20", + "B": "每个目标服务器最多 20 个连接", + "C": "最大空闲连接为 20", + "D": "连接超时 20 秒" + }, + "answer": "B", + "explanation": "setDefaultMaxPerRoute(20) 表示每个目标服务器(路由)最多 20 个连接。setMaxTotal(200) 才是总连接池大小。", + "source": null, + "related": [] + } + ] +} \ No newline at end of file diff --git a/topics/networking/cors/fill_blank.json b/topics/networking/cors/fill_blank.json new file mode 100644 index 0000000..31c3a61 --- /dev/null +++ b/topics/networking/cors/fill_blank.json @@ -0,0 +1,197 @@ +{ + "topic": "cors", + "type": "fill_blank", + "schema_version": "1.0.0", + "generated": "2026-09-02T21:20:00+08:00", + "questions": [ + { + "id": "fb-001", + "type": "fill_blank", + "difficulty": 2, + "tags": [ + "CORS", + "同源策略", + "浏览器安全" + ], + "question": "浏览器的同源策略通过比较 URL 的____三元组来判断两个源是否相同,这三个要素分别是协议、域名和____。", + "answer": [ + "协议", + "端口" + ], + "answer_rule": "all", + "explanation": "同源策略(Same-Origin Policy)是浏览器的核心安全机制。'同源'指的是两个 URL 的协议(如 http/https)、域名(如 example.com)和端口(如 80/443)三者完全一致。只要其中任意一个不同,浏览器就认为是跨源请求,会限制脚本对跨源资源的访问。", + "source": null, + "related": [] + }, + { + "id": "fb-002", + "type": "fill_blank", + "difficulty": 2, + "tags": [ + "CORS", + "Origin", + "跨源请求" + ], + "question": "CORS(跨源资源共享)中,浏览器在跨源请求头中自动添加____字段,其值由协议、域名和端口组成,用于告知服务器请求来源。", + "answer": [ + "Origin" + ], + "answer_rule": "all", + "explanation": "Origin 是浏览器在发起跨源请求时自动附加的 HTTP 头字段,格式为 \"scheme://host:port\"(如 \"http://localhost:3000\")。服务器通过读取该字段来决定是否允许该跨源请求。注意 Origin 不包含路径信息,这是出于隐私考虑。", + "source": null, + "related": [] + }, + { + "id": "fb-003", + "type": "fill_blank", + "difficulty": 3, + "tags": [ + "CORS", + "预检请求", + "OPTIONS" + ], + "question": "对于非简单请求,浏览器会先自动发送一个____方法的预检请求(Preflight Request),服务器返回允许的跨源策略后,浏览器才会发出真正的请求。", + "answer": [ + "OPTIONS" + ], + "answer_rule": "all", + "explanation": "预检请求(Preflight Request)使用 HTTP OPTIONS 方法,在实际请求之前发送。它携带 Access-Control-Request-Method 和 Access-Control-Request-Headers 告知服务器即将发起的跨源请求信息。服务器通过响应头告知浏览器是否允许该请求,从而避免不安全的跨源请求到达服务器。", + "source": null, + "related": [] + }, + { + "id": "fb-004", + "type": "fill_blank", + "difficulty": 3, + "tags": [ + "CORS", + "简单请求", + "预检请求" + ], + "question": "满足以下条件的请求属于简单请求:方法为 GET、____ 或 DELETE,且请求头仅包含 CORS 安全列表字段(如 Accept、Content-Type 限于 text/plain、multipart/form-data、application/x-www-form-urlencoded)。", + "answer": [ + "POST", + "PUT" + ], + "answer_rule": "any", + "explanation": "简单请求(Simple Request)不需要预检,浏览器直接发送并附带 Origin 头。判定标准:①方法为 GET、HEAD 或 POST(注意不是 PUT/DELETE);②Content-Type 只限于 text/plain、multipart/form-data、application/x-www-form-urlencoded 三者之一;③没有自定义请求头。不满足任何一条都会触发预检。", + "source": null, + "related": [] + }, + { + "id": "fb-005", + "type": "fill_blank", + "difficulty": 4, + "tags": [ + "CORS", + "Cookie", + "Access-Control-Allow-Credentials" + ], + "question": "当服务器响应 Access-Control-Allow-Origin 设置为通配符 * 时,____设置为 true 将不生效,浏览器会拒绝携带 Cookie 的跨源请求。", + "answer": [ + "Access-Control-Allow-Credentials", + "allowCredentials" + ], + "answer_rule": "any", + "explanation": "CORS 规范明确禁止在携带凭据(credentials)时使用通配符 *。当请求附带 Cookie(fetch 的 credentials: 'include' 或 XMLHttpRequest 的 withCredentials = true)时,服务器必须将 Access-Control-Allow-Origin 设置为具体的 Origin 值(不能是 *),同时将 Access-Control-Allow-Credentials 设为 true。这是安全考量,防止任意来源都能携带用户凭据访问资源。", + "source": null, + "related": [] + }, + { + "id": "fb-006", + "type": "fill_blank", + "difficulty": 2, + "tags": [ + "CORS", + "开发环境", + "跨域" + ], + "question": "前端开发时,如果前端运行在 localhost:3000,后端运行在 localhost:8080,由于____不同,浏览器会将其视为跨域请求。", + "answer": [ + "端口", + "port" + ], + "answer_rule": "any", + "explanation": "同源策略的三元组中,协议(都是 http)和域名(都是 localhost)相同,但端口号不同(3000 vs 8080),因此被判定为跨源。即使都在本机,浏览器依然严格执行同源策略。常见的解决方式包括开发代理(如 Vite 的 proxy 配置)或后端配置 CORS 头。", + "source": null, + "related": [] + }, + { + "id": "fb-007", + "type": "fill_blank", + "difficulty": 3, + "tags": [ + "CORS", + "WebSocket" + ], + "question": "WebSocket 协议本身不受 CORS 限制,但 WebSocket 握手阶段的 HTTP Upgrade 请求会受到____策略的影响,浏览器可能会拒绝握手。", + "answer": [ + "同源", + "CORS", + "同源策略" + ], + "answer_rule": "any", + "explanation": "WebSocket 连接通过 HTTP Upgrade 机制建立,浏览器在发起 WebSocket 握手时会检查目标 URL 是否符合 CORS 策略。如果服务器没有在响应中返回合适的 Access-Control-Allow-Origin 头,某些浏览器可能拒绝握手。不过一旦 WebSocket 连接建立成功,后续的数据帧传输完全不受 CORS 限制,这与 HTTP 请求的 CORS 机制有本质区别。", + "source": null, + "related": [] + }, + { + "id": "fb-008", + "type": "fill_blank", + "difficulty": 3, + "tags": [ + "CORS", + "Nginx", + "反向代理" + ], + "question": "生产环境中常用的跨域解决方案是通过 Nginx 配置____,让前端和后端请求走同一个域名的同一端口,由 Nginx 在服务端转发请求。", + "answer": [ + "反向代理", + "reverse proxy" + ], + "answer_rule": "any", + "explanation": "Nginx 反向代理是解决跨域的生产级方案。原理:前端请求统一发到 Nginx(如 https://api.example.com),Nginx 根据 location 规则将 /api/* 的请求代理到实际后端服务。因为浏览器只和 Nginx 通信,不存在跨源问题。相比 CORS 头方案,反向代理还有统一入口、负载均衡、SSL 终端等优势。配置示例:location /api/ { proxy_pass http://backend:8080/; }", + "source": null, + "related": [] + }, + { + "id": "fb-009", + "type": "fill_blank", + "difficulty": 3, + "tags": [ + "CORS", + "Vite", + "开发代理" + ], + "question": "Vite 开发服务器中通过 server.proxy 配置反向代理时,需要设置 ____: true 使得代理请求的 Host 头与目标服务器一致,避免目标服务器返回错误。", + "answer": [ + "changeOrigin", + "change_origin" + ], + "answer_rule": "any", + "explanation": "Vite 的开发代理基于 http-proxy-middleware。changeOrigin: true 会将请求头中的 Host 字段修改为目标服务器的 host,这样目标服务器收到的请求就像直接发给自己一样。例如前端请求 localhost:3000/api/users,代理到 backend:8080,如果不开 changeOrigin,Host 头仍是 localhost:3000,后端可能无法正确路由。配置示例:proxy: { '/api': { target: 'http://localhost:8080', changeOrigin: true } }", + "source": null, + "related": [] + }, + { + "id": "fb-010", + "type": "fill_blank", + "difficulty": 4, + "tags": [ + "CORS", + "CSRF", + "同源策略", + "安全" + ], + "question": "同源策略在安全方面的一个重要作用是防止____攻击:恶意网站无法直接读取用户在银行网站的 Cookie,因此无法伪造合法请求来冒充用户向银行服务器发送转账指令。", + "answer": [ + "CSRF", + "跨站请求伪造" + ], + "answer_rule": "any", + "explanation": "CSRF(Cross-Site Request Forgery,跨站请求伪造)是一种利用用户已登录状态发起恶意请求的攻击。虽然同源策略能阻止恶意网站读取银行网站的响应数据,但仅靠同源策略并不能完全防御 CSRF——因为同源策略只限制'读',不完全限制'写'(如表单提交、图片标签等仍可触发跨源请求)。完整防御 CSRF 还需要 CSRF Token、SameSite Cookie 属性等额外机制。同源策略是第一道防线,但不是唯一防线。", + "source": null, + "related": [] + } + ] +} \ No newline at end of file diff --git a/topics/networking/cors/meta.json b/topics/networking/cors/meta.json new file mode 100644 index 0000000..99872fc --- /dev/null +++ b/topics/networking/cors/meta.json @@ -0,0 +1,43 @@ +{ + "slug": "cors", + "name": "Cors", + "description": "", + "tags": [ + "Access-Control-Allow-Credentials", + "CORS", + "CSRF", + "Cookie", + "Nginx", + "OPTIONS", + "Origin", + "Vite", + "WebSocket", + "反向代理", + "同源策略", + "安全", + "开发代理", + "开发环境", + "浏览器安全", + "简单请求", + "跨域", + "跨源请求", + "预检请求" + ], + "difficulty_range": [ + 2, + 4 + ], + "schema_version": "1.0.0", + "updated": "2026-09-02", + "question_files": [ + "single_choice", + "fill_blank" + ], + "stats": { + "total": 20, + "by_type": { + "single_choice": 10, + "fill_blank": 10 + } + } +} diff --git a/topics/networking/cors/single_choice.json b/topics/networking/cors/single_choice.json new file mode 100644 index 0000000..aa31501 --- /dev/null +++ b/topics/networking/cors/single_choice.json @@ -0,0 +1,209 @@ +{ + "topic": "cors", + "type": "single_choice", + "schema_version": "1.0.0", + "generated": "2026-09-02T21:20:00+08:00", + "questions": [ + { + "id": "sc-001", + "type": "single_choice", + "difficulty": 2, + "tags": [ + "CORS", + "同源策略" + ], + "question": "浏览器同源策略判断跨域的标准是什么?", + "options": { + "A": "是否同一台服务器", + "B": "协议 + 域名 + 端口三元组是否完全一致", + "C": "是否同一网段", + "D": "是否使用相同浏览器" + }, + "answer": "B", + "explanation": "源(Origin)= 协议 + 域名 + 端口。三者必须完全一致才算同源,任何一个不一样就是跨域,浏览器会拦截。", + "source": null, + "related": [] + }, + { + "id": "sc-002", + "type": "single_choice", + "difficulty": 3, + "tags": [ + "CORS", + "预检请求" + ], + "question": "浏览器在什么情况下会发送 OPTIONS 预检请求?", + "options": { + "A": "GET 请求", + "B": "POST 请求", + "C": "PUT/DELETE/自定义 Header 等非简单请求", + "D": "所有请求" + }, + "answer": "C", + "explanation": "简单请求(GET/POST/HEAD + 简单 Header)浏览器直接发,看响应头判断。预检请求(PUT/DELETE/自定义 Header 等)浏览器先发 OPTIONS 探路,确认安全后才发真正请求。", + "source": null, + "related": [] + }, + { + "id": "sc-003", + "type": "single_choice", + "difficulty": 3, + "tags": [ + "CORS", + "Access-Control-Allow-Origin" + ], + "question": "Access-Control-Allow-Origin: * 同时带 Cookie 时会怎样?", + "options": { + "A": "正常工作", + "B": "浏览器会拒绝", + "C": "Cookie 自动删除", + "D": "请求自动重试" + }, + "answer": "B", + "explanation": "当请求携带凭据(Cookie、HTTP Auth)时,Access-Control-Allow-Origin 不能是通配符 *,必须是具体的源。这是为了防止任意网站都能通过你浏览器里的 Cookie 访问受保护资源。", + "source": null, + "related": [] + }, + { + "id": "sc-004", + "type": "single_choice", + "difficulty": 2, + "tags": [ + "CORS", + "同源" + ], + "question": "http://localhost:3000 和 http://localhost:8080 是否同源?", + "options": { + "A": "同源", + "B": "跨域", + "C": "取决于浏览器", + "D": "取决于协议" + }, + "answer": "B", + "explanation": "端口不同(3000 ≠ 8080)就是跨域。前后端分离开发时极其常见,前端在 3000 端口,后端 API 在 8080 端口。", + "source": null, + "related": [] + }, + { + "id": "sc-005", + "type": "single_choice", + "difficulty": 3, + "tags": [ + "CORS", + "WebSocket" + ], + "question": "WebSocket 不受同源策略限制的原因是什么?", + "options": { + "A": "WebSocket 使用 UDP", + "B": "WebSocket 连接一旦建立就是全双工通信,不受 CORS 限制", + "C": "WebSocket 自带加密", + "D": "WebSocket 不经过浏览器" + }, + "answer": "B", + "explanation": "WebSocket 连接一旦建立就是全双工通信,不受 CORS 限制。但建立连接的握手阶段(HTTP Upgrade 请求)仍然受同源策略影响。", + "source": null, + "related": [] + }, + { + "id": "sc-006", + "type": "single_choice", + "difficulty": 4, + "tags": [ + "CORS", + "Nginx" + ], + "question": "生产环境中解决跨域最常用的方式是什么?", + "options": { + "A": "前端开发代理", + "B": "Nginx 反向代理", + "C": "后端设置 CORS 响应头", + "D": "使用 WebSocket" + }, + "answer": "B", + "explanation": "生产环境中 Nginx 反向代理是最常用的方式。前端请求 app.example.com/api/,Nginx 代理到后端 localhost:8080,对浏览器来说是同源。", + "source": null, + "related": [] + }, + { + "id": "sc-007", + "type": "single_choice", + "difficulty": 3, + "tags": [ + "CORS", + "预检请求" + ], + "question": "网络面板里一个请求出现两次,第一次是什么?", + "options": { + "A": "重试请求", + "B": "OPTIONS 预检请求", + "C": "缓存验证", + "D": "DNS 解析" + }, + "answer": "B", + "explanation": "网络面板里一个请求出现两次,第一次是 OPTIONS 预检请求,不是 bug。浏览器先发 OPTIONS 探路,确认允许后才发真正请求。", + "source": null, + "related": [] + }, + { + "id": "sc-008", + "type": "single_choice", + "difficulty": 2, + "tags": [ + "CORS", + "同源" + ], + "question": "https://app.example.com 和 https://api.example.com 是否同源?", + "options": { + "A": "同源", + "B": "跨域", + "C": "取决于端口", + "D": "取决于协议" + }, + "answer": "B", + "explanation": "子域名不同(app.example.com ≠ api.example.com)就是跨域。即使在同一台服务器上,子域名不同也属于跨域。", + "source": null, + "related": [] + }, + { + "id": "sc-009", + "type": "single_choice", + "difficulty": 4, + "tags": [ + "CORS", + "同源策略", + "安全" + ], + "question": "同源策略防止的攻击场景是什么?", + "options": { + "A": "DDoS 攻击", + "B": "恶意网站冒充用户向银行发起请求", + "C": "SQL 注入", + "D": "XSS 攻击" + }, + "answer": "B", + "explanation": "同源策略防止的典型场景:你登录了银行网站(bank.com),浏览器存了 cookie,然后访问恶意网站(evil.com),evil.com 试图请求 bank.com/api/transfer 冒充你转账。同源策略会拦截这个请求。", + "source": null, + "related": [] + }, + { + "id": "sc-010", + "type": "single_choice", + "difficulty": 3, + "tags": [ + "CORS", + "Vite" + ], + "question": "Vite 开发服务器中 server.proxy 配置的作用是什么?", + "options": { + "A": "加速构建", + "B": "将 /api 请求代理到后端,浏览器看到的请求始终在同源", + "C": "压缩代码", + "D": "热更新" + }, + "answer": "B", + "explanation": "Vite 的 server.proxy 将 /api 请求代理到后端(如 localhost:8080),浏览器看到的请求始终在 localhost:3000,同源,不触发跨域。changeOrigin: true 修改请求头中的 Host 为目标地址。", + "source": null, + "related": [] + } + ] +} \ No newline at end of file diff --git a/topics/networking/domain-sharding/fill_blank.json b/topics/networking/domain-sharding/fill_blank.json new file mode 100644 index 0000000..35009b5 --- /dev/null +++ b/topics/networking/domain-sharding/fill_blank.json @@ -0,0 +1,197 @@ +{ + "topic": "domain-sharding", + "type": "fill_blank", + "schema_version": "1.0.0", + "generated": "2026-09-02T21:20:00+08:00", + "questions": [ + { + "id": "fb-001", + "type": "fill_blank", + "question": "浏览器对同一域名最多只能同时建立 ____ 个 TCP 连接,这是域名分片技术产生的根本原因。", + "answer": [ + "6" + ], + "answer_rule": "any", + "difficulty": 1, + "tags": [ + "域名分片", + "浏览器限制", + "TCP连接" + ], + "explanation": "HTTP/1.1 协议下,浏览器对单个域名的并发 TCP 连接数限制为 6 个(Chrome、Firefox 等主流浏览器均遵循此限制)。这一限制会导致页面加载大量资源时出现排队等待,成为性能瓶颈。域名分片正是为了绕过这一限制而产生的优化手段。", + "source": null, + "related": [] + }, + { + "id": "fb-002", + "type": "fill_blank", + "question": "域名分片的核心思路是使用 ____ 个子域名,让浏览器误以为是不同域名,从而绕过单域名并发连接数限制。", + "answer": [ + "多个", + "多个子域名" + ], + "answer_rule": "any", + "difficulty": 1, + "tags": [ + "域名分片", + "子域名" + ], + "explanation": "域名分片(Domain Sharding)通过将资源分布到多个子域名上(如 img1.example.com、img2.example.com),利用浏览器按域名计算并发连接数的规则,使浏览器为每个子域名各分配独立的 TCP 连接池,从而突破单域名 6 连接的上限。", + "source": null, + "related": [] + }, + { + "id": "fb-003", + "type": "fill_blank", + "question": "如果使用 3 个子域名进行域名分片,每个子域名有 6 个 TCP 连接,则总共可以实现 ____ 个并行连接。", + "answer": [ + "18" + ], + "answer_rule": "any", + "difficulty": 1, + "tags": [ + "域名分片", + "并行连接", + "计算" + ], + "explanation": "计算公式非常直接:子域名数量 × 每域名并发连接数 = 总并行连接数。3 个子域名 × 6 个连接/域名 = 18 个并行连接。这意味着页面可以同时下载 18 个资源文件,相比单域名的 6 个连接,吞吐量理论上提升 3 倍。", + "source": null, + "related": [] + }, + { + "id": "fb-004", + "type": "fill_blank", + "question": "域名分片会增加额外的 ____ 开销,每次解析耗时约 ____ 毫秒,这是域名分片的主要代价之一。", + "answer": [ + "DNS解析", + "5-50" + ], + "answer_rule": "all", + "difficulty": 2, + "tags": [ + "域名分片", + "DNS解析", + "性能开销" + ], + "explanation": "每增加一个子域名就多一次 DNS 查询。首次解析需要向 DNS 服务器递归查询,耗时约 5-50ms(取决于 DNS 服务器距离和网络状况)。虽然浏览器有 DNS 缓存,但首次访问或缓存过期时仍需付出这一延迟。子域名越多,DNS 解析的累积开销越大。", + "source": null, + "related": [] + }, + { + "id": "fb-005", + "type": "fill_blank", + "question": "域名分片的代价除了 DNS 解析开销外,还包括 TLS 握手增加和 ____ 碎片化。", + "answer": [ + "缓存" + ], + "answer_rule": "any", + "difficulty": 2, + "tags": [ + "域名分片", + "缓存碎片化", + "TLS" + ], + "explanation": "域名分片的三大代价:①DNS 解析增加延迟;②每个新子域名都需要独立的 TLS 握手(HTTPS 场景下额外 1-2 个 RTT);③缓存碎片化——浏览器的 HTTP 缓存是按域名隔离的,同一资源分散到不同子域名后无法共享缓存,导致缓存命中率下降,重复下载增多。", + "source": null, + "related": [] + }, + { + "id": "fb-006", + "type": "fill_blank", + "question": "使用域名分片时,所有子域名的 DNS 记录应解析到 ____ ,即同一台服务器。", + "answer": [ + "同一台机器", + "同一台服务器", + "同一个IP", + "同一IP" + ], + "answer_rule": "any", + "difficulty": 2, + "tags": [ + "域名分片", + "DNS配置", + "服务器" + ], + "explanation": "域名分片的目的是欺骗浏览器多开连接,而非真的将资源部署到不同服务器。因此所有子域名(如 img1.example.com、img2.example.com)的 A 记录或 CNAME 应指向同一台服务器(或同一个负载均衡地址)。这样资源还是由同一台机器提供,只是浏览器认为它们来自不同域名而已。", + "source": null, + "related": [] + }, + { + "id": "fb-007", + "type": "fill_blank", + "question": "使用域名分片时,子域名数量建议控制在 ____ 个为宜,过多反而得不偿失。", + "answer": [ + "3-4", + "3到4" + ], + "answer_rule": "any", + "difficulty": 3, + "tags": [ + "域名分片", + "最佳实践", + "子域名数量" + ], + "explanation": "一般推荐 3-4 个子域名。太少(1-2 个)提升不明显,太多(6+)则 DNS 解析和 TLS 握手的开销会超过并行连接带来的收益,缓存碎片化问题也更严重。3-4 个子域名 × 6 连接 = 18-24 个并行连接,对于大多数页面已经足够,且开销可控。", + "source": null, + "related": [] + }, + { + "id": "fb-008", + "type": "fill_blank", + "question": "在前端实现域名分片时,需要在 HTML 中将资源 URL 的域名部分替换为不同的 ____ 来实现。", + "answer": [ + "子域名" + ], + "answer_rule": "any", + "difficulty": 2, + "tags": [ + "域名分片", + "前端实现", + "HTML" + ], + "explanation": "实现域名分片需要在前端代码层面修改:将原来统一的资源域名(如 static.example.com)替换为多个子域名(如 static1.example.com、static2.example.com)。通常通过配置一个子域名列表,然后在加载图片、JS、CSS 等资源时轮询(round-robin)分配不同子域名。这要求前端构建工具或模板引擎支持动态替换。", + "source": null, + "related": [] + }, + { + "id": "fb-009", + "type": "fill_blank", + "question": "HTTP/2 引入了 ____ 技术,允许在一个 TCP 连接上并发传输多个请求和响应,这使得域名分片变得不再必要。", + "answer": [ + "多路复用", + "multiplexing" + ], + "answer_rule": "any", + "difficulty": 3, + "tags": [ + "HTTP/2", + "多路复用", + "域名分片淘汰" + ], + "explanation": "HTTP/2 的多路复用(Multiplexing)允许在单个 TCP 连接上同时传输多个请求/响应流,彻底解决了 HTTP/1.1 的队头阻塞问题。既然一个连接就能并行处理所有资源请求,通过域名分片多开连接就失去了意义。加上域名分片的 DNS 开销和缓存碎片化代价,HTTP/2 时代域名分片已被视为反模式。", + "source": null, + "related": [] + }, + { + "id": "fb-010", + "type": "fill_blank", + "question": "域名分片的所有子域名最终都需要通过 DNS 解析到 ____ ,资源实际由同一台机器或集群提供服务。", + "answer": [ + "同一台机器", + "同一个IP", + "同一个服务器", + "同一个地址" + ], + "answer_rule": "any", + "difficulty": 1, + "tags": [ + "域名分片", + "DNS解析", + "服务器部署" + ], + "explanation": "域名分片的本质是一种「欺骗」浏览器的技巧:虽然使用了不同的子域名,但这些子域名的 DNS 记录最终都指向同一台服务器(或同一个 CDN 地址/负载均衡器)。资源并没有真正分散部署,只是利用浏览器按域名分配连接池的机制来获得更多的并发下载通道。", + "source": null, + "related": [] + } + ] +} \ No newline at end of file diff --git a/topics/networking/domain-sharding/meta.json b/topics/networking/domain-sharding/meta.json new file mode 100644 index 0000000..0160338 --- /dev/null +++ b/topics/networking/domain-sharding/meta.json @@ -0,0 +1,44 @@ +{ + "slug": "domain-sharding", + "name": "Domain Sharding", + "description": "", + "tags": [ + "DNS解析", + "DNS配置", + "HTML", + "HTTP/2", + "TCP连接", + "TLS", + "前端实现", + "域名分片", + "域名分片淘汰", + "多路复用", + "子域名", + "子域名数量", + "并行连接", + "性能开销", + "最佳实践", + "服务器", + "服务器部署", + "浏览器限制", + "缓存碎片化", + "计算" + ], + "difficulty_range": [ + 1, + 3 + ], + "schema_version": "1.0.0", + "updated": "2026-09-02", + "question_files": [ + "single_choice", + "fill_blank" + ], + "stats": { + "total": 20, + "by_type": { + "single_choice": 10, + "fill_blank": 10 + } + } +} diff --git a/topics/networking/domain-sharding/single_choice.json b/topics/networking/domain-sharding/single_choice.json new file mode 100644 index 0000000..f36a490 --- /dev/null +++ b/topics/networking/domain-sharding/single_choice.json @@ -0,0 +1,209 @@ +{ + "topic": "domain-sharding", + "type": "single_choice", + "schema_version": "1.0.0", + "generated": "2026-09-02T21:20:00+08:00", + "questions": [ + { + "id": "sc-001", + "type": "single_choice", + "difficulty": 2, + "tags": [ + "域名分片", + "浏览器限制" + ], + "question": "浏览器对同一域名最多同时开多少个 TCP 连接?", + "options": { + "A": "2 个", + "B": "4 个", + "C": "6 个", + "D": "10 个" + }, + "answer": "C", + "explanation": "浏览器对同一域名最多同时开 6 个 TCP 连接,这是浏览器厂商的经验值,不是协议规定。这个限制在 HTTP/2 时代已经过时。", + "source": null, + "related": [] + }, + { + "id": "sc-002", + "type": "single_choice", + "difficulty": 3, + "tags": [ + "域名分片", + "原理" + ], + "question": "域名分片的原理是什么?", + "options": { + "A": "增加服务器数量", + "B": "用多个子域名指向同一服务器,每个域名都能开 6 个连接", + "C": "使用 HTTP/2 协议", + "D": "增大 TCP 窗口" + }, + "answer": "B", + "explanation": "域名分片用多个子域名指向同一台服务器,每个域名都能开 6 个连接。例如 3 个子域名 × 6 个连接 = 18 个并行连接。", + "source": null, + "related": [] + }, + { + "id": "sc-003", + "type": "single_choice", + "difficulty": 3, + "tags": [ + "域名分片", + "代价" + ], + "question": "域名分片的代价不包括以下哪项?", + "options": { + "A": "DNS 解析开销", + "B": "TLS 握手开销", + "C": "缓存碎片化", + "D": "加密失败" + }, + "answer": "D", + "explanation": "域名分片的代价包括:DNS 解析开销(每个子域名都要单独解析,5-50ms)、TLS 握手开销(每个新域名要单独做 TLS 握手)、缓存碎片化(不同域名的缓存独立,不能共享)。", + "source": null, + "related": [] + }, + { + "id": "sc-004", + "type": "single_choice", + "difficulty": 4, + "tags": [ + "域名分片", + "HTTP/2" + ], + "question": "为什么 HTTP/2 不需要域名分片?", + "options": { + "A": "HTTP/2 支持更多连接", + "B": "HTTP/2 的多路复用让一个 TCP 连接就能真正并行", + "C": "HTTP/2 自动分片", + "D": "HTTP/2 使用 UDP" + }, + "answer": "B", + "explanation": "HTTP/2 的多路复用让一个 TCP 连接就能真正并行处理多个请求/响应,不再需要多个 TCP 连接来并行,域名分片变成了负优化。", + "source": null, + "related": [] + }, + { + "id": "sc-005", + "type": "single_choice", + "difficulty": 2, + "tags": [ + "域名分片", + "实现" + ], + "question": "域名分片的实现方式是什么?", + "options": { + "A": "修改服务器配置", + "B": "前端 HTML 里把资源 URL 的域名换掉", + "C": "使用 CDN", + "D": "修改 DNS 解析" + }, + "answer": "B", + "explanation": "域名分片只需在前端 HTML 里把资源 URL 的域名换掉(如 img1.example.com、img2.example.com),服务器端不用改动,所有子域名 DNS 解析到同一台机器就行。", + "source": null, + "related": [] + }, + { + "id": "sc-006", + "type": "single_choice", + "difficulty": 4, + "tags": [ + "域名分片", + "缓存" + ], + "question": "域名分片导致缓存碎片化的原因是什么?", + "options": { + "A": "缓存服务器过载", + "B": "不同域名的缓存独立,不能共享", + "C": "DNS 缓存过期", + "D": "TLS 握手失败" + }, + "answer": "B", + "explanation": "域名分片导致缓存碎片化,因为不同域名的缓存独立,不能共享,可能重复下载相同资源。这是域名分片的主要代价之一。", + "source": null, + "related": [] + }, + { + "id": "sc-007", + "type": "single_choice", + "difficulty": 3, + "tags": [ + "域名分片", + "数量" + ], + "question": "域名分片的子域名数量一般是多少?", + "options": { + "A": "1-2 个", + "B": "3-4 个", + "C": "10-20 个", + "D": "越多越好" + }, + "answer": "B", + "explanation": "域名分片的子域名数量一般 3-4 个就够了。过多子域名意味着更多 DNS 查询和 TLS 握手,反而拖慢加载速度。", + "source": null, + "related": [] + }, + { + "id": "sc-008", + "type": "single_choice", + "difficulty": 2, + "tags": [ + "域名分片", + "DNS" + ], + "question": "域名分片中,所有子域名的 DNS 解析应该指向什么?", + "options": { + "A": "不同的服务器", + "B": "同一台机器", + "C": "CDN 节点", + "D": "负载均衡器" + }, + "answer": "B", + "explanation": "域名分片中所有子域名的 DNS 解析应该指向同一台机器,服务器端不用改动。前端只需把资源 URL 的域名换掉。", + "source": null, + "related": [] + }, + { + "id": "sc-009", + "type": "single_choice", + "difficulty": 4, + "tags": [ + "域名分片", + "HTTP/2", + "负优化" + ], + "question": "在 HTTP/2 站点上使用域名分片会导致什么?", + "options": { + "A": "性能提升", + "B": "负优化——多了 DNS 和 TLS 开销,还破坏了缓存统一性", + "C": "没有影响", + "D": "自动降级到 HTTP/1.1" + }, + "answer": "B", + "explanation": "HTTP/2 的多路复用让域名分片变成了负优化——多了 DNS 和 TLS 开销,还破坏了缓存统一性。如果站点已经上了 HTTP/2,应该去掉域名分片。", + "source": null, + "related": [] + }, + { + "id": "sc-010", + "type": "single_choice", + "difficulty": 3, + "tags": [ + "域名分片", + "演进" + ], + "question": "HTTP 连接复用的演进中,域名分片出现在哪个阶段?", + "options": { + "A": "HTTP/1.0 之前", + "B": "HTTP/1.1 + Keep-Alive 之后,HTTP/2 之前", + "C": "HTTP/2 之后", + "D": "HTTP/3 之后" + }, + "answer": "B", + "explanation": "域名分片是 HTTP/1.1 时代的 hack,出现在 HTTP/1.1 + Keep-Alive 之后,HTTP/2 之前。HTTP/2 的多路复用从根本上解决了这个问题。", + "source": null, + "related": [] + } + ] +} \ No newline at end of file diff --git a/topics/networking/go-build-strip/fill_blank.json b/topics/networking/go-build-strip/fill_blank.json new file mode 100644 index 0000000..1ce473f --- /dev/null +++ b/topics/networking/go-build-strip/fill_blank.json @@ -0,0 +1,200 @@ +{ + "topic": "go-build-strip", + "type": "fill_blank", + "schema_version": "1.0.0", + "generated": "2026-09-02T21:20:00+08:00", + "questions": [ + { + "id": "fb-001", + "type": "fill_blank", + "difficulty": 2, + "tags": [ + "Go", + "编译", + "符号表" + ], + "question": "使用 go build -____ 命令可以剥离 Go 二进制文件中的符号表(symbol table),从而减小文件体积。", + "answer": [ + "s", + "-s" + ], + "answer_rule": "any", + "explanation": "-s 标志告诉 Go 链接器在生成二进制文件时不写入符号表。符号表包含了所有函数名、变量名、类型名等调试信息,生产环境中通常不需要。去掉符号表后,go tool nm 等工具将无法列出符号,二进制体积也会减小。", + "source": null, + "related": [] + }, + { + "id": "fb-002", + "type": "fill_blank", + "difficulty": 2, + "tags": [ + "Go", + "编译", + "DWARF", + "调试信息" + ], + "question": "使用 go build -____ 命令可以剥离 DWARF 调试信息,进一步减小 Go 二进制文件体积。", + "answer": [ + "w", + "-w" + ], + "answer_rule": "any", + "explanation": "-w 标志会去掉 DWARF(Debugging With Attributed Record Formats)调试信息。DWARF 是一种标准化的调试数据格式,包含了源码行号、局部变量、类型信息等。去掉 DWARF 后,dlv(Delve)等调试器将无法正常工作,go tool addr2line 也无法将地址翻译为行号。", + "source": null, + "related": [] + }, + { + "id": "fb-003", + "type": "fill_blank", + "difficulty": 3, + "tags": [ + "Go", + "编译", + "体积优化" + ], + "question": "生产环境构建 Go 二进制时,推荐使用 go build -s -____ 组合标志,同时剥离符号表和 DWARF 调试信息以实现体积最小化。", + "answer": [ + "w" + ], + "answer_rule": "all", + "explanation": "-s 和 -w 组合使用可以最大程度地减小二进制体积。-s 去掉符号表,-w 去掉 DWARF 调试信息,两者独立且互补。通常可将二进制体积减少约 20%-30%。Go 编译器支持这两个标志独立或组合使用。注意:这个组合一旦应用,后续反查行号信息将不可用。", + "source": null, + "related": [] + }, + { + "id": "fb-004", + "type": "fill_blank", + "difficulty": 3, + "tags": [ + "Go", + "编译", + "体积优化" + ], + "question": "使用 -s -w 编译标志对 Go 二进制进行体积优化,通常可以将文件大小减少约____。", + "answer": [ + "20-30%", + "20%到30%", + "20%-30%" + ], + "answer_rule": "any", + "explanation": "根据实际测试,-s -w 组合通常能使 Go 二进制体积减少 20%-30%。具体比例取决于原始二进制中符号表和调试信息的占比。一般来说,包含大量函数和类型的大型项目减小效果更明显。例如一个原本 20MB 的 Go 二进制,去掉后可能只有 14-16MB。", + "source": null, + "related": [] + }, + { + "id": "fb-005", + "type": "fill_blank", + "difficulty": 3, + "tags": [ + "Go", + "panic", + "堆栈信息" + ], + "question": "使用 -s -w 编译的 Go 程序在发生 panic 时,堆栈跟踪信息中只能显示____名,而无法显示完整的源码路径。", + "answer": [ + "文件", + "file", + "文件名" + ], + "answer_rule": "any", + "explanation": "因为 -w 去掉了 DWARF 调试信息,panic 的堆栈跟踪虽然仍能显示函数名和文件名(这些信息被嵌入在 Go 运行时的 pclntab 中,与 DWARF 无关),但无法显示完整的源码绝对路径。函数名因为已编入 pclntab(Program Counter Line Table)所以仍然可用,但行号和路径信息不完整。", + "source": null, + "related": [] + }, + { + "id": "fb-006", + "type": "fill_blank", + "difficulty": 4, + "tags": [ + "Go", + "pprof", + "性能分析" + ], + "question": "对使用 -s -w 编译的 Go 二进制文件运行 pprof 进行性能分析时,函数调用栈中只会显示内存____,而无法显示具体的函数名和行号信息。", + "answer": [ + "地址", + "address" + ], + "answer_rule": "any", + "explanation": "pprof 在分析 profiling 数据时需要符号信息来将内存地址翻译为函数名+行号。如果二进制用了 -s 去掉符号表,pprof 就无法解析这些地址,只能显示原始的十六进制地址。即使只用了 -w 去 DWARF,pprof 仍能显示函数名(符号表还在),只是没有行号。要让 pprof 正常工作,最好保存未 strip 的调试版本。", + "source": null, + "related": [] + }, + { + "id": "fb-007", + "type": "fill_blank", + "difficulty": 3, + "tags": [ + "Go", + "ldflags", + "编译时注入" + ], + "question": "使用 go build -ldflags \"-X ____.version=1.0.0\" 可以在编译时注入版本号等变量,这与 -s -w 组合使用互不影响。", + "answer": [ + "main", + "包路径" + ], + "answer_rule": "any", + "explanation": "-ldflags \"-X importpath.name=value\" 用于在编译时设置 string 类型的包级变量值。完整路径格式为 \"fully/qualified/package.Path\"。例如 main 包中的 version 变量:-ldflags \"-X main.version=1.0.0\"。这个操作通过链接器完成,与 -s -w 的符号剥离独立——因为 -X 是在编译时直接将值写入二进制数据段,不需要运行时符号表参与。", + "source": null, + "related": [] + }, + { + "id": "fb-008", + "type": "fill_blank", + "difficulty": 2, + "tags": [ + "Go", + "开发环境", + "调试" + ], + "question": "在本地开发和调试阶段,构建 Go 程序时____使用 -s -w 标志,以保留完整的调试信息便于使用 Delve 调试器和 pprof 工具。", + "answer": [ + "不应该", + "不建议", + "不要" + ], + "answer_rule": "any", + "explanation": "本地开发阶段保留调试信息至关重要。Delve(dlv)调试器依赖 DWARF 信息来设置断点、查看变量值、单步调试;pprof 依赖符号表来解析函数名和行号。如果开发时用了 -s -w,调试体验会严重下降。最佳实践:开发/测试环境不 strip,仅在 CI/CD 的生产构建中使用 -s -w。", + "source": null, + "related": [] + }, + { + "id": "fb-009", + "type": "fill_blank", + "difficulty": 4, + "tags": [ + "Go", + "addr2line", + "调试" + ], + "question": "当使用 -s -w 编译的程序 panic 输出中包含十六进制地址时,可使用 go tool ____ 反查对应的源码文件和行号,前提是能找到对应的未 strip 二进制。", + "answer": [ + "addr2line" + ], + "answer_rule": "all", + "explanation": "go tool addr2line 接收二进制文件和地址列表,输出对应的源码文件和行号。但它需要未 strip 的二进制(包含 DWARF 信息)作为输入。因此生产中如果要保留事后调试能力,需要在构建时同时保存 strip 和未 strip 两个版本。未 strip 版本用于事后分析,strip 版本用于部署。", + "source": null, + "related": [] + }, + { + "id": "fb-010", + "type": "fill_blank", + "difficulty": 5, + "tags": [ + "Go", + "Rust", + "Java", + "跨语言对比" + ], + "question": "在 Rust 中通过 Cargo.toml 的 [profile.release] 设置 strip = true 可实现类似 Go 的 -s 效果;在 Java 中,____/R8 等工具通过代码缩减和混淆实现类似的二进制体积优化。", + "answer": [ + "ProGuard" + ], + "answer_rule": "all", + "explanation": "不同语言都有二进制体积优化手段:Go 用 -s -w 剥离符号和调试信息;Rust 在 Cargo.toml 中设置 strip = true 或使用 strip 命令,效果类似;Java 使用 ProGuard(开源)或 R8(Android 官方,Google 开发的 ProGuard 替代品)进行 tree-shaking(移除未使用的类和方法)、混淆和优化。虽然原理不同(Go/Rust 是剥离元数据,Java 是缩减代码),但目标一致——减小部署体积。其他语言也有类似工具:C/C++ 的 strip 命令、.NET 的 PublishTrimmed 等。", + "source": null, + "related": [] + } + ] +} \ No newline at end of file diff --git a/topics/networking/go-build-strip/meta.json b/topics/networking/go-build-strip/meta.json new file mode 100644 index 0000000..c40663f --- /dev/null +++ b/topics/networking/go-build-strip/meta.json @@ -0,0 +1,42 @@ +{ + "slug": "go-build-strip", + "name": "Go Build Strip", + "description": "", + "tags": [ + "DWARF", + "Go", + "Java", + "Rust", + "addr2line", + "ldflags", + "panic", + "pprof", + "体积优化", + "堆栈信息", + "开发环境", + "性能分析", + "符号表", + "编译", + "编译时注入", + "调试", + "调试信息", + "跨语言对比" + ], + "difficulty_range": [ + 2, + 5 + ], + "schema_version": "1.0.0", + "updated": "2026-09-02", + "question_files": [ + "single_choice", + "fill_blank" + ], + "stats": { + "total": 20, + "by_type": { + "single_choice": 10, + "fill_blank": 10 + } + } +} diff --git a/topics/networking/go-build-strip/single_choice.json b/topics/networking/go-build-strip/single_choice.json new file mode 100644 index 0000000..d749d31 --- /dev/null +++ b/topics/networking/go-build-strip/single_choice.json @@ -0,0 +1,218 @@ +{ + "topic": "go-build-strip", + "type": "single_choice", + "schema_version": "1.0.0", + "generated": "2026-09-02T21:20:00+08:00", + "questions": [ + { + "id": "sc-001", + "type": "single_choice", + "difficulty": 2, + "tags": [ + "Go", + "编译", + "-s" + ], + "question": "Go 编译标志 -s 的作用是什么?", + "options": { + "A": "启用安全模式", + "B": "剥离符号表(函数名、变量名等调试符号)", + "C": "启用静态编译", + "D": "开启优化" + }, + "answer": "B", + "explanation": "-s 标志剥离符号表(函数名、变量名等调试符号),-w 标志剥离 DWARF 调试信息。-s -w 组合体积最小化,最适合发布部署。", + "source": null, + "related": [] + }, + { + "id": "sc-002", + "type": "single_choice", + "difficulty": 3, + "tags": [ + "Go", + "编译", + "-w" + ], + "question": "Go 编译标志 -w 的作用是什么?", + "options": { + "A": "剥离符号表", + "B": "剥离 DWARF 调试信息(gdb/lldb 所需的详细信息)", + "C": "启用警告", + "D": "开启 Web 支持" + }, + "answer": "B", + "explanation": "-w 标志剥离 DWARF 调试信息,这是 gdb/lldb 等调试器所需的详细信息。剥离后无法用 gdb 调试。", + "source": null, + "related": [] + }, + { + "id": "sc-003", + "type": "single_choice", + "difficulty": 3, + "tags": [ + "Go", + "编译", + "体积" + ], + "question": "使用 -s 标志编译后,二进制体积大约能减少多少?", + "options": { + "A": "5-10%", + "B": "20-30%", + "C": "50-60%", + "D": "70-80%" + }, + "answer": "B", + "explanation": "使用 -s 标志剥离符号表后,二进制体积大约能减少 20-30%。实际节省取决于程序复杂度。", + "source": null, + "related": [] + }, + { + "id": "sc-004", + "type": "single_choice", + "difficulty": 4, + "tags": [ + "Go", + "编译", + "panic" + ], + "question": "使用 -s 标志后,panic 堆栈输出会有什么变化?", + "options": { + "A": "完全看不到函数名", + "B": "函数名还在,但完整源码路径丢失", + "C": "没有变化", + "D": "只显示地址" + }, + "answer": "B", + "explanation": "使用 -s 剥离符号表后,函数名还在,但完整源码路径丢失。例如从 /home/user/app/main.go:42 变成 main.go:42。", + "source": null, + "related": [] + }, + { + "id": "sc-005", + "type": "single_choice", + "difficulty": 3, + "tags": [ + "Go", + "编译", + "pprof" + ], + "question": "剥离符号表后,go tool pprof 会显示什么?", + "options": { + "A": "完整的函数名", + "B": "只有地址(如 0x4a3b20)", + "C": "错误信息", + "D": "没有变化" + }, + "answer": "B", + "explanation": "剥离符号表后 pprof 只显示地址(如 0x4a3b20),无法定位到具体函数。线上排查性能问题时非常痛苦。", + "source": null, + "related": [] + }, + { + "id": "sc-006", + "type": "single_choice", + "difficulty": 2, + "tags": [ + "Go", + "编译", + "生产环境" + ], + "question": "生产环境发布 Go 程序时应该使用什么编译标志?", + "options": { + "A": "不加任何标志", + "B": "-s -w", + "C": "只用 -w", + "D": "只用 -s" + }, + "answer": "B", + "explanation": "生产环境发布应该使用 -s -w 组合,体积最小化,最适合发布部署。同时可以结合 -ldflags 注入版本号等构建信息。", + "source": null, + "related": [] + }, + { + "id": "sc-007", + "type": "single_choice", + "difficulty": 4, + "tags": [ + "Go", + "编译", + "-ldflags" + ], + "question": "go build -ldflags=\"-s -w -X main.Version=1.0\" 中 -X 的作用是什么?", + "options": { + "A": "设置编译器参数", + "B": "在剥离调试信息的同时注入编译时变量", + "C": "启用交叉编译", + "D": "设置输出文件名" + }, + "answer": "B", + "explanation": "-X 用于在编译时注入变量值,可以同时剥离调试信息(-s -w)和注入版本号、构建时间等信息。", + "source": null, + "related": [] + }, + { + "id": "sc-008", + "type": "single_choice", + "difficulty": 3, + "tags": [ + "Go", + "编译", + "调试" + ], + "question": "以下哪种场景不应该使用 -s 标志?", + "options": { + "A": "生产环境部署", + "B": "CI/CD 构建产物", + "C": "本地开发调试", + "D": "嵌入式/IoT" + }, + "answer": "C", + "explanation": "本地开发调试需要完整调试信息,不应该使用 -s 标志。生产环境部署、CI/CD 构建产物、嵌入式/IoT 都适合使用 -s -w。", + "source": null, + "related": [] + }, + { + "id": "sc-009", + "type": "single_choice", + "difficulty": 4, + "tags": [ + "Go", + "编译", + "addr2line" + ], + "question": "使用 -s -w 后线上出问题,如何通过地址反查行号?", + "options": { + "A": "go tool pprof", + "B": "go tool addr2line", + "C": "go tool trace", + "D": "go tool vet" + }, + "answer": "B", + "explanation": "如果保留了构建时生成的 debug info 备份,可以用 go tool addr2line 通过地址反查行号。这是 -s -w 后排查问题的方法之一。", + "source": null, + "related": [] + }, + { + "id": "sc-010", + "type": "single_choice", + "difficulty": 2, + "tags": [ + "Go", + "编译", + "语言对比" + ], + "question": "以下哪种语言有类似 Go -s -w 的功能?", + "options": { + "A": "Python", + "B": "Rust(strip = true in Cargo.toml)", + "C": "JavaScript", + "D": "Shell" + }, + "answer": "B", + "explanation": "Rust 在 Cargo.toml 中设置 strip = true 可以剥离调试信息,类似 Go 的 -s -w。C/C++ 有 strip 命令,Java 有 ProGuard/R8。", + "source": null, + "related": [] + } + ] +} \ No newline at end of file diff --git a/topics/networking/http-connection-cost/fill_blank.json b/topics/networking/http-connection-cost/fill_blank.json new file mode 100644 index 0000000..0d4f4bc --- /dev/null +++ b/topics/networking/http-connection-cost/fill_blank.json @@ -0,0 +1,164 @@ +{ + "topic": "http-connection-cost", + "type": "fill_blank", + "schema_version": "1.0.0", + "generated": "2026-09-02T21:20:00+08:00", + "questions": [ + { + "id": "fb-001", + "type": "fill_blank", + "difficulty": 2, + "tags": [ + "TCP", + "内存" + ], + "question": "操作系统为每个 TCP 连接分配的内核内存大约是____KB。", + "answer": [ + "3-10" + ], + "explanation": "操作系统为每个 TCP 连接分配的内核内存大约是 3-10 KB,包括发送/接收缓冲区和 TCP 控制块。", + "source": null, + "related": [] + }, + { + "id": "fb-002", + "type": "fill_blank", + "difficulty": 3, + "tags": [ + "线程模型" + ], + "question": "传统线程模型(Java BIO)每连接的内存消耗大约是____MB。", + "answer": [ + "1" + ], + "explanation": "传统线程模型每连接约 1 MB(主要是线程栈),协程模型(Go/async)仅需约 4 KB,异步事件驱动(Nginx)约 2 KB。", + "source": null, + "related": [] + }, + { + "id": "fb-003", + "type": "fill_blank", + "difficulty": 4, + "tags": [ + "百万并发" + ], + "question": "百万并发场景下,使用传统线程模型需要约____TB 内存,这在物理上不可行。", + "answer": [ + "1" + ], + "explanation": "传统线程模型每连接约 1 MB,百万并发需要约 1 TB 内存,这在物理上不可行。协程模型约 4 GB 是可行的。", + "source": null, + "related": [] + }, + { + "id": "fb-004", + "type": "fill_blank", + "difficulty": 2, + "tags": [ + "TCP", + "端口" + ], + "question": "客户端临时端口是____位整数,理论上限 65535。", + "answer": [ + "16" + ], + "explanation": "客户端端口是 16 位整数,理论上限 65535。每个 TCP 连接需要一个唯一的四元组。", + "source": null, + "related": [] + }, + { + "id": "fb-005", + "type": "fill_blank", + "difficulty": 3, + "tags": [ + "Go", + "goroutine" + ], + "question": "Go goroutine 的初始栈大小是____KB,可动态增长到 1 GB。", + "answer": [ + "2" + ], + "explanation": "Go goroutine 初始栈大小为 2 KB(可动态增长到 1 GB),Java 线程默认栈大小为 1 MB。相同内存下 Go 能创建的并发单元数量是 Java 的约 500 倍。", + "source": null, + "related": [] + }, + { + "id": "fb-006", + "type": "fill_blank", + "difficulty": 3, + "tags": [ + "TLS", + "HTTPS" + ], + "question": "HTTPS 场景下 TLS 会话缓存每个连接额外消耗____KB 内存。", + "answer": [ + "50-100" + ], + "explanation": "HTTPS 场景下 TLS 会话缓存每个连接额外消耗 50-100 KB 内存,百万并发下可能吃掉 50 GB 内存。", + "source": null, + "related": [] + }, + { + "id": "fb-007", + "type": "fill_blank", + "difficulty": 2, + "tags": [ + "文件描述符" + ], + "question": "每个 TCP 连接占____个文件描述符(fd)。", + "answer": [ + "1" + ], + "explanation": "每个 TCP 连接占 1 个文件描述符(fd),受系统 ulimit 限制。百万并发需要调大 ulimit 到至少 1048576。", + "source": null, + "related": [] + }, + { + "id": "fb-008", + "type": "fill_blank", + "difficulty": 4, + "tags": [ + "异步模型" + ], + "question": "异步事件驱动模型(如 Nginx)每连接的内存消耗大约是____KB。", + "answer": [ + "2" + ], + "explanation": "异步事件驱动模型(Nginx)每连接约 2 KB,几乎不占位置。这是最高效的连接模型,但编程复杂度最高。", + "source": null, + "related": [] + }, + { + "id": "fb-009", + "type": "fill_blank", + "difficulty": 3, + "tags": [ + "Epoll", + "IO多路复用" + ], + "question": "撑住百万并发的关键手段包括____/Kqueue(不为每个连接开线程)。", + "answer": [ + "Epoll" + ], + "explanation": "Epoll/Kqueue 是 IO 多路复用机制,不为每个连接开线程,一个线程就能监控百万文件描述符。", + "source": null, + "related": [] + }, + { + "id": "fb-010", + "type": "fill_blank", + "difficulty": 2, + "tags": [ + "TCP", + "四元组" + ], + "question": "TCP 连接的四元组包括源 IP、源端口、目标 IP 和目标____。", + "answer": [ + "端口" + ], + "explanation": "TCP 连接的四元组是:源 IP、源端口、目标 IP、目标端口。协议号(TCP=6)是固定的,不属于四元组。", + "source": null, + "related": [] + } + ] +} \ No newline at end of file diff --git a/topics/networking/http-connection-cost/meta.json b/topics/networking/http-connection-cost/meta.json new file mode 100644 index 0000000..e1e4f70 --- /dev/null +++ b/topics/networking/http-connection-cost/meta.json @@ -0,0 +1,38 @@ +{ + "slug": "http-connection-cost", + "name": "Http Connection Cost", + "description": "", + "tags": [ + "Epoll", + "Go", + "HTTPS", + "IO多路复用", + "TCP", + "TLS", + "goroutine", + "内存", + "四元组", + "异步模型", + "文件描述符", + "百万并发", + "端口", + "线程模型" + ], + "difficulty_range": [ + 2, + 4 + ], + "schema_version": "1.0.0", + "updated": "2026-09-02", + "question_files": [ + "single_choice", + "fill_blank" + ], + "stats": { + "total": 20, + "by_type": { + "single_choice": 10, + "fill_blank": 10 + } + } +} diff --git a/topics/networking/http-connection-cost/single_choice.json b/topics/networking/http-connection-cost/single_choice.json new file mode 100644 index 0000000..bd52b9c --- /dev/null +++ b/topics/networking/http-connection-cost/single_choice.json @@ -0,0 +1,213 @@ +{ + "topic": "http-connection-cost", + "type": "single_choice", + "schema_version": "1.0.0", + "generated": "2026-09-02T21:20:00+08:00", + "questions": [ + { + "id": "sc-001", + "type": "single_choice", + "difficulty": 2, + "tags": [ + "TCP", + "内存", + "内核" + ], + "question": "操作系统为每个 TCP 连接分配的内核内存大约是多少?", + "options": { + "A": "300-1000 KB", + "B": "3-10 KB", + "C": "1-2 KB", + "D": "50-100 KB" + }, + "answer": "B", + "explanation": "操作系统为每个 TCP 连接分配的内核内存大约是 3-10 KB,包括发送/接收缓冲区和 TCP 控制块。50-100 KB 是 HTTPS 场景下 TLS 会话的额外消耗。", + "source": null, + "related": [] + }, + { + "id": "sc-002", + "type": "single_choice", + "difficulty": 3, + "tags": [ + "线程模型", + "协程模型" + ], + "question": "传统线程模型(Java BIO)每连接的内存消耗大约是多少?", + "options": { + "A": "约 4 KB", + "B": "约 10 KB", + "C": "约 1 MB", + "D": "约 10 MB" + }, + "answer": "C", + "explanation": "传统线程模型每连接约 1 MB(主要是线程栈),协程模型(Go/async)仅需约 4 KB,异步事件驱动(Nginx)约 2 KB。", + "source": null, + "related": [] + }, + { + "id": "sc-003", + "type": "single_choice", + "difficulty": 4, + "tags": [ + "百万并发", + "内存" + ], + "question": "百万并发场景下,使用传统线程模型需要多少内存?", + "options": { + "A": "约 10 GB", + "B": "约 50 GB", + "C": "约 1 TB", + "D": "约 4 GB" + }, + "answer": "C", + "explanation": "传统线程模型每连接约 1 MB,百万并发需要约 1 TB 内存,这在物理上不可行。协程模型约 4 GB 是可行的。", + "source": null, + "related": [] + }, + { + "id": "sc-004", + "type": "single_choice", + "difficulty": 2, + "tags": [ + "TCP", + "端口" + ], + "question": "客户端临时端口的理论上限是多少?", + "options": { + "A": "1024", + "B": "32768", + "C": "65535", + "D": "1048576" + }, + "answer": "C", + "explanation": "客户端端口是 16 位整数,理论上限 65535。每个 TCP 连接需要一个唯一的四元组(源IP:源端口:目标IP:目标端口)。", + "source": null, + "related": [] + }, + { + "id": "sc-005", + "type": "single_choice", + "difficulty": 3, + "tags": [ + "Go", + "goroutine", + "协程" + ], + "question": "Go goroutine 的初始栈大小是多少?", + "options": { + "A": "1 KB", + "B": "2 KB", + "C": "4 KB", + "D": "8 KB" + }, + "answer": "B", + "explanation": "Go goroutine 初始栈大小为 2 KB(可动态增长到 1 GB),Java 线程默认栈大小为 1 MB。相同内存下 Go 能创建的并发单元数量是 Java 的约 500 倍。", + "source": null, + "related": [] + }, + { + "id": "sc-006", + "type": "single_choice", + "difficulty": 3, + "tags": [ + "TLS", + "HTTPS", + "内存" + ], + "question": "HTTPS 场景下 TLS 会话缓存每个连接额外消耗多少内存?", + "options": { + "A": "3-10 KB", + "B": "50-100 KB", + "C": "200-500 KB", + "D": "1-2 MB" + }, + "answer": "B", + "explanation": "HTTPS 场景下 TLS 会话缓存每个连接额外消耗 50-100 KB 内存,百万并发下可能吃掉 50 GB 内存。因此考虑在 CDN 边缘终止 TLS。", + "source": null, + "related": [] + }, + { + "id": "sc-007", + "type": "single_choice", + "difficulty": 2, + "tags": [ + "文件描述符", + "ulimit" + ], + "question": "每个 TCP 连接占多少个文件描述符?", + "options": { + "A": "0 个", + "B": "1 个", + "C": "2 个", + "D": "4 个" + }, + "answer": "B", + "explanation": "每个 TCP 连接占 1 个文件描述符(fd),受系统 ulimit 限制。百万并发需要调大 ulimit 到至少 1048576。", + "source": null, + "related": [] + }, + { + "id": "sc-008", + "type": "single_choice", + "difficulty": 4, + "tags": [ + "异步模型", + "Nginx", + "事件驱动" + ], + "question": "异步事件驱动模型(如 Nginx)每连接的内存消耗大约是多少?", + "options": { + "A": "约 1 MB", + "B": "约 10 KB", + "C": "约 4 KB", + "D": "约 2 KB" + }, + "answer": "D", + "explanation": "异步事件驱动模型(Nginx)每连接约 2 KB,几乎不占位置。这是最高效的连接模型,但编程复杂度最高。", + "source": null, + "related": [] + }, + { + "id": "sc-009", + "type": "single_choice", + "difficulty": 3, + "tags": [ + "Epoll", + "Kqueue", + "IO多路复用" + ], + "question": "撑住百万并发的关键手段中,Epoll/Kqueue 的作用是什么?", + "options": { + "A": "为每个连接开一个线程", + "B": "不为每个连接开线程,一个线程监控百万 fd", + "C": "加密所有连接", + "D": "压缩数据传输" + }, + "answer": "B", + "explanation": "Epoll/Kqueue 是 IO 多路复用机制,不为每个连接开线程,一个线程就能监控百万文件描述符,是撑住百万并发的关键手段之一。", + "source": null, + "related": [] + }, + { + "id": "sc-010", + "type": "single_choice", + "difficulty": 2, + "tags": [ + "TCP", + "资源" + ], + "question": "TCP 连接的四元组中不包括以下哪项?", + "options": { + "A": "源 IP", + "B": "源端口", + "C": "协议号", + "D": "目标端口" + }, + "answer": "C", + "explanation": "TCP 连接的四元组是:源 IP、源端口、目标 IP、目标端口。协议号(TCP=6)是固定的,不属于四元组。", + "source": null, + "related": [] + } + ] +} \ No newline at end of file diff --git a/topics/networking/http-handshake/fill_blank.json b/topics/networking/http-handshake/fill_blank.json new file mode 100644 index 0000000..e467115 --- /dev/null +++ b/topics/networking/http-handshake/fill_blank.json @@ -0,0 +1,167 @@ +{ + "topic": "http-handshake", + "type": "fill_blank", + "schema_version": "1.0.0", + "generated": "2026-09-02T21:20:00+08:00", + "questions": [ + { + "id": "fb-001", + "type": "fill_blank", + "difficulty": 2, + "tags": [ + "TCP", + "三次握手" + ], + "question": "TCP 三次握手中,第二次握手服务器发送的标志位是____。", + "answer": [ + "SYN-ACK" + ], + "explanation": "第二次握手服务器发送 SYN-ACK(Synchronize-Acknowledge),表示收到客户端的连接请求并确认,同时自己也准备好建立连接。", + "source": null, + "related": [] + }, + { + "id": "fb-002", + "type": "fill_blank", + "difficulty": 3, + "tags": [ + "TLS", + "HTTPS" + ], + "question": "TLS 1.3 相比 TLS 1.2 将握手耗时从 2-RTT 优化到了____-RTT。", + "answer": [ + "1" + ], + "explanation": "TLS 1.3 优化了握手流程,将握手耗时从 TLS 1.2 的 2-RTT 优化到 1-RTT。TLS 1.3 的 0-RTT 恢复可以在恢复会话时做到 0-RTT。", + "source": null, + "related": [] + }, + { + "id": "fb-003", + "type": "fill_blank", + "difficulty": 2, + "tags": [ + "HTTP/1.x" + ], + "question": "HTTP/1.0 和 HTTP/1.1 在应用层____(有/没有)握手。", + "answer": [ + "没有" + ], + "explanation": "HTTP/1.0 和 HTTP/1.1 没有应用层握手。TCP 连接建立后,TLS 握手完成后,客户端直接发送 HTTP 请求。", + "source": null, + "related": [] + }, + { + "id": "fb-004", + "type": "fill_blank", + "difficulty": 3, + "tags": [ + "HTTP/2", + "SETTINGS" + ], + "question": "HTTP/2 在连接建立后通过____帧交换连接参数。", + "answer": [ + "SETTINGS" + ], + "explanation": "HTTP/2 在连接建立后通过 SETTINGS 帧交换参数,如最大并发流数、窗口大小等。这不算严格的「握手」,但起到类似的协商作用。", + "source": null, + "related": [] + }, + { + "id": "fb-005", + "type": "fill_blank", + "difficulty": 4, + "tags": [ + "HTTP/3", + "QUIC" + ], + "question": "HTTP/3 基于 QUIC 协议,QUIC 将传输层握手和____握手合二为一。", + "answer": [ + "TLS 1.3" + ], + "explanation": "QUIC 把传输层握手和 TLS 1.3 握手合二为一,首次连接只需 1-RTT,恢复连接可达 0-RTT。", + "source": null, + "related": [] + }, + { + "id": "fb-006", + "type": "fill_blank", + "difficulty": 2, + "tags": [ + "TCP", + "三次握手" + ], + "question": "TCP 三次握手的本质是双方各确认一次____能力。", + "answer": [ + "收发" + ], + "explanation": "三次握手的本质是双方各确认一次收发能力,确保连接可靠。客户端确认自己能发能收,服务器确认自己能发能收。", + "source": null, + "related": [] + }, + { + "id": "fb-007", + "type": "fill_blank", + "difficulty": 3, + "tags": [ + "TLS", + "HTTPS" + ], + "question": "TLS 握手完成的三件事包括证书验证、密钥协商和____确定。", + "answer": [ + "加密套件" + ], + "explanation": "TLS 握手完成的事包括:证书验证(确认服务器身份)、密钥协商(生成对称加密密钥)、加密套件确定(双方商定使用的加密算法)。", + "source": null, + "related": [] + }, + { + "id": "fb-008", + "type": "fill_blank", + "difficulty": 4, + "tags": [ + "HTTP/3", + "QUIC" + ], + "question": "HTTP/3 基于____协议运行在 UDP 之上。", + "answer": [ + "QUIC" + ], + "explanation": "HTTP/3 基于 QUIC 协议,QUIC 运行在 UDP 之上。QUIC 内置了 TLS 1.3、可靠传输、多路复用和 0-RTT 恢复。", + "source": null, + "related": [] + }, + { + "id": "fb-009", + "type": "fill_blank", + "difficulty": 3, + "tags": [ + "TLS", + "0-RTT" + ], + "question": "TLS 1.3 的 0-RTT 恢复虽然快,但存在____攻击风险。", + "answer": [ + "重放" + ], + "explanation": "0-RTT 恢复虽然快,但存在重放攻击风险。0-RTT 发送的数据不能保证幂等性,因此不适合做敏感操作(如支付)。", + "source": null, + "related": [] + }, + { + "id": "fb-010", + "type": "fill_blank", + "difficulty": 4, + "tags": [ + "HTTP/2", + "TCP" + ], + "question": "HTTP/2 的 SETTINGS 交换发生在____层,解决的是「怎么高效通信」的问题。", + "answer": [ + "应用" + ], + "explanation": "HTTP/2 的 SETTINGS 交换发生在应用层,目的是协商 HTTP 协议参数(如最大并发流、窗口大小等)。TCP 握手解决的是「能不能通信」。", + "source": null, + "related": [] + } + ] +} \ No newline at end of file diff --git a/topics/networking/http-handshake/meta.json b/topics/networking/http-handshake/meta.json new file mode 100644 index 0000000..71c129c --- /dev/null +++ b/topics/networking/http-handshake/meta.json @@ -0,0 +1,34 @@ +{ + "slug": "http-handshake", + "name": "Http Handshake", + "description": "", + "tags": [ + "0-RTT", + "HTTP/1.x", + "HTTP/2", + "HTTP/3", + "HTTPS", + "QUIC", + "SETTINGS", + "TCP", + "TLS", + "三次握手" + ], + "difficulty_range": [ + 2, + 4 + ], + "schema_version": "1.0.0", + "updated": "2026-09-02", + "question_files": [ + "single_choice", + "fill_blank" + ], + "stats": { + "total": 20, + "by_type": { + "single_choice": 10, + "fill_blank": 10 + } + } +} diff --git a/topics/networking/http-handshake/single_choice.json b/topics/networking/http-handshake/single_choice.json new file mode 100644 index 0000000..dfd35e5 --- /dev/null +++ b/topics/networking/http-handshake/single_choice.json @@ -0,0 +1,211 @@ +{ + "topic": "http-handshake", + "type": "single_choice", + "schema_version": "1.0.0", + "generated": "2026-09-02T21:20:00+08:00", + "questions": [ + { + "id": "sc-001", + "type": "single_choice", + "difficulty": 2, + "tags": [ + "TCP", + "三次握手" + ], + "question": "TCP 三次握手中,第一次握手客户端发送的标志位是什么?", + "options": { + "A": "SYN", + "B": "ACK", + "C": "FIN", + "D": "RST" + }, + "answer": "A", + "explanation": "第一次握手客户端发送 SYN(Synchronize)标志,表示发起连接请求。ACK 是确认标志,FIN 是结束连接标志,RST 是重置连接标志。", + "source": null, + "related": [] + }, + { + "id": "sc-002", + "type": "single_choice", + "difficulty": 3, + "tags": [ + "TLS", + "HTTPS", + "握手" + ], + "question": "TLS 1.2 的握手需要多少个 RTT(往返)?", + "options": { + "A": "0-RTT", + "B": "1-RTT", + "C": "2-RTT", + "D": "3-RTT" + }, + "answer": "C", + "explanation": "TLS 1.2 需要 2-RTT 完成密钥协商。TLS 1.3 优化到 1-RTT,TLS 1.3 的 0-RTT 恢复可以在恢复会话时做到 0-RTT。", + "source": null, + "related": [] + }, + { + "id": "sc-003", + "type": "single_choice", + "difficulty": 2, + "tags": [ + "HTTP/1.x", + "应用层握手" + ], + "question": "HTTP/1.0 和 HTTP/1.1 有应用层握手吗?", + "options": { + "A": "有,通过 OPTIONS 请求", + "B": "有,通过 GET 请求", + "C": "没有,TCP 连上后直接发请求", + "D": "有,通过 SETTINGS 帧" + }, + "answer": "C", + "explanation": "HTTP/1.0 和 HTTP/1.1 没有应用层握手。TCP 连接建立后,TLS 握手完成后,客户端直接发送 HTTP 请求。SETTINGS 帧是 HTTP/2 的特性。", + "source": null, + "related": [] + }, + { + "id": "sc-004", + "type": "single_choice", + "difficulty": 3, + "tags": [ + "HTTP/2", + "SETTINGS" + ], + "question": "HTTP/2 的 SETTINGS 交换发生在哪个层?", + "options": { + "A": "传输层", + "B": "安全层", + "C": "应用层", + "D": "物理层" + }, + "answer": "C", + "explanation": "HTTP/2 的 SETTINGS 交换发生在应用层,双方通过 SETTINGS 帧协商 HTTP 协议参数(如最大并发流数、窗口大小等)。它不是传输层的 TCP 握手。", + "source": null, + "related": [] + }, + { + "id": "sc-005", + "type": "single_choice", + "difficulty": 4, + "tags": [ + "HTTP/3", + "QUIC" + ], + "question": "HTTP/3 基于 QUIC 协议,QUIC 运行在什么之上?", + "options": { + "A": "TCP", + "B": "UDP", + "C": "ICMP", + "D": "SCTP" + }, + "answer": "B", + "explanation": "QUIC 运行在 UDP 之上,它把传输层握手和 TLS 1.3 握手合二为一。首次连接只需 1-RTT,恢复连接可达 0-RTT。", + "source": null, + "related": [] + }, + { + "id": "sc-006", + "type": "single_choice", + "difficulty": 2, + "tags": [ + "TCP", + "三次握手" + ], + "question": "TCP 三次握手的本质目的是什么?", + "options": { + "A": "分配端口号", + "B": "双方各确认一次收发能力", + "C": "协商传输速率", + "D": "加密数据" + }, + "answer": "B", + "explanation": "三次握手的本质是双方各确认一次收发能力,确保连接可靠。客户端确认自己能发能收,服务器确认自己能发能收。", + "source": null, + "related": [] + }, + { + "id": "sc-007", + "type": "single_choice", + "difficulty": 3, + "tags": [ + "TLS", + "HTTPS", + "重放攻击" + ], + "question": "TLS 1.3 的 0-RTT 恢复存在什么安全风险?", + "options": { + "A": "中间人攻击", + "B": "重放攻击", + "C": "DDoS 攻击", + "D": "SQL 注入" + }, + "answer": "B", + "explanation": "0-RTT 恢复虽然快,但存在重放攻击风险。0-RTT 发送的数据不能保证幂等性,因此不适合做敏感操作(如支付)。", + "source": null, + "related": [] + }, + { + "id": "sc-008", + "type": "single_choice", + "difficulty": 4, + "tags": [ + "TCP", + "三次握手" + ], + "question": "为什么 TCP 握手是三次而不是两次?", + "options": { + "A": "三次握手更快", + "B": "两次握手无法防止已失效的连接请求到达服务器", + "C": "三次握手更安全", + "D": "三次握手节省带宽" + }, + "answer": "B", + "explanation": "两次握手无法防止已失效的连接请求到达服务器。如果客户端的旧 SYN 延迟到达,服务器会误以为是新请求并建立连接,白白浪费资源。三次握手让客户端有最后一次确认的机会。", + "source": null, + "related": [] + }, + { + "id": "sc-009", + "type": "single_choice", + "difficulty": 3, + "tags": [ + "TLS", + "HTTPS" + ], + "question": "TLS 握手完成的三件事不包括以下哪项?", + "options": { + "A": "证书验证", + "B": "密钥协商", + "C": "加密套件确定", + "D": "IP 地址分配" + }, + "answer": "D", + "explanation": "TLS 握手完成的事包括:证书验证(确认服务器身份)、密钥协商(生成对称加密密钥)、加密套件确定(双方商定使用的加密算法)。IP 地址分配是 DHCP 的工作。", + "source": null, + "related": [] + }, + { + "id": "sc-010", + "type": "single_choice", + "difficulty": 4, + "tags": [ + "HTTP/3", + "QUIC", + "TCP" + ], + "question": "HTTP/3 相比 HTTP/2 解决了 TCP 的什么问题?", + "options": { + "A": "带宽不足", + "B": "队头阻塞", + "C": "加密不够", + "D": "连接数限制" + }, + "answer": "B", + "explanation": "HTTP/3 基于 QUIC 解决了 TCP 的队头阻塞问题——单个流丢包不影响其他流。HTTP/2 虽然有多路复用,但底层 TCP 的队头阻塞仍然存在。", + "source": null, + "related": [] + } + ] +} \ No newline at end of file diff --git a/topics/networking/http-keepalive/fill_blank.json b/topics/networking/http-keepalive/fill_blank.json new file mode 100644 index 0000000..b737437 --- /dev/null +++ b/topics/networking/http-keepalive/fill_blank.json @@ -0,0 +1,201 @@ +{ + "topic": "http-keepalive", + "type": "fill_blank", + "schema_version": "1.0.0", + "generated": "2026-09-02T21:20:00+08:00", + "questions": [ + { + "id": "fb-001", + "type": "fill_blank", + "difficulty": 2, + "tags": [ + "HTTP/1.0", + "TCP", + "连接模型" + ], + "question": "HTTP/1.0 不支持 Keep-Alive,每次请求都需要建立一个新的 TCP 连接,即____模型。", + "answer": [ + "一请求一连接", + "一次请求一个连接", + "短连接" + ], + "answer_rule": "any", + "explanation": "HTTP/1.0 默认行为是每次 HTTP 请求都经历完整的 TCP 三次握手,请求完成后立即关闭连接。这意味着每次请求都要付出建立和拆除连接的开销,在页面包含大量资源时效率极低。这是后来引入 Keep-Alive 机制的直接动因。", + "source": null, + "related": [] + }, + { + "id": "fb-002", + "type": "fill_blank", + "difficulty": 2, + "tags": [ + "HTTP/1.1", + "Keep-Alive", + "默认行为" + ], + "question": "与 HTTP/1.0 不同,HTTP/1.1 默认启用____机制,无需显式声明即可复用 TCP 连接。", + "answer": [ + "Keep-Alive", + "持久连接", + "keep-alive" + ], + "answer_rule": "any", + "explanation": "HTTP/1.1 将 Keep-Alive(持久连接)作为默认行为。客户端和服务器建立连接后,可以在这个连接上依次发送和接收多个请求/响应,而不需要每次请求都重新建连。如果需要关闭连接,需要显式发送 Connection: close 头。", + "source": null, + "related": [] + }, + { + "id": "fb-003", + "type": "fill_blank", + "difficulty": 2, + "tags": [ + "Connection头", + "Keep-Alive", + "HTTP头" + ], + "question": "HTTP 协议通过 ____ 请求头来协商连接复用行为。当值为 Keep-Alive 表示____,当值为 close 表示____。", + "answer": [ + "Connection,希望复用连接,请求完成后关闭连接", + "Connection 希望复用连接 请求完成后关闭连接" + ], + "answer_rule": "any", + "explanation": "Connection 是 HTTP 中控制连接行为的首部字段。在 HTTP/1.1 中默认 Keep-Alive,通常不需要显式声明;但在 HTTP/1.0 中需要显式发送 Connection: Keep-Alive 才能启用复用。Connection: close 则用于告诉对端:这是最后一个请求,处理完请关闭连接。", + "source": null, + "related": [] + }, + { + "id": "fb-004", + "type": "fill_blank", + "difficulty": 3, + "tags": [ + "Keep-Alive参数", + "timeout", + "max" + ], + "question": "Keep-Alive 的 timeout 参数表示____,max 参数表示____,两者任一达到限制服务器即关闭连接。", + "answer": [ + "空闲超时时间,最大请求次数", + "空闲超时时间 最大请求数量" + ], + "answer_rule": "any", + "explanation": "timeout 指连接在空闲状态下(没有新请求)的最大存活时间,单位通常为秒;max 指该连接上允许处理的最大请求次数。例如 Keep-Alive: timeout=5, max=100 表示空闲超过 5 秒或已处理 100 个请求后关闭连接。服务器通过这两个参数防止连接被客户端长期占用。", + "source": null, + "related": [] + }, + { + "id": "fb-005", + "type": "fill_blank", + "difficulty": 3, + "tags": [ + "队头阻塞", + "HTTP/1.1", + "串行" + ], + "question": "HTTP/1.1 Keep-Alive 模式下,同一连接上的请求是严格____的,前一个请求的响应未返回之前不能发送下一个请求,这被称为____。", + "answer": [ + "串行,队头阻塞", + "串行 队头阻塞" + ], + "answer_rule": "any", + "explanation": "HTTP/1.1 的管道化(pipelining)虽然理论上允许客户端连续发送多个请求,但浏览器出于兼容性和安全性考虑基本未启用。实际中同一连接上的请求仍然是串行的——必须等上一个响应完全接收后才能发送下一个请求。这种「队头阻塞」(head-of-line blocking)是 HTTP/1.1 最大的性能瓶颈。", + "source": null, + "related": [] + }, + { + "id": "fb-006", + "type": "fill_blank", + "difficulty": 3, + "tags": [ + "浏览器", + "并发连接", + "队头阻塞" + ], + "question": "为规避 HTTP/1.1 的队头阻塞问题,主流浏览器对同一域名默认开启____个 TCP 连接来并行加载资源。", + "answer": [ + "6" + ], + "answer_rule": "all", + "explanation": "Chrome、Firefox 等主流浏览器对同一域名同时打开 6 个 TCP 连接(HTTP/1.1),将资源分配到不同连接上并行下载。这是一种工程上的折中:既利用了 Keep-Alive 复用,又通过多连接规避了串行问题。但这会消耗更多连接数和内存,也是推动 HTTP/2 普及的重要原因之一。", + "source": null, + "related": [] + }, + { + "id": "fb-007", + "type": "fill_blank", + "difficulty": 4, + "tags": [ + "HTTP演进", + "Keep-Alive", + "HTTP/2", + "HTTP/3" + ], + "question": "HTTP 连接复用的演进路线为:无 Keep-Alive(HTTP/1.0)→____(HTTP/1.1)→____(HTTP/2)→____(HTTP/3,基于 QUIC 解决 TCP 层队头阻塞)。", + "answer": [ + "Keep-Alive,多路复用,QUIC" + ], + "answer_rule": "ordered", + "explanation": "HTTP/1.0 每请求一连接;HTTP/1.1 引入 Keep-Alive 实现连接复用但有队头阻塞;HTTP/2 通过多路复用在单连接上交错传输多个流,解决了应用层队头阻塞;HTTP/3 基于 QUIC 协议用 UDP 替代 TCP,连传输层的队头阻塞也一并解决。这条演进路线的核心驱动力就是不断提升连接复用效率。", + "source": null, + "related": [] + }, + { + "id": "fb-008", + "type": "fill_blank", + "difficulty": 3, + "tags": [ + "空闲连接", + "内存", + "文件描述符" + ], + "question": "Keep-Alive 空闲连接虽然不传输数据,但仍占用服务器的____和____资源,大量空闲连接会导致资源耗尽。", + "answer": [ + "内存,文件描述符", + "内存 fd", + "内存 socket" + ], + "answer_rule": "any", + "explanation": "每条 TCP 连接在内核中需要维护 socket 缓冲区(消耗内存)和一个文件描述符(fd)。如果服务器对所有客户端保持大量空闲的 Keep-Alive 连接,内存和 fd 可能被耗尽,导致新连接无法建立。这就是为什么服务器必须配置合理的 keepalive_timeout 和 max connections 来限制空闲连接数。", + "source": null, + "related": [] + }, + { + "id": "fb-009", + "type": "fill_blank", + "difficulty": 3, + "tags": [ + "服务器", + "连接关闭", + "RST" + ], + "question": "当服务器单方面关闭了一个 Keep-Alive 空闲连接时,客户端可能并不知道连接已断开,下次请求时会收到____错误。", + "answer": [ + "Connection reset by peer", + "连接被重置", + "连接重置" + ], + "answer_rule": "any", + "explanation": "服务器因超时或负载原因关闭了空闲连接后发送 FIN 或 RST 包,但如果客户端此时没有监听,可能不会感知。当下次请求尝试使用这条已关闭的连接时,会收到 Connection reset by peer(对端重置连接)或 Broken pipe 错误。成熟的客户端库会自动重试新建连接来处理此情况。", + "source": null, + "related": [] + }, + { + "id": "fb-010", + "type": "fill_blank", + "difficulty": 2, + "tags": [ + "Keep-Alive", + "利弊", + "权衡" + ], + "question": "Keep-Alive 的核心优势是____,主要代价是空闲时____,需要根据场景权衡使用。", + "answer": [ + "减少建连开销、提升吞吐,占用内存和文件描述符", + "减少握手开销 占用资源" + ], + "answer_rule": "any", + "explanation": "Keep-Alive 通过复用 TCP 连接避免了反复建连和拆连的开销,显著提升高频请求场景的吞吐和延迟。但代价是空闲连接会持续占用服务器的内存和文件描述符资源。因此在高频访问的场景应开启,而对低频或一次性的请求,Keep-Alive 的收益很小,反而白白占用资源。", + "source": null, + "related": [] + } + ] +} \ No newline at end of file diff --git a/topics/networking/http-keepalive/meta.json b/topics/networking/http-keepalive/meta.json new file mode 100644 index 0000000..24c1946 --- /dev/null +++ b/topics/networking/http-keepalive/meta.json @@ -0,0 +1,50 @@ +{ + "slug": "http-keepalive", + "name": "Http Keepalive", + "description": "", + "tags": [ + "Connection头", + "HTTP/1.0", + "HTTP/1.1", + "HTTP/2", + "HTTP/3", + "HTTP头", + "HTTP演进", + "Keep-Alive", + "Keep-Alive参数", + "RST", + "TCP", + "max", + "timeout", + "串行", + "内存", + "利弊", + "并发连接", + "文件描述符", + "服务器", + "权衡", + "浏览器", + "空闲连接", + "连接关闭", + "连接模型", + "队头阻塞", + "默认行为" + ], + "difficulty_range": [ + 2, + 4 + ], + "schema_version": "1.0.0", + "updated": "2026-09-02", + "question_files": [ + "single_choice", + "fill_blank" + ], + "stats": { + "total": 20, + "by_type": { + "single_choice": 10, + "fill_blank": 10 + } + } +} diff --git a/topics/networking/http-keepalive/single_choice.json b/topics/networking/http-keepalive/single_choice.json new file mode 100644 index 0000000..5f1757c --- /dev/null +++ b/topics/networking/http-keepalive/single_choice.json @@ -0,0 +1,210 @@ +{ + "topic": "http-keepalive", + "type": "single_choice", + "schema_version": "1.0.0", + "generated": "2026-09-02T21:20:00+08:00", + "questions": [ + { + "id": "sc-001", + "type": "single_choice", + "difficulty": 2, + "tags": [ + "Keep-Alive", + "HTTP/1.1" + ], + "question": "HTTP/1.1 默认是否启用 Keep-Alive?", + "options": { + "A": "默认不启用", + "B": "默认启用", + "C": "需要手动配置", + "D": "仅 HTTPS 启用" + }, + "answer": "B", + "explanation": "HTTP/1.1 默认启用 Keep-Alive,建一次连接后可以连续发多个请求,不用每次重新握手。这是 HTTP/1.1 最重要的默认优化。", + "source": null, + "related": [] + }, + { + "id": "sc-002", + "type": "single_choice", + "difficulty": 3, + "tags": [ + "Keep-Alive", + "Connection头" + ], + "question": "服务器通过哪个响应头告诉客户端连接可以复用?", + "options": { + "A": "Connection: close", + "B": "Connection: Keep-Alive", + "C": "Keep-Alive: true", + "D": "Reuse: true" + }, + "answer": "B", + "explanation": "服务器通过 Connection: Keep-Alive 响应头告诉客户端这个连接可以复用。Connection: close 表示不要复用。", + "source": null, + "related": [] + }, + { + "id": "sc-003", + "type": "single_choice", + "difficulty": 3, + "tags": [ + "Keep-Alive", + "timeout", + "max" + ], + "question": "Keep-Alive: timeout=5, max=100 中 max=100 的含义是?", + "options": { + "A": "最大并发连接数为 100", + "B": "空闲超时 100 秒", + "C": "单连接最多处理 100 个请求后强制关闭", + "D": "最大响应大小 100KB" + }, + "answer": "C", + "explanation": "max=100 表示单连接最多处理 100 个请求后强制关闭。timeout=5 表示空闲超过 5 秒没新请求就关闭连接。两者取先触发的那个。", + "source": null, + "related": [] + }, + { + "id": "sc-004", + "type": "single_choice", + "difficulty": 3, + "tags": [ + "队头阻塞", + "HTTP/1.1" + ], + "question": "Keep-Alive 引入了什么问题?", + "options": { + "A": "带宽不足", + "B": "队头阻塞", + "C": "DNS 解析慢", + "D": "TLS 握手失败" + }, + "answer": "B", + "explanation": "Keep-Alive 解决了频繁握手问题,但引入了队头阻塞——同一连接上的请求严格串行,后一个请求必须等前一个响应返回。浏览器用多开 6 个连接来弥补。", + "source": null, + "related": [] + }, + { + "id": "sc-005", + "type": "single_choice", + "difficulty": 4, + "tags": [ + "HTTP/1.1", + "浏览器", + "6连接" + ], + "question": "HTTP/1.1 的 Keep-Alive 存在队头阻塞,浏览器用什么方式弥补?", + "options": { + "A": "使用 HTTP/2", + "B": "同时开 6 个 TCP 连接", + "C": "使用 UDP 协议", + "D": "增大窗口大小" + }, + "answer": "B", + "explanation": "浏览器同时开 6 个 TCP 连接来弥补 HTTP/1.1 的队头阻塞问题——一条收银通道排队太慢,所以超市开了 6 个收银台,但每个收银台内部还是串行的。", + "source": null, + "related": [] + }, + { + "id": "sc-006", + "type": "single_choice", + "difficulty": 2, + "tags": [ + "Keep-Alive", + "HTTP/1.0" + ], + "question": "HTTP/1.0 的连接模型是怎样的?", + "options": { + "A": "一个连接串行多个请求", + "B": "一个请求一个连接,用完即弃", + "C": "一个连接并行多个请求", + "D": "使用 UDP 协议" + }, + "answer": "B", + "explanation": "HTTP/1.0 没有 Keep-Alive,一个请求一个连接,用完即弃。每个资源都要走一遍握手流程,光握手就比传数据花的时间还多。", + "source": null, + "related": [] + }, + { + "id": "sc-007", + "type": "single_choice", + "difficulty": 4, + "tags": [ + "Keep-Alive", + "空闲连接" + ], + "question": "Keep-Alive 连接在空闲时会占用什么资源?", + "options": { + "A": "只占用 CPU", + "B": "占用内存和文件描述符", + "C": "不占用任何资源", + "D": "只占用网络带宽" + }, + "answer": "B", + "explanation": "Keep-Alive 连接在空闲时仍然占内存和 fd。如果客户端长时间不发新请求,服务器应该主动关闭。合理设置 timeout 参数很重要。", + "source": null, + "related": [] + }, + { + "id": "sc-008", + "type": "single_choice", + "difficulty": 3, + "tags": [ + "Keep-Alive", + "演进" + ], + "question": "HTTP 连接复用的演进路线正确的是?", + "options": { + "A": "Keep-Alive → HTTP/1.0 → HTTP/2", + "B": "无 Keep-Alive → Keep-Alive → HTTP/2 多路复用 → HTTP/3 QUIC", + "C": "HTTP/2 → Keep-Alive → HTTP/3", + "D": "TCP → UDP → HTTP/3" + }, + "answer": "B", + "explanation": "演进路线:HTTP/1.0 无 Keep-Alive → HTTP/1.1 默认 Keep-Alive → 浏览器并行连接缓解队头阻塞 → HTTP/2 多路复用 → HTTP/3 QUIC 基于 UDP 的多路复用。", + "source": null, + "related": [] + }, + { + "id": "sc-009", + "type": "single_choice", + "difficulty": 2, + "tags": [ + "Keep-Alive", + "Connection头" + ], + "question": "不想复用连接时,服务器应该返回什么头?", + "options": { + "A": "Connection: Keep-Alive", + "B": "Connection: close", + "C": "Keep-Alive: false", + "D": "Connection: none" + }, + "answer": "B", + "explanation": "不想复用连接时,服务器返回 Connection: close,告诉客户端处理完当前请求后关闭连接。", + "source": null, + "related": [] + }, + { + "id": "sc-010", + "type": "single_choice", + "difficulty": 3, + "tags": [ + "Keep-Alive", + "服务器" + ], + "question": "服务器因超时关闭了连接,但客户端下次复用时才发现连接已死,这种情况应该怎么处理?", + "options": { + "A": "忽略错误", + "B": "开启连接健康检查或设置合理的超时", + "C": "增大连接池", + "D": "使用 HTTP/1.0" + }, + "answer": "B", + "explanation": "服务器因超时关闭了连接,但客户端不知道,下次复用时才发现连接已死。需要开启连接健康检查或设置合理的超时来避免这个问题。", + "source": null, + "related": [] + } + ] +} \ No newline at end of file diff --git a/topics/networking/keepalive-scenarios/fill_blank.json b/topics/networking/keepalive-scenarios/fill_blank.json new file mode 100644 index 0000000..2a16753 --- /dev/null +++ b/topics/networking/keepalive-scenarios/fill_blank.json @@ -0,0 +1,202 @@ +{ + "topic": "keepalive-scenarios", + "type": "fill_blank", + "schema_version": "1.0.0", + "generated": "2026-09-02T21:20:00+08:00", + "questions": [ + { + "id": "fb-001", + "type": "fill_blank", + "difficulty": 2, + "tags": [ + "高频请求", + "Keep-Alive", + "适用场景" + ], + "question": "当客户端需要短时间内向同一服务器发起____请求时,Keep-Alive 连接复用的收益最大。", + "answer": [ + "大量", + "多个", + "高频" + ], + "answer_rule": "any", + "explanation": "Keep-Alive 的核心价值在于复用已建立的 TCP 连接,避免反复握手。当客户端需要在同一服务器上获取大量资源(如页面加载、API 批量调用)时,复用一条连接比每次都新建连接节省大量 RTT 和 CPU 开销。高频请求是 Keep-Alive 最典型的应用场景。", + "source": null, + "related": [] + }, + { + "id": "fb-002", + "type": "fill_blank", + "difficulty": 3, + "tags": [ + "移动端", + "高延迟", + "握手开销" + ], + "question": "在移动端____网络环境下,TCP 和 TLS 握手的 RTT 开销相对更大,使用 Keep-Alive 省去重复握手尤为重要。", + "answer": [ + "高延迟", + "弱网", + "高时延" + ], + "answer_rule": "any", + "explanation": "移动网络的典型 RTT 在 50-200ms 甚至更高,TCP 三次握手至少需要 1.5 个 RTT,TLS 1.2 还需额外 1-2 个 RTT。如果每次请求都重新握手,延迟代价非常显著。Keep-Alive 让后续请求在已建立的安全连接上直接发送,省去了宝贵的握手时间。", + "source": null, + "related": [] + }, + { + "id": "fb-003", + "type": "fill_blank", + "difficulty": 2, + "tags": [ + "微服务", + "内部通信", + "Keep-Alive" + ], + "question": "微服务架构中,服务间调用频率极高,Keep-Alive 是内部 RPC/HTTP 通信的____。", + "answer": [ + "标配", + "默认配置", + "基础能力" + ], + "answer_rule": "any", + "explanation": "微服务之间通常有大量高频的内部调用(如订单服务调用库存、用户、支付等),每次调用都新建连接的开销不可接受。gRPC、Dubbo 等微服务框架默认就使用 HTTP/2 或自定义协议的长连接,Keep-Alive 是服务间通信的基本配置。", + "source": null, + "related": [] + }, + { + "id": "fb-004", + "type": "fill_blank", + "difficulty": 2, + "tags": [ + "低频请求", + "一次性请求", + "Keep-Alive" + ], + "question": "对于只发一次请求的客户端(如一次性 Webhook 回调),Keep-Alive 的复用优势____,反而增加了服务器维护空闲连接的负担。", + "answer": [ + "几乎为零", + "不明显", + "基本没有" + ], + "answer_rule": "any", + "explanation": "Keep-Alive 的优势在于「多次复用」,只发一次请求的场景下建连开销只付出一次,不需要复用。开启 Keep-Alive 反而让服务器多维护一条空闲连接直到超时,浪费内存和文件描述符。对于此类场景,发送 Connection: close 让服务器立即关闭连接更合理。", + "source": null, + "related": [] + }, + { + "id": "fb-005", + "type": "fill_blank", + "difficulty": 4, + "tags": [ + "安全隔离", + "Keep-Alive", + "不适用场景" + ], + "question": "在需要严格____的安全隔离场景中(如网关对不可信客户端),不宜使用 Keep-Alive,应每次请求后立即关闭连接。", + "answer": [ + "请求隔离", + "连接隔离", + "隔离" + ], + "answer_rule": "any", + "explanation": "安全隔离场景要求每个请求使用独立的连接,以防止前一个请求的上下文(如认证状态、缓冲区残留数据)影响后续请求。例如反向代理对不可信客户端通常使用短连接,避免连接被恶意复用导致的安全风险。Keep-Alive 复用连接会模糊请求边界,不利于安全审计和隔离。", + "source": null, + "related": [] + }, + { + "id": "fb-006", + "type": "fill_blank", + "difficulty": 3, + "tags": [ + "大文件传输", + "连接策略" + ], + "question": "大文件下载/上传时,应该使用____连接而非 Keep-Alive 连接,避免大体积传输长时间占用连接池资源。", + "answer": [ + "独立", + "单独", + "专用" + ], + "answer_rule": "any", + "explanation": "大文件传输会长时间占用连接,如果和其他小请求共享 Keep-Alive 连接池,会导致小请求排队等待。正确做法是为大文件传输创建独立的连接(或使用专门的连接池),与业务 API 的连接池隔离,确保小请求的低延迟不受影响。", + "source": null, + "related": [] + }, + { + "id": "fb-007", + "type": "fill_blank", + "difficulty": 3, + "tags": [ + "Nginx", + "keepalive_timeout", + "配置" + ], + "question": "Nginx 中 keepalive_timeout 指令控制____,keepalive_requests 指令控制____。", + "answer": [ + "空闲连接超时时间,单个连接最大请求数", + "空闲超时时间 单连接最大请求数" + ], + "answer_rule": "any", + "explanation": "keepalive_timeout 默认 75s,表示空闲超过此时间后 Nginx 主动关闭连接;keepalive_requests 默认 1000,表示单个连接最多处理 1000 个请求后关闭。在高并发场景下应适当调大 keepalive_requests 以充分发挥连接复用优势,同时 keepalive_timeout 不宜过长以避免空闲连接堆积。", + "source": null, + "related": [] + }, + { + "id": "fb-008", + "type": "fill_blank", + "difficulty": 3, + "tags": [ + "Tomcat", + "keepAliveTimeout", + "单位" + ], + "question": "Tomcat 的 keepAliveTimeout 配置单位是____(注意与 Nginx 的秒不同),超时后服务器关闭空闲连接。", + "answer": [ + "毫秒" + ], + "answer_rule": "all", + "explanation": "Tomcat 的 keepAliveTimeout 单位为毫秒,默认值因版本而异(如 20000 即 20 秒)。这与 Nginx 的 keepalive_timeout 使用秒作为单位不同,配置时需要特别注意,避免因单位混淆导致超时设置偏差 1000 倍。Spring Boot 中通常通过 server.connection-timeout 等属性间接配置。", + "source": null, + "related": [] + }, + { + "id": "fb-009", + "type": "fill_blank", + "difficulty": 4, + "tags": [ + "公共API", + "连接数限制", + "防护" + ], + "question": "对外提供的公共 API 服务必须限制客户端的____数量,防止恶意或不当客户端耗尽服务器连接资源,影响其他正常用户。", + "answer": [ + "并发连接", + "连接", + "Keep-Alive连接" + ], + "answer_rule": "any", + "explanation": "公共 API 面向不可控的外部用户,如果不加限制,某个客户端可能建立大量 Keep-Alive 连接长期占用资源(连接耗尽攻击)。需要通过限流(rate limiting)、最大连接数限制、较短的 keepalive_timeout 等手段进行防护。Nginx 可通过 limit_conn 模块控制单 IP 的并发连接数。", + "source": null, + "related": [] + }, + { + "id": "fb-010", + "type": "fill_blank", + "difficulty": 4, + "tags": [ + "连接数瓶颈", + "超时调优", + "运维" + ], + "question": "当出现连接数瓶颈时,缩短 Keep-Alive 超时时间可以更快释放空闲连接,但会导致____增加,需要在____和资源释放之间找到平衡。", + "answer": [ + "重连开销,连接复用效率" + ], + "answer_rule": "all", + "explanation": "缩短 keepalive_timeout 能更快回收空闲连接,缓解连接数不足的问题,但代价是客户端后续请求需要重新建连(三次握手 + 可能的 TLS 握手),增加了延迟和 CPU 开销。最佳实践是:先调大最大连接数,再适当缩短超时时间,同时监控重连率和延迟 P99 来验证效果。", + "source": null, + "related": [] + } + ] +} \ No newline at end of file diff --git a/topics/networking/keepalive-scenarios/meta.json b/topics/networking/keepalive-scenarios/meta.json new file mode 100644 index 0000000..4d6572f --- /dev/null +++ b/topics/networking/keepalive-scenarios/meta.json @@ -0,0 +1,50 @@ +{ + "slug": "keepalive-scenarios", + "name": "Keepalive Scenarios", + "description": "", + "tags": [ + "Keep-Alive", + "Nginx", + "Tomcat", + "keepAliveTimeout", + "keepalive_timeout", + "一次性请求", + "不适用场景", + "低频请求", + "公共API", + "内部通信", + "单位", + "大文件传输", + "安全隔离", + "微服务", + "握手开销", + "移动端", + "超时调优", + "运维", + "连接数瓶颈", + "连接数限制", + "连接策略", + "适用场景", + "配置", + "防护", + "高延迟", + "高频请求" + ], + "difficulty_range": [ + 2, + 4 + ], + "schema_version": "1.0.0", + "updated": "2026-09-02", + "question_files": [ + "single_choice", + "fill_blank" + ], + "stats": { + "total": 20, + "by_type": { + "single_choice": 10, + "fill_blank": 10 + } + } +} diff --git a/topics/networking/keepalive-scenarios/single_choice.json b/topics/networking/keepalive-scenarios/single_choice.json new file mode 100644 index 0000000..1a4c7c8 --- /dev/null +++ b/topics/networking/keepalive-scenarios/single_choice.json @@ -0,0 +1,209 @@ +{ + "topic": "keepalive-scenarios", + "type": "single_choice", + "schema_version": "1.0.0", + "generated": "2026-09-02T21:20:00+08:00", + "questions": [ + { + "id": "sc-001", + "type": "single_choice", + "difficulty": 2, + "tags": [ + "Keep-Alive", + "使用场景" + ], + "question": "以下哪种场景最适合启用 Keep-Alive?", + "options": { + "A": "用户一天登录一次,查一次个人信息", + "B": "前端页面加载 30 个资源", + "C": "下载 2GB 大文件", + "D": "银行转账操作" + }, + "answer": "B", + "explanation": "前端页面加载 30 个资源属于高频请求同一服务器,Keep-Alive 省掉大量握手开销。一次性低频请求、大文件传输、安全隔离场景不太适合。", + "source": null, + "related": [] + }, + { + "id": "sc-002", + "type": "single_choice", + "difficulty": 3, + "tags": [ + "Keep-Alive", + "移动端", + "延迟" + ], + "question": "移动端 30 次请求 × 150ms 握手,有 Keep-Alive 时总握手耗时大约是多少?", + "options": { + "A": "4.5 秒", + "B": "150ms", + "C": "3 秒", + "D": "5 秒" + }, + "answer": "B", + "explanation": "有 Keep-Alive 时只需要握手一次,30 次请求的总握手耗时约 150ms(只握手一次)。无 Keep-Alive 时需要 4.5 秒(30 × 150ms)。", + "source": null, + "related": [] + }, + { + "id": "sc-003", + "type": "single_choice", + "difficulty": 3, + "tags": [ + "Keep-Alive", + "安全隔离" + ], + "question": "以下哪种场景不建议使用 Keep-Alive?", + "options": { + "A": "微服务内部通信", + "B": "银行转账等敏感操作", + "C": "前端页面加载", + "D": "移动端 API 调用" + }, + "answer": "B", + "explanation": "银行转账等敏感操作需要安全隔离,每次操作用全新连接,确保上一次的上下文不会残留。多租户 SaaS 也需要隔离。", + "source": null, + "related": [] + }, + { + "id": "sc-004", + "type": "single_choice", + "difficulty": 4, + "tags": [ + "Keep-Alive", + "大文件" + ], + "question": "大文件传输和小请求混用同一连接池会导致什么问题?", + "options": { + "A": "DNS 解析失败", + "B": "大文件独占连接时间过长,导致小请求排队", + "C": "TLS 握手失败", + "D": "连接池自动扩容" + }, + "answer": "B", + "explanation": "大文件传输独占连接时间过长,导致小请求排队。应该分离:大文件用独立连接(Connection: close),小请求用连接池复用。", + "source": null, + "related": [] + }, + { + "id": "sc-005", + "type": "single_choice", + "difficulty": 2, + "tags": [ + "Keep-Alive", + "Nginx" + ], + "question": "Nginx 中 keepalive_timeout 10s 表示什么?", + "options": { + "A": "连接最大存活 10 秒", + "B": "空闲 10 秒后关闭连接", + "C": "每 10 秒发一次心跳", + "D": "最大并发 10 个连接" + }, + "answer": "B", + "explanation": "keepalive_timeout 10s 表示空闲 10 秒后关闭连接。keepalive_requests 100 表示单连接最多处理 100 个请求。", + "source": null, + "related": [] + }, + { + "id": "sc-006", + "type": "single_choice", + "difficulty": 3, + "tags": [ + "Keep-Alive", + "连接数" + ], + "question": "当连接数是瓶颈时,应该怎么做?", + "options": { + "A": "关掉 Keep-Alive", + "B": "缩短超时时间而不是关掉 Keep-Alive", + "C": "增大连接池到无限大", + "D": "使用 HTTP/1.0" + }, + "answer": "B", + "explanation": "连接数是瓶颈时应该缩短超时时间(如 keepalive_timeout 5s),而不是关掉 Keep-Alive。这样既保留了复用优势,又释放了空闲连接。", + "source": null, + "related": [] + }, + { + "id": "sc-007", + "type": "single_choice", + "difficulty": 4, + "tags": [ + "Keep-Alive", + "公共API" + ], + "question": "公共 API 忘记限制 Keep-Alive 会导致什么安全问题?", + "options": { + "A": "数据泄露", + "B": "恶意客户端长期霸占连接", + "C": "SQL 注入", + "D": "XSS 攻击" + }, + "answer": "B", + "explanation": "公共 API 忘记限制 Keep-Alive 超时和最大请求数,一个恶意客户端可以长期霸占连接。应该设置 keepalive_timeout 和 keepalive_requests 上限。", + "source": null, + "related": [] + }, + { + "id": "sc-008", + "type": "single_choice", + "difficulty": 2, + "tags": [ + "Keep-Alive", + "决策" + ], + "question": "请求间隔小于 Keep-Alive 超时时间时,应该启用 Keep-Alive 吗?", + "options": { + "A": "不应该", + "B": "应该", + "C": "无所谓", + "D": "取决于请求大小" + }, + "answer": "B", + "explanation": "请求间隔小于 Keep-Alive 超时时间时应该启用 Keep-Alive,因为连接可以被复用,省掉握手开销。", + "source": null, + "related": [] + }, + { + "id": "sc-009", + "type": "single_choice", + "difficulty": 3, + "tags": [ + "Keep-Alive", + "Tomcat" + ], + "question": "Java Tomcat 中 keepAliveTimeout=\"20000\" 的单位是什么?", + "options": { + "A": "秒", + "B": "毫秒", + "C": "微秒", + "D": "分钟" + }, + "answer": "B", + "explanation": "Java Tomcat 中 keepAliveTimeout=\"20000\" 的单位是毫秒,即 20 秒。maxKeepAliveRequests=\"200\" 表示单连接最多处理 200 个请求。", + "source": null, + "related": [] + }, + { + "id": "sc-010", + "type": "single_choice", + "difficulty": 4, + "tags": [ + "Keep-Alive", + "决策" + ], + "question": "对于 API 网关同时服务移动端和 Web 端,Keep-Alive 配置应该怎么考虑?", + "options": { + "A": "移动端短超时,Web 端长超时", + "B": "移动端延迟高可以设长超时,Web 端延迟低可以设短超时,或取折中值", + "C": "统一设为最短超时", + "D": "统一关闭 Keep-Alive" + }, + "answer": "B", + "explanation": "移动端延迟高,Keep-Alive 收益大,超时可以设长一些(如 30 秒)。Web 端延迟低但并发高,超时可以短一些(如 10 秒)。可以按来源区分配置,或取折中值(10-15 秒)。", + "source": null, + "related": [] + } + ] +} \ No newline at end of file