一、簡單介紹
衆所周知,redis非常快,而且是單線程的,這裏說的單線程是指單線程處理命令,而實際上redis是多線程的,很多異步操作都是給後臺線程進行操作的。
高效的原因:
- 內存級,讀寫速度快,不受磁盤IO限制
- 數據結構設計簡單以及高效,很多結構操作都是O(1)
- 單線程
(1)沒有競爭,不需要鎖
(2)沒有線程切換,不需要上下文切換
(3)串行執行,保證每個操作都是原子性的 - IO多路複用架構,非阻塞IO
二、整體流程圖
三、整體代碼邏輯
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;
...
}
...
...
...