Redis源碼分析(一)redis.c //redis-server.c
入口函數 int main()
4450 int main(int argc, char **argv) {4451 initServerConfig();4452 if (argc == 2) {4453 ResetServerSaveParams();4454 loadServerConfig(argv[1]);4455 } else if (argc > 2) {4456 fprintf(stderr,"Usage: ./redis-server [/path/to/redis.conf]\n");4457 exit(1);4458 } else {4459 redisLog(REDIS_WARNING,"Warning: no config file specified, using the default config. In order to specify a config file use 'redis-server /path/to/redis.conf'");4460 }4461 initServer();4462 if (server.daemonize) daemonize();4463 redisLog(REDIS_NOTICE,"Server started, Redis version " REDIS_VERSION);4464 #ifdef __linux__4465 linuxOvercommitMemoryWarning();4466 #endif4467 if (rdbLoad(server.dbfilename) == REDIS_OK)4468 redisLog(REDIS_NOTICE,"DB loaded from disk");4469 if (aeCreateFileEvent(server.el, server.fd, AE_READABLE,4470 acceptHandler, NULL, NULL) == AE_ERR) oom("creating file event");4471 redisLog(REDIS_NOTICE,"The server is now ready to accept connections on port %d", server.port);4472 aeMain(server.el);4473 aeDeleteEventLoop(server.el);4474 return 0;4475 }
初始化參數 initServerConfig()
static void initServerConfig() {server.dbnum = REDIS_DEFAULT_DBNUM;//初始化redis的數據庫個數16server.port = REDIS_SERVERPORT;//端口6379server.verbosity = REDIS_DEBUG;//默認這個為0// 這個是日志級別,有三種//#define REDIS_DEBUG 0 //#define REDIS_NOTICE 1 //#define REDIS_WARNING 2server.maxidletime = REDIS_MAXIDLETIME;//最大空閑時間60*5,也是客戶端的超時時間server.saveparams = NULL;//持久化策略server.logfile = NULL; /* NULL = log on standard output, 也可以在后期腳本文件中將參數傳入 */server.bindaddr = NULL;server.glueoutputbuf = 1;//在向客戶端應答時,是否把較小的包合并為一個包發送,默認為開啟。server.daemonize = 0;設置redis是否以守護進程方式運行,默認為no。server.pidfile = "/var/run/redis.pid";server.dbfilename = "dump.rdb";server.requirepass = NULL;//設置redis連接的密碼,設置之后客戶端連接redis時需要先通過auth命令提供密碼進行驗證才能進行后續操作,默認沒有密碼。設置操作:requirepass mypassserver.shareobjects = 0;server.sharingpoolsize = 1024;server.maxclients = 0;server.maxmemory = 0;ResetServerSaveParams();appendServerSaveParams(60*60,1); /* save after 1 hour and 1 change */appendServerSaveParams(300,100); /* save after 5 minutes and 100 changes */appendServerSaveParams(60,10000); /* save after 1 minute and 10000 changes *//* Replication related */server.isslave = 0;server.masterhost = NULL;server.masterport = 6379;server.master = NULL;server.replstate = REDIS_REPL_NONE;}
初始化函數中的server變量包含
223 /* Global server state structure */224 struct redisServer {225 int port; //端口號226 int fd; //文件描述符227 redisDb *db; // 一個數組,保存著服務器中的所有數據庫228 dict *sharingpool; //存放數據的哈希表229 unsigned int sharingpoolsize;230 long long dirty; /* changes to DB from the last save */231 list *clients; //客戶端鏈表232 list *slaves, *monitors;233 char neterr[ANET_ERR_LEN]; //網絡錯誤信息存儲,最多256個char234 aeEventLoop *el; //事件循環結構體 235 int cronloops; /* number of times the cron function run */236 list *objfreelist; /* A list of freed objects to avoid malloc() */237 time_t lastsave; /* Unix time of last save succeeede */238 size_t usedmemory; /* Used memory in megabytes */239 /* Fields used only for stats */240 time_t stat_starttime; /* server start time */241 long long stat_numcommands; /* number of processed commands */242 long long stat_numconnections; /* number of connections received */243 /* Configuration */244 int verbosity;245 int glueoutputbuf;246 int maxidletime; //最大空閑時間247 int dbnum; //數據庫的數量248 int daemonize;249 char *pidfile;250 int bgsaveinprogress;251 pid_t bgsavechildpid;252 struct saveparam *saveparams; //持久化參數253 int saveparamslen;254 char *logfile; //日志文件路徑255 char *bindaddr;256 char *dbfilename;257 char *requirepass;258 int shareobjects;259 /* Replication related */260 int isslave;261 char *masterhost;262 int masterport;263 redisClient *master; /* client that is master for this slave */264 int replstate;265 unsigned int maxclients; 266 unsigned int maxmemory;267 /* Sort parameters - qsort_r() is only available under BSD so we268 * have to take this state global, in order to pass it to sortCompare() */269 int sort_desc;270 int sort_alpha;271 int sort_bypattern;272 };273
server函數中的redisDb *db;結構體
190 typedef struct redisDb { 191 dict *dict; //當前數據庫的鍵空間192 dict *expires; //鍵的過期時間193 int id; //數據庫ID標識194 } redisDb;195
server函數中的dict結構體
結構體位于:dict.h
67 //哈希表的定義 68 typedef struct dict { 69 //指向實際的哈希表記錄(用數組+開鏈的形式進行保存) 70 dictEntry **table; 71 //type中包含一系列哈希表需要用到的函數 72 dictType *type; 73 //size表示哈希表的大小,為2的指數 74 unsigned long size; 75 //sizemask=size-1,方便哈希值根據size取模 76 unsigned long sizemask; 77 //used記錄了哈希表中有多少記錄 78 unsigned long used; 79 void *privdata; 80 } dict; 81 82 //對Hash表進行迭代遍歷時使用的迭代器83 typedef struct dictIterator { 84 dict *ht; 85 int index; 86 dictEntry *entry, *nextEntry; 87 } dictIterator; 88
dict結構體中的dictEntry
46 //實際存放數據的地方47 typedef struct dictEntry {48 void *key;49 void *val;50 struct dictEntry *next;51 } dictEntry;52
dict結構體中的dictType
53 //要作用于哈希表上的相關函數54 /*55 * dictType在哈希系統中包含了一系列可由應用程序定義的56 * 函數指針57 */58 typedef struct dictType {59 unsigned int (*hashFunction)(const void *key);//哈希函數60 void *(*keyDup)(void *privdata, const void *key);//key復制61 void *(*valDup)(void *privdata, const void *obj);//value復制62 int (*keyCompare)(void *privdata, const void *key1, const void *key2);//key比較63 void (*keyDestructor)(void *privdata, void *key);//key銷毀64 void (*valDestructor)(void *privdata, void *obj);//value銷毀65 } dictType;66
server函數中的list結構體
adlist.h
67 /* 68 * redis中的雙向鏈表 69 * head標識鏈表的頭指針 70 * tail標識鏈表的尾結點 71 * dup/free/match是三個函數指針 72 * dup用于復制鏈表,返回值也是一個函數指針 73 * free用于釋放鏈表 74 * match用于判斷鏈表中是否存在*key的值 75 * len標識鏈表的長度 76 * listIter鏈表的迭代器,通過此迭代器可以對鏈表進行遍歷 77 * 至于為什么要提供這種迭代器,可以查看設計模式相關的書箱 78 */ 79 80 typedef struct list { 81 listNode *head;//鏈表的頭結點 82 listNode *tail;//鏈表的尾節點 83 void *(*dup)(void *ptr);//復制鏈表 84 void (*free)(void *ptr);//釋放內存 85 int (*match)(void *ptr, void *key);//匹配 86 unsigned int len; //標識鏈表的長度, 87 listIter iter; 88 } list; 89
listNode結構體
40 /* 41 * redis中最基本的結構用以標識鏈表中的結點 42 * *prev標識上一個節點 43 * *next標識下一個節點 44 * *value標識節點的值 45 */ 46 47 typedef struct listNode { 48 struct listNode *prev; 49 struct listNode *next; 50 void *value; 51 } listNode; 52
迭代器
53 /* 54 * 迭代器用于鏈表的遍歷 55 * next將要遍歷的下一個元素 56 * direction遍歷鏈表的方向 57 * 方向由下面的兩個宏進行標識 58 * AL_START_HEAD表示向前 59 * AL_START_TAIL表示向后 60 */ 61 62 typedef struct listIter { 63 listNode *next; 64 int direction; 65 } listIter; 66
server函數中的aeEventLoop *el結構體
ae.h
112
113 /* State of an event based program */
114 /**
115 * 事件循環結構體
116 */
117 typedef struct aeEventLoop {
118 //用于標識下一個定時器
119 long long timeEventNextId;
120 //文件事件
121 aeFileEvent *fileEventHead;
122 //定時器事件
123 aeTimeEvent *timeEventHead;
124 //stop用于停止事件輪詢
125 int stop;
126 } aeEventLoop;
127
aeEventLoopd的aeFileEvent和aeTimeEvent
78 typedef struct aeFileEvent {79 int fd;80 /**81 * AE_READABLE|AE_WRITEABLE_AE_EXCEPTION中的一個82 * 表示要監聽的事件類型83 */84 int mask; /* one of AE_(READABLE|WRITABLE|EXCEPTION) */85 //文件事件相應的處理函數86 aeFileProc *fileProc;87 aeEventFinalizerProc *finalizerProc;88 void *clientData;89 //下一個文件事件90 struct aeFileEvent *next;91 } aeFileEvent;92 93 /* Time event structure */94 /**95 * redis自已定義的定時器事件96 * 其實現是一個鏈表,其中的每一個結點是一個Timer97 * when_sec與when_ms指定了定時器發生的時間98 * timeProc為響應函數99 * finalizerProc為刪除定時器的析構函數
100 */
101 typedef struct aeTimeEvent {
102 //定時器的id
103 long long id; /* time event identifier. */
104 long when_sec; /* seconds */
105 long when_ms; /* milliseconds */
106 aeTimeProc *timeProc;
107 aeEventFinalizerProc *finalizerProc;
108 //定義了該定時器有的數據情況
109 void *clientData;
110 struct aeTimeEvent *next;
111 } aeTimeEvent;
常數定義部分
/* Error codes */#define REDIS_OK 0#define REDIS_ERR -1/* Static server configuration */#define REDIS_SERVERPORT 6379 /* TCP port */#define REDIS_MAXIDLETIME (60*5) /* default client timeout */#define REDIS_IOBUF_LEN 1024#define REDIS_LOADBUF_LEN 1024#define REDIS_STATIC_ARGS 4#define REDIS_DEFAULT_DBNUM 16#define REDIS_CONFIGLINE_MAX 1024#define REDIS_OBJFREELIST_MAX 1000000 /* Max number of objects to cache */#define REDIS_MAX_SYNC_TIME 60 /* Slave can't take more to sync */#define REDIS_EXPIRELOOKUPS_PER_CRON 100 /* try to expire 100 keys/second */#define REDIS_MAX_WRITE_PER_EVENT (1024*64)#define REDIS_REQUEST_MAX_SIZE (1024*1024*256) /* max bytes in inline command *//* Hash table parameters */#define REDIS_HT_MINFILL 10 /* Minimal hash table fill 10% *//* Command flags */#define REDIS_CMD_BULK 1 /* Bulk write command */#define REDIS_CMD_INLINE 2 /* Inline command *//* REDIS_CMD_DENYOOM reserves a longer comment: all the commands marked withthis flags will return an error when the 'maxmemory' option is set in theconfig file and the server is using more than maxmemory bytes of memory.In short this commands are denied on low memory conditions. */#define REDIS_CMD_DENYOOM 4/* Object types */#define REDIS_STRING 0#define REDIS_LIST 1#define REDIS_SET 2#define REDIS_HASH 3/* Object types only used for dumping to disk */#define REDIS_EXPIRETIME 253#define REDIS_SELECTDB 254#define REDIS_EOF 255/* Defines related to the dump file format. To store 32 bits lengths for short* keys requires a lot of space, so we check the most significant 2 bits of* the first byte to interpreter the length:** 00|000000 => if the two MSB are 00 the len is the 6 bits of this byte* 01|000000 00000000 => 01, the len is 14 byes, 6 bits + 8 bits of next byte* 10|000000 [32 bit integer] => if it's 01, a full 32 bit len will follow* 11|000000 this means: specially encoded object will follow. The six bits* number specify the kind of object that follows.* See the REDIS_RDB_ENC_* defines.** Lenghts up to 63 are stored using a single byte, most DB keys, and may* values, will fit inside. */#define REDIS_RDB_6BITLEN 0#define REDIS_RDB_14BITLEN 1#define REDIS_RDB_32BITLEN 2#define REDIS_RDB_ENCVAL 3#define REDIS_RDB_LENERR UINT_MAX/* When a length of a string object stored on disk has the first two bits* set, the remaining two bits specify a special encoding for the object* accordingly to the following defines: */#define REDIS_RDB_ENC_INT8 0 /* 8 bit signed integer */#define REDIS_RDB_ENC_INT16 1 /* 16 bit signed integer */#define REDIS_RDB_ENC_INT32 2 /* 32 bit signed integer */#define REDIS_RDB_ENC_LZF 3 /* string compressed with FASTLZ *//* Client flags */#define REDIS_CLOSE 1 /* This client connection should be closed ASAP */#define REDIS_SLAVE 2 /* This client is a slave server */#define REDIS_MASTER 4 /* This client is a master server */#define REDIS_MONITOR 8 /* This client is a slave monitor, see MONITOR *//* Slave replication state - slave side */#define REDIS_REPL_NONE 0 /* No active replication */#define REDIS_REPL_CONNECT 1 /* Must connect to master */#define REDIS_REPL_CONNECTED 2 /* Connected to master *//* Slave replication state - from the point of view of master* Note that in SEND_BULK and ONLINE state the slave receives new updates* in its output queue. In the WAIT_BGSAVE state instead the server is waiting* to start the next background saving in order to send updates to it. */#define REDIS_REPL_WAIT_BGSAVE_START 3 /* master waits bgsave to start feeding it */#define REDIS_REPL_WAIT_BGSAVE_END 4 /* master waits bgsave to start bulk DB transmission */#define REDIS_REPL_SEND_BULK 5 /* master is sending the bulk DB */#define REDIS_REPL_ONLINE 6 /* bulk DB already transmitted, receive updates *//* List related stuff */#define REDIS_HEAD 0#define REDIS_TAIL 1/* Sort operations */#define REDIS_SORT_GET 0#define REDIS_SORT_DEL 1#define REDIS_SORT_INCR 2#define REDIS_SORT_DECR 3#define REDIS_SORT_ASC 4#define REDIS_SORT_DESC 5#define REDIS_SORTKEY_MAX 1024/* Log levels */#define REDIS_DEBUG 0#define REDIS_NOTICE 1#define REDIS_WARNING 2/* Anti-warning macro... */#define REDIS_NOTUSED(V) ((void) V)