Predixy是一个代理,代理本质上就是用来转发请求的。其主要功能就是接收客户端的请求,然后把客户端请求转发给redis服务端,在redis服务端处理完消息请求后,接收它的响应,并把这个响应返回给客户端。

1.整体架构

Predixy的架构比较简单,它采用多线程的模式。入口代码逻辑也比较清晰:



Main函数里创建一个代理,然后在init方法里面创建多个处理线程Handler,handler的个数是由配置文件配置,这个一般可以根据CPU的核数来配置。Proxy初始化完成之后,就在run里面起handler线程,Proxy里面有一个用来保存handler的vector,创建的多个handler之间基本上是相互独立。整个predixy的流程基本就在handler的run方法中,入口是Handler::run()



Handler是一个事件循环。mEventLoop是创建Handler时初始化的Multiplexor(后续的Multiplexor我们统一以epoll为例),在mEventLoop->wait里面处理注册到epoll上的fd事件。

注册到epoll里面的事件主要有:

1、连接事件:在创建Handler的时候,创建mEventLoop,并把监听事件加入到epoll里面

2、已经建立连接的客户端的收包和回包

3、到redis服务端的请求转发和回包消息转发给客户端

主要的两个函数是wait和postEvent:wait里面主要是处理客户端请求读取,redis回包读取,postEvent主要是处理客户端请求转发给redis和redis回包转发给客户端;在wait中收到的包,会在接下来的postEvent中立马尽量发出去,如果一次发送不完,会注册写事件到epoll中,等可写了会进行再次发送。

2.事件循环

上面介绍了大体的逻辑,下面我们来仔细看看事件循环里两个主要的函数wait和postEvent。

Wait的入口在EpollMultiplexor::wait,有事件来了之后,就循环调用handler的handlerEvent方法,入口在Handler::handleEvent:



这里我们看到有3种连接的类型

1、ListenType

监听的socket,在创建监听socket的时候指定的类型

2、AcceptType

连接建立后,就会创建一个AcceptConnection,类型转为AcceptType

3、ConnectType

客户端有消息过来时,连接是AcceptConnection,读取了客户端消息,在转发时,需要拿到predixy和redis的连接来做这个转发的动作,这时候的连接就是ConnectConnection。

handleListenEvent:主要是处理接收连接,把新来的连接加入到epoll

handleAcceptConnectionEvent:读取客户端的消息AcceptConnection::readEvent,如果有可写事件,则会addPostEvent,使得前面没发完的消息可以再次发送。

readEvent里面会读消息-->AcceptConnection::parse解析请求,把请求放入mRequests队列--->Handler::handleRequest

然后根据路由,选择server,getConnectConnection,把消息加入到mSendRequests队列。

handleConnectConnectionEvent:和handleAcceptConnectionEvent是类似的,不过处理的是响应的消息,读从redis到predixy的回包,然后ConnectConnection::handleResponse

PostEvent入口是Handler::postEvent(),这里会统一处理写,postConnectConnectionEvent是predixy往redis写消息,写了之后会把请求放到mSentRequests队列;postAcceptConnectionEvent是predixy往客户端写。

这里的写操作都会先进行一个合包fill,然后掉接口写,如果写完了,会把写事件删掉,如果一次没写完,会把写事件加到epoll里面,等epoll可写了就会返回写事件。

3.排队机制

从上面我们可以看到predixy的处理流程涉及到几个队列,主要是下面3个:

AcceptConnection::mRequests : 客户端连接的请求队列

ConnectConncetion::mSendRequests : 到redis端的待发送队列

ConnectConncetion::mSentRequests : 到redis端的已发送队列

这几个队列的配合关系如下图所示:

1、客户端发送请求给predixy,predixy先将req消息放到AcceptConnection::mRequests队列

2、解析消息后,将req放到对应redis连接的ConnectConncetion::mSendRequests队列里面,这里可能有多个redis,所以这一步需要先路由到具体的server

3、Predixy转发请求给redis

4、将req从ConnectConncetion::mSendRequests 转移到ConnectConncetion::mSentRequests

5、Redis处理消息完毕回响应给predixy

6、Predixy将响应消息对应的req从ConnectConncetion::mSentRequests移除,并通知对应的AcceptConnection

7、AcceptConnection接收到响应后吧消息返回给客户端并出队

这里有一个细节,predixy需要保证同一个连接先发送的请求需要先回复,因此predixy收到redis回包后,从mSentRequests中取出头部请求,只有当mRequests队首的请求完成了才能回,否则在mRequests队列里面等待

postEvent在回包的时候,则会从队首开始,对所有done为true的请求处理回包。

4.路由选择

在predixy向redis转发请求时,需要先根据路由策略取到server,得知是向哪个redis转发,然后从server获取ConnectConncetion进行操作。

在Proxy::init中会先根据配置初始化ServerPool,ServerPool里面有一个个ServerGroup,ServerGroup里面是Server

StandaloneServerPool和ClusterServerPool中serverGroup的组织方式不同:StandaloneServerPool中的mGroupPool是配置文件指定哪些server属于同一个ServerGroup,

此外,ClusterServerPool中,每个ServerGroup负责若干个slots,而StandaloneServerPool,每个group也负责一部分数据,负责的数据的方式取决于mDist的值,有modula和random 2种。StandaloneServerPool和ClusterServerPool都有std::vector mServerPool,用于实现randomkey。

从ServerPool中获取server分2个步骤:

1、找到ServerGroup,如何寻找取决于数据分布方式

2、从ServerGroup中找一个server,如何寻找取决于命令的读写属性,节点的角色和权重

每种ServerPool需要实现下面几个接口:

GetServerFunc mGetServerFunc; //从ServerPool获取server

RefreshRequestFunc mRefreshrequestFunc; //发送请求,刷新server信息

HandleResponseFunc mHandleResponseFunc; //处理刷新server请求的回包

Server中只有server的ip和port信息,具体的连接由每个handler自己维护连接池:mConnectionPool

5.共享连接和独占连接

Predixy有2种使用到后端连接的方式

SharedConnection :无上下文的请求使用

PrivateConnection :有上下文的请求使用

每个handler为每个Server都维护2类连接池mPrivateConns和mShareConns。PrivateConnection是每个带Context的请求独占一个,一般这类请求通过长连接发起,predixy上维护的请求数量也是有限的。

6.多key命令的拆分

多key命令,例如mget、mset、de、unlink这类命令在parse的时候,会拆开。例如:mget a b c会拆分成mget a,mget b。Mget c;一个请求被拆分成多个请求依次入队列,会将第一个设置为leader,后续的设置为follower,在处理回包的时候,对后端返回的请求进行调整。

predixy源码学习的更多相关文章

  1. predixy源码学习--开篇

    最近开始研究predixy.predixy是一款高性能全功能redis代理 ,网上有的文章大部分都是功能上的介绍,很少有源码相关的分享. predixy的相关介绍在github: https://gi ...

  2. Java集合专题总结(1):HashMap 和 HashTable 源码学习和面试总结

    2017年的秋招彻底结束了,感觉Java上面的最常见的集合相关的问题就是hash--系列和一些常用并发集合和队列,堆等结合算法一起考察,不完全统计,本人经历:先后百度.唯品会.58同城.新浪微博.趣分 ...

  3. jQuery源码学习感想

    还记得去年(2015)九月份的时候,作为一个大四的学生去参加美团霸面,结果被美团技术总监教育了一番,那次问了我很多jQuery源码的知识点,以前虽然喜欢研究框架,但水平还不足够来研究jQuery源码, ...

  4. MVC系列——MVC源码学习:打造自己的MVC框架(四:了解神奇的视图引擎)

    前言:通过之前的三篇介绍,我们基本上完成了从请求发出到路由匹配.再到控制器的激活,再到Action的执行这些个过程.今天还是趁热打铁,将我们的View也来完善下,也让整个系列相对完整,博主不希望烂尾. ...

  5. MVC系列——MVC源码学习:打造自己的MVC框架(三:自定义路由规则)

    前言:上篇介绍了下自己的MVC框架前两个版本,经过两天的整理,版本三基本已经完成,今天还是发出来供大家参考和学习.虽然微软的Routing功能已经非常强大,完全没有必要再“重复造轮子”了,但博主还是觉 ...

  6. MVC系列——MVC源码学习:打造自己的MVC框架(二:附源码)

    前言:上篇介绍了下 MVC5 的核心原理,整篇文章比较偏理论,所以相对比较枯燥.今天就来根据上篇的理论一步一步进行实践,通过自己写的一个简易MVC框架逐步理解,相信通过这一篇的实践,你会对MVC有一个 ...

  7. MVC系列——MVC源码学习:打造自己的MVC框架(一:核心原理)

    前言:最近一段时间在学习MVC源码,说实话,研读源码真是一个痛苦的过程,好多晦涩的语法搞得人晕晕乎乎.这两天算是理解了一小部分,这里先记录下来,也给需要的园友一个参考,奈何博主技术有限,如有理解不妥之 ...

  8. 我的angularjs源码学习之旅2——依赖注入

    依赖注入起源于实现控制反转的典型框架Spring框架,用来削减计算机程序的耦合问题.简单来说,在定义方法的时候,方法所依赖的对象就被隐性的注入到该方法中,在方法中可以直接使用,而不需要在执行该函数的时 ...

  9. ddms(基于 Express 的表单管理系统)源码学习

    ddms是基于express的一个表单管理系统,今天抽时间看了下它的代码,其实算不上源码学习,只是对它其中一些小的开发技巧做一些记录,希望以后在项目开发中能够实践下. 数据层封装 模块只对外暴露mod ...

随机推荐

  1. Python爬虫系列(四):Beautiful Soup解析HTML之把HTML转成Python对象

    在前几篇文章,我们学会了如何获取html文档内容,就是从url下载网页.今天开始,我们将讨论如何将html转成python对象,用python代码对文档进行分析. (牛小妹在学校折腾了好几天,也没把h ...

  2. CH5E07 划分大理石(背包dp+二进制拆分)

    传送门     大意: 有价值分别为1..6的大理石各a[1..6]块,现要将它们分成两部分,使得两部分价值之和相等,问是否可以实现.其中大理石的总数不超过20000. 解题思路: 妥妥的多重背包+二 ...

  3. 一、华为模拟器eNSP下载与安装教程

    简单介绍一下 eNSP: eNSP是一款由华为提供的免费的图形化网络仿真工具平台,它将完美呈现真实设备实景(包括华为最新的ARG3路由器和X7系列的交换机),支持大型网络模拟,让你有机会在没有真实设备 ...

  4. 【高并发】高并发环境下如何优化Tomcat配置?看完我懂了!

    写在前面 Tomcat作为最常用的Java Web服务器,随着并发量越来越高,Tomcat的性能会急剧下降,那有没有什么方法来优化Tomcat在高并发环境下的性能呢? Tomcat运行模式 Tomca ...

  5. STC15F2K60S2串口通信的应用。

    前言:由于不可抗拒因素,初始的STC12C5A60S2芯片由于无法进行烧录(...因为没带有锁紧座的开发板),暂且使用STC15F2K60S2芯片.. 一 串行通信概述: 串口通信有SPI IIC U ...

  6. 如何实现Jenkins 编译结果通知到QQ好友及QQ群组<很遗憾 2019年1月1日腾讯停止了webqq机器人的服务支持>

    Jenkins-NotifyQQ NotifyQQ 运行于Docker 本文介绍mac 环境下实现Jenkins编译结果QQ即时通知 Jenkins 安装使用及iOS自动化打包,邮件通知请参考本人博客 ...

  7. 选择IT行业的自我心得,希望能帮助到各位!(三)失败篇

    可能很多小伙伴会说人人创业岂不是人人都能成功,岂不是人人都能成功,是不是每个人都能开上保时捷,法拉利泡着美女,很多人也会说你看他看她多轻松,做个IT一样就赚钱赚钱了. 那么又有多少人能理解到你的心酸了 ...

  8. 《带你装B,带你飞》pytest修仙之路5 - yield操作

    1. 简介 上一篇中,我们刚刚实现了在每个用例之前执行初始化操作,那么用例执行完之后如需要清除数据(或还原)操作,可以使用 yield 来实现.fixture通过scope参数控制setup级别,既然 ...

  9. E. Max Gcd

    单点时限: 2.0 sec 内存限制: 512 MB 一个数组a,现在你需要删除某一项使得它们的gcd最大,求出这个最大值. 输入格式 第一行输入一个正整数n,表示数组的大小,接下来一行n个数,第i个 ...

  10. 这份Mybatis总结,我觉得你很需要!

    前言 只有光头才能变强. 文本已收录至我的GitHub精选文章,欢迎Star:https://github.com/ZhongFuCheng3y/3y Mybatis应该是国内用得最多的「数据访问层」 ...