predixy源码学习
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源码学习的更多相关文章
- predixy源码学习--开篇
最近开始研究predixy.predixy是一款高性能全功能redis代理 ,网上有的文章大部分都是功能上的介绍,很少有源码相关的分享. predixy的相关介绍在github: https://gi ...
- Java集合专题总结(1):HashMap 和 HashTable 源码学习和面试总结
2017年的秋招彻底结束了,感觉Java上面的最常见的集合相关的问题就是hash--系列和一些常用并发集合和队列,堆等结合算法一起考察,不完全统计,本人经历:先后百度.唯品会.58同城.新浪微博.趣分 ...
- jQuery源码学习感想
还记得去年(2015)九月份的时候,作为一个大四的学生去参加美团霸面,结果被美团技术总监教育了一番,那次问了我很多jQuery源码的知识点,以前虽然喜欢研究框架,但水平还不足够来研究jQuery源码, ...
- MVC系列——MVC源码学习:打造自己的MVC框架(四:了解神奇的视图引擎)
前言:通过之前的三篇介绍,我们基本上完成了从请求发出到路由匹配.再到控制器的激活,再到Action的执行这些个过程.今天还是趁热打铁,将我们的View也来完善下,也让整个系列相对完整,博主不希望烂尾. ...
- MVC系列——MVC源码学习:打造自己的MVC框架(三:自定义路由规则)
前言:上篇介绍了下自己的MVC框架前两个版本,经过两天的整理,版本三基本已经完成,今天还是发出来供大家参考和学习.虽然微软的Routing功能已经非常强大,完全没有必要再“重复造轮子”了,但博主还是觉 ...
- MVC系列——MVC源码学习:打造自己的MVC框架(二:附源码)
前言:上篇介绍了下 MVC5 的核心原理,整篇文章比较偏理论,所以相对比较枯燥.今天就来根据上篇的理论一步一步进行实践,通过自己写的一个简易MVC框架逐步理解,相信通过这一篇的实践,你会对MVC有一个 ...
- MVC系列——MVC源码学习:打造自己的MVC框架(一:核心原理)
前言:最近一段时间在学习MVC源码,说实话,研读源码真是一个痛苦的过程,好多晦涩的语法搞得人晕晕乎乎.这两天算是理解了一小部分,这里先记录下来,也给需要的园友一个参考,奈何博主技术有限,如有理解不妥之 ...
- 我的angularjs源码学习之旅2——依赖注入
依赖注入起源于实现控制反转的典型框架Spring框架,用来削减计算机程序的耦合问题.简单来说,在定义方法的时候,方法所依赖的对象就被隐性的注入到该方法中,在方法中可以直接使用,而不需要在执行该函数的时 ...
- ddms(基于 Express 的表单管理系统)源码学习
ddms是基于express的一个表单管理系统,今天抽时间看了下它的代码,其实算不上源码学习,只是对它其中一些小的开发技巧做一些记录,希望以后在项目开发中能够实践下. 数据层封装 模块只对外暴露mod ...
随机推荐
- wireshark抓包实战(六),过滤器
目录 一.抓包过滤器 1.语法来源 2.语法 二.显示过滤器 1.语法来源 2.关键要素 wireshark中,过滤器有两种,一种是抓包过滤器,一种是显示过滤器! 抓包过滤器适合大网络环境,配置与抓包 ...
- 从谷歌面试翻车到offer收割的心路历程
首先声明,这只是我的播客随感,其中无法避免有一些个人色彩的见解,请不要在意,我尊敬任何的互联网公司,尊敬研究生期间的老师同学,我只希望给在求学路上的CS同学一些启发. 先介绍一下背景,我是ACM铜牌退 ...
- Spring Cloud和eureka启动报错 解决版本依赖关系
导读 An attempt was made to call a method that does not exist. The attempt was made from the following ...
- 【课程学习】课程2:十行代码高效完成深度学习POC
本文用户记录黄埔学院学习的心得,并补充一些内容. 课程2:十行代码高效完成深度学习POC,主讲人为百度深度学习技术平台部:陈泽裕老师. 因为我是CV方向的,所以内容会往CV方向调整一下,有所筛检. 课 ...
- CVPR2020|3D-VID:基于LiDar Video信息的3D目标检测框架
作者:蒋天园 Date:2020-04-18 来源:3D-VID:基于LiDar Video信息的3D目标检测框架|CVPR2020 Brief paper地址:https://arxiv.org/p ...
- Activity A 跳转到Activity B 生命周期
又被生命周期折磨了一段时间,这次是被onPause 和 onStop 折磨了,一直认为Activity A 跳转到到 Activity B的生命周期是onPause(A),onStop(A),onCr ...
- JMF 下载安装与测试 测试成功
本来就是想在自己写的java里面加入实习的摄像头监控,然后个各种百度了一下,就用JMF来弄了,不过这个东西貌似比较旧,网上的资料虽然说有,但是也不是太多,并且遇到的一下问题也不能解决,总之经过了一天的 ...
- python初学(二)
1.输入一个整数列表L,判断L中是否存在相同的数字: (1)若存在,输出YES,否则输出NO: (2)若存在,输出YES,同时输出相同的数字:否则输出NO. l=list(input()) print ...
- [一起读源码]走进C#并发队列ConcurrentQueue的内部世界 — .NET Core篇
在上一篇<走进C#并发队列ConcurrentQueue的内部世界>中解析了Framework下的ConcurrentQueue实现原理,经过抛砖引玉,得到了一众大佬的指点,找到了.NET ...
- 用Python绘制全球疫情变化地图
目前全球疫情仍然比较严重,为了能清晰地看到疫情爆发以来至现在全球疫情的变化趋势,我绘制了一张疫情变化地图,完整代码共 230 行,需要的朋友在公众号回复关键字 疫情地图 即可. 废话不多说,先上图 下 ...