redis之单线程命令处理

一、简单介绍

众所周知,redis非常快,而且是单线程的,这里说的单线程是指单线程处理命令,而实际上redis是多线程的,很多异步操作都是给后台线程进行操作的。
高效的原因:

  1. 内存级,读写速度快,不受磁盘IO限制
  2. 数据结构设计简单以及高效,很多结构操作都是O(1)
  3. 单线程
    (1)没有竞争,不需要锁
    (2)没有线程切换,不需要上下文切换
    (3)串行执行,保证每个操作都是原子性的
  4. IO多路复用架构,非阻塞IO

二、整体流程图

redis之单线程命令处理

三、整体代码逻辑

3.1 首先建立监听套接字

main()
    ...
    initServer() ->
        ...
        //创建I/O多路复用器
        aeCreateEventLoop() ->
            aeApiCreate()
        ...
        //创建监听socket
        listenToPort() ->
            ...
             anetTcp6Server()
            ...
            anetTcpServer()
            ...

3.2 注册监听事件处理回调

initServer()
    ...
    //注册接收链接事件处理回调
     for (j = 0; j < server.ipfd_count; j++) {
        if (aeCreateFileEvent(server.el, server.ipfd[j], AE_READABLE,
            acceptTcpHandler,NULL) == AE_ERR)
            {
                serverPanic(
                    "Unrecoverable error creating server.ipfd file event.");
            }
    }
    ...
    //注册定时任务定时器事件处理回调
    aeCreateTimeEvent(server.el, 1, serverCron, NULL, NULL)
    ...

3.3 主循环

void aeMain(aeEventLoop *eventLoop) {
    eventLoop->stop = 0;
    while (!eventLoop->stop) {
        aeProcessEvents(eventLoop, AE_ALL_EVENTS|
                                   AE_CALL_BEFORE_SLEEP|
                                   AE_CALL_AFTER_SLEEP);
    }
}

aeProcessEvents()
    ...
    numevents = aeApiPoll(eventLoop, tvp);
    ...
     for (j = 0; j < numevents; j++) {
          ...
            if (mask & AE_READABLE) {
                fe->rfileProc(eventLoop,fd,fe->clientData,mask);
            }
            ...
            if (mask & AE_WRITABLE) {
                fe->wfileProc(eventLoop,fd,fe->clientData,mask);
            }
           ...
        }
    }

3.4 回调
3.4.1 接受链接,并注册read处理函数

 fe->rfileProc(eventLoop,fd,fe->clientData,mask);
 acceptTcpHandler() ->
     acceptCommonHandler() ->
         createClient() ->
             connSetReadHandler(conn, readQueryFromClient);
                ...
                conn->type->set_read_handler(conn, func);
                ...

其中type是在创建conn的时候使用的全局变量CT_Socket
connCreateAcceptedSocket() ->
    ...
    connCreateSocket()
        ...
        conn->type = &CT_Socket;
        ...

ConnectionType CT_Socket = {
    .ae_handler = connSocketEventHandler,
    .close = connSocketClose,
    .write = connSocketWrite,
    .read = connSocketRead,
    .accept = connSocketAccept,
    .connect = connSocketConnect,
    .set_write_handler = connSocketSetWriteHandler,
    .set_read_handler = connSocketSetReadHandler,
    .get_last_error = connSocketGetLastError,
    .blocking_connect = connSocketBlockingConnect,
    .sync_write = connSocketSyncWrite,
    .sync_read = connSocketSyncRead,
    .sync_readline = connSocketSyncReadLine
};

static int connSocketSetReadHandler(connection *conn, ConnectionCallbackFunc func) {
    if (func == conn->read_handler) return C_OK;

    conn->read_handler = func;
    if (!conn->read_handler)
        aeDeleteFileEvent(server.el,conn->fd,AE_READABLE);
    else
        if (aeCreateFileEvent(server.el,conn->fd,
                    AE_READABLE,conn->type->ae_handler,conn) == AE_ERR) return C_ERR;
    return C_OK;
}

3.4.2 回调read 进行数据读取

readQueryFromClient() ->
    ...
    connRead()
    ...

3.4.3 解析命令

readQueryFromClient() ->
    ...
    processInputBuffer()
        ...
         while(c->qb_pos < sdslen(c->querybuf)){
          ...
            if (c->reqtype == PROTO_REQ_INLINE) {
                if (processInlineBuffer(c) != C_OK) break;
            } else if (c->reqtype == PROTO_REQ_MULTIBULK) {
                if (processMultibulkBuffer(c) != C_OK) break;
            } else {
                serverPanic("Unknown request type");
            }
        ...
        }
    }
        ...

3.4.4 执行命令

readQueryFromClient() ->
    ...
     processInputBuffer() ->
         ...
         processCommandAndResetClient() ->
             ...
             processCommand()
             ...
         ...
     ...

3.4.5 将响应写入队列中

processCommand() ->
    ...
    call()
        ...
        //根据不同的请求命令,回调不同的处理函数,这里使用get命令做示范
        c->cmd->proc(c);
        ...
    ...

//t_string.c
getCommand() ->
    getGenericCommand() 
        ...
        // 1.获取对应key的值
        lookupKeyReadOrReply()
        // 处理响应数据
        addReply() ->
            ...
            // 2.将此客户端连接加入到 链表server.clients_pending_write 中
            prepareClientToWrite() ->
                ...
                clientInstallWriteHandler()
                    ...
                    listAddNodeHead(server.clients_pending_write,c); //一个客户端只会添加一次
                    ...
             // 3.将结果写入到待发送区        
           _addReplyToBuffer()         

3.4.6 将数据发送给客户端
//在select之前将数据发送出去

beforeSleep() ->
    ...
    handleClientsWithPendingWritesUsingThreads() ->
        ...
        handleClientsWithPendingWrites()
            ...
            // 遍历有待发送数据的客户端队列
            listRewind(server.clients_pending_write,&li);
            while((ln = listNext(&li))) {
                client *c = listNodeValue(ln);
                c->flags &= ~CLIENT_PENDING_WRITE;
                listDelNode(server.clients_pending_write,ln);
                ...
                /* Try to write buffers to the client socket. */
                if (writeToClient(c,0) == C_ERR) continue;
                ...
              }  
            ...
        ...
    ...

猜你喜欢

转载自blog.51cto.com/happytree007/2583978
今日推荐