Predixy
29 Jun 2019
layout: post title: predixy代码走读 categories: [database] tags: [proxy,redis,predixy]
我发现很多公司用redis proxy,开源选型都用了predixy,所以来读一读
入口是handler->run函数
Request::init();
Response::init();
auto conf = mProxy->conf();
refreshServerPool();
while (!mStop) {
mEventLoop->wait(100000, this);
postEvent();
long timeout = conf->clientTimeout();
if (timeout > 0) {
int num = checkClientTimeout(timeout);
if (num > 0) {
postEvent();
}
}
refreshServerPool();
checkConnectionPool();
timeout = mProxy->serverPool()->serverTimeout();
if (timeout > 0) {
int num = checkServerTimeout(timeout);
if (num > 0) {
postEvent();
}
}
}
其中 eventloop的wait是epoll_wait的封装
template<class T>
int EpollMultiplexor::wait(long usec, T* handler)
{
int timeout = usec < 0 ? usec : usec / 1000;
logVerb("epoll wait with timeout %ld usec", usec);
int num = epoll_wait(mFd, mEvents, MaxEvents, timeout);
logVerb("epoll wait return with event count:%d", num);
if (num == -1) {
if (errno == EINTR) {
return 0;
}
Throw(EpollWaitFail, "handler %d epoll wait fail %s", handler->id(), StrError());
}
for (int i = 0; i < num; ++i) {
Socket* s = static_cast<Socket*>(mEvents[i].data.ptr);
int evts = 0;
evts |= (mEvents[i].events & EPOLLIN) ? ReadEvent : 0;
evts |= (mEvents[i].events & EPOLLOUT) ? WriteEvent : 0;
evts |= (mEvents[i].events & (EPOLLERR|EPOLLHUP)) ? ErrorEvent : 0;
handler->handleEvent(s, evts);
}
return num;
}
hanldeEvent根据来的连接类型,处理不同的connect,listen,accept和connect(主要是后面这俩)
如果是读事件,就直接读了,一口气读完?readEvent
主要是把epoll拿到的fd和事件进行处理,标记,主要是addPostEvent来做,标记之后,真正的epoll_ctl动作封装在postEvent中,内部有postConnectConnectionEvent和postAcceptConnectionEvent来处理
在这样框架下,主要的工作就在于维护内部的数据
转发,有接收前端消息有发送后端消息
- PostConnectConn的列表,PostAcceptConnect的列表,主要是addPostEvent和PostEvent来维护改动
- epoll_wait拿到的事件属性也保存了,根据这个属性,在post函数中处理,如果是写事件,一次写不完,重新加数据,下次写。总之不要出现互相影响(一个重要的优化点)
- 在处理期间会检测这个connect是不是正常的,不正常直接修改成err
- PostConnectConn针对不同的命令区分成共用的和私有的
ref
- https://github.com/joyieldInc/predixy