152 / 163 · C11 · 约 8 分钟
分布式系统
先记住这句话
分布式系统把多台机器经网络连成整体服务。单机、磁盘和链路会故障,但冗余能让用户感觉系统几乎永不中断。通信本质不可靠,必须依靠校验和、确认与重传来构建可用协议。
故障既是挑战也是机遇
单台机器随时可能崩溃,磁盘可能损坏,网线可能被挖断。真正的技巧是把许多不完美的部件组合起来,让整体服务对客户端看起来始终在线。这正是当今大型网站背后的核心思路。
通信的不可靠本质
无论广域互联网还是机房内高速互连,数据包都可能因电气干扰、设备故障或交换机内存耗尽而消失。即使所有硬件都完好,突发流量仍会挤爆缓冲区并导致丢包。因此必须把每一次发送都视为可能失败。
用校验和发现损坏
发送前对数据字节求和(或计算更强的CRC),把结果连同报文一起发出。接收方重新计算并比较;不匹配就丢弃该包。简单加法校验便宜但漏检率较高,设计时需在速度与强度之间权衡。
在不可靠层上构造可靠传输
无连接尽力而为服务只保证尽快尝试投递。要获得可靠字节流,发送方必须设置超时、等待确认,并在必要时重传。丢失、重复和乱序都由这一层处理,应用因此可以假设数据最终会完整到达。
常见误区
- 把网络当成永远可靠的管道
- 忘记处理重复到达的报文
- 选用过弱的校验和以致漏检损坏
运行一个例子
最低标准 C11 · 完整程序 · 下载 .c
#include <stdio.h>
#include <string.h>
#define BUF 64
typedef struct {
char data[BUF];
unsigned checksum;
} Packet;
unsigned compute_cs(const char *d) {
unsigned s = 0;
for (int i = 0; d[i]; i++) s += (unsigned char)d[i];
return s;
}
int main(void) {
Packet p;
strcpy(p.data, "hello");
p.checksum = compute_cs(p.data);
printf("Client: sending '%s' cs=%u\n", p.data, p.checksum);
for (int try = 1; try <= 2; try++) {
printf("Try %d: ", try);
if (try == 1) {
printf("lost\n");
continue;
}
printf("ok\n");
unsigned rcs = compute_cs(p.data);
if (rcs == p.checksum) {
printf("Server: got '%s' ok, reply 'ack'\n", p.data);
}
}
return 0;
}
在本地编译
gcc -std=c11 -Wall -Wextra -Wpedantic -Werror ostep-48-distributed-systems.c -o example && ./example预期结果
Client: sending 'hello' cs=532
Try 1: lost
Try 2: ok
Server: got 'hello' ok, reply 'ack'
CHECK YOUR UNDERSTANDING
合上答案,试着解释。
即使所有链路和主机都正常工作,数据包为什么仍可能丢失?
查看参考答案
路由器或终端主机的缓冲区在突发流量时会溢出,只能丢弃多余的包。
继续查证
标准草案与官方章节会更新;版本标记只说明示例最低要求。