void redisProcessCallbacks(redisAsyncContext *ac) { redisContext *c = &(ac->c); redisCallback cb; void *reply = NULL; int status; while((status = redisGetReply(c,&reply)) == REDIS_OK) { if (reply == NULL) { /* When the connection is being disconnected and there are * no more replies, this is the cue to really disconnect. */ if (c->flags & REDIS_DISCONNECTING && sdslen(c->obuf) == 0) { __redisAsyncDisconnect(ac); return; } /* When the connection is not being disconnected, simply stop * trying to get replies and wait for the next loop tick. */ break; } /* Even if the context is subscribed, pending regular callbacks will * get a reply before pub/sub messages arrive. */ if (__redisShiftCallback(&ac->replies,&cb) != REDIS_OK) { /* No more regular callbacks, the context *must* be subscribed. */ /* assert(c->flags & REDIS_SUBSCRIBED); */ if (c->flags & REDIS_SUBSCRIBED) { __redisGetSubscribeCallback(ac,reply,&cb); } /* lig: monitor callback */ if (c->flags & REDIS_MONITOR) { __redisGetMonitorCallback(ac,&cb); } } if (cb.fn != NULL) { __redisRunCallback(ac,&cb,reply); c->fn->freeObject(reply); /* Proceed with free'ing when redisAsyncFree() was called. */ if (c->flags & REDIS_FREEING) { __redisAsyncFree(ac); return; } } else { /* No callback for this reply. This can either be a NULL callback, * or there were no callbacks to begin with. Either way, don't * abort with an error, but simply ignore it because the client * doesn't know what the server will spit out over the wire. */ c->fn->freeObject(reply); } } /* Disconnect when there was an error reading the reply */ if (status != REDIS_OK) __redisAsyncDisconnect(ac); }
void redisAsyncHandleWrite(redisAsyncContext *ac) { redisContext *c = &(ac->c); int done = 0; // 连接创建失败??? if (!(c->flags & REDIS_CONNECTED)) { /* Abort connect was not successful. */ if (__redisAsyncHandleConnect(ac) != REDIS_OK) return; // 再尝试检测 flag /* Try again later when the context is still not connected. */ if (!(c->flags & REDIS_CONNECTED)) return; } if (redisBufferWrite(c,&done) == REDIS_ERR) { __redisAsyncDisconnect(ac); } else { /* Continue writing when not done, stop writing otherwise */ // 当缓冲区中还有数据待发送时,会再次注册写事件 if (!done) _EL_ADD_WRITE(ac); // 当缓冲区中没有数据待发送时,会再次注册写事件 else _EL_DEL_WRITE(ac); /* Always schedule reads after writes */ _EL_ADD_READ(ac); } }
/* This function should be called when the socket is readable. * It processes all replies that can be read and executes their callbacks. */ void redisAsyncHandleRead(redisAsyncContext *ac) { redisContext *c = &(ac->c); // 如果连接已经断开,尝试连接 if (!(c->flags & REDIS_CONNECTED)) { /* Abort connect was not successful. */ if (__redisAsyncHandleConnect(ac) != REDIS_OK) return; /* Try again later when the context is still not connected. */ if (!(c->flags & REDIS_CONNECTED)) return; } // 读取数据 if (redisBufferRead(c) == REDIS_ERR) { // 读取错误,断开连接并做一些清理工作 __redisAsyncDisconnect(ac); } else { // 读取数据成功,再次注册读事件, /* Always re-schedule reads */ _EL_ADD_READ(ac); // 并处理回调函数 redisProcessCallbacks(ac); } }
void redisAsyncHandleWrite(redisAsyncContext *ac) { redisContext *c = &(ac->c); int done = 0; int status; if (!(c->flags & REDIS_CONNECTED)) { /* Abort connect was not successful. */ if (__redisAsyncHandleConnect(ac) != REDIS_OK) return; /* Try again later when the context is still not connected. */ if (!(c->flags & REDIS_CONNECTED)) return; } pthread_mutex_lock(&ac->lock); status = redisBufferWrite(c,&done); pthread_mutex_unlock(&ac->lock); if (status == REDIS_ERR) { __redisAsyncDisconnect(ac); } else { /* Continue writing when not done, stop writing otherwise */ if (!done) _EL_ADD_WRITE(ac); else _EL_DEL_WRITE(ac); /* Always schedule reads after writes */ _EL_ADD_READ(ac); } }
void redisAsyncHandleWrite(redisAsyncContext *ac) { redisContext *c = &(ac->c); int done = 0; if (redisBufferWrite(c, &done) == REDIS_ERR) { __redisAsyncDisconnect(ac); } else { /* Continue writing when not done, stop writing otherwise */ if (!done) { if (ac->ev.addWrite) ac->ev.addWrite(ac->ev.data); } else { if (ac->ev.delWrite) ac->ev.delWrite(ac->ev.data); } /* Always schedule reads after writes */ if (ac->ev.addRead) ac->ev.addRead(ac->ev.data); /* Fire onConnect when this is the first write event. */ if (!(c->flags & REDIS_CONNECTED)) { c->flags |= REDIS_CONNECTED; if (ac->onConnect) ac->onConnect(ac); } } }
/* This function should be called when the socket is readable. * It processes all replies that can be read and executes their callbacks. */ void redisAsyncHandleRead(redisAsyncContext *ac) { redisContext *c = &(ac->c); if (!(c->flags & REDIS_CONNECTED)) { /* Abort connect was not successful. */ if (__redisAsyncHandleConnect(ac) != REDIS_OK) return; /* Try again later when the context is still not connected. */ if (!(c->flags & REDIS_CONNECTED)) return; } if (redisBufferRead(c) == REDIS_ERR) { __redisAsyncDisconnect(ac); } else { /* Always re-schedule reads */ #ifdef _WIN32 // There appears to be a bug in the Linux version of _EL_ADD_READ which will not reschedule // the read if already reading. This is a problem if there is a large number of async GET // operations. If the receive buffer is exhausted with the data returned, the read would // not be rescheduled, and the async operations would cease. This forces the read to recur. _EL_FORCE_ADD_READ(ac); #else _EL_ADD_READ(ac); #endif redisProcessCallbacks(ac); } }
/* This function should be called when the socket is readable. * It processes all replies that can be read and executes their callbacks. */ void redisAsyncHandleRead(redisAsyncContext *ac) { redisContext *c = &(ac->c); if (redisBufferRead(c) == REDIS_ERR) { __redisAsyncDisconnect(ac); } else { /* Always re-schedule reads */ if (ac->ev.addRead) ac->ev.addRead(ac->ev.data); redisProcessCallbacks(ac); } }
/* Internal helper function to detect socket status the first time a read or * write event fires. When connecting was not succesful, the connect callback * is called with a REDIS_ERR status and the context is free'd. */ static int __redisAsyncHandleConnect(redisAsyncContext *ac) { redisContext *c = &(ac->c); if (redisCheckSocketError(c) == REDIS_ERR) { /* Try again later when connect(2) is still in progress. */ if (errno == EINPROGRESS) return REDIS_OK; if (ac->onConnect) ac->onConnect(ac,REDIS_ERR); __redisAsyncDisconnect(ac); return REDIS_ERR; } /* Mark context as connected. */ c->flags |= REDIS_CONNECTED; if (ac->onConnect) ac->onConnect(ac,REDIS_OK); return REDIS_OK; }
/* This function should be called when the socket is readable. * It processes all replies that can be read and executes their callbacks. */ void redisAsyncHandleRead(redisAsyncContext *ac) { redisContext *c = &(ac->c); if (!(c->flags & REDIS_CONNECTED)) { /* Abort connect was not successful. */ if (__redisAsyncHandleConnect(ac) != REDIS_OK) return; /* Try again later when the context is still not connected. */ if (!(c->flags & REDIS_CONNECTED)) return; } if (redisBufferRead(c) == REDIS_ERR) { __redisAsyncDisconnect(ac); } else { /* Always re-schedule reads */ _EL_ADD_READ(ac); redisProcessCallbacks(ac); } }
int redisAsyncHandleWriteComplete(redisAsyncContext *ac, int written) { redisContext *c = &(ac->c); int done = 0; int rc; rc = redisBufferWriteDone(c, written, &done); if (rc == REDIS_ERR) { __redisAsyncDisconnect(ac); } else { /* Continue writing when not done, stop writing otherwise */ if (!done) _EL_ADD_WRITE(ac); else _EL_DEL_WRITE(ac); /* Always schedule reads after writes */ _EL_ADD_READ(ac); } return REDIS_OK; }
/* Internal helper function to detect socket status the first time a read or * write event fires. When connecting was not succesful, the connect callback * is called with a REDIS_ERR status and the context is free'd. */ static int __redisAsyncHandleConnect(redisAsyncContext *ac) { redisContext *c = &(ac->c); // 当一个套接口出错时,它会被 select 调用标记为既可读又可写。 // 因此,我们要用 getsockopt() 判断是否连接真的创建成功了 // 检测 socket 错误 if (redisCheckSocketError(c,c->fd) == REDIS_ERR) { /* Try again later when connect(2) is still in progress. */ if (errno == EINPROGRESS) return REDIS_OK; if (ac->onConnect) ac->onConnect(ac,REDIS_ERR); __redisAsyncDisconnect(ac); return REDIS_ERR; } // 连接是成功的 /* Mark context as connected. */ c->flags |= REDIS_CONNECTED; if (ac->onConnect) ac->onConnect(ac,REDIS_OK); return REDIS_OK; }
void redisProcessCallbacks(redisAsyncContext *ac) { redisContext *c = &(ac->c); redisCallback cb; void *reply = NULL; int status; while((status = redisGetReply(c,&reply)) == REDIS_OK) { // 回复内容为空 if (reply == NULL) { // REDIS_DISCONNECTING 表示连接断开的时候遇到了错误,如果此时 // 写缓冲区为空,会再次尝试断开。之后的回调函数都得不到执行 // 的机会 /* When the connection is being disconnected and there are * no more replies, this is the cue to really disconnect. */ if (c->flags & REDIS_DISCONNECTING && sdslen(c->obuf) == 0) { __redisAsyncDisconnect(ac); return; } // 监视模式下 /* If monitor mode, repush callback */ if(c->flags & REDIS_MONITORING) { __redisPushCallback(&ac->replies,&cb); } /* When the connection is not being disconnected, simply stop * trying to get replies and wait for the next loop tick. */ break; } /* Even if the context is subscribed, pending regular callbacks will * get a reply before pub/sub messages arrive. */ // 获取回调函数 redisCallback if (__redisShiftCallback(&ac->replies,&cb) != REDIS_OK) { /* * A spontaneous reply in a not-subscribed context can be the error * reply that is sent when a new connection exceeds the maximum * number of allowed connections on the server side. * * This is seen as an error instead of a regular reply because the * server closes the connection after sending it. * * To prevent the error from being overwritten by an EOF error the * connection is closed here. See issue #43. * * Another possibility is that the server is loading its dataset. * In this case we also want to close the connection, and have the * user wait until the server is ready to take our request. */ if (((redisReply*)reply)->type == REDIS_REPLY_ERROR) { c->err = REDIS_ERR_OTHER; snprintf(c->errstr,sizeof(c->errstr),"%s",((redisReply*)reply)->str); __redisAsyncDisconnect(ac); return; } /* No more regular callbacks and no errors, the context *must* be subscribed or monitoring. */ assert((c->flags & REDIS_SUBSCRIBED || c->flags & REDIS_MONITORING)); if(c->flags & REDIS_SUBSCRIBED) __redisGetSubscribeCallback(ac,reply,&cb); } // 执行回调函数 if (cb.fn != NULL) { __redisRunCallback(ac,&cb,reply); c->reader->fn->freeObject(reply); // 可能是之前遇到了不可修复的错误,被标记了 REDIS_FREEING。 // 倘若是在 callback 里面遇到的错误,redis 不能立即释放连接, // 因为可能需要返回一些数据给客户端。因此 redis 选择在 callback // 执行过后,才释放(关闭)连接。这个方法好酷。 /* Proceed with free'ing when redisAsyncFree() was called. */ if (c->flags & REDIS_FREEING) { __redisAsyncFree(ac); return; } // 没有设置回调函数,直接释放收到的数据 } else { /* No callback for this reply. This can either be a NULL callback, * or there were no callbacks to begin with. Either way, don't * abort with an error, but simply ignore it because the client * doesn't know what the server will spit out over the wire. */ c->reader->fn->freeObject(reply); } } // 出错了,则直接关闭 /* Disconnect when there was an error reading the reply */ if (status != REDIS_OK) __redisAsyncDisconnect(ac); }
/* Tries to do a clean disconnect from Redis, meaning it stops new commands * from being issued, but tries to flush the output buffer and execute * callbacks for all remaining replies. When this function is called from a * callback, there might be more replies and we can safely defer disconnecting * to redisProcessCallbacks(). Otherwise, we can only disconnect immediately * when there are no pending callbacks. */ void redisAsyncDisconnect(redisAsyncContext *ac) { redisContext *c = &(ac->c); c->flags |= REDIS_DISCONNECTING; if (!(c->flags & REDIS_IN_CALLBACK) && ac->replies.head == NULL) __redisAsyncDisconnect(ac); }
void redisProcessCallbacks(redisAsyncContext *ac) { redisContext *c = &(ac->c); redisCallback cb; void *reply = NULL; int status; while((status = redisGetReply(c,&reply)) == REDIS_OK) { if (reply == NULL) { /* When the connection is being disconnected and there are * no more replies, this is the cue to really disconnect. */ if (c->flags & REDIS_DISCONNECTING && sdslen(c->obuf) == 0) { __redisAsyncDisconnect(ac); return; } /* When the connection is not being disconnected, simply stop * trying to get replies and wait for the next loop tick. */ break; } /* Even if the context is subscribed, pending regular callbacks will * get a reply before pub/sub messages arrive. */ if (__redisShiftCallback(&ac->replies,&cb) != REDIS_OK) { /* A spontaneous reply in a not-subscribed context can only be the * error reply that is sent when a new connection exceeds the * maximum number of allowed connections on the server side. This * is seen as an error instead of a regular reply because the * server closes the connection after sending it. To prevent the * error from being overwritten by an EOF error the connection is * closed here. See issue #43. */ if ( !(c->flags & REDIS_SUBSCRIBED) && ((redisReply*)reply)->type == REDIS_REPLY_ERROR ) { c->err = REDIS_ERR_OTHER; snprintf(c->errstr,sizeof(c->errstr),"%s",((redisReply*)reply)->str); __redisAsyncDisconnect(ac); return; } /* No more regular callbacks and no errors, the context *must* be subscribed. */ assert(c->flags & REDIS_SUBSCRIBED); __redisGetSubscribeCallback(ac,reply,&cb); } if (cb.fn != NULL) { __redisRunCallback(ac,&cb,reply); c->reader->fn->freeObject(reply); /* Proceed with free'ing when redisAsyncFree() was called. */ if (c->flags & REDIS_FREEING) { __redisAsyncFree(ac); return; } } else { /* No callback for this reply. This can either be a NULL callback, * or there were no callbacks to begin with. Either way, don't * abort with an error, but simply ignore it because the client * doesn't know what the server will spit out over the wire. */ c->reader->fn->freeObject(reply); } } /* Disconnect when there was an error reading the reply */ if (status != REDIS_OK) __redisAsyncDisconnect(ac); }