基础架构
单机房的内部架构
单机房典型架构角色功能:
- DNS服务器:域名映射为公网IP(机房入口)。
- LVS (Linux Virtual Server):提供公网IP作为机房入口,作为四层负载均衡器分发请求到Nginx集群。
- Nginx:七层负载均衡器,根据HTTP(S)协议头/URL路径转发请求到对应的业务HTTP服务器。
- 业务服务层 (HTTP服务):处理用户请求,根据URL向RPC服务发送业务逻辑处理请求。
- 业务服务层 (RPC服务):执行核心业务逻辑,访问数据层或调用其他RPC服务。
- 存储层:所有存储系统的集合,包括分布式缓存(如Redis)、关系型数据库(如MySQL)、NoSQL数据库。
- 服务发现:保存每个服务的动态访问地址列表,供其他服务查找(功能与DNS相似)。
- 消息中间件:请求异步执行、服务间解耦、请求削峰填谷。
客户端连接机房的技术:DNS
- TCP/IP通信基础:IP地址(标识设备、网络寻址)。
- 域名:易读易记的字符型地址方案,DNS实现域名到IP的翻译。
- DNS意义:
- 互联网公司创建易记域名。
- 通过多IP映射实现负载均衡。
- 架构升级/IP变更时,仅修改DNS,对用户无干扰。
- 可实现灵活负载均衡策略(如剔除宕机IP)。
- 域名结构:层次化树形结构,根 -> 顶级域名 (TLD) -> 二级域名 -> 三级域名等。
- 域名服务器类型:
- 根域名服务器:全球互联网中枢,掌握全部顶级域名名称与IP映射。
- 顶级域名服务器:管理顶级域名下的二级域名解析,寻找二级域名服务器IP。
- 权威域名服务器:对特定域名进行解析,最终决定域名解析到哪个IP(DNS核心)。
- 本地域名服务器 (本地DNS):不属层级结构,但对DNS重要(缓存),由网络运营商提供,主机查询时首先查询。
- 域名解析过程 (递归查询):
- 客户端查询本地缓存(浏览器缓存、Hosts文件)。
- 无则向本地域名服务器发起请求。
- 本地域名服务器向根域名服务器请求。
- 根返回顶级域名服务器地址。
- 本地域名服务器向顶级域名服务器请求。
- 顶级返回权威域名服务器地址(或直接返回IP)。
- 本地域名服务器向权威域名服务器请求。
- 权威返回最终IP,本地域名服务器缓存并返回给客户端。
- DNS解析记录格式:Domain (域名), TTL (生存周期), Class (协议类型,IN-因特网), Type (记录类型,A-IPv4, AAAA-IPv6), Rdata (记录数据)。
HTTP DNS
- DNS存在的问题:
- 访问延迟高:递归查询耗时。
- 调度精准性差:本地域名服务器 IP 不准确定位客户端地理位置,可能返回非最优节点。
- 域名转发:运营商为节约资源转请求到其他运营商DNS,导致IP不符,访问慢。
- DNS劫持:攻击方式,解析到错误或恶意IP (运营商劫持、黑客篡改Hosts)。
- HTTP DNS原理:
- 客户端指定HTTP DNS服务器IP(固定IP),使用HTTP调用域名解析接口,传入域名和客户端IP。
- HTTP DNS服务器向权威DNS服务器发起解析请求,返回最优IP。
- 客户端获取IP后,直接向此IP发送业务请求。
- HTTP DNS解决的问题:
- 降低域名解析延迟:直接访问,缩短解析链路。
- 防止域名劫持:绕过运营商本地DNS。
- 调度精准性更高:获取真实客户端IP,基于精确位置/运营商信息解析到更近/最优IP。
- 快速生效:不受传统DNS多级缓存影响,更新更快覆盖全量客户端。
- HTTP DNS实践:
- 容灾策略(重要):
- 先尝试HTTP DNS。
- 失败则降级到本地DNS。
- 再失败则使用预留的域名兜底IP地址。
- 安全策略:使用HTTPS。
- IP地址选取策略:HTTP DNS下发多个最优IP,客户端依次校验连通性。
- 批量拉取策略:冷启动/网络切换时批量拉取映射数据并缓存。
- 容灾策略(重要):
接入层的技术演进
- 早期架构问题:业务HTTP服务器直接绑定公网IP。
- 可用性低:DNS无法高效感知实例宕机。
- 可扩展性差:扩容需额外配置DNS,生效慢。
- 安全风险高:业务IP暴露公网。
- 解决方案:引入中间层 (Nginx)。
Nginx
- 代理分类:
- 正向代理:代理客户端(如VPN),客户端知道自己访问的是代理。
- 反向代理:代理服务器(如Nginx),客户端不知道访问的是代理,对外表现为目标服务器。
- Nginx作为反向代理:
- 强大的基于域名和HTTP URL的路由转发功能。
- 通过
nginx.conf配置worker_processes(工作进程数),worker_connections(连接数),http块 (包含server和upstream)。 server块:listen(监听端口),server_name(虚拟主机名),location(URL路径处理规则),proxy_pass(反向代理目标)。upstream块:定义服务池,包含server实例地址。
- Nginx负载均衡策略:
- 轮询 (默认):按时间顺序分发,故障自动剔除。
- 加权轮询:按权重分配请求,权重高更容易被访问。
- ip_hash:根据客户端IP哈希,确保同一IP请求访问同一实例 (不保证负载均衡)。
- least_conn:转发给连接数最少的服务器 (适用于请求响应时间长,实现更好负载均衡)。
- url_hash (第三方):对URL哈希,提高相同请求缓存命中率。
- fair (第三方):根据响应时间、失败数、请求量综合选择最空闲服务器。
- Nginx作为七层负载均衡器:基于HTTP协议(OSI第七层)。
- Nginx实时感知业务服务地址变更:
- 问题:业务频繁迭代扩容导致
upstream配置频繁变更。 - 解决方案:
ngx_lua模块:嵌入Lua脚本。ngx_http_dyups_module模块:Nginx无需重启热更新upstream配置。- 流程:定期从服务注册中心获取最新地址列表 -> 生成最新
upstream配置 -> 通过ngx_http_dyups_module更新。
- 问题:业务频繁迭代扩容导致
- Nginx优势:
- DNS指向Nginx,业务服务器IP切换无需配置DNS。
- 实现客户端与业务服务器间的负载均衡。
- 对外只暴露一个公网IP,节约IP资源。
- 保护业务服务器,增强安全性。
- 增强系统可扩展性,准实时生效。
- 提高业务服务器可用性(实例故障自动迁移)。
LVS (Linux Virtual Server)
- 定义:Linux虚拟服务器集群系统,Linux内核一部分,运行于操作系统层面。
- LVS vs. Nginx:
- Nginx:OSI七层(应用层),异步转发,强调“代理”,支持失败转移,功能强大。
- LVS:OSI四层(网络层),同步转发,强调“转发”,性能更高(免去应用层解析)。
- 常用名词:
- DS (Director Server):四层负载均衡器节点 (LVS服务器)。
- RS (Real Server):DS请求转发目的地 (真实工作服务器)。
- VIP (Virtual Server IP):客户端请求目的IP (DS的公网IP)。
- DIP (Director Server IP):DS与RS通信IP (DS的内网IP)。
- RIP (Real Server IP):后端服务器IP。
- CIP (Client IP):客户端IP。
- LVS转发模式:
- NAT模式 (Network Address Translation):
- 原理:修改请求报文的目标IP/端口。
- 特点:请求/响应均经过DS重写,DS是RS网关。
- 条件:DS需两块网卡,DS与RS同局域网,DS为网关。
- 缺点:响应数据大,DS易成性能瓶颈。
- FULLNAT模式 (Full NAT):
- 原理:在NAT基础上再次进行源IP地址转换(将CIP改为DIP)。
- 特点:不要求DS与RS同局域网或DS为网关。
- 缺点:请求到达RS后丢失客户端IP地址。
- 推荐模式:网络环境适应性强。
- TUN模式 (IP隧道模式):
- 原理:DS将请求报文封装到新数据包,目的IP为RS的RIP,通过IP隧道发送。RS解密原数据包,直接响应客户端。
- 特点:DIP/RIP不一定同网络,RS/DS网络需支持IP隧道。RS需配置VIP绑定到lo网卡。
- 优点:DS只转发请求,RS直接响应,性能高于NAT。
- DR模式 (Direct Routing):
- 原理:DS改写请求报文的MAC地址将请求转发到RS,RS直接通过网关返回响应给客户端。
- 特点:DS/RS需同物理网络。RS需配置VIP绑定到lo网卡。
- 优点:性能最好(不涉及IP隧道加密/解密)。
- NAT模式 (Network Address Translation):
- 模式优劣势总结:
- NAT:RS无需VIP,DS作网关,性能一般。
- FULLNAT:RS无需VIP,网络要求低,丢失客户端IP,性能一般。
- TUN:性能好,服务器需支持IP隧道,RS需配置VIP。
- DR:性能好,DS/RS同物理网络,RS需配置VIP。
- 选择:强网络适应性选FULLNAT,高性能选DR。
LVS+Nginx接入层架构
- 组合优势:
- LVS性能高,便于Nginx集群化,提高Nginx可扩展性 (LVS作为Nginx的四层负载均衡)。
- Nginx功能强大,作为业务HTTP服务器的七层负载均衡器,提高业务服务高可用/可扩展性。
- 单点问题:LVS是单点故障。
- LVS高可用方案:Keepalived + VIP (主从热备)。
- Keepalived:主节点A和从节点B。主节点通过ARP告知VIP对应的MAC为MAC-A。
- 故障切换:从节点B监听到主节点A宕机后,代替主节点A回复ARP,告知VIP对应的MAC为MAC-B,流量转向B。
- LVS性能瓶颈:单台LVS有上限。
- LVS水平扩展:多台LVS对外提供服务,配置N个VIP,客户端依赖DNS轮询决定访问哪台LVS。
- 最终完备架构特点:
- DNS轮询扩展LVS性能。
- Keepalived保证LVS高可用。
- LVS扩展Nginx性能。
- Nginx作为七层负载均衡器,提高业务HTTP服务的高可用/可扩展性。
服务发现
- 定义:为每个服务提供可以自动发现下游服务地址列表的能力。
- 服务注册中心:管理每个服务的地址列表 (注册) 和将某服务的地址告知调用者 (发现)。
- 注册与发现流程:
- 服务实例启动后,将地址信息注册到服务注册中心。
- 调用者向服务注册中心查询服务地址。
- 服务注册中心返回地址列表。
- 调用者向任意实例发起调用。
- 通信方式:主动查询 或 订阅推送 (服务注册中心主动推送最新地址列表)。
- 业界组件:Spring Cloud Eureka, CNCF CoreDNS。
- 可用地址管理:
- 服务实例启动注册,退出注销。
- 探活能力(关键):
- 主动探活:服务注册中心周期性探测(效率低,不适合大规模)。
- 心跳探活 (推荐):服务实例定期发送心跳包,服务注册中心更新最近心跳时间,超时则认为不可用并摘除。
- 摘除保护策略:若已摘除地址数超阈值(如30%),停止摘除并报警,防止过度摘除导致服务不可用。
- 地址变更推送:
- 推送风暴问题:大规模推送会占用大量网络带宽。
- 解决方案:
- 推送增量数据而非全量。
- 服务注册中心部署大量实例,分摊推送压力。
- 推拉结合:少量节点推送,其他节点周期性拉取。
- 服务发现的价值:调用者无需关心被调用者地址,服务可弹性伸缩,真正为微服务带来弹性。
RPC服务 (Remote Procedure Call)
- 定位:
- HTTP服务:服务于后台外部,与用户请求交互(校验、打包响应),更像业务服务层的“网关”。
- RPC服务:服务于后台内部,实现核心业务逻辑,强调“微服务”。
- RPC目标:屏蔽网络编程细节,像调用本地方法一样调用远程方法。
- RPC通信流程:
- 序列化:方法输入参数对象转化为二进制数据。
- 编码:二进制数据编码为约定协议格式的数据包。
- 网络发送。
- 解码:被调用方将数据包解码。
- 反序列化:得到方法名和输入参数。
- 执行方法。
- 结果返回:同样流程返回给调用方。
- RPC框架:gRPC, Thrift等,可利用协议定义文件生成脚手架代码。
- RPC与HTTP关系:RPC是一种设计,与HTTP不是对立概念,RPC底层网络通信可使用TCP或HTTP实现。
- HTTP服务调用RPC服务:HTTP服务通过服务发现获取RPC服务地址,再通过RPC协议进行通信。
存储层技术:MySQL优势
- 关系型数据库:采用关系模型组织数据,以行和列形式存储数据(二维表),使用SQL操作数据。
- 主键:唯一标识行记录的属性组。
- MySQL优势:
- 易用、功能强大(事务、触发器、存储过程),管理工具多。
- 支持千万级数据记录的大型数据库。
- GPL开源协议,可自由定制。
- InnoDB事务性存储引擎符合ACID模型,保证完整可靠存储。
- 高可用架构1:主从模式
- 组成:一台Master(处理写请求),若干Slave(从库)。
- 数据一致性:Master与Slave通过主从复制技术保持。
- 高可用:Master故障时,Slave提升为Master。
- 主从复制原理:
- Master数据变更写入binlog (二进制日志文件)。
- Slave启动I/O线程与Master建立连接,读取binlog。
- I/O线程将日志保存到relay log (中继日志文件)。
- Slave启动SQL线程从relay log获取日志,本地重新执行SQL回放数据。
- 异步复制:Master提交事务不等待Slave确认,可能导致数据丢失。
- 半同步复制 (MySQL 5.5+):Master提交事务前等待至少一个Slave确认接收binlog (写入relay log),减少数据丢失风险,但影响写性能。
- 复制风暴:过多Slave直接复制Master导致压力倍增,可采用级联复制 (Slave向Slave复制)。
- 高可用架构2:MHA (Master High Availability)
- 目标:10-30s内完成MySQL主从集群自动故障检测和切换,最大程度保证数据一致性。
- 组成:
- MHA Manager (管理节点):决策层,负责故障检测、切换。
- MHA Node (数据节点):部署在每台MySQL服务器上,修复主从数据差异。
- 故障转移流程:
- MHA Manager周期探测Master心跳,失联则认为宕机。
- MHA Manager判断哪个Slave的binlog最接近Master。
- MHA Node尝试SSH访问Master,若可达则获取binlog,对比Slave数据并补齐差异;若不可达则对比Slave间relay log差异并补齐。
- MHA Manager构建新主从关系,备选Slave提升为Master。
- 优势:与半同步复制结合可大幅降低数据丢失风险。
- 高可用架构3:MMM (Multi-Master Replication Manager for MySQL)
- 目标:MySQL双主故障切换和管理。
- 组成:Master 1 (写请求),Master 2 (备用,与Master 1互复制,一般半同步),若干Slave (异步复制Master 1),mmm-monitor (检测、决策),mmm-agent (节点代理),write vip (对外写IP,绑定其中一个Master),read vip (对外读IP,绑定Slave)。
- 故障转移流程:agent/monitor检测Master 1宕机 -> monitor要求Master 1移除write vip -> monitor要求Master 2绑定write vip成为Master -> monitor要求Slave向Master 2复制数据。
- 缺点:较古老,不支持MySQL GTID,社区不活跃,处于无人维护状态。
- 高可用架构4:MGR (MySQL Group Replication)
- 版本:MySQL 5.7.17+。
- 特性:
- 一致性高:基于分布式共识算法Paxos,保证多节点数据一致性。
- 容错性高:只要不超过一半节点宕机,即可对外服务。
- 灵活性强:支持单主模式(自动选主)和多主模式(每个节点可处理写请求)。
- 组成:至少3个MySQL节点组成复制组。事务提交需超过一半节点决议通过。
- 多主模式:写请求高并发场景易产生事务冲突,导致大量回滚。官方推荐单主模式。
- 单主模式:MGR自动选Master, Master故障时自动根据权重/ID选主。
- 优势:强一致性(数据不丢失),高容错性。
- 缺点:每个写请求涉及与复制组内多数节点通信,写性能不及异步/半同步复制。适合高一致性要求、写请求量不大的场景。
存储层技术:Redis
- Redis特性:
- 基于内存,性能高。
- 支持丰富数据类型(String, List, Hash, Set, ZSet)。
- 支持分布式(主从、哨兵、集群),可无限扩展。
- 单线程事件驱动,数据操作原子性。
- 应用场景:缓存系统、计数器、限流、排行榜、社交网络等。
- 高可用架构1:主从模式
- 组成:1 Master + 若干 Slave。
- 复制流程:
- Slave连接Master,发送PSYNC命令。
- Master执行BGSAVE生成RDB快照,并创建缓冲区记录数据变更命令。
- Master发送RDB快照给Slave。
- Slave加载RDB到内存,接收Master数据变更命令。
- Master发送缓冲区中记录的变更命令。
- Slave执行命令,保持数据一致。
- 问题:Master向过多Slave复制数据会导致“复制风暴”。
- 高可用架构2:哨兵模式 (Sentinel)
- 目标:Master宕机后自动竞选新Master,实现自动主从切换。
- 组成:主从模式基础 + 若干Sentinel服务器。
- Sentinel角色:监控Master/Slave心跳。当超过N个Sentinel节点认为Master宕机,则协商选举Slave为新Master。
- 客户端访问:先访问Sentinel集群获取Master地址,Master故障时从Sentinel获取新Master地址。
- 优势:无需人工干预主从切换,客户端自动获取新Master地址。
- 高可用架构3:集群模式 (Redis Cluster)
- 目标:分布式存储,解决单个Master内存/QPS限制。
- 组成:多个Redis节点(Master + 若干 Slave组成节点组,代表一个数据分片)。
- 要求:至少3个Master,每个Master至少1个Slave。节点间可相互通信。
- 数据分片:基于哈希槽 (Hash Slot),共16384个槽位,Master瓜分。
- 计算公式:
slot = CRC16(Key) mod 16384。
- 计算公式:
- 客户端访问流程:
- 客户端连接任一节点,获取槽位与Master映射关系并缓存。
- 读/写数据时,根据Key计算槽位。
- 根据槽位在本地缓存定位Master地址,发送请求。
- Gossip协议:用于集群元信息(节点加入、扩缩容、故障转移)的灵活自动变更,最终一致性协议。
- 消息类型:meet, ping, pong, fail。
- 节点故障转移:
- 主观下线:节点A认为节点B下线。
- 客观下线:集群中超过一半节点认为B主观下线。
- B客观下线后,其从节点接管槽位,提升为Master。
- 优势:去中心化架构,槽位管理便捷,可扩展性强,自动故障发现/恢复,Redis官方出品。
- 缺点:集群节点数量多时(上千个),Gossip协议会造成大量网络通信,形成“Gossip风暴”,严重占用网络带宽。
中心化集群架构 (解决Gossip风暴)
- 思路:舍弃去中心化,拥抱中心化,使用中间代理维护集群节点元信息。
- Twemproxy (Twitter开源):
- 原理:客户端请求 -> Twemproxy -> Redis节点。Twemproxy根据路由规则转发。
- 优势:客户端透明,减少连接数,Twemproxy集中路由。
- 不足:无友好管理后台,不支持平滑扩缩容(主要痛点)。
- Codis (豌豆荚开源):
- 数据划分:N个槽位(默认1024),
slot = CRC32(Key) mod N。 - 核心组件:
- Codis Server:二次开发Redis服务器,支持数据迁移。
- Codis Proxy:中间代理,接收/转发客户端请求。
- ZooKeeper集群:保存Redis集群元信息(槽位、节点地址、Proxy地址),提供服务发现。
- Codis Dashboard/Codis Fe:集群运维管理工具(扩缩容、槽位迁移、Web操作页面)。
- 架构:每个数据分片定义为Redis Server Group (Master + 若干 Slave)。
- 客户端访问流程:
- 客户端从ZooKeeper获取Codis Proxy列表,选择连接。
- 客户端向Codis Proxy发送请求。
- Codis Proxy根据Key计算槽位,通过ZooKeeper定位负责槽位的Redis Server Group。
- Codis Proxy转发请求到Redis节点。
- Redis节点处理,结果返回给Codis Proxy。
- Codis Proxy返回给客户端。
- 平滑扩缩容 (槽位迁移):A节点将槽位S关联数据复制到B,期间A继续对外服务。若请求数据D属于S,且正在迁移,则A强制D迁移到B再转发请求到B。
- 故障宕机:新版本Codis建议每个Redis Server Group引入Sentinel节点处理。
- 推荐:Redis集群节点较多时(上千),推荐使用Codis类中心化架构。
- 数据划分:N个槽位(默认1024),
存储层技术:LSM Tree
- 特性:对高并发写友好,兼顾查询效率。NoSQL数据库(如BigTable, HBase, Cassandra, TiDB)核心数据结构。
- 原理:基于“磁盘/内存顺序读/写性能远高于随机读/写性能”结论。
- 组成部分:
- MemTable (内存中):保存最新更新数据,按Key字典序有序组织 (红黑树/跳跃表)。
- 写请求直接在MemTable处理:新增、修改、删除(标记为tombstone)。
- 预写日志 (WAL):数据修改命令提交MemTable前先追加记录到磁盘WAL文件,保证可靠性。
- Immutable MemTable (只读内存):MemTable达到一定大小后转为只读,后台线程将其持久化为SSTable。
- SSTable (Sorted String Table, 磁盘文件):持久化的、有序且不可变的键值对存储结构。
- Key-Value连续存储,文件尾部存储稀疏索引(提高查找速度)。
- 合并操作 (Compaction):周期性对SSTable进行合并,清除冗余记录。
- 使用Leveled Compaction Strategy (LCS):SSTable分Level层级,Level N文件合并下沉到Level N+1。
- LCS保证:所有Level SSTable大小一致(如160MB),每个Level限制文件总大小,单个/同Level内SSTable之间数据有序且无Key重叠。
- MemTable (内存中):保存最新更新数据,按Key字典序有序组织 (红黑树/跳跃表)。
- 读/写数据流程:
- 写流程:WAL -> MemTable -> (满) Immutable MemTable -> SSTable (Level 0) -> (满) 合并下沉到Level 1 -> ... -> Level N+1。
- 读流程:按数据新旧程度,MemTable -> Immutable MemTable -> Level 0 SSTable (最新) -> Level 1 SSTable -> ... -> Level N SSTable。
存储层技术:NoSQL数据库
- 文档数据库 (MongoDB, CouchDB):
- 特点:采用JSON格式存储数据,解决关系型数据库Schema扩展不便问题。
- 适用场景:数据量大/增长快,字段定义不明确/变化快(如商品参数)。
- 不适用场景:需事务(无法保证多文档原子性),需复杂查询(如join)。
- 列式数据库 (BigTable, HBase):
- 特点:按列存储数据,而非行式。
- 优势:统计查询效率高(只读取关注列),存储空间利用率高(字典表压缩)。
- 适用场景:海量数据插入、极少修改(如用户行为收集),数据分析(离线统计)。
- 不适用场景:高频删除/修改,不适合在线用户,需事务。
- 全文搜索数据库 (Elasticsearch):
- 问题:关系型数据库模糊查询效率低(LIKE语句全量扫描)。
- 原理:建立单词到文档的索引关系作为“倒排索引”。
- 优势:高效关键词搜索,海量数据复杂查询,数据统计/聚合。
- 不适用场景:高频更新数据(实际是删除旧数据+创建新数据),需事务。
- 图数据库 (Neo4j, Titan):
- 特点:以“图”这种数据结构存储数据(节点-实体,关系-节点间关联)。
- 优势:直观数据模型,易查询节点间关系,解决关系型数据库不擅长处理实体关系问题。
- 适用场景:强调关系、需复杂关系查询/分析的业务(如社交网络、知识图谱)。
- NewSQL数据库 (Google Spanner, TiDB, CockroachDB):
- 定位:结合关系型数据库和NoSQL两者优点。
- 底层:使用分布式键值存储系统(LSM Tree模型)。
- 革新:放弃NoSQL主从复制,以更小粒度数据分片作为高可用单位,通过Paxos/Raft等分布式共识算法复制数据,采用分布式事务。
- 优势:保留NoSQL可扩展性,提供关系型数据库事务能力。
- 现状:数据库领域后起之秀,但仍是小众产品,需更多考验。
消息中间件技术
- 定义:生产者创建消息投递到队列,消费者从队列读取消息,完成通信。
- 通信模式优点:
- 生产者单方面完成消息通信,无需等待消费者。
- 消费者根据自身能力拉取新消息,灵活。
- 核心用途:
- 异步化:将非核心业务逻辑异步处理,大幅降低用户请求响应时间。
- 流量削峰:消费者根据自身处理能力灵活读取消息,平滑高并发流量,提高服务稳定性。
- 解耦:服务间通过消息队列通信,消除直接依赖,利于职责分离,减少事故。
Kafka
- 定义:分布式、高性能、高可扩展性的消息队列系统,主要用于日志收集和消息中间件。
- 整体架构组件:
- Producer (生产者):生产消息。
- Consumer (消费者):消费消息。
- Topic (主题):消息类型。
- Partition (分区):Topic消息分布式存储在多个Partition中,实现负载均衡。每个Partition消息有序,不同Partition间消息无序。由Broker管理。
- Broker:Kafka核心,接收/存储消息到Partition,处理消费者消费逻辑。多个Broker组成Kafka集群。
- Consumer Group (消费者组):多个消费者实例组成,一个Topic对应一个或多个Consumer Group。
- 规定:一个Partition消息只能被Consumer Group中一个消费者实例消费。
- ZooKeeper (Kafka 0.9+):负责Kafka集群元信息管理。
- Broker注册、Topic元信息管理 (Partition与Broker关联)。
- 生产者负载均衡 (消息写入Partition)。
- 消费者负载均衡 (记录“哪个消费者实例消费哪个Partition”,宕机后Rebalance)。
- 消费进度Offset记录 (Kafka 0.9+版本已改为Broker本地磁盘保存,提升写性能)。
- 生产者发送消息流程:
- 指定Partition则直接使用。
- 未指定但有Key则对Key哈希选Partition。
- 既未指定Partition也未指定Key则轮询选择Partition。
- 发送到选定的Partition对应Broker,Broker顺序写入磁盘。
- 消费者消费消息:
- 以Consumer Group方式工作。
- 一个Consumer Group可消费多个Topic,一个Topic也可被多个Consumer Group消费。
- 拉取(pull)模式:消费者主动控制消费速度,实现削峰。
Kafka的高可用
- Partition多副本:每个Partition有1个Leader和若干Follower。
- Leader处理生产者写入和消费者读取。
- Follower周期性向Leader请求消息复制,作为Leader数据备份。
- 副本存储在不同Broker上,防止Broker宕机导致Partition不可用。
- ISR (In-Sync Replica) 机制:Leader维护与自己数据一致的Follower列表。
- Follower长时间未复制或数据落后过多,则从ISR移除。
- 只有ISR中所有Follower都确认收到消息,Leader才认为消息写入成功。
- 特点:同步复制和异步复制的折中,避免Follower宕机对性能/可用性影响,保证多副本数据一致性。
- 故障恢复:
- Controller角色:Kafka集群中选举一个Controller(借助ZooKeeper分布式锁),负责Partition Leader选举和副本重分配。
- 故障恢复流程:
- Broker故障,与ZooKeeper断连。
- ZooKeeper通知Controller。
- Controller查询受影响的Partition (Leader由故障Broker负责)。
- Controller从每个Partition Leader ISR中选择一个Follower提升为Leader。
- Controller通知相关Broker。
- 被选举的Follower变为Leader,其他Follower转而向新Leader复制数据。
多机房:主备机房
- 问题:单机房是单点故障(人为破坏、自然灾害、断电、火灾等)。
- 方案:在主机房所在城市建设一个备机房,完全复制主机房架构。
- 正常情况:仅主机房工作。
- 存储层:备机房数据库作为主机房从库,通过专线进行数据复制。
- 故障切换:主机房故障时,备机房:
- 存储层从库提升为主库。
- 修改DNS解析地址指向备机房。
- “同城灾备”特点:
- 优点:架构搭建简单。
- 缺点:
- 资源浪费:备机房大部分时间空闲。
- 可用性存疑:备机房无实战经验,关键时刻可能掉链子。
多机房:同城双活
- 动机:解决主备机房资源浪费和可用性存疑问题。
- 改造:将两个机房的接入层IP地址都配置到DNS,实现“双活”,两个机房都能处理部分用户请求。
- 存储层改造:
- 一个机房作为主库机房(如A机房)。
- 另一个机房(如B机房)的所有写数据请求跨机房访问A机房对应的主库。
- 读数据请求依赖各存储系统自带的主从复制功能,在本地处理。
- 优点:写请求延迟增大但可接受(同城距离近,专线延迟5ms左右),写请求占比较低。可将“同城双活”视为单机房使用。
- 灵活实施:
- “同城”不局限行政区域:强调物理距离近,以缓解跨机房访问延迟,同时避免物理距离过近导致单点失效。
- “同城双活”可扩展为“同城多活”:可部署更多机房,但存储层需选出唯一主库机房。
- 分流与故障切流:
- 分流策略:根据用户ID/DeviceID哈希映射到不同机房。推荐DeviceID(未登录用户)。
- 分流环节:
- DNS分流 (粗糙):解析结果不确定,同一用户可能路由到不同机房,策略变更生效慢。
- 客户端分流 (推荐):
- 分流配置平台:部署在各机房,工程师配置域名分流比例 (如
sharding,idc包含lower/upper范围和domain专用域名)。 - 客户端流程:发起请求时若有分流配置,则DeviceID哈希取模,根据范围选择专用域名发送请求。
- 准实时生效:Nginx响应Header带最新分流配置版本号,客户端若发现本地版本旧,则主动拉取最新配置。
- 客户端主动容灾:连续N次访问A机房失败 -> 猜测A不可用 -> 尝试拉取A机房配置 (若失败) -> 转而向B机房拉取配置 -> 应用最新配置切流到B机房。
- 分流配置平台:部署在各机房,工程师配置域名分流比例 (如
- HTTP DNS或其他接入层组件:也可支持分流。
- 切流操作:若故障机房是主库,需将备机房存储层数据库提升为主库。
- 两地三中心:
- 在“同城双活”基础上增加一个异地备机房(距离较远)。
- 备机房只做数据备份,不对外提供服务。
- 笔者看法:投入产出比低,多数场景下“同城双活”足够。
多机房:异地多活
- 适用场景:服务于全球用户的世界级应用(如Facebook, Instagram)。
- “同城双活”对全球应用的问题:
- 用户访问延迟:跨国访问物理距离远,延迟高。
- 数据合规问题:各国要求本国用户数据独立存储。
- 灾难问题:部署国战争、暴乱、自然灾害可能导致全球应用不可用。
- “异地多活”架构要点:
- 每个机房都在本机房内处理写数据请求:由于异地机房间网络延迟巨大,无法跨机房写主库。每个机房独立部署各存储层主库,存储层间无主从关系。
- 数据互通:每个机房将本机房写入的数据复制到其他机房,实现全球数据可见。
- 通过DRC (Data Replicate Center) 工具实现存储系统间的双向数据复制 (主主复制)。
- 业界项目:阿里巴巴 (Otter, RedisShake, MongoShake), 携程 (XPipe)。
MySQL DRC的原理
- DRC工具结构:
- Sync-out:伪从,模拟MySQL从库,从本地主库获取binlog数据变更记录,实时传输到远端Sync-in。
- Sync-in:扮演数据库客户端,收到远端数据后还原SQL语句并在本机房主库执行。
- 断点续传与数据防重:
- 使用GTID (Global Transaction Identifier):MySQL对每个已提交事务的全局唯一编号。
- Sync-out记录最新传输成功的GTID,故障恢复后从GTID位置继续传输。
- Sync-in保存已写入数据GTID集合,收到数据前检查是否已存在,实现数据防重。
- 防止数据回环:
- 在主库创建辅助表,记录来自其他机房的数据变更事务。
- Sync-in写入数据时,同时在辅助表插入记录。
- Sync-out解析binlog,若发现事务有写辅助表,则说明来自其他机房,不传输此数据。
- 数据冲突:
- 难以解决:在不同机房几乎同时修改同一数据。
- LWW (Last Write Wins) 策略:最后写入者胜利(基于修改时间戳),但不同机房时间戳可能不一致,不准确。
- 避免冲突:
- 业务逻辑不依赖数据库自增主键,采用分布式唯一ID。
- 机房分流尽量保证不同用户被分流到不同机房,避免同一用户数据在多机房同时修改。
- 总结:MySQL DRC技术核心要点包括伪从、断点续传、数据防重、防止数据回环和数据冲突。
Redis DRC的原理
- 伪从:Sync-out模拟Redis从库,向Redis主库复制写命令,暂存到本地。
- 断点续传和数据防重:
- Redis无GTID,需自定义递增唯一ID绑定到每个写命令。
- Sync-out保存已传输写命令ID,恢复后继续传输。
- Sync-in保存最近成功写入的写命令ID,若收到ID小于等于此ID则丢弃。
- 防止数据回环:
- 思路1 (不推荐):改造Redis写命令格式携带机房信息(维护成本高)。
- 思路2 (推荐):Redis主库识别客户端角色。增加
REDIS_SYNC_IN标志,若写命令来自Sync-in,则不复制给Sync-out。
- 数据冲突:
- 比MySQL更复杂,Redis命令丰富,难实现统一防冲突数据结构。
- 同样建议尽量避免数据冲突,通过用户分区等方式。
分流策略
- 全球应用分流要求:必须考虑用户地理位置。
- DNS地域解析:为不同国家用户配置不同机房接入层IP,多数用户可直达目标机房。
- DNS分流缺点:用户网络环境(VPN, 非本国SIM卡, 出国旅行)可能干扰解析结果。
- 固定用户所在国方案 (推荐):用户注册国策略。
- 新用户注册时客户端携带国家信息作为注册国,后台保存。
- 用户登录时,根据注册国映射到预配置的机房,即使出国/使用VPN也固定访问此机房。
- 优化:监控用户访问请求国家维度,若连续较长时间与注册国不同,可修改注册国,提升体验。
数据复制链路
- 全网状结构:每个机房都与所有其他机房建立双向数据复制链路。
- 链路数:
C(N,2) * 2。N个机房时链路数激增(如6个机房15条链路),维护成本高。
- 链路数:
- 星状结构 (推荐):约定一个机房为中心机房。
- 其他机房主库数据仅复制到中心机房。
- 中心机房再将数据复制到所有其他机房。
- 优势:显著降低链路数(N-1 * 2),降低架构复杂度。