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 ...
随机推荐
- Linux网络安全篇,认识防火墙(一)
一.概念 防火墙分为软件防火墙和硬件防火墙.我们的主要讨论范围为软件防火墙. 软件防火墙又分为网络型和单一型的管理. 1.单一主机型防火墙 (1)数据包过滤型的Netfilter (2)依据服务软件程 ...
- CH5105 Cookies (线性dp)
传送门 解题思路: 贪心的想,贪婪值越大的孩子应该分得更多的饼干,那么先sort一遍在此基础上进行dp.最直观的方向,可以设dp[i][j]为前i个孩子一共分得j块饼干的怨恨最小值.然后转移第i+1个 ...
- group_concat有长度限制
group_concat有长度限制 group_concat 详细用法请点此链接. group_concat有长度限制!长度陷阱用了group_concat后,select里如果使用了li ...
- 深入浅出node.js游戏服务器开发1——基础架构与框架介绍
2013年04月19日 14:09:37 MJiao 阅读数:4614 深入浅出node.js游戏服务器开发1——基础架构与框架介绍 游戏服务器概述 没开发过游戏的人会觉得游戏服务器是很神秘的 ...
- ant-design-vue表单生成组件form-create快速上手
介绍 form-create 是一个可以通过 JSON 生成具有动态渲染.数据收集.验证和提交功能的表单生成器.并且支持生成任何 Vue 组件.结合内置17种常用表单组件和自定义组件,再复杂的表单都可 ...
- 谈谈MySQL的索引
目录 索引 前言 是什么 B树 B+树 B树和B+树结构上异同 有什么用 怎么用 索引 前言 总所周知,数据库查询是数据库的最主要功能之一.我们都希望查询数据的速度能尽可能的快.而支撑这一快速的背后就 ...
- Liunx常用操作(一)-删除命令
在linux命令行模式下,如何一次性快速删除一行刚刚输入的命令? 经常在命令行输入命令的时候,一段文字都需要删除,一个字段一个字段,比较耗费时间 以下提供一些命令,配合在一起操作,可以一定程度提高工作 ...
- SpringCloud(六)学习笔记之Zuul
Zuul 在云平台上提供动态路由,监控,弹性,安全等边缘服务的框架.Zuul 相当于是设备和 Netflix 流应用的 Web 网站后端所有请求的前门 Hystrix+Ribbon(不使用Feign) ...
- PHP 语法字符串函数 strcmp、strlen 使用及实现
说明 这里基于 php7.2.5 进行测试,php7 之后内部结构变化应该不是太大,但与 php5.X 有差别. 函数分类 用户自定义函数 say(); function say() { echo & ...
- 总结vscode调试vue,nodejs的各种方法
之前写项目一直都是console.log()来调试的,浪费了很多时间,现在整理一下用vscode对nuxt(vue)前后端进行调试的方法 前端的调试 chrome+launch 使用chrome调试, ...