? ? ? ? ? 分析了一段時間的struct結構體的redis代碼,越到最后越發現其實很多代碼都是大同小異的。在struct包中還有1,2個文件還沒分析,是關于set集合的一些東西,就放在下次分析好了,在選擇下個分析的對象時,我考慮了一下,最后決定先把簡單的test包下的東西看看一下,畢竟結構體這塊有點復雜,所以這次分析個簡單點的。test包下的文件并不多,代碼量也很少,總共5個文件:
1.memtest.c 內存檢測
2.redis_benchmark.c 用于redis性能測試的實現。
3.redis_check_aof.c 用于更新日志檢查的實現。
4.redis_check_dump.c 用于本地數據庫檢查的實現。
5.testhelp.c 一個C風格的小型測試框架。
今天討論3和5,先看看5,testhelp.c被解釋為一個C風格的小型測試框架,看見這標題,一定想到這是一個很大的文件吧,都被稱為測試框架了,其實不然,我貼出整個代碼:
~~~
/* 預處理 */
#ifndef __TESTHELP_H
#define __TESTHELP_H
/* 開始時失敗總數為0 */
int __failed_tests = 0;
/* 開始時測試總的例子數為0 */
int __test_num = 0;
/* 宏定義測試方法,輸入參數,輸入描述語,判斷的式子作為參數 */
/* 有完全體現了函數式編程的思想 */
#define test_cond(descr,_c) do { \
__test_num++; printf("%d - %s: ", __test_num, descr); \
//判斷式子在此處調用
if(_c) printf("PASSED\n"); else {printf("FAILED\n"); __failed_tests++;} \
} while(0);
/* 測試報告在最結尾輸出 */
#define test_report() do { \
printf("%d tests, %d passed, %d failed\n", __test_num, \
__test_num-__failed_tests, __failed_tests); \
if (__failed_tests) { \
printf("=== WARNING === We have failed tests here...\n"); \
exit(1); \
} \
} while(0);
#endif
~~~
調用形式也是極其簡單:
~~~
* test_cond("Check if 1 == 1", 1==1)
* test_cond("Check if 5 > 10", 5 > 10)
* test_report()
~~~
其實就是宏定義了一個判別表達式真假的方法,又用到了函數式編程的思想,把_c整個表達式的值傳入了另一個函數中,就這么簡單。好,下一個文件redis_check_aof.c用于日志檢測的,必然和文件的操作相關的,同樣列出里面的API:
~~~
/* 方法API */
int consumeNewline(char *buf) /* 消除buf前面的換行符,即比較buf字符串中的前2個字符 */
int readLong(FILE *fp, char prefix, long *target) /* 從文件中讀取long類型值 */
int readBytes(FILE *fp, char *target, long length) /* 從文件中讀取字節 */
int readString(FILE *fp, char**target) /* 文件中讀取字符串 */
int readArgc(FILE *fp, long *target) /* 文件中讀取參數,首字符以“*”開頭 */
off_t process(FILE *fp) /* 返回fp文件的偏移量 */
~~~
read的很多操作其實都是非常類似的,我就舉出其中的一個read方法當做例子,就是純粹文件的簡單操作,熟悉C語言的同學一定很熟悉:
~~~
/* 從文件中讀取long類型值 */
int readLong(FILE *fp, char prefix, long *target) {
char buf[128], *eptr;
//定位到文件的讀取位置
epos = ftello(fp);
//將文件中的內容讀取到buf中
if (fgets(buf,sizeof(buf),fp) == NULL) {
//如果為空直接返回
return 0;
}
//如果讀取到的首字符不等于預期值,則提示報錯
if (buf[0] != prefix) {
ERROR("Expected prefix '%c', got: '%c'",buf[0],prefix);
return 0;
}
//將字符串值轉成long類型值
*target = strtol(buf+1,&eptr,10);
return consumeNewline(eptr);
}
~~~
里面有一個比較特別的方法,consumeNewline()消除換行符的方法:
~~~
/* 消除buf前面的換行符,即比較buf字符串中的前2個字符 */
int consumeNewline(char *buf) {
if (strncmp(buf,"\r\n",2) != 0) {
//如果不是等于"\r\n",則提示出錯
ERROR("Expected \\r\\n, got: %02x%02x",buf[0],buf[1]);
return 0;
}
return 1;
}
~~~
里面比較復雜的方法是off_t process(FILE *fp)獲取文件偏移量的操作,這個方法是用來后面截斷文件的操作,截斷后面的空文件的部分:
~~~
off_t pos = process(fp);
//截斷文件的操作,從問價頭部到后面的偏移量,沒有用的空間截去
if (ftruncate(fileno(fp), pos) == -1) {
printf("Failed to truncate AOF\n");
exit(1);
} else {
printf("Successfully truncated AOF\n");
}
~~~
以上就是我所分析的內容了,非常簡單,比起struct比,簡單太多了,可以細細體會其中方法涉及的技巧,比如我們會考慮日志文件分析時會想到用consumeNewline()換行操作的設計嗎
- 前言
- (一)--Redis結構解析
- (二)--結構體分析(1)
- (三)---dict哈希結構
- (四)-- sds字符串
- (五)--- sparkline微線圖
- (六)--- ziplist壓縮列表
- (七)--- zipmap壓縮圖
- (八)--- t_hash哈希轉換
- (九)--- t_list,t_string的分析
- (十)--- testhelp.h小型測試框架和redis-check-aof.c日志檢測
- (十一)--- memtest內存檢測
- (十二)--- redis-check-dump本地數據庫檢測
- (十三)--- redis-benchmark性能測試
- (十四)--- rdb.c本地數據庫操作
- (十五)--- aof-append only file解析
- (十六)--- config配置文件
- (十七)--- multi事務操作
- (十八)--- db.c內存數據庫操作
- (十九)--- replication主從數據復制的實現
- (二十)--- ae事件驅動
- (二十一)--- anet網絡通信的封裝
- (二十二)--- networking網絡協議傳輸
- (二十三)--- CRC循環冗余算法和RAND隨機數算法
- (二十四)--- tool工具類(2)
- (二十五)--- zmalloc內存分配實現
- (二十六)--- slowLog和hyperloglog
- (二十七)--- rio系統I/O的封裝
- (二十八)--- object創建和釋放redisObject對象
- (二十九)--- bio后臺I/O服務的實現
- (三十)--- pubsub發布訂閱模式
- (三十一)--- latency延遲分析處理
- (三十二)--- redis-cli.c客戶端命令行接口的實現(1)
- (三十三)--- redis-cli.c客戶端命令行接口的實現(2)
- (三十四)--- redis.h服務端的實現分析(1)
- (三十五)--- redis.c服務端的實現分析(2)
- (三十六)--- Redis中的11大優秀設計