C++ / a working model

152 / 163   ·   C11   ·   约 8 分钟

分布式系统

先记住这句话

分布式系统把多台机器经网络连成整体服务。单机、磁盘和链路会故障,但冗余能让用户感觉系统几乎永不中断。通信本质不可靠,必须依靠校验和、确认与重传来构建可用协议。

本篇内容
  1. 故障既是挑战也是机遇
  2. 通信的不可靠本质
  3. 用校验和发现损坏
  4. 在不可靠层上构造可靠传输
  5. 运行示例
  6. 动手练习

官方章节 PDF

故障既是挑战也是机遇

单台机器随时可能崩溃,磁盘可能损坏,网线可能被挖断。真正的技巧是把许多不完美的部件组合起来,让整体服务对客户端看起来始终在线。这正是当今大型网站背后的核心思路。

通信的不可靠本质

无论广域互联网还是机房内高速互连,数据包都可能因电气干扰、设备故障或交换机内存耗尽而消失。即使所有硬件都完好,突发流量仍会挤爆缓冲区并导致丢包。因此必须把每一次发送都视为可能失败。

用校验和发现损坏

发送前对数据字节求和(或计算更强的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

合上答案,试着解释。

即使所有链路和主机都正常工作,数据包为什么仍可能丢失?

查看参考答案

路由器或终端主机的缓冲区在突发流量时会溢出,只能丢弃多余的包。

继续查证

标准草案与官方章节会更新;版本标记只说明示例最低要求。

回到目录