redis啟動初始化過程
redis伺服器例項的執行入口函式main
(redis.c):
2064 /* =================================== Main! ================================ */
2065
2066 int main(int argc, char **argv) {
2067 initServerConfig();
2068 initServer();
2069 if (argc == 2) {
2070 ResetServerSaveParams();
2071 loadServerConfig(argv[1]);
2072 redisLog(REDIS_NOTICE,"Configuration loaded");
2073 } else if (argc > 2) {
2074 fprintf(stderr,"Usage: ./redis-server [/path/to/redis.conf]\n");
2075 exit(1);
2076 }
2077 redisLog(REDIS_NOTICE,"Server started");
2078 if (loadDb("dump.rdb") == REDIS_OK)
2079 redisLog(REDIS_NOTICE,"DB loaded from disk");
2080 if (aeCreateFileEvent(server.el, server.fd, AE_READABLE,
2081 acceptHandler, NULL, NULL) == AE_ERR) oom("creating file event");
2082 redisLog(REDIS_NOTICE,"The server is now ready to accept connections");
2083 aeMain(server.el);
2084 aeDeleteEventLoop(server.el);
2085 return 0;
2086 }
首先進行初始化操作,通過呼叫initServerConfig
和initServer
設定全域性物件server
的初始值,其型別為:
93 /* Global server state structure */
94 struct redisServer {
95 int port;
96 int fd;
97 dict **dict;
98 long long dirty; /* changes to DB from the last save */
99 list *clients;
100 char neterr[ANET_ERR_LEN];
101 aeEventLoop *el;
102 int verbosity;
103 int cronloops;
104 int maxidletime;
105 int dbnum;
106 list *objfreelist; /* A list of freed objects to avoid malloc() */
107 int bgsaveinprogress;
108 time_t lastsave;
109 struct saveparam *saveparams;
110 int saveparamslen;
111 char *logfile;
112 };
下面是這個兩個函式分別對該全域性物件的初始化.
函式initServerConfig
的實現(redis.c):
556 static void initServerConfig() {
557 server.dbnum = REDIS_DEFAULT_DBNUM;
558 server.port = REDIS_SERVERPORT;
559 server.verbosity = REDIS_DEBUG;
560 server.maxidletime = REDIS_MAXIDLETIME;
561 server.saveparams = NULL;
562 server.logfile = NULL; /* NULL = log on standard output */
563 ResetServerSaveParams();
564
565 appendServerSaveParams(60*60,1); /* save after 1 hour and 1 change */
566 appendServerSaveParams(300,100); /* save after 5 minutes and 100 changes */
567 appendServerSaveParams(60,10000); /* save after 1 minute and 10000 changes */
568 }
全域性變數server
預設有16
個資料庫物件, 這個引數可以通過啟動時載入的配置檔案進行覆蓋.redis底層通過TCP網路協議進行資料傳輸, 因此需要監聽伺服器埠,預設是埠6379.當已經連線到redis伺服器的客戶端處於空閒狀態時, redis伺服器有可能會關閉這個客戶端的連線.這裡的空閒指的是redis協議層面上的空閒,而不是TCP層次的連線空閒.欄位verbosity
和logfile
控制log輸出, 可以輸出到指定的檔案,也可以輸出到控制檯.欄位saveparams
在定時事件處理中使用, 定時事件有可能會把記憶體中的資料儲存到磁碟, 判斷的依據就是該欄位的值,這裡設定了三個標準, 滿足其中任何一個,定時事件都會把記憶體資料儲存到磁碟檔案.
函式initServer
的實現(redis.c):
570 static void initServer() {
571 int j;
572
573 signal(SIGHUP, SIG_IGN);
574 signal(SIGPIPE, SIG_IGN);
575
576 server.clients = listCreate();
577 server.objfreelist = listCreate();
578 createSharedObjects();
579 server.el = aeCreateEventLoop();
580 server.dict = malloc(sizeof(dict*)*server.dbnum);
581 if (!server.dict || !server.clients || !server.el || !server.objfreelist)
582 oom("server initialization"); /* Fatal OOM */
583 server.fd = anetTcpServer(server.neterr, server.port, NULL);
584 if (server.fd == -1) {
585 redisLog(REDIS_WARNING, "Opening TCP port: %s", server.neterr);
586 exit(1);
587 }
588 for (j = 0; j < server.dbnum; j++) {
589 server.dict[j] = dictCreate(&sdsDictType,NULL);
590 if (!server.dict[j])
591 oom("server initialization"); /* Fatal OOM */
592 }
593 server.cronloops = 0;
594 server.bgsaveinprogress = 0;
595 server.lastsave = time(NULL);
596 server.dirty = 0;
597 aeCreateTimeEvent(server.el, 1000, serverCron, NULL, NULL);
598 }
欄位clients
是個list
型別,用於記錄連線到redis伺服器的客戶端.欄位objfreelist
也是個list型別,用於儲存空閒的robj
型別的物件,objfreelist
相當於一個robj
型別的物件池, 在分配robj
型別的物件時,如果該物件池中有空閒物件,則直接取走用, 否則需要動態分配robj
型別的物件.
函式listCreate
建立一個list型別的物件,其實現為(adlist.c):
8 /* Create a new list. The created list can be freed with
9 * AlFreeList(), but private value of every node need to be freed
10 * by the user before to call AlFreeList().
11 *
12 * On error, NULL is returned. Otherwise the pointer to the new list. */
13 list *listCreate(void)
14 {
15 struct list *list;
16
17 if ((list = malloc(sizeof(*list))) == NULL)
18 return NULL;
19 list->head = list->tail = NULL;
20 list->len = 0;
21 list->dup = NULL;
22 list->free = NULL;
23 list->match = NULL;
24 return list;
25 }
和list
相關的結構定義為(adlist.h):
10 typedef struct listNode {
11 struct listNode *prev;
12 struct listNode *next;
13 void *value;
14 } listNode;
15
16 typedef struct list {
17 listNode *head;
18 listNode *tail;
19 void *(*dup)(void *ptr);
20 void (*free)(void *ptr);
21 int (*match)(void *ptr, void *key);
22 int len;
23 } list;
list連結串列結構圖如下所示:
函式createSharedObjects
建立了一些共享的robj
型別的物件, 這些物件都是根據不同的客戶端命令,響應客戶端時傳送的.函式aeCreateEventLoop
建立了一個事件迴圈物件,並賦值給server.el
, redis伺服器的整個邏輯就不斷的在事件迴圈中處理到來的事件.事件迴圈相關結構的定義為(ae.h):
11 /* File event structure */
12 typedef struct aeFileEvent {
13 int fd;
14 int mask; /* one of AE_(READABLE|WRITABLE|EXCEPTION) */
15 aeFileProc *fileProc;
16 aeEventFinalizerProc *finalizerProc;
17 void *clientData;
18 struct aeFileEvent *next;
19 } aeFileEvent;
20
21 /* Time event structure */
22 typedef struct aeTimeEvent {
23 long long id; /* time event identifier. */
24 long when_sec; /* seconds */
25 long when_ms; /* milliseconds */
26 aeTimeProc *timeProc;
27 aeEventFinalizerProc *finalizerProc;
28 void *clientData;
29 struct aeTimeEvent *next;
30 } aeTimeEvent;
31
32 /* State of an event based program */
33 typedef struct aeEventLoop {
34 long long timeEventNextId;
35 aeFileEvent *fileEventHead;
36 aeTimeEvent *timeEventHead;
37 int stop;
38 } aeEventLoop;
型別aeEventLoop
管理兩類事件物件, 包括檔案事件物件和定時事件物件, 欄位fileEventHead
和timeEventHead
分別指向這兩類事件物件的連結串列頭.
接著初始化指標陣列server.dict
,每個指標指向函式dictCreate
動態分配的dict
物件.函式anetTcpServer
通過socket介面建立描述符,並繫結到之前的預設埠上, redis伺服器就是通過這個socket介面來接收客戶端的連線請求.欄位cronloops
記錄定時事件的觸發次數.欄位bgsaveinprogress
標識是否正在非同步儲存記憶體資料到磁碟檔案.欄位dirty
記錄客戶端更新操作的次數.欄位lastsave
記錄上次寫磁碟的時間戳.redis伺服器使用dirty
和lastsave
這兩個欄位來判斷是否儲存記憶體資料到磁碟.在函式initServer
的最後,使用函式aeCreateTimeEvent
建立一個定時事件物件.
函式aeDeleteTimeEvent
的實現(ae.c):
100 long long aeCreateTimeEvent(aeEventLoop *eventLoop, long long milliseconds,
101 aeTimeProc *proc, void *clientData,
102 aeEventFinalizerProc *finalizerProc)
103 {
104 long long id = eventLoop->timeEventNextId++;
105 aeTimeEvent *te;
106
107 te = malloc(sizeof(*te));
108 if (te == NULL) return AE_ERR;
109 te->id = id;
110 aeAddMillisecondsToNow(milliseconds,&te->when_sec,&te->when_ms);
111 te->timeProc = proc;
112 te->finalizerProc = finalizerProc;
113 te->clientData = clientData;
114 te->next = eventLoop->timeEventHead;
115 eventLoop->timeEventHead = te;
116 return id;
117 }
定時事件在當前時間+milliseconds毫秒後觸發, 該引數傳入的是1000,因此在1秒後觸發.定時事件觸發後將執行函式指標timeProc
中的邏輯,該引數被傳入的地址是serverCron
.函式指標finalizerProc
在定時事件被刪除的時候呼叫,來處理客戶端私有資料物件clientData
.新建立的定時事件被放到事件迴圈物件eventLoop
的連結串列timeEventHead
中.
回到main
函式, 如果啟動redis時,指定了配置檔案引數, 則讀取配置檔案中的資料,並覆蓋全域性物件server
中相同欄位的值.接著嘗試裝載之前儲存到磁碟的資料庫檔案dump.rdb
,函式loadDb
的實現完全是根據寫入磁碟資料庫檔案的saveDb
函式來實現的, 後續我們在定時事件中再看saveDb
實現,在此略過分析loadDb
的實現.接下來呼叫函式aeCreateFileEvent
建立一個檔案事件物件, 用來監聽客戶端的連線請求,該物件只在可讀時候被觸發, 觸發後的執行函式為acceptHandler
,該函式獲得連線客戶端的socket描述符.
函式aeCreateFileEvent
的實現(ae.c):
38 int aeCreateFileEvent(aeEventLoop *eventLoop, int fd, int mask,
39 aeFileProc *proc, void *clientData,
40 aeEventFinalizerProc *finalizerProc)
41 {
42 aeFileEvent *fe;
43
44 fe = malloc(sizeof(*fe));
45 if (fe == NULL) return AE_ERR;
46 fe->fd = fd;
47 fe->mask = mask;
48 fe->fileProc = proc;
49 fe->finalizerProc = finalizerProc;
50 fe->clientData = clientData;
51 fe->next = eventLoop->fileEventHead;
52 eventLoop->fileEventHead = fe;
53 return AE_OK;
54 }
檔案事件物件中有mask
欄位, 因為監聽的檔案描述符被觸發時, 可能是可讀,可寫,或者帶外訊息, 其他欄位含義類似定時事件結構.通過初始化過程, redis伺服器例項中會存在一個定時事件物件和一個檔案事件物件, 這兩個物件一直存在. 當然隨著系統執行, 當新的客戶端連線到redis伺服器時, 會動態的建立新的檔案事件物件.
函式main
中通過呼叫函式aeMain
進入事件迴圈處理主流程.
函式aeMain
的實現(ae.c):
313 void aeMain(aeEventLoop *eventLoop)
314 {
315 eventLoop->stop = 0;
316 while (!eventLoop->stop)
317 aeProcessEvents(eventLoop, AE_ALL_EVENTS);
318 }
通過呼叫aeProcessEvents
監聽並處理客戶端的連線請求和redis協議命令.
相關文章
- 【原始碼】Redis Server啟動過程原始碼RedisServer
- 走近原始碼:Redis的啟動過程原始碼Redis
- SpringBoot啟動流程分析(一):SpringApplication類初始化過程Spring BootAPP
- Redis核心原理與實踐--Redis啟動過程原始碼分析Redis原始碼
- Spring MVC原始碼(一) ----- 啟動過程與元件初始化SpringMVC原始碼元件
- SpringBoot啟動流程分析(四):IoC容器的初始化過程Spring Boot
- Service啟動過程
- SpringBoot啟動過程Spring Boot
- Windows 啟動過程Windows
- App 啟動過程(含 Activity 啟動過程) | 安卓 offer 收割基APP安卓
- Angular的啟動過程Angular
- Spring啟動過程(一)Spring
- Linux 啟動過程分析Linux
- Android App啟動過程AndroidAPP
- SpringBoot 系列-啟動過程Spring Boot
- jmeter 啟動過程剖析JMeter
- iOS App啟動過程iOSAPP
- Liferay 啟動過程分析
- Spring Boot 啟動過程Spring Boot
- main的啟動過程AI
- DUBBO服務啟動過程
- Linux系統啟動過程Linux
- Linux核心Kernel啟動過程Linux
- HDFS啟動過程+安全模式模式
- 計算機啟動過程計算機
- app的啟動過程(三)APP
- Spring Security 啟動過程分析Spring
- Eureka Server啟動過程分析Server
- 7.neutron-server啟動——neutron api啟動過程ServerAPI
- Spring啟動過程——原始碼分析Spring原始碼
- 根Activity元件的啟動過程元件
- Android效能優化之啟動過程(冷啟動和熱啟動)Android優化
- SpringIOC初始化過程學習Spring
- SpringIOC初始化過程--詳解Spring
- Netty NioEventLoop 啟動過程原始碼分析NettyOOP原始碼
- 原始碼|HDFS之NameNode:啟動過程原始碼
- Spring Boot原始碼分析-啟動過程Spring Boot原始碼
- Spring MVC 啟動過程原始碼分析SpringMVC原始碼