一、redis下载编译
这里没什么好说的
用的版本是redis-2.8.17
1)redis-server是可执行程序
2)mian函数在redis.c里面
3)如果要修改调试 这届在src目录下 修改后make或者make clean;make 就行
从main函数说起这里先说两个部分一个是 redis里面的回调函数 还有一个是redis里面的log日志
二、redis里的回调函数
先看下代码;这是把redis里面的回调函数拿出来修改下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
|
/* redis里的回调函数 */ #include<stdio.h> #include<stdlib.h> static
void zmalloc_default_oom( size_t
size) { printf ( "zmalloc_default_oom\n" ); fprintf (stderr,
"zmalloc: Out of memory trying to allocate %d bytes\n" ,size); fflush (stderr); } static
void (*zmalloc_oom_handler)( size_t ) = zmalloc_default_oom; void
zmalloc_set_oom_handler( void
(*oom_handler)( size_t ))
{ printf ( "zmalloc_set_oom_handler\n" ); zmalloc_oom_handler = oom_handler; } void
redisOutOfMemoryHandler( size_t
allocation_size) { printf ( "redisOutOfMemoryHandler------:%d\n" ,allocation_size); } int
main( void ) { //zmalloc_set_oom_handler(redisOutOfMemoryHandler); zmalloc_oom_handler(10); getchar (); return
0; } |
运行结果
zmalloc_default_oom
zmalloc:Out of memory trying to allocate 10 bytes
我们可以看到默认情况下,在没有注册回调函数的情况下zmalloc_oom_handler是指向 zmalloc_default_oom函数的
假如注册了回调函数的情况下,则调用的是 注册了的回调函数
1
2
3
4
5
6
7
|
int
main( void ) { zmalloc_set_oom_handler(redisOutOfMemoryHandler); zmalloc_oom_handler(10); getchar (); return
0; } |
运行结果
zmalloc_set_oom_handler
redisOutOfMemoryHandler----------:10
现在看看redis的代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
|
int
main( int argc,
char **argv)
{ struct
timeval tv; /* We need to initialize our libraries, and the server configuration. */ #ifdef INIT_SETPROCTITLE_REPLACEMENT //初始化参数 spt_init(argc, argv); #endif setlocale (LC_COLLATE, "" ); /* zmalloc_enable_thread_safeness() 开启了内存分配管理的线程安全变量,当内存分配时, redis会统计一个总内存分配量,这是一个共享资源, 所以需要原子性操作,在redis的内存分配代码里, 当需要原子操作时,就需要打开线程安全变量。 */ zmalloc_enable_thread_safeness(); /* zmalloc_set_oom_handler() 是一个内存分配错误处理, 当无法得到需要的内存量时, 会调用redisOutOfMemoryHandler函数。 */ zmalloc_set_oom_handler(redisOutOfMemoryHandler); srand ( time (NULL)^getpid()); gettimeofday(&tv,NULL); dictSetHashFunctionSeed(tv.tv_sec^tv.tv_usec^getpid()); server.sentinel_mode = checkForSentinelMode(argc,argv); initServerConfig(); .......... } |
1
|
zmalloc_set_oom_handler注册回调函数 |
1
|
redisOutOfMemoryHandler主要是个 log 日志打印,即在内存分配失败的时候触发回调函数,打印 log 。 |
1
2
3
4
5
6
7
8
9
10
11
12
13
|
void
*zmalloc( size_t
size) { void
*ptr = malloc (size+PREFIX_SIZE); if
(!ptr) zmalloc_oom_handler(size); #ifdef HAVE_MALLOC_SIZE update_zmalloc_stat_alloc(zmalloc_size(ptr)); return
ptr; #else *(( size_t *)ptr) = size; update_zmalloc_stat_alloc(size+PREFIX_SIZE); return
( char *)ptr+PREFIX_SIZE; #endif } |
在分配内存失败的时候,触发回调函数
三、redis的log日志
由于redis是单线程的 所以在redis.c里面的log没有做成多线程
这样的log,在单线程下 速度很快,因为无锁。但是在多线程下是不安全
简化了下 redis的log 单是大抵就是这样
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
|
#include <stdio.h> #include <stdlib.h> #include <stdarg.h> /* Log levels */ #define REDIS_DEBUG 0 #define REDIS_VERBOSE 1 #define REDIS_NOTICE 2 #define REDIS_WARNING 3 #define REDIS_MAX_LOGMSG_LEN 1024 /* 默认信息长度 */ void
redisLogRaw( int
level, const
char *msg); void
redisLog( int
level, const
char *fmt, ...); /* verbosity表示开启log的级别 需要写log的时候,log级别小于等于verbosity写log 否则不会写log */ struct
redisServer { int
verbosity; /* 日志级别*/ char
*logfile; /* Path of log file */ }; struct
redisServer server; /* server global state */ void
redisLog( int
level, const
char *fmt, ...) { //如果level级别大于verbosity则不打印 if
(level> server.verbosity) { return ; } va_list
ap; char
msg[REDIS_MAX_LOGMSG_LEN]; va_start (ap, fmt); vsnprintf(msg,
sizeof (msg), fmt, ap); va_end (ap); redisLogRaw(level,msg); } void
redisLogRaw( int
level, const
char *msg) { #if 1 FILE
*fp; char
buf[64]; // int rawmode = (level & REDIS_LOG_RAW); //int log_to_stdout = server.logfile[0] == '\0'; //level &= 0xff; /* clear flags */ //if (level < server.verbosity) return; if (server.logfile != NULL) { fp= fopen (server.logfile, "a" ); } else { fp=stdout; } int
off; // struct timeval tv; //gettimeofday(&tv,NULL); //off = strftime(buf,sizeof(buf),"%d %b %H:%M:%S.",localtime(&tv.tv_sec)); //snprintf(buf+off,sizeof(buf)-off,"%03d",(int)tv.tv_usec/1000); //fprintf(fp,"[%d] %s %c %s\n",(int)getpid(),buf,c[level],msg); fprintf (fp, " %s\n" ,msg); fflush (fp); if (server.logfile != NULL) { fclose (fp); } #endif } int
main( void ) { server.verbosity=2; server.logfile=NULL; redisLog(1, "11111111\n" ); redisLog(2, "22222\n" ); redisLog(3, "333\n" ); getchar (); return
0; } |
关于log日志 怎么在不影响性能的情况下 最快最多的 写日志能,
多线程用锁的话必然会影响速度
用双队列这种方式好像挺不错,可以把log的内容的放到队列里,一个队列负责接收log,一个队列负责打印log
打印完的log队列,然后跟接收log队列互换,在继续 这种方法陈硕的 《linux多线程网络编程》介绍过
好像谷歌的glog好像性能不错,什么时候有时间把glog看完 再来讨论log日志的实现