计算机网络相关八股复习
采集插件需要增量采集,避免重复抓取、重复入库、浪费性能,你在 Go 里怎么做去重?
Redis Bitmap/ 布隆过滤器
| 对比项 | Redis Set | Redis Bitmap | 布隆过滤器 (本地 / RedisBloom) |
|---|---|---|---|
| 准确性 | 100% 精准,无误判 | 100% 精准,无误判 | 概率型:不存在 = 一定不存在,存在 = 可能误判,误判率可控 (0.01%~1%) |
| 存储数据 | 完整存原始字符串 (URL / 字符串) | 只能存数字下标(偏移量),无法直接存字符串 | 不存原始数据,多次 hash 映射 bit 位,只存标记位 |
| 内存开销 | 极大,单条 URL≈40~60Byte;1000 万 URL≈500MB+ | 稠密数据极省:1 亿 bit≈12MB;稀疏数据巨浪费(下标离散空出大量 bit) | 极致节省:100 万数据、0.01% 误判≈2.3MB,单元素仅十几 bit,是 Set 的 1/200 左右 |
| 增删能力 | 支持新增、删除、随机剔除成员 | 支持 SETBIT/GETBIT/DEL,单个 bit 可清零删除 | 标准布隆不支持删除(计数布隆除外,空间翻倍) |
| 查询复杂度 | O(1),SISMEMBER | O(1) GETBIT | O (k),k = 哈希函数数量 (7~14),固定耗时,不随数据量变大而变慢 |
| 数据格式限制 | 任意字符串:URL、手机号、文本都行 | 必须转为数字 ID,不能直接存 URL | 任意字符串,自动 hash 转下标,无需人工编码 ID |
四次挥手:断开 TCP 连接(全双工,两端可单独关发送)
标志位:FIN(结束)、ACK(确认),任意一方都能主动发起关闭,以客户端主动关闭举例:
第一次挥手:C→S:FIN=1
客户端不再发数据,发 FIN 报文,C 进入FIN_WAIT_1。
我不再发数据了。
第二次挥手:S→C:ACK=1
服务端回复 ACK 确认关闭客户端发送通道,S 进入CLOSE_WAIT;
此时服务端仍能向客户端发剩余数据,客户端收到 ACK 进入FIN_WAIT_2。
收到关闭请求,我不再收你的数据,但我还有数据可以发给你。
第三次挥手:S→C:FIN=1
服务端数据全部发完,发送 FIN,S 进入LAST_ACK。
我的数据发完了,我也要关闭发送。
第四次挥手:C→S:ACK=1
客户端回复 ACK,进入TIME_WAIT(等待 2MSL);
服务端收到 ACK 立刻CLOSED;客户端等待 2MSL 后无报文到达,最终关闭。
收到,连接彻底关闭。
三次握手两次行不行?四次挥手三次行不行?
一、为什么三次握手不能改成两次?
两次握手流程(假想)
- 客户端发 SYN
- 服务端回复 SYN+ACK,连接直接建立
致命问题:历史滞留 SYN 报文
客户端之前失效、迟到的旧SYN包延迟到达服务器:
- 服务器收到SYN → 立马回复SYN+ACK、建立连接、分配端口资源
- 客户端早已断开,无视这个ACK,不会收发数据
- 服务器一直阻塞占用资源,空等数据,造成资源浪费
三次握手的作用:
客户端第三次ACK用来确认服务端收到连接,只有客户端确认后,服务端才正式建连。迟到的旧SYN到达后,客户端不会回复ACK,服务器收不到第三次报文,自动释放连接,避免无效连接。
两次无法校验客户端是否在线、报文是否过期。
二、四次挥手能不能简化成三次?
特殊场景可以三次,正常大多必须四次
TCP是全双工通信,收发通道互相独立:A关发通道≠B不能继续发数据。
1. 常规四次(标准情况)
- C发FIN(C不再发数据)
- S回ACK(确认收到关闭,S还能继续发剩余数据)
- S数据发完再发FIN(S也不再发数据)
- C回ACK
第2、3步不能合并:中间存在服务端遗留数据传输时间,必须分开,所以是4次。
2. 可以变成3次挥手的特例
服务端收到FIN时,恰好数据全部发送完毕:
服务端可以把「ACK确认 + FIN关闭」合并成一个报文:
- C→S:FIN
- S→C:ACK+FIN(合并第二条、第三条报文)
- C→S:ACK
👉 此时就变成三次挥手,属于特例,不是通用规范。
三、高频面试小结
- 握手必须三次,两次绝对不行:防冗余SYN造成服务端僵死连接。
- 挥手默认四次,极端情况三次可行:只有服务端无剩余数据时才能合并ACK与FIN。
补充考点:TIME-WAIT为什么必须等2MSL
- 保证最后一个ACK丢包时,被动关闭方能重发FIN;
- 等待网络中旧报文全部过期,避免新连接收到历史残包。
WebSocket vs HTTP
1. 连接模型本质
HTTP:短连接 / 一问一答(单向)
- 请求→响应,连接用完就断(HTTP1.1长连接只是复用TCP,依旧一问一答)
- 只能客户端主动发请求,服务器不能主动推送数据
WebSocket:长连接全双工
- 先通过一次HTTP握手升级协议,后续全程复用同一条TCP连接
- 客户端、服务端任意一方随时主动发消息(服务端可主动推送)
2. 通信流程
HTTP
客户端发请求 → 服务器返回数据 → TCP断开/闲置断开
实时场景(聊天、推送)只能用轮询/长轮询,频繁发HTTP请求,开销大。WebSocket
① 客户端发HTTP GET带升级头(Upgrade:websocket)握手
② 服务端返回101 Switching Protocols,协议切换成功
③ 之后全双工二进制/文本传输,不再走HTTP协议
3. 头部开销
- HTTP:每次请求都带 Cookie、Header,头部冗余大
- WebSocket:握手后报文头极短(2~10字节),传输成本极低
4. 适用场景
HTTP
普通接口查询、静态资源下载、表单提交(一次性请求)
WebSocket
聊天室、实时大屏、股票行情、IM聊天、设备实时上报(需要服务端主动推送)
5. 版本补充
- HTTP/2 支持服务端推送,但仍是基于请求模型,做不到任意时刻自由互发消息,替代不了WS。
DNS、DHCP、ARP、ICMP 简明区分
1. ARP(地址解析协议)
链路层→网络层过渡,局域网内网
作用:IP地址 ↔ MAC地址互相转换
- 广播发ARP请求:已知IP,查对方网卡MAC
- 单播ARP应答:回复本机MAC
工作环境:同一个局域网,跨路由无效。
2. ICMP(网际控制报文协议)
网络层协议,依附IP承载
作用:差错报告、网络连通探测
典型应用:ping、traceroute(路由追踪)
- 目标不可达、超时、路由异常全靠ICMP上报。
3. DNS(域名系统)
应用层
作用:域名→IP解析(www.baidu.com → 服务器IP)
客户端发DNS查询→DNS服务器返回IP,之后才建立TCP连接。
4. DHCP(动态主机配置协议)
应用层(UDP 67/68端口)
作用:自动给终端分配IP、子网掩码、网关、DNS地址
四步流程:DISCOVER→OFFER→REQUEST→ACK
电脑插网线自动拿IP就是DHCP。
层级
- 二层附近:ARP
- 网络层:ICMP
- 应用层:DHCP、DNS
sync.map 底层
type Map struct {
mu Mutex // 互斥锁,仅保护 dirty map
read atomic.Value // 只读 map(readOnly 结构体),无锁访问
dirty map[any]*entry // 脏 map,包含最新数据,需要加锁访问
misses int // 读未命中计数器,触发 dirty 升级为 read
}
type readOnly struct {
m map[any]*entry
amended bool // true:dirty 有 read 没有的新数据
}
协程泄露
启动了一个 goroutine,但它永远无法退出、一直存活在后台,占用内存和资源不释放
redis防止雪崩
设置 TTL 时在基础过期时间上 + [1~30min]随机秒数,打散过期高峰,避免同一批淘汰
永不过期策略
热点数据不设置过期时间,靠主动更新 / 淘汰机制淘汰。
定时任务主动刷新热点缓存
在缓存过期前,后台异步主动更新缓存,避免过期空窗。
高可用架构:主从 + 哨兵 Sentinel / Redis Cluster 集群
主节点挂了自动故障切换,从顶替上位,缓存不整体瘫痪。
多级缓存(本地缓存 Caffeine/Guava + Redis)
应用 JVM 本地缓存做兜底,Redis 崩了优先走本地缓存,不会直达 DB。
限流 + 熔断 + 降级(Sentinel、Resilience4j)
限流:限制落到 DB 的 QPS
熔断:DB 压力过大直接熔断,返回兜底数据,不再查库
降级:非核心接口直接返回默认值
DB 层:分库分表、连接池限流
数据库连接池设合理最大值,防止连接被打满。





