Redis后端分布式与高并发架构演进
这七张图片构成了一个非常系统化的、由浅入深的后端分布式与高并发架构演进知识图谱。我将按照网络基础→\rightarrow→性能优化连接池→\rightarrow→多路复用高并发核心→\rightarrow→解耦异步消息队列→\rightarrow→分布式高可用Redis主从与哨兵的逻辑顺序建立起一个完全不矛盾、环环相扣的架构演进知识体系。一、 阶段一基础网络通信与阻塞式 I/O图 1 图1所有的互联网应用最底层的根基都是客户端Client与服务端Server的通信。工作原理服务端创建一个ServerSocket监听特定端口当客户端发起连接时服务端调用accept()方法接收连接并返回一个标准的socket。痛点为什么需要演进图中明确标注了“阻塞式”。在传统的 BIOBlocking IO模型中当一个线程在执行read()或write()读写流数据时如果没有数据到来该线程就会一直死等阻塞。这意味着一个线程只能处理一个客户端连接。如果有成千上万个用户同时访问服务器需要创建同等数量的线程系统会直接崩溃。二、阶段二缓解线程开销的利器——连接池图 2图2既然频繁地为每个客户端创建和销毁线程/连接代价太高工程师们想出了一个妥协的高招引入连接池Connection Pool。工作原理*提前准备系统启动时先初始化一部分连接如图中的init_connection 10。动态伸缩随着用户变多连接数按照一定的算法比例像“滑动窗口”一样增长但有一个硬性天花板如max_connection 100。循环利用当某个线程用完连接调用close()时连接并不会被真正销毁而是像图中绿色的conn一样重新放回连接池的队尾供下一个用户复用。痛点为什么还不够连接池虽然省去了频繁创建连接的开销但它依旧没有解决“一个连接/线程同时只能干一件事”的阻塞本质。面对真正的海量并发如电商秒杀光靠堆积连接池大小是无济于事的。三、阶段三高并发的灵魂——I/O 多路复用图 3 图 4 图3图4为了彻底解决几万个连接把服务器卡死的问题架构引入了I/O 多路复用Multiplexing技术这也是 Redis 能够做到单线程却快到飞起的核心秘密。我们把图 4、图 5 串起来看1. 什么是多路复用图 3 图 4传统的做法是“一个保安盯着一个客户”。而I/O 多路复用如底层的epoll机制Java 中的Selector是“一个保安单线程盯着一整个大厅的客户”。如图 5 所示client1到client4发起了不同的请求连接、读、写。这些请求并不会直接让服务器去开 4 个线程处理而是全部放进一个队列/多路复用器中。事件分发器Dispatcher像一个熟练的流水线调度员谁的数据准备好了触发了事件它就把这个事件分发给对应的事件处理器连接应答处理器、命令请求处理器等去处理。四、阶段四应用间的解耦与异步——消息队列图 5图5当单机性能通过 I/O 多路复用压榨到极致后如果业务系统依然过于庞大例如支付成功后要发短信、发优惠券、通知物流让高并发的服务器在线死等这些长耗时业务是不现实的。于是我们引入了发布/订阅Pub/Sub消息队列。工作原理*生产者Producer负责发送消息。它不需要知道谁来消费直接把消息扔给对应的 Topic主题如news或sport。消费者Consumer负责订阅自己感兴趣的主题。如图中消费者只订阅了sport主题那么它就只会接收并处理sport的消息哪怕生产者发了再多news消息也与其无关。主流技术选型图最下方Kafka吞吐量极其恐怖适合大数据、日志收集场景。RocketMQJava 语言编写对分布式事务支持极好阿里开源适合金融和电商核心业务。RabbitMQErlang 语言编写天然具备极高的并发性和低延迟稳定性极强。五、阶段五数据层的高可用架构演进图 6 图 7当高并发的流量穿过网络、通过了消息队列最终落到了数据库/缓存以 Redis 为例时单台服务器单机如果宕机整个系统就会瘫痪。因此架构必须向分布式高可用演进。1. 基础版主从复制 读写分离图 6图6架构一个 Master主节点负责处理写请求如set k1 v1多个 Follower/Slave从节点负责处理读请求如get k1。两个致命痛点最终一致性问题主节点写完数据后同步到从节点是有时间延迟的。在这段延迟内客户端去读从节点可能会读到旧数据允许短时间不一致但最终必须一致。主节点宕机断服如果 Master 挂了Follower 只能傻傻地等待从节点不会自动篡位。此时整个集群将无法提供写服务。2. 终极高可用版哨兵模式图 7 - 哨兵模式图7为了解决主节点挂了无人主持大局的问题引入了哨兵Sentinel。监控阶段图 7 上半部分* 哨兵就像一个巡逻兵通过定时任务如setInterval()向 Master 和 Follower 发送健康检查心跳。当 Master 突然宕机哨兵检测到其状态变为unavailable就会立刻向所有 Follower 发送远程调用通知notify并组织准备进行新皇选举startLeading。故障转移阶段图 7 下半部分哨兵会在现有的 Follower 中随机按一定算法规则挑选一台机器将其提升为新的 Master。原先的那些 Follower 会被通知去同步这个新 Master 的数据。最妙的地方在于当那个挂掉的旧 Master 恢复健康后它失去了往日的荣光会自动变成新 Master 的从机Follower重新加入集群。注图中为了演示只画了一个哨兵在生产环境中为了防止哨兵自己产生误判通常会部署奇数个3个或以上的哨兵集群进行共同决策。六、总结完整的请求流转顺序我们将这七张图融合成一个动态的场景Step 1从单机连接到突破瓶颈图 1 图 2最初客户端通过标准的 ServerSocket图 1 发起阻塞式连接。为了避免频繁创建线程系统引入了 ConnectionPool图 2 循环复用连接。然而面对真正海量的瞬时并发单纯堆积连接池无济于事架构逼迫网络层向非阻塞演进。Step 2核心战力压榨单机性能图 4 图 3为了彻底解决阻塞问题服务器换上了高并发的灵魂武器——I/O 多路复用图 4。它让单个线程能够同时盯紧成千上万个连接Selector 机制并将准备就绪的事件分发处理。在 Redis 内部图 3这种多路复用配合其标志性的“单线程核心命令处理多线程网络 I/O”让单机读写性能直接飙到极致。Step 3内部分流应用异步解耦图 5当网络层能够轻松吞下海量请求后面对长耗时、非核心的业务服务器不再傻傻地在线等待。而是通过 PUB/SUB 发布订阅机制图 5 充当中间人把消息直接扔给 Kafka、RocketMQ 或 RabbitMQ 后立刻返回实现应用层的完美解耦与流量削峰。Step 4数据扩容主从读写分离图 6当庞大的流量最终穿透应用层、涌入底层数据层时单机存储成为新的瓶颈。系统随之演进为 主从复制架构图 6。利用 Spring Cloud LoadBalancer 等负载均衡算法将写请求送往 Master 主节点读请求分流到各大 Follower 从节点实现读写分离。Step 5终极闭环自动化高可用图 7分布式集群最怕主节点突然宕机。于是 哨兵Sentinel集群图 7 登场它们像巡逻兵一样定时健康检查。一旦 Master 挂掉哨兵立刻自动主持大局快速选举出新的 Master并让断线重连的旧 Master 降级为从机整个高并发、高可用系统自此实现 24 小时永不宕机的闭环。