红联Linux门户
Linux协助

并发服务器(五):Redis事例研讨

发布时刻:2018-03-08 09:32:15来历:linux.cn作者:qhwdw
这是我写的并发网络服务器系列文章的第五部分。在前四部分中咱们评论了并发服务器的结构,这篇文章咱们将去研讨一个在出产体系中很多运用的服务器的事例—— Redis。
Redis 是一个十分有魅力的项目,我重视它很久了。它最让我入神的一点便是它的 C 源代码十分明晰。它也是一个高性能、大并发的内存数据库服务器的十分好的比如,它是研讨网络并发服务器的一个十分好的事例,因而,咱们不能错失这个好机会。
咱们来看看前四部分评论的概念在实在国际中的运用程序。
 
本系列的一切文章有:
本系列的一切文章:
第一节 - 简介(http://www.138comgov138.com/linux/32842.html)
第二节 - 线程(http://www.138comgov138.com/linux/32844.html)
第三节 - 作业驱动(http://www.138comgov138.com/linux/32997.html)
第四节 - libuv(http://www.138comgov138.com/linux/33257.html)
 
作业处理库
Redis 开始发布于 2009 年,它最牛逼的一件作业大约便是它的速度 —— 它能够处理很多的并发客户端衔接。需求特别指出的是,它是用一个单线程来完结的,而且还不对保存在内存中的数据运用任何杂乱的锁或许同步机制。
Redis 之所以如此牛逼是由于,它在给定的体系上运用了其可用的最快的作业循环,并将它们封装成由它完成的作业循环库(在 Linux 上是 epoll,在 BSD 上是 kqueue,等等)。这个库的姓名叫做 ae。ae 使得编写一个快速服务器变得很简略,只需在它内部没有堵塞即可,而 Redis 则确保 注1 了这一点。
在这儿,咱们的爱好点主要是它对文件作业的支撑 —— 当文件描述符(如网络套接字)有一些风趣的未决作业时将调用注册的回调函数。与 libuv 相似,ae 支撑多路作业循环(参看本系列的第三节和第四节)和不应该感到意外的 aeCreateFileEvent 信号:
int aeCreateFileEvent(aeEventLoop *eventLoop, int fd, int mask,
aeFileProc *proc, void *clientData);
它在 fd 上运用一个给定的作业循环,为新的文件作业注册一个回调(proc)函数。当运用的是 epoll 时,它将调用 epoll_ctl 在文件描述符上增加一个作业(或许是 EPOLLIN、EPOLLOUT、也或许两者都有,取决于 mask 参数)。ae 的 aeProcessEvents 功用是 “运转作业循环和发送回调函数”,它在底层调用了 epoll_wait。
 
处理客户端恳求
咱们经过盯梢 Redis 服务器代码来看一下,ae 怎么为客户端作业注册回调函数的。initServer 启动时,经过注册一个回调函数来读取正在监听的套接字上的作业,经过运用回调函数 acceptTcpHandler 来调用 aeCreateFileEvent。当新的衔接可用时,这个回调函数被调用。它调用 accept 注2 ,接下来是 acceptCommonHandler,它转而去调用 createClient 以初始化新客户端衔接所需求的数据结构。
createClient 的作业是去监听来自客户端的入站数据。它将套接字设置为非堵塞形式(一个异步作业循环中的关键因素)并运用 aeCreateFileEvent 去注册别的一个文件作业回调函数以读取作业 —— readQueryFromClient。每逢客户端发送数据,这个函数将被作业循环调用。
readQueryFromClient 就让咱们希望的那样 —— 解析客户端指令和动作,并经过查询和/或操作数据来回复。由于客户端套接字对错堵塞的,所以这个函数有必要能够处理 EAGAIN,以及部分数据;从客户端中读取的数据是累积在客户端专用的缓冲区中,而完好的查询或许被分割在回调函数的多个调用傍边。
 
将数据发送回客户端
在前面的内容中,我说到了 readQueryFromClient 完毕了发送给客户端的回复。这在逻辑上是正确的,由于 readQueryFromClient 准备要发送回复,但它不真实去做本质的发送 —— 由于这儿并不能确保客户端套接字现已准备好写入/发送数据。咱们有必要为此运用作业循环机制。
Redis 是这样做的,它注册一个 beforeSleep 函数,每次作业循环行将进入休眠时,调用它去等候套接字变得能够读取/写入。beforeSleep 做的其间一件作业便是调用 handleClientsWithPendingWrites。它的作用是经过调用 writeToClient 去测验当即发送一切可用的回复;假如一些套接字不行用时,那么当套接字可用时,它将注册一个作业循环去调用 sendReplyToClient。这能够被看作为一种优化 —— 假如套接字可用于当即发送数据(一般是 TCP 套接字),这时并不需求注册作业 ——直接发送数据。由于套接字对错堵塞的,它从不会去堵塞循环。
 
为什么 Redis 要完成它自己的作业库?
在 第四节 中咱们评论了运用 libuv 来构建一个异步并发服务器。需求留意的是,Redis 并没有运用 libuv,或许任何相似的作业库,而是它去完成自己的作业库 —— ae,用 ae 来封装 epoll、kqueue 和 select。事实上,Antirez(Redis 的创立者)恰好在 2011 年的一篇文章(http://oldblog.antirez.com/post/redis-win32-msft-patch.html) 中答复了这个问题。他的答复的关键是:ae 只需大约 770 行他了解的十分透彻的代码;而 libuv 代码量十分巨大,也没有供给 Redis 所需的额定功用。
现在,ae 的代码大约增长到 1300 多行,比起 libuv 的 26000 行(这是在没有 Windows、测验、示例、文档的状况下的数据)来说那是小巫见大巫了。libuv 是一个十分归纳的库,这使它更杂乱,而且很难去习惯其它项目的特别需求;另一方面,ae 是专门为 Redis 规划的,与 Redis 一起演进,只包含 Redis 所需求的东西。
这是我 前些年在一篇文章中(http://eli.thegreenplace.net/2017/benefits-of-dependencies-in-software-projects-as-a-function-of-effort/) 说到的软件项目依靠联络的另一个很好的示例:
依靠的优势与在软件项目上花费的作业量成反比。
在某种程度上,Antirez 在他的文章中也说到了这一点。他说到,供给很多附加价值(在我的文章中的“根底” 依靠)的依靠比像 libuv 这样的依靠更有意义(它的比如是 jemalloc 和 Lua),关于 Redis 特定需求,其功用的完成适当简略。
 
Redis 中的多线程
在 Redis 的绝大多数前史中,它都是一个不折不扣的单线程的东西。一些人觉得这太不行思议了,有这种主意完全能够了解。Redis 本质上是受网络捆绑的 —— 只需数据库巨细合理,关于任何给定的客户端恳求,其大部分延时都是糟蹋在网络等候上,而不是在 Redis 的数据结构上。
但是,现在作业现已不再那么简略了。Redis 现在有几个新功用都用到了线程:
1.“慵懒” 内存开释。
2.在后台线程中运用 fsync 调用写一个 耐久化日志。
3.运转需求履行一个长周期运转的操作的用户界说模块。
关于前两个特性,Redis 运用它自己的一个简略的 bio(它是 “Background I/O" 的首字母缩写)库。这个库是依据 Redis 的需求进行了硬编码,它不能用到其它的当地 —— 它运转预设数量的线程,每个 Redis 后台作业类型需求一个线程。
而关于第三个特性,Redis 模块 能够界说新的 Redis 指令,而且遵从与一般 Redis 指令相同的规范,包含不堵塞主线程。假如在模块中自界说的一个 Redis 指令,希望去履行一个长周期运转的操作,这将创立一个线程在后台去运转它。在 Redis 源码树中的 src/modules/helloblock.c 供给了这样的一个示例。
有了这些特性,Redis 运用线程将一个作业循环结合起来,在一般的事例中,Redis 具有了更快的速度和弹性,这有点相似于在本体系文章中 第四节 评论的作业行列。
 
注1:Redis 的一个中心部分是:它是一个 内存中 数据库;因而,查询从不会运转太长的时刻。当然了,这将会带来各式各样的其它问题。在运用分区的状况下,服务器或许终究路由一个恳求到另一个实例上;在这种状况下,将运用异步 I/O 来防止堵塞其它客户端。
注2:运用 anetAccept;anet 是 Redis 对 TCP 套接字代码的封装。
 
怎么让网站不下线而从Redis 2迁移到Redis 3:http://www.138comgov138.com/linux/32802.html
ubuntu 17.04装置最新版别redis:http://www.138comgov138.com/linux/32785.html
Linux Redis重启数据丢掉解决方案:http://www.138comgov138.com/linux/32076.html
Ubuntu16.04下装置nginx+mysql+php+redis:http://www.138comgov138.com/linux/31935.html
在阿里云上敞开Redis默许的6379端口:http://www.138comgov138.com/linux/31347.html