系统设计基础

系统设计是软件工程中用于构建可靠、高效、可扩展软件系统的核心学科。本文将介绍系统设计的基础知识、核心概念和实用模式。

1. 系统设计基础

1.1 什么是系统设计

系统设计(System Design)是指在构建软件系统之前,对系统的架构、组件、数据流和交互方式进行规划和设计的过程。良好的系统设计能够:

  • 支撑业务增长:系统能够应对用户量和数据量的增长
  • 保障服务稳定:具备容错能力,减少单点故障影响
  • 优化资源效率:合理分配计算、存储和网络资源
  • 降低维护成本:模块化设计便于后续迭代和扩展

系统设计通常发生在项目早期,需要在性能成本复杂度可维护性之间取得平衡。

1.2 性能 vs 可扩展性 vs 可用性

指标定义关注点
性能(Performance)系统响应速度和吞吐量延迟、QPS、TPS
可扩展性(Scalability)系统处理增长负载的能力水平扩展、垂直扩展
可用性(Availability)系统正常运行的时间比例99.9%、99.99%、99.999%

三者之间的权衡

可用性 99.9%  = 每年停机约 8.7 小时
可用性 99.99% = 每年停机约 52.6 分钟
可用性 99.999% = 每年停机约 5.3 分钟

在实际设计中,我们通常无法同时最大化这三个指标,需要根据业务场景做出权衡。例如:金融系统更注重一致性(Consistency),而社交媒体可能更注重可用性(Availability)。

1.3 一致性模型

一致性模型定义了分布式系统中数据同步和读取的规则。

强一致性(Strong Consistency)

定义:任何读取操作都会返回最近一次写入的结果。

特点

  • 所有节点的数据完全一致
  • 写入后立即可读
  • 通常通过分布式锁或共识协议实现

适用场景:金融交易、库存管理、订单处理

示意图

客户端A --写入--> 节点1 --同步--> 节点2 --同步--> 节点3
        |                              |                              |
        v                              v                              v
     读取返回                    读取返回                      读取返回
     相同结果                    相同结果                      相同结果

最终一致性(Eventual Consistency)

定义:写入操作后,数据最终会在所有节点上达到一致状态,但短期内可能存在不一致。

特点

  • 写入后可立即读取,但可能读到旧值
  • 系统会异步同步数据
  • 冲突解决策略(如最后写入获胜)

适用场景:社交媒体帖子、评论、缓存更新

弱一致性(Weak Consistency)

定义:读取操作不保证能读取到最新的写入结果。

特点

  • 无法预测何时会读到新值
  • 性能最高但一致性最弱
  • 通常用于实时性要求高的场景

示例:DNS 解析、视频直播弹幕

一致性模型对比

特性强一致性最终一致性弱一致性
读取延迟
写入延迟
数据新鲜度始终最新最终最新不保证
实现复杂度

2. 核心概念

2.1 负载均衡(Load Balancing)

负载均衡将传入的网络流量分配到多个服务器,以优化资源使用和提高吞吐量。

负载均衡算法

1. 轮询(Round Robin)

// 简单轮询实现
int current_index = 0;
vector<Server> servers = {server1, server2, server3};
 
Server get_next_server() {
    Server selected = servers[current_index];
    current_index = (current_index + 1) % servers.size();
    return selected;
}

特点

  • 适用于服务器性能相近的场景
  • 无状态,实现简单
  • 无法感知服务器实际负载

2. 最少连接(Least Connections)

// 最少连接算法
struct Server {
    string address;
    int active_connections;
    // ...
};
 
Server select_server(vector<Server>& servers) {
    // 选择活动连接数最少的服务器
    auto it = min_element(servers.begin(), servers.end(),
        [](const Server& a, const Server& b) {
            return a.active_connections < b.active_connections;
        });
    return *it;
}

特点

  • 动态感知服务器负载
  • 适合处理请求耗时差异大的场景
  • 需要维护连接状态

3. IP 哈希(IP Hash)

// IP哈希算法
Server select_server(vector<Server>& servers, const string& client_ip) {
    // 对客户端IP进行哈希
    hash<string> hasher;
    size_t hash_value = hasher(client_ip);
    
    // 取模选择服务器
    int index = hash_value % servers.size();
    return servers[index];
}

特点

  • 同一客户端IP始终路由到同一服务器
  • 有利于会话保持(Session Persistence)
  • 适合需要用户亲和性的场景

负载均衡器类型

类型工作层示例
L4 负载均衡传输层(TCP/UDP)HAProxy、LVS
L7 负载均衡应用层(HTTP/HTTPS)Nginx、AWS ALB

2.2 缓存(Caching)

缓存是提高系统性能的核心技术,通过存储热点数据减少数据库访问。

缓存策略

1. Cache-Aside(旁路缓存)

读操作:
1. 应用先查缓存
2. 缓存命中则直接返回
3. 缓存未命中,查数据库
4. 将结果写入缓存,返回

写操作:
1. 先更新数据库
2. 再删除(而非更新)缓存
# Cache-Aside 模式伪代码
def get_user(user_id):
    # 1. 尝试从缓存获取
    user = redis.get(f"user:{user_id}")
    if user:
        return user
    
    # 2. 缓存未命中,从数据库获取
    user = db.query("SELECT * FROM users WHERE id = ?", user_id)
    
    # 3. 写入缓存,设置过期时间
    redis.setex(f"user:{user_id}", 3600, user)
    return user
 
def update_user(user_id, data):
    # 1. 先更新数据库
    db.execute("UPDATE users SET ... WHERE id = ?", user_id, data)
    
    # 2. 删除缓存(而非更新)
    redis.delete(f"user:{user_id}")

2. Write-Through(写穿透)

数据同时写入缓存和数据库,保证强一致性,但写入性能较低。

3. Write-Behind(写回)

先写入缓存,异步批量写入数据库。性能高但存在数据丢失风险。

缓存淘汰策略

策略原理适用场景
LRU最近最少使用通用场景
LFU最不频繁使用访问频率差异大
FIFO先进先出简单场景

Redis vs Memcached

特性RedisMemcached
数据结构String, Hash, List, Set, Sorted Set仅 String
持久化支持 RDB、AOF不支持
集群原生支持集群模式需要客户端分片
内存管理支持虚拟内存纯内存

2.3 数据库分片(Database Sharding)

分片是将数据水平拆分到多个数据库/表的技术。

┌─────────────────────────────────────────┐
│              用户表 (10亿条)              │
└─────────────────────────────────────────┘
                    │
        ┌───────────┼───────────┐
        ▼           ▼           ▼
   ┌─────────┐ ┌─────────┐ ┌─────────┐
   │ Shard 1 │ │ Shard 2 │ │ Shard 3 │
   │ user_id │ │ user_id │ │ user_id │
   │ % 3 = 0 │ │ % 3 = 1 │ │ % 3 = 2 │
   └─────────┘ └─────────┘ └─────────┘

分片策略

1. 哈希分片(Hash Sharding)

// 哈希分片
int get_shard(long user_id, int num_shards) {
    return user_id % num_shards;
}

优点:数据分布均匀
缺点:扩容困难(需要重新哈希所有数据)

2. 范围分片(Range Sharding)

// 范围分片(按用户ID范围)
int get_shard(long user_id) {
    if (user_id < 1000000000) return 0;  // 1-10亿
    if (user_id < 2000000000) return 1;  // 10-20亿
    return 2;
}

优点:适合范围查询
缺点:可能产生热点

2.4 索引(Indexing)

索引是加速数据库查询的数据结构。

-- 创建索引
CREATE INDEX idx_user_email ON users(email);
CREATE INDEX idx_order_date_status ON orders(create_date, status);
 
-- 复合索引遵循最左前缀原则
-- 索引 (a, b, c) 可以加速:
-- - 查询 a
-- - 查询 a, b
-- - 查询 a, b, c
-- 但无法加速仅查询 b 或 c 的查询

B+树 vs 哈希索引

特性B+树索引哈希索引
查询类型范围查询 (>, <, BETWEEN)等值查询 (=)
排序支持不支持
内存占用较高较低
适用场景大多数场景简单等值查询

2.5 CDN(Content Delivery Network)

CDN 通过在全球部署边缘节点,加速内容分发。

用户请求 → CDN边缘节点 → 缓存命中返回
                │
                └→ 缓存未命中 → 源站获取 → 缓存 → 返回

CDN 的优势

  • 低延迟:内容就近返回
  • 减轻源站压力:静态资源由CDN承担
  • 提高可用性:分布式架构避免单点故障
  • 安全防护:提供DDoS防护、WAF等功能

2.6 DNS(Domain Name System)

DNS 将域名解析为IP地址,是互联网的基础设施。

用户浏览器 → DNS解析请求 → DNS服务器 → 返回IP地址

DNS 记录类型

类型用途示例
AIPv4地址example.com → 93.184.216.34
AAAAIPv6地址example.com → 2606:2800:220:1::
CNAME域名别名www.example.com → example.com
MX邮件服务器example.com → mail.example.com

DNS 负载均衡

  • 轮询返回多个IP
  • 地理位置感知解析
  • 动态健康检查

3. 架构模式

3.1 单体架构 vs 微服务

单体架构(Monolithic Architecture)

┌──────────────────────────────┐
│         单体应用               │
│  ┌─────┐ ┌─────┐ ┌─────┐    │
│  │ 用户 │ │ 订单 │ │ 支付 │    │
│  │ 服务 │ │ 服务 │ │ 服务 │    │
│  └─────┘ └─────┘ └─────┘    │
│  ┌─────────────────────┐    │
│  │      数据库          │    │
│  └─────────────────────┘    │
└──────────────────────────────┘

优点

  • 开发、部署简单
  • 团队协作门槛低
  • 测试容易

缺点

  • 代码耦合度高
  • 部署粒度粗
  • 技术栈单一

微服务架构(Microservices Architecture)

┌──────────┐  ┌──────────┐  ┌──────────┐
│ 用户服务  │  │ 订单服务  │  │ 支付服务  │
│  :8001   │  │  :8002   │  │  :8003   │
└────┬─────┘  └────┬─────┘  └────┬─────┘
     │              │              │
     └──────────────┼──────────────┘
                    │
             ┌──────┴──────┐
             │  API Gateway │
             └─────────────┘

优点

  • 服务独立部署
  • 技术栈灵活
  • 故障隔离

缺点

  • 复杂度高(服务间通信、分布式事务)
  • 运维挑战大
  • 数据一致性困难

3.2 事件驱动架构(Event-Driven Architecture)

事件驱动架构通过事件的产生、消费和处理来实现系统解耦。

┌─────────┐    事件    ┌─────────┐    事件    ┌─────────┐
│ 生产者  │ ────────→ │  消息队列 │ ←─────── │ 消费者  │
└─────────┘            └─────────┘            └─────────┘
// 事件定义示例
struct OrderCreatedEvent {
    string order_id;
    string user_id;
    double amount;
    long timestamp;
};
 
// 生产者:发布事件
void publish_order_created(const OrderCreatedEvent& event) {
    string payload = json_encode(event);
    mq.publish("order.events", payload);
}
 
// 消费者:订阅并处理事件
void on_order_created(const string& payload) {
    auto event = json_decode<OrderCreatedEvent>(payload);
    // 发送通知、更新库存、记录日志等
}

3.3 CQRS(Command Query Responsibility Segregation)

CQRS 将读操作和写操作分离到不同的模型。

┌─────────────┐         ┌─────────────┐
│   Commands  │         │    Queries   │
│   (写操作)   │         │   (读操作)    │
└──────┬──────┘         └──────┬──────┘
       │                       │
       ▼                       ▼
┌─────────────┐         ┌─────────────┐
│    Command  │         │    Query    │
│    Model    │ ─同步──→│   Model     │
│  (写入优化)  │         │  (读取优化)  │
└─────────────┘         └─────────────┘

适用场景

  • 读写负载差异大
  • 需要独立优化读写性能
  • 复杂业务域

3.4 Saga 模式

Saga 用于管理分布式事务,将大事务拆分为多个本地事务。

编排式 Saga(Choreography)

      ┌──────┐       ┌──────┐       ┌──────┐
      │订单  │       │库存  │       │支付  │
      │服务  │       │服务  │       │服务  │
      └──┬───┘       └──┬───┘       └──┬───┘
         │              │              │
         │ 创建订单 ──→ │              │
         │              │ 扣减库存 ──→ │
         │              │              │ 扣款
         │              │              │
         │ ←────────────┘              │
         │     失败回滚                │

编排器 Saga(Orchestration)

┌─────────────────────────────────────┐
│           Saga 编排器                │
│                                     │
│  1. 调用订单服务创建订单              │
│  2. 调用库存服务扣减库存              │
│  3. 调用支付服务扣款                 │
│  4. 若任一步骤失败,执行补偿事务      │
└─────────────────────────────────────┘

4. 可扩展性设计

4.1 水平扩展 vs 垂直扩展

扩展方式原理优点缺点
垂直扩展(Scale Up)增加单机硬件资源简单、无代码改动有物理上限、成本非线性
水平扩展(Scale Out)增加机器数量无理论上限复杂度高、需要数据分片

实际建议

  • 优先垂直扩展,成本效益高
  • 达到瓶颈后转向水平扩展
  • 数据库通常先垂直扩展,后考虑分片

4.2 数据分区(Partitioning)

数据分区将数据拆分到多个物理存储单元。

按键分区(Key-based Partitioning)

// 哈希分区
int partition(const string& key, int num_partitions) {
    return hash(key) % num_partitions;
}

按范围分区(Range Partitioning)

// 按时间范围分区
string partition_by_date(long timestamp) {
    time_t t = timestamp;
    struct tm* tm_info = localtime(&t);
    char buffer[32];
    strftime(buffer, 32, "%Y%m", tm_info);
    return string(buffer);  // "202604"
}

4.3 复制(Replication)

复制将数据同步到多个节点,提高可用性和读取性能。

┌─────────────────────────────────────────┐
│              主从复制                     │
├─────────────────────────────────────────┤
│                                         │
│    写入 ──→ 主库 ──→ 从库1                │
│                  └──→ 从库2                │
│                  └──→ 从库3                │
│                                         │
└─────────────────────────────────────────┘

复制模式

模式同步方式一致性延迟
同步复制写入所有副本后返回强一致
异步复制写入主库后立即返回最终一致
半同步写入主库+至少一个从库弱强一致

4.4 一致性哈希(Consistent Hashing)

一致性哈希用于在分布式系统中均匀分配数据,同时最小化数据迁移。

                    ┌─────────┐
              ┌────→│  节点A  │←────┐
              │     └─────────┘     │
              │                     │
    ┌─────────┼─────────┐           │
    │         │         │           │
    ▼         ▼         ▼           │
┌───────┐ ┌───────┐ ┌───────┐     ┌─────────┐
│  0-90° │ │90-180°│ │180-270│ ... │330-360° │
└───────┘ └───────┘ └───────┘     └─────────┘

虚拟节点:为每个物理节点创建多个虚拟节点,提高负载均衡性。

// 一致性哈希实现
class ConsistentHash {
    // 物理节点
    vector<string> physical_nodes;
    // 虚拟节点数量
    int virtual_nodes_per_node;
    // 哈希环
    map<size_t, string> hash_ring;
 
public:
    ConsistentHash(const vector<string>& nodes, int vnodes = 150) {
        physical_nodes = nodes;
        virtual_nodes_per_node = vnodes;
        
        for (const auto& node : nodes) {
            add_node(node);
        }
    }
    
    void add_node(const string& node) {
        for (int i = 0; i < virtual_nodes_per_node; i++) {
            string vnode = node + "#" + to_string(i);
            size_t hash_val = hash(vnode);
            hash_ring[hash_val] = node;
        }
    }
    
    string get_node(const string& key) {
        if (hash_ring.empty()) return "";
        
        size_t hash_val = hash(key);
        auto it = hash_ring.lower_bound(hash_val);
        
        if (it == hash_ring.end()) {
            it = hash_ring.begin();  // 环绕到开头
        }
        
        return it->second;
    }
};

5. 高可用设计

5.1 冗余与故障转移

冗余通过部署多个副本确保单点故障不影响整体服务。

┌─────────────┐      ┌─────────────┐
│   主节点    │ ←──→ │   备节点    │
│  (Active)   │ 心跳  │  (Standby)  │
└──────┬──────┘      └─────────────┘
       │
       ▼
  故障检测 → 自动切换 → 流量切换

故障转移类型

类型原理RTO数据丢失
自动故障转移系统自动检测并切换取决于复制模式
手动故障转移人工介入决策较长可能更少

5.2 降级(Degradation)

降级是在系统压力过大时,主动放弃部分功能以保证核心功能可用。

降级策略示例

正常情况:
┌──────────────────────────────────┐
│         完整的商品详情页           │
│  标题、价格、库存、评论、推荐、评分  │
└──────────────────────────────────┘

降级情况:
┌──────────────────────────────────┐
│         降级的商品详情页           │
│       标题、价格、库存              │
│       (评论、推荐、评分暂时关闭)     │
└──────────────────────────────────┘

降级实现

// 功能降级装饰器
class FeatureFlag {
public:
    static bool is_enabled(const string& feature) {
        // 检查开关配置
        return config.get_bool("feature." + feature, true);
    }
};
 
string get_product_detail(long product_id) {
    ProductDetail detail;
    
    // 基础功能始终可用
    detail.basic_info = get_basic_info(product_id);
    
    // 可选功能根据开关决定
    if (FeatureFlag::is_enabled("product.comments")) {
        detail.comments = get_comments(product_id);
    }
    
    if (FeatureFlag::is_enabled("product.recommendations")) {
        detail.recommendations = get_recommendations(product_id);
    }
    
    return detail;
}

5.3 熔断器(Circuit Breaker)

熔断器防止级联故障,当下游服务持续失败时快速失败。

        正常           半开            打开
      ┌──────┐       ┌──────┐       ┌──────┐
      │  ●   │       │  ◐   │       │  ○   │
      │ 闭合  │ ───→ │ 半开  │ ───→ │ 打开  │
      └──────┘ 失败   └──────┘ 超时  └──────┘
        │              │              │
        │              ↓              │
        │           尝试请求           │
        │              │              │
        │              ↓              │
        └──────── 成功 ────────────────┘
class CircuitBreaker {
    enum State { CLOSED, OPEN, HALF_OPEN };
    
    State state = CLOSED;
    int failure_count = 0;
    int success_count = 0;
    const int threshold = 5;  // 失败阈值
    const int recovery_timeout = 60;  // 恢复尝试间隔(秒)
    time_t last_failure_time;
    
public:
    bool allow_request() {
        switch (state) {
            case CLOSED:
                return true;
            case OPEN:
                if (time(nullptr) - last_failure_time > recovery_timeout) {
                    state = HALF_OPEN;
                    return true;
                }
                return false;
            case HALF_OPEN:
                return true;  // 允许单个请求测试
        }
        return false;
    }
    
    void record_success() {
        if (state == HALF_OPEN) {
            success_count++;
            if (success_count >= 2) {
                state = CLOSED;
                failure_count = 0;
                success_count = 0;
            }
        } else {
            failure_count = 0;
        }
    }
    
    void record_failure() {
        last_failure_time = time(nullptr);
        failure_count++;
        
        if (state == HALF_OPEN) {
            state = OPEN;  // 回到打开状态
        } else if (failure_count >= threshold) {
            state = OPEN;  // 熔断打开
        }
    }
};

5.4 健康检查

健康检查用于监控服务状态,及时发现和处理故障。

# Kubernetes 健康检查配置
livenessProbe:
  httpGet:
    path: /healthz
    port: 8080
  initialDelaySeconds: 10
  periodSeconds: 15
  failureThreshold: 3
 
readinessProbe:
  httpGet:
    path: /ready
    port: 8080
  initialDelaySeconds: 5
  periodSeconds: 10

健康检查端点实现

// 健康检查端点
void handle_health_check(evhtp_request_t *req) {
    HealthStatus status;
    
    // 检查依赖服务
    status.database = check_database() ? "healthy" : "unhealthy";
    status.cache = check_redis() ? "healthy" : "unhealthy";
    status.external_api = check_external_api() ? "healthy" : "unhealthy";
    
    // 聚合健康状态
    bool overall_healthy = (status.database == "healthy") &&
                           (status.cache == "healthy");
    
    string response = json_encode(status);
    
    if (overall_healthy) {
        evbuffer_add_printf(req->buffer_out, "%s", response.c_str());
        evhtp_send_reply(req, EVHTP_RES_OK);
    } else {
        evbuffer_add_printf(req->buffer_out, "%s", response.c_str());
        evhtp_send_reply(req, EVHTP_RES_SVCUNAVAIL);
    }
}

6. 案例分析

6.1 设计短链接系统

需求分析

功能要求
生成短链接接受原始URL,返回短码
访问跳转访问短链接时重定向到原始URL
容量支持1000亿URL
性能延迟 < 10ms

架构设计

用户请求短链接
      │
      ▼
┌─────────────┐     ┌─────────────┐
│   API服务   │ ──→ │   缓存层    │
└──────┬──────┘     │  (Redis)    │
       │            └─────────────┘
       │ yes 缓存命中
       │      │
       │      ▼
       │   直接返回
       │
       │ no 缓存未命中
       │      │
       ▼      ▼
┌─────────────────┐
│   存储层        │
│ (分布式数据库)  │
└─────────────────┘

短码生成算法

// 62进制编码
class ShortCodeGenerator {
    const string chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
    
    string to_base62(long n) {
        string result;
        while (n > 0) {
            result = chars[n % 62] + result;
            n /= 62;
        }
        return result;
    }
    
    // 使用分布式ID生成器(如Snowflake)的ID
    string generate(long distributed_id) {
        return to_base62(distributed_id);
    }
};

存储设计

-- 短链接表
CREATE TABLE short_urls (
    short_code VARCHAR(12) PRIMARY KEY,
    original_url TEXT NOT NULL,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    expires_at TIMESTAMP,
    click_count BIGINT DEFAULT 0,
    INDEX idx_created_at (created_at),
    INDEX idx_expires_at (expires_at)
) ENGINE=InnoDB;
 
-- 分片策略:按short_code哈希分片
-- 扩展时:一致性哈希重新分配

6.2 设计分布式锁

分布式锁的要求

要求说明
互斥任意时刻只有一个客户端能持有锁
死锁避免锁最终可释放(TTL机制)
容错节点故障时锁能自动释放
公平按请求顺序获取锁(可选)

Redis 分布式锁实现

class DistributedLock {
    RedisClient& redis;
    string lock_key;
    string lock_value;  // 唯一标识,用于安全释放
    int ttl_seconds;
    
public:
    DistributedLock(RedisClient& r, const string& key, int ttl = 30)
        : redis(r), lock_key(key), ttl_seconds(ttl) {
        // 生成唯一值(UUID + 时间戳)
        lock_value = generate_unique_id();
    }
    
    // 获取锁
    bool acquire() {
        // SET key value NX EX seconds
        string result = redis.command(
            "SET %s %s NX EX %d",
            lock_key.c_str(), lock_value.c_str(), ttl_seconds
        );
        return result == "OK";
    }
    
    // 释放锁(Lua脚本保证原子性)
    bool release() {
        // 只有持有者才能释放
        const char* lua_script = R"(
            if redis.call('get', KEYS[1]) == ARGV[1] then
                return redis.call('del', KEYS[1])
            else
                return 0
            end
        )";
        string result = redis.eval(lua_script, 1, lock_key, lock_value);
        return result == "1";
    }
    
    // 续期( watchdog 机制)
    bool extend(int additional_seconds) {
        const char* lua_script = R"(
            if redis.call('get', KEYS[1]) == ARGV[1] then
                return redis.call('expire', KEYS[1], ARGV[2])
            else
                return 0
            end
        )";
        string result = redis.eval(lua_script, 1, lock_key, lock_value, additional_seconds);
        return result == "1";
    }
};

Redlock 算法

获取当前时间戳(ms)
for 每个Redis节点:
    尝试获取锁,记录耗时
    若成功且耗时 < 超时时间,则锁获取成功

若成功节点数 > N/2 + 1:
    计算锁的有效期 = 初始TTL - 获取耗时
    返回成功
否则:
    释放所有节点的锁
    返回失败

6.3 设计消息队列

消息队列的核心功能

功能说明
异步通信生产者和消费者解耦
削峰填谷缓解流量洪峰
可靠传输消息不丢失
顺序保证按序处理(可选)

架构设计

┌─────────┐    生产消息     ┌─────────┐
│ 生产者1 │ ─────────────→ │         │
├─────────┤                 │         │
│ 生产者2 │ ─────────────→ │  Broker  │
├─────────┤                 │         │
│ 生产者3 │ ─────────────→ │         │
└─────────┘                 └────┬────┘
                                 │
              ┌──────────────────┼──────────────────┐
              │                  │                  │
              ▼                  ▼                  ▼
        ┌──────────┐       ┌──────────┐       ┌──────────┐
        │ 消费者1  │       │ 消费者2  │       │ 消费者3  │
        │ Topic A  │       │ Topic B  │       │ Topic C  │
        └──────────┘       └──────────┘       └──────────┘

消息持久化

// 消息结构
struct Message {
    string id;           // 全局唯一ID
    string topic;         // 主题
    string body;          // 消息内容
    int partition;        // 分区
    long timestamp;       // 时间戳
    map<string, string> headers;  // 扩展头
};
 
// 消息存储(追加写 WAL)
class MessageStore {
    vector<Partition> partitions;
    
    bool append(const Message& msg) {
        int partition = select_partition(msg.topic, msg.key);
        Partition& p = partitions[partition];
        
        // 写入内存缓冲
        p.buffer.push_back(msg);
        
        // 异步刷盘
        if (should_flush()) {
            flush_to_disk(p);
        }
        
        // 复制到副本
        replicate_to_followers(p, msg);
        
        return true;
    }
};

消费语义

语义说明实现难度
At Most Once消息最多投递一次简单
At Least Once消息至少投递一次中等
Exactly Once消息恰好投递一次困难

参考资料


相关主题