中间件学习路径:日志、消息、网关与数据高可用
使用方式
中间件学习的难点不是安装,而是理解每种组件在系统里承担的状态:连接、队列、分区、副本和一致性。这条路径按“可观测 → 异步解耦 → 流量入口 → 数据高可用”推进。
四个阶段按依赖顺序排列,每个阶段建议留 3–4 小时:
- 先读:先理解组件保存什么状态、失败时丢什么,再看命令和配置。
- 再做:用 Docker Compose 或测试集群搭最小拓扑,不要只改现成集群。
- 后测:能画出请求或消息从生产者到消费者的完整路径,并指出每个失败点。
每个阶段都要至少制造一次故障,观察组件如何降级,而不是只看健康状态的截图。
阶段一:日志与搜索
目标:能把分散的日志采集、缓冲、索引、查询串起来,并说清全文索引和标签索引的成本差异。
阅读顺序:
- elasticsearch — 倒排索引、分片、副本和查询模型。
- logstash — input、filter、output 管道的缓冲和背压。
- kibana — 索引模式、查询和可视化。
- loki — 只索引标签、不索引全文的日志存储模型。
- vector — 轻量采集、转换和路由,适合作为 agent。
- filebeat — 文件采集、registry 和多行日志处理。
- opensearch — Elasticsearch 的开源分支与兼容差异。
- loki-vs-elk — 两种日志方案的选型边界。
- logql-cheatsheet — Loki 查询语法速查。
- opensearch-dashboards — OpenSearch 的查询与看板入口。
动手任务:用 Docker Compose 搭一套 Loki + Vector,采集容器日志,按标签查询错误日志;再对比一次全文搜索和标签过滤的查询成本。
docker compose up -d
docker compose logs vector --tail 20
curl -s 'http://localhost:3100/loki/api/v1/labels'
curl -sG 'http://localhost:3100/loki/api/v1/query_range' \
--data-urlencode 'query={container="app"} |= "ERROR"' \
--data-urlencode 'limit=20'
过关标准:
- 能解释 Elasticsearch 的索引和 Loki 的标签索引分别适合什么查询。
- 知道日志管道在消费者变慢时,缓冲、丢弃和阻塞三种策略的后果。
- 能说出日志保留、分片数量和存储成本之间的关系。
阶段二:消息与协调
目标:理解消息系统如何在生产者、broker、消费者之间传递状态,以及顺序、重复和积压从哪来。
阅读顺序:
- kafka-broker — topic、partition、offset 和 broker 的职责。
- zookeeper — 元数据、选举和 Kafka 旧架构中的角色。
- schema-registry — 消息格式演进和兼容性策略。
- rabbitmq — exchange、queue、ack 和路由模型。
- kafka-connect — 用连接器把外部系统接入 Kafka。
- redpanda — 兼容 Kafka 协议的另一套存储实现。
- ksql — 在流上做持续查询和聚合。
- kafka-cheatsheet — Kafka 常用命令速查。
- kafka-vs-rabbitmq — 日志流与任务队列的选型差异。
动手任务:用 Compose 启动 Kafka 和 Schema Registry,创建一个三分区 topic,用 key 验证分区内顺序,再启动两个消费者观察 rebalance 和积压。
docker compose up -d kafka schema-registry
docker compose exec kafka kafka-topics --bootstrap-server localhost:9092 \
--create --topic events --partitions 3 --replication-factor 1
docker compose exec kafka kafka-console-producer --bootstrap-server localhost:9092 \
--topic events --property parse.key=true --property key.separator=:
docker compose exec kafka kafka-console-consumer --bootstrap-server localhost:9092 \
--topic events --group lab --property print.key=true
docker compose exec kafka kafka-consumer-groups --bootstrap-server localhost:9092 \
--describe --group lab
过关标准:
- 能解释为什么 Kafka 只保证分区内有序,以及 key 如何决定分区。
- 知道消费者组、offset 提交和 rebalance 之间的关系。
- 能说清 at-least-once 下重复消息为什么必须由消费端做幂等。
阶段三:网关与接入
目标:能让外部流量安全、可观测地进入系统,理解反向代理、负载均衡、VIP 和身份认证的职责边界。
阅读顺序:
- nginx — 反向代理、upstream、超时和缓冲区。
- haproxy — L4/L7 负载均衡、健康检查和连接队列。
- keepalived — VRRP 虚拟 IP 和主备切换。
- keycloak — OIDC/OAuth2 身份提供方、realm 和客户端。
- openresty — 基于 Nginx 的可编程网关。
- oauth2-proxy — 给没有原生认证的应用加上统一登录。
- nginx-cheatsheet — Nginx 配置与排障速查。
- haproxy-vs-nginx — 负载均衡与反向代理的选型。
- nginx-vs-caddy — 自动 HTTPS 与配置复杂度的权衡。
动手任务:用 Nginx 反向代理两个后端,配置健康检查和超时;用 HAProxy 做一次主动健康检查;再用 Keycloak + oauth2-proxy 保护一个测试应用。
nginx -t
curl -I -H 'Host: demo.local' http://127.0.0.1/
docker compose exec haproxy sh -c 'echo "show stat" | socat stdio /var/run/haproxy.sock'
curl -s http://localhost:8080/realms/lab/.well-known/openid-configuration
过关标准:
- 能区分反向代理、负载均衡、API 网关和身份代理各自负责什么。
- 知道健康检查失败、超时和熔断分别会触发什么行为。
- 能说清会话保持、无状态令牌和统一登出之间的关系。
阶段四:数据与高可用
目标:理解连接池、主从切换、分片副本和向量索引,能判断故障时数据是否安全、服务是否可写。
阅读顺序:
- pgbouncer — 连接池模式、事务边界和连接复用。
- redis-cluster — 16384 个 hash slot、主从和故障转移。
- patroni — PostgreSQL 高可用、etcd 共识和 leader 切换。
- qdrant — 向量集合、分片、副本和检索参数。
- elasticsearch-cluster — 主节点、数据节点、分片分配和脑裂防护。
- proxysql — MySQL 读写分离、连接池和查询规则。
- mysql-router — InnoDB Cluster 的路由和故障转移。
- pgvector — 在 PostgreSQL 内做向量检索的索引与权衡。
- orchestrator — MySQL 拓扑发现和主从切换。
- valkey — Redis 兼容实现与迁移注意点。
动手任务:搭一套 PostgreSQL + PgBouncer,观察连接复用;搭三主三从 Redis Cluster,制造一次主节点故障并确认槽位迁移;在 Qdrant 建集合写入向量并查询。
docker compose up -d postgres pgbouncer
psql -h 127.0.0.1 -p 6432 -U app -c 'select count(*) from pg_stat_activity;'
redis-cli -c -p 7000 cluster info
redis-cli -c -p 7000 cluster nodes
curl -s http://localhost:6333/collections
过关标准:
- 能解释 PgBouncer 的 session、transaction、statement 三种池模式的区别。
- 知道 Redis Cluster 的 key 如何映射到 slot,以及多 key 操作为什么受限。
- 能说清 PostgreSQL 主从切换时,连接串、VIP 和池化层各自需要做什么。
- 知道向量检索的召回率、延迟和索引内存之间如何取舍。
常见误区
- 只会启动容器,不理解副本、quorum 和故障转移的实际行为。
- 把 Kafka 当普通队列用,忽略分区顺序和消费位点。
- 认为增加分区一定能提升吞吐,忽略 key 倾斜和 rebalance 成本。
- 连接池开得越大越好,结果把数据库连接数打满。
- 有副本就认为数据安全,不验证复制延迟和备份恢复。
- 网关只配转发,不配超时、重试上限和请求体限制。
- 日志和指标全部保留,成本和查询延迟同时失控。
读完自测
合上页面,先回答,再展开对照。
- Kafka 如何保证同一业务实体的消息顺序?增加分区会带来什么影响?
- PgBouncer 的事务池模式为什么要求应用不能依赖会话级状态?
- Redis Cluster 的主节点故障后,客户端为什么可能短暂写入失败?
答案
- 用业务实体 ID 作为消息 key,Kafka 对 key 做哈希后固定写到同一个分区,分区内 offset 有序。增加分区会改变 key 到分区的映射,同一 key 的历史消息和新消息可能落在不同分区,顺序不再有保证;扩容前要评估这一点。
- 事务池模式在事务结束后就把连接归还给池,下一个事务可能复用给其他客户端。会话级临时表、
SET 会话变量、预备语句和会话锁都可能丢失或串扰,因此应用必须把状态放在事务内或外部存储。
- 主节点故障后,集群需要先判定失败、提升从节点并迁移槽位。这段时间内部分槽位没有可写主节点,客户端会收到
CLUSTERDOWN 或重定向失败;如果客户端缓存了旧拓扑,还会继续访问失效节点,直到刷新路由表。
相关条目