Redis windows 2.6版本並發出錯解決方法


在使用MS opentech開發的redis 2.6 windows版本時,會出現在連接數過多(在我的機器上是超過800)時,會出現如下錯誤

"Error registering fd event for the new client: Result too large (fd=xxxxx)"

在網上卻收不到任何結果.在看過代碼之后,發現windows版本的redis的實現是有問題的.

原版的redis是使用accept來獲取一個新的連接的fd,然后使用這個fd作為aeEventLoop->events的index來存儲並獲取events的值,從而處理事件.而MS OpenTech使用IOCP模型實現了一個aeWinAccept來獲取fd.關鍵代碼如下:

int aeWinQueueAccept(SOCKET listensock) {
    aeSockState *sockstate;
    aeSockState *accsockstate;
    DWORD result, bytes;
    SOCKET acceptsock;
    aacceptreq * areq;

    if ((sockstate = aeGetSockState(iocpState, (int)listensock)) == NULL) {
        errno = WSAEINVAL;
        return -1;
    }

    //這里使用了socket的返回值來初始化aeEventLoop中的fd值
    acceptsock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (acceptsock == INVALID_SOCKET) {
        errno = WSAEINVAL;
        return -1;
    }

    accsockstate = aeGetSockState(iocpState, (int)acceptsock);
    if (accsockstate == NULL) {
        errno = WSAEINVAL;
        return -1;
    }
    ...
int aeWinAccept(int fd, struct sockaddr *sa, socklen_t *len) {
    aeSockState *sockstate;
    int acceptsock;
    int result;
    SOCKADDR *plocalsa;
    SOCKADDR *premotesa;
    int locallen, remotelen;
    aacceptreq * areq;
    SOCKET listenSock = (SOCKET)fd;

    if ((sockstate = aeGetSockState(iocpState, fd)) == NULL) {
        errno = WSAEINVAL;
        return SOCKET_ERROR;
    }

   //在這里取回之前存在aeEventLoop中的socket句柄作為fd
    areq = sockstate->reqs;
    if (areq == NULL) {
        errno = WSAEINVAL;
        return SOCKET_ERROR;
    }

    sockstate->reqs = areq->next;

    acceptsock = (int)areq->accept;
    ...
int aeCreateFileEvent(aeEventLoop *eventLoop, int fd, int mask,
        aeFileProc *proc, void *clientData)
{
    aeFileEvent *fe;
    if (fd >= eventLoop->setsize) {
        errno = ERANGE;
        return AE_ERR;
    }
    //在這里判斷fd是否超過pool的size,並通過fd的值來取得事件
    fe = &eventLoop->events[fd];
    ...

Windows版本的redis和原版的redis的實現是一樣的,重要的是accept的返回值和aeWinAccept所返回的socket句柄邏輯不一樣導致windows版本的redis出現了bug.在Linux中,accept的返回是遞增的,因此linux版本的redis是可以處理eventLoop->setsize個連接的,而在windows上socket返回的句柄會以大概12的步進增長,所以windows版本的redis就只能處理約eventLoop->setsize/12個連接了.

 

解決方法:

我的解決方法比較hack,我在aeEventLoop中添加一個fd_map的數組,用於把fd映射到0到eventLoop->setsize的值,這樣每次從events中存取數據就通過查詢fd_map來獲取index,從而達到讓redis能處理eventLoop->setsize個連接的效果.

 不過由於這個方法比較猥瑣,我就沒有提交patch了,而是提交了一個issue,不知道下一個版本會不會解決這個問題.

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM