分布式进阶(十)——分布式框架之高性能:Redis线程模型

作者简介:大家好,我是smart哥,前中兴通讯、美团架构师,现某互联网公司CTO

联系qq:184480602,加我进群,大家一起学习,一起进步,一起对抗互联网寒冬

学习必须往深处挖,挖的越深,基础越扎实!

阶段1、深入多线程

阶段2、深入多线程设计模式

阶段3、深入juc源码解析


阶段4、深入jdk其余源码解析


阶段5、深入jvm源码解析

码哥源码部分

码哥讲源码-原理源码篇【2024年最新大厂关于线程池使用的场景题】

码哥讲源码【炸雷啦!炸雷啦!黄光头他终于跑路啦!】

码哥讲源码-【jvm课程前置知识及c/c++调试环境搭建】

​​​​​​码哥讲源码-原理源码篇【揭秘join方法的唤醒本质上决定于jvm的底层析构函数】

码哥源码-原理源码篇【Doug Lea为什么要将成员变量赋值给局部变量后再操作?】

码哥讲源码【你水不是你的错,但是你胡说八道就是你不对了!】

码哥讲源码【谁再说Spring不支持多线程事务,你给我抽他!】

终结B站没人能讲清楚红黑树的历史,不服等你来踢馆!

打脸系列【020-3小时讲解MESI协议和volatile之间的关系,那些将x86下的验证结果当作最终结果的水货们请闭嘴】

在各类分布式框架中,还有一类是和高性能息息相关的,那就是分布式缓存。目前生产环境常用的开源分布式缓存框架就是Redis和Memcached,它们之间的主要区别,我这里简单比较下:

  • Redis拥有更多的数据结构,支持更丰富的数据操作。
  • Memcached没有原生的集群模式,而Redis原生支持cluster模式。

目前Redis在工业环境中用得比较多,所以本系列只介绍Redis。如果读者对Memcached感兴趣,可以参考其官方文档

一、Redis线程模型

Redis是典型的单线程架构,所有的读写操作都是在服务端的一条主线程中完成的。Redis客户端与服务端的模型如下图,每次客户端调用都经历了 发送命令 、 执行命令 、 返回结果 三个过程:

既然是单线程,为什么Redis能实现这么高的读写性能呢?

因为Redis基于 Reactor 模式开发了自己的网络事件处理器—— 文件事件处理器(file
event handler)
 。

文件事件处理器,是单线程模式运行的,但是使用了I/O多路复用技术,同时监听多个套接字,并为套接字关联不同的事件处理器,当套接字上产生文件事件时,就会调用相应的事件处理器进行处理,这样就可以实现高性能的网络通信模型,又可以跟内部其他单线程的模块进行对接,保证了Redis内部线程模型的简单性。

文件事件处理器一共包含四个部分,它们分别是 套接字(Socket) 、 I/O多路复用程序 、 文件事件分派器 以及 事件处理器 ,如下图:

可以看到,I/O多路复用程序负责监听多个socket,并向文件事件派发器传递那些产生了事件的socket。

尽管多个文件事件可能会并发地出现,但I/O多路复用程序总是会将所有产生的socket都放到同一个队列里边,然后文件事件处理器会以有序、同步的方式处理该队列中的每个socket:根据每个socket当前产生的事件,来选择对应的事件处理器来处理。

1.1 套接字(socket)

Redis服务端通过Socket与客户端连接。每当一个套接字准备好执行连接应答(accept)、读取(read)、写入(write)、关闭(close)等操作时,就会产生一个文件事件。 因为一个服务器通常会连接多个套接字, 所以多个文件事件有可能会并发地出现。

比如,当socket变得可读时,socket就会产生一个AE_READABLE事件;当socket变得可写的时候,socket会产生一个AE_WRITABLE事件。

1.2 I/O 多路复用程序

I/O 多路复用程序主要负责监听多个套接字,并向文件事件分派器传送那些产生了事件的套接字。

I/O多路复用程序会将所有产生事件的套接字都入队到一个队列里, 然后通过这个队列,以有序(sequentially)、同步(synchronously)、每次一个套接字的方式向文件事件分派器传送套接字。 当上一个套接字产生的事件被处理完毕之后, I/O多路复用程序才会继续向文件事件分派器传送下一个套接字。

Redis 使用的IO多路复用技术主要有:selectepollevportkqueue等。每个IO多路复用函数库在 Redis 源码中都对应一个单独的文件,比如ae_select.c,ae_epoll.c, ae_kqueue.c等。Redis 会根据不同的操作系统,按照不同的优先级选择多路复用技术。事件响应框架一般都采用该架构,比如 netty 和 libevent:

1.3 文件事件分派器

文件事件分派器,负责监听多个套接字,接收 IO 多路复用程序传来的套接字,并根据套接字产生的事件类型, 调用相应的事件处理器,比如:
如果是客户端要连接redis,那么会为socket关联连接应答处理器;
如果是客户端要写数据到redis,那么会为socket关联命令请求处理器;
如果是客户端要从redis读数据,那么会为socket关联命令回复处理器。

1.4 事件处理器

服务器会为执行不同任务的套接字关联不同的事件处理器,这些处理器是一个个函数, 它们定义了某个事件发生时, 服务器应该执行的动作。

二、示例

我们以一次Redis客户端的请求,来看下上述的整个流程:

  1. Redis服务启动初始化的时候,Redis会将【连接应答处理器】跟AE_READABLE事件关联起来;
  2. 如果一个Redis客户端发起连接请求,此时会产生一个AE_READABLE事件,然后由【连接应答处理器】来处理跟客户端建立连接,创建客户端对应的socket,同时将这个socket的AE_READABLE事件跟【命令请求处理器】关联起来;
  3. 当客户端向Redis发起命令请求时,首先就会在socket产生一个AE_READABLE事件,然后由对应的【命令请求处理器】来处理。这个【命令请求处理器】就会从socket中读取请求相关数据,然后进行执行和处理;
  4. 当Redis准备好了给客户端的响应数据后,就会将socket的AE_WRITABLE事件跟【命令回复处理器】关联起来,当客户端准备好读取响应数据时,就会在socket上产生一个AE_WRITABLE事件,对应的【命令回复处理器】会将准备好的响应数据写入socket,供客户端来读取;
  5. 【命令回复处理器】写完之后,就会删除这个socket的AE_WRITABLE事件和【命令回复处理器】的关联关系。

三、总结

Redis属于单线程模型,但是效率非常高,每秒能够读写上万条数据,其主要原因如下:

  1. 纯内存操作;
  2. 核心是基于非阻塞的IO多路复用机制;
  3. 单线程反而避免了多线程的频繁上下文切换问题。
  • 24
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值