日志系统性能对比分析
2013-10-17 18:32
591 查看
作者:邹祁峰
邮箱:Qifeng.zou.job@gmail.com
博客:http://blog.csdn.net/qifengzou
日期:2013.10.17
转载请注明来自"祁峰"的CSDN博客
以下将对常用的日志系统进行较简单的实现和测试,因采用以下方式的日志系统都是依赖于以下基本的实现过程,因此其基本上能较为准确的反应出各实现方式的性能差异!
主函数主要负责侦听指定端口,等待接受客户端的连接请求,同时启动子进程与客户端进行交互。[注意:实际应用中可以使用进程池和线程池机制进行完善,但是测试过程中不必过于复杂]
-> 接收代码
此函数由服务端子进程调用,用于接收客户端发送的日志信息,并将信息写入指定的日志文件中!
图1 TCP日志系统测试结果
图2 UDP日志系统测试结果
主函数主要负责绑定指定文件,并等待接收客户端的连接请求,再启动子进程处理与客户端的交互![注:实际应用中可使用进程池或线程池机制进行完善]
-> 接收代码
此函数被子进程调用,主要负责接收客户端的日志信息,并将信息写入到指定的日志文件中!
图3 U-TCP日志系统测试结果
图4 U-UDP日志系统测试结果
图5 同步日志系统(无锁)测试结果
图6 同步日志系统(加锁)测试结果
表1 性能排序
总结:以上6种日志系统中,同步日志系统(无锁)的性能比其他5种日志系统的性能明显优异,而使用加锁的日志系统性能明显比其他的差很多!
注意:使用共享内存的日志缓存+无锁机制+同步机制+SVR进程的日志系统的性能在同步日志系统[无锁]的基础上提高150%以上,关于此日志系统的设计我将在后续的博文中给出设计思路。
邮箱:Qifeng.zou.job@gmail.com
博客:http://blog.csdn.net/qifengzou
日期:2013.10.17
转载请注明来自"祁峰"的CSDN博客
1 引言
日志系统主要负责记录系统运行过程中的行为和数据,这些行为和数据将作为系统恢复、错误查找、数据纠正的重要依据,其重要性可见一斑!但是往往对于一个有高性能要求的信息系统而言,日志系统往往又是整个系统的瓶颈所在,针对这个问题,以下为寻找一个更优的日志系统设计方案做一些前期的探索工作。以下将对常用的日志系统进行较简单的实现和测试,因采用以下方式的日志系统都是依赖于以下基本的实现过程,因此其基本上能较为准确的反应出各实现方式的性能差异!
2 TCP日志系统[异步]
2.1 服务端代码
-> 主函数代码主函数主要负责侦听指定端口,等待接受客户端的连接请求,同时启动子进程与客户端进行交互。[注意:实际应用中可以使用进程池和线程池机制进行完善,但是测试过程中不必过于复杂]
int main(int argc, const char *argv[]) { int ret = 0, sckid = -1, clifd = 0, len = 0; struct sockaddr_in svraddr, cliaddr; /* Create socket */ sckid = socket(AF_INET, SOCK_STREAM, 0); if(sckid < 0) { fprintf(stderr, "Socket failed! errmsg:[%d]%s\n", errno, strerror(errno)); return -1; } /* Bind port */ bzero(&svraddr, sizeof(svraddr)); svraddr.sin_family = AF_INET; svraddr.sin_addr.s_addr = htonl(INADDR_ANY); svraddr.sin_port = htons(PORT); ret = bind(sckid, (struct sockaddr *)&svraddr, sizeof(svraddr)); if(ret < 0) { fprintf(stderr, "Bind failed! errmsg:[%d]%s\n", errno, strerror(errno)); return -1; } ret = listen(sckid, 20); if(ret < 0) { fprintf(stderr, "Listen failed! errmsg:[%d]%s\n", errno, strerror(errno)); return -1; } /* Receive client connection */ while(1) { memset(&cliaddr, 0, sizeof(cliaddr)); len = sizeof(cliaddr); clifd = accept(sckid, (struct sockaddr *)&cliaddr, &len); ret = fork(); if(ret < 0) { fprintf(stderr, "Fork failed! errmsg:[%d]%s\n", errno, strerror(errno)); return -1; } if(0 == ret) { close(sckid); recv_msg(clifd); exit(1); } } close(sckid); return 0; }
-> 接收代码
此函数由服务端子进程调用,用于接收客户端发送的日志信息,并将信息写入指定的日志文件中!
int recv_msg(int clifd) { int ret = 0, fd = -1; char buf[1024] = {0}; fd = open("test.log", O_CREAT|O_WRONLY|O_APPEND, 0666); if(fd < 0) { fprintf(stderr, "Open failed! errmsg:[%d]%s\n", errno, strerror(errno)); return -1; } while(1) { memset(buf, 0, sizeof(buf)); ret = read(clifd, buf, sizeof(buf) - 1); if(ret < 0) { if(EINTR == errno) { continue; } fprintf(stderr, "Read failed! errmsg:[%d]%s\n", errno, strerror(errno)); return -1; } else if(0 == ret) { break; } write(fd, buf, ret); } close(clifd); close(fd); return 0; }
2.2 客户端代码
此函数负责连接至远程服务端,并将日志信息发送至远程服务端!int main(int argc, const char *argv[]) { int ret = 0, sckid = -1, idx = 0; struct sockaddr_in server; char buf[BUFLEN] = {0}; memset(&server, 0, sizeof(server)); /* Create socket */ sckid = socket(AF_INET, SOCK_STREAM, 0); if(sckid < 0) { fprintf(stderr, "Socket failed! errmsg:[%d]%s\n", errno, strerror(errno)); return -1; } /* Connect to server */ server.sin_family = AF_INET; inet_pton(AF_INET, "127.0.0.1", &server.sin_addr); server.sin_port = htons(PORT); ret = connect(sckid, (void *)&server, sizeof(server)); if(ret < 0) { fprintf(stderr, "Connect failed! errmsg:[%d]%s\n", errno, strerror(errno)); return -1; } /* Send log information */ for(idx=0; idx<LOOP; idx++) { snprintf(buf, sizeof(buf), "This is just a test![%d]\n", idx); ret = write(sckid, buf, strlen(buf)); if(ret < 0) { if(EINTR == errno) { --idx; continue; } fprintf(stderr, "Write failed! errmsg:[%d]%s\n", errno, strerror(errno)); break; } } close(sckid); return 0; }
2.3 测试结果
撰写100w条日志的测试结果如下图所示:[注:请关注红线区域的系统调用情况]图1 TCP日志系统测试结果
3 UDP日志系统[异步]
3.1 服务端代码
此模块负责绑定指定端口,并接收UDP客户端发送过来的数据,并将数据写入到指定的日志文件中。[注:在实际的应用过程中,可以引入线程池机制进行完善]int main(int argc, const char *argv[]) { int ret = 0, sckid = -1, fd = 0, len = 0; char msg[BUFLEN] = {0}; struct sockaddr_in svraddr, fromaddr; /* Create socket */ sckid = socket(AF_INET, SOCK_DGRAM, 0); if(sckid < 0) { fprintf(stderr, "Socket failed! errmsg:[%d]%s\n", errno, strerror(errno)); return -1; } /* Bind port */ bzero(&svraddr, sizeof(svraddr)); svraddr.sin_family = AF_INET; svraddr.sin_addr.s_addr = htonl(INADDR_ANY); svraddr.sin_port = htons(PORT); ret = bind(sckid, (struct sockaddr *)&svraddr, sizeof(svraddr)); if(ret < 0) { fprintf(stderr, "Bind failed! errmsg:[%d]%s\n", errno, strerror(errno)); return -1; } fd = open("test.log", O_CREAT|O_WRONLY|O_APPEND, 0666); if(fd < 0) { fprintf(stderr, "Open failed! errmsg:[%d]%s\n", errno, strerror(errno)); return -1; } /* Receive log information */ while(1) { memset(&fromaddr, 0, sizeof(fromaddr)); len = sizeof(fromaddr); ret = recvfrom(sckid, msg, sizeof(msg), 0, (struct sockaddr *)&fromaddr, &len); if(ret < 0) { if(EINTR == ret) { continue; } fprintf(stderr, "Recvfrom failed! errmsg:[%d]%s\n", errno, strerror(errno)); return -1; } write(fd, msg, ret); } close(sckid); close(fd); return 0; }
3.2 客户端代码
客户端代码主要将日志信息发送到服务端指定端口!int main(int argc, const char *argv[]) { int ret = 0, sckid = -1, idx = 0; struct sockaddr_in server; char buf[BUFLEN] = {0}; memset(&server, 0, sizeof(server)); /* Create socket */ sckid = socket(AF_INET, SOCK_DGRAM, 0); if(sckid < 0) { fprintf(stderr, "Socket failed! errmsg:[%d]%s\n", errno, strerror(errno)); return -1; } /* Connect to server */ server.sin_family = AF_INET; inet_pton(AF_INET, "127.0.0.1", &server.sin_addr); server.sin_port = htons(PORT); ret = connect(sckid, (void *)&server, sizeof(server)); if(ret < 0) { fprintf(stderr, "Connect failed! errmsg:[%d]%s\n", errno, strerror(errno)); return -1; } /* Send log information */ for(idx=0; idx<LOOP; idx++) { snprintf(buf, sizeof(buf), "This is just a test![%d]\n", idx); ret = write(sckid, buf, strlen(buf)); if(ret < 0) { if(EINTR == errno) { --idx; continue; } fprintf(stderr, "Write failed! errmsg:[%d]%s\n", errno, strerror(errno)); break; } } close(sckid); return 0; }
3.3 测试结果
撰写100w条日志的测试结果如下图所示:[注:请关注红线区域的系统调用情况]图2 UDP日志系统测试结果
4 U-TCP日志系统[异步]
4.1 服务端代码
-> 主函数代码主函数主要负责绑定指定文件,并等待接收客户端的连接请求,再启动子进程处理与客户端的交互![注:实际应用中可使用进程池或线程池机制进行完善]
int main(int argc, const char *argv[]) { int ret = 0, sckid = -1, clifd = 0, len = 0, flag = 1; struct sockaddr_un svraddr, cliaddr; /* Create socket */ sckid = socket(AF_UNIX, SOCK_STREAM, 0); if(sckid < 0) { fprintf(stderr, "Socket failed! errmsg:[%d]%s\n", errno, strerror(errno)); return -1; } setsockopt(sckid, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof(flag)); /* Bind port */ bzero(&svraddr, sizeof(svraddr)); svraddr.sun_family = AF_UNIX; snprintf(svraddr.sun_path, sizeof(svraddr.sun_path), "%s", SVRPATH); ret = bind(sckid, (struct sockaddr *)&svraddr, sizeof(svraddr)); if(ret < 0) { fprintf(stderr, "Bind failed! errmsg:[%d]%s\n", errno, strerror(errno)); return -1; } ret = listen(sckid, 20); if(ret < 0) { fprintf(stderr, "Listen failed! errmsg:[%d]%s\n", errno, strerror(errno)); return -1; } /* Send log information */ while(1) { memset(&cliaddr, 0, sizeof(cliaddr)); len = sizeof(cliaddr); clifd = accept(sckid, (struct sockaddr *)&cliaddr, &len); ret = fork(); if(ret < 0) { fprintf(stderr, "Fork failed! errmsg:[%d]%s\n", errno, strerror(errno)); return -1; } else if(0 == ret) { close(sckid); recv_msg(clifd); exit(1); } } close(sckid); return 0; }
-> 接收代码
此函数被子进程调用,主要负责接收客户端的日志信息,并将信息写入到指定的日志文件中!
int recv_msg(int clifd) { int ret = 0, fd = -1; char buf[1024] = {0}; fd = open("test.log", O_CREAT|O_WRONLY|O_APPEND, 0666); if(fd < 0) { fprintf(stderr, "Open failed! errmsg:[%d]%s\n", errno, strerror(errno)); return -1; } while(1) { memset(buf, 0, sizeof(buf)); ret = read(clifd, buf, sizeof(buf) - 1); if(ret < 0) { if(EINTR == errno) { continue; } fprintf(stderr, "Read failed! errmsg:[%d]%s\n", errno, strerror(errno)); return -1; } else if(0 == ret) { break; } write(fd, buf, ret); } close(clifd); close(fd); return 0; }
4.2 客户端代码
此代码主要负责侦听指定文件,同时将日志信息发送到服务端!int main(int argc, const char *argv[]) { int ret = 0, sckid = -1, idx = 0, flag = 1; struct sockaddr_un cliaddr, svraddr; char buf[BUFLEN] = {0}; memset(&cliaddr, 0, sizeof(cliaddr)); memset(&svraddr, 0, sizeof(svraddr)); /* Create socket */ sckid = socket(AF_UNIX, SOCK_STREAM, 0); if(sckid < 0) { fprintf(stderr, "Socket failed! errmsg:[%d]%s\n", errno, strerror(errno)); return -1; } setsockopt(sckid, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof(flag)); /* Bind file */ cliaddr.sun_family = AF_UNIX; snprintf(cliaddr.sun_path, sizeof(cliaddr.sun_path), "%s", CLI_LSN); ret = bind(sckid, (struct sockaddr *)&cliaddr, sizeof(cliaddr)); if(ret < 0) { fprintf(stderr, "Bind failed! errmsg:[%d]%s\n", errno, strerror(errno)); return -1; } /* Connect to server */ svraddr.sun_family = AF_UNIX; snprintf(svraddr.sun_path, sizeof(svraddr.sun_path), "%s", SVR_LSN); ret = connect(sckid, (void *)&svraddr, sizeof(svraddr)); if(ret < 0) { fprintf(stderr, "Connect failed! errmsg:[%d]%s\n", errno, strerror(errno)); return -1; } /* Send log information */ for(idx=0; idx<LOOP; idx++) { snprintf(buf, sizeof(buf), "This is just a test![%d]\n", idx); ret = write(sckid, buf, strlen(buf)); if(ret < 0) { if(EINTR == errno) { --idx; continue; } fprintf(stderr, "Write failed! errmsg:[%d]%s\n", errno, strerror(errno)); break; } } close(sckid); return 0; }
4.3 测试结果
客户端写100W条日志的测试结果如下图所示:[注:请注意红线区域的系统调用情况]图3 U-TCP日志系统测试结果
5 U-UDP日志系统[异步]
5.1 服务端代码
此代码负责侦听指定文件,同时接受客户端发送过来的数据,再将信息写入指定日志文件中。[注:实际实现过程,可使用线程池进行完善]int main(int argc, const char *argv[]) { int ret = 0, sckid = -1, len = 0, flag = 1, fd = -1; struct sockaddr_un svraddr, fromaddr; char msg[MSGLEN] = {0}; /* Create socket */ sckid = socket(AF_UNIX, SOCK_DGRAM, 0); if(sckid < 0) { fprintf(stderr, "Socket failed! errmsg:[%d]%s\n", errno, strerror(errno)); return -1; } setsockopt(sckid, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof(flag)); /* Bind port */ bzero(&svraddr, sizeof(svraddr)); svraddr.sun_family = AF_UNIX; snprintf(svraddr.sun_path, sizeof(svraddr.sun_path), "%s", SVRPATH); ret = bind(sckid, (struct sockaddr *)&svraddr, sizeof(svraddr)); if(ret < 0) { fprintf(stderr, "Bind failed! errmsg:[%d]%s\n", errno, strerror(errno)); return -1; } setsockopt(sckid, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof(flag)); fd = open("test.log", O_CREAT|O_WRONLY|O_APPEND, 0666); if(fd < 0) { fprintf(stderr, "Open failed! errmsg:[%d]%s\n", errno, strerror(errno)); return -1; } /* Send log information */ while(1) { len = sizeof(fromaddr); memset(&fromaddr, 0, sizeof(fromaddr)); fromaddr.sun_family = AF_UNIX; ret = recvfrom(sckid, msg, sizeof(msg), 0, (struct sockaddr *)&fromaddr, &len); if(ret < 0) { if(EINTR == errno) { continue; } fprintf(stderr, "Recvfrom failed! errmsg:[%d]%s\n", errno, strerror(errno)); break; } write(fd, msg, ret); } close(sckid); close(fd); return 0; }
5.2 客户端代码
客户端代码侦听指定文件后,再将日志信息发送到服务端!int main(int argc, const char *argv[]) { int ret = 0, sckid = -1, idx = 0, flag = 1; struct sockaddr_un cliaddr, svraddr; char buf[BUFLEN] = {0}; memset(&cliaddr, 0, sizeof(cliaddr)); memset(&svraddr, 0, sizeof(svraddr)); /* Create socket */ sckid = socket(AF_UNIX, SOCK_DGRAM, 0); if(sckid < 0) { fprintf(stderr, "Socket failed! errmsg:[%d]%s\n", errno, strerror(errno)); return -1; } setsockopt(sckid, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof(flag)); /* Bind file */ cliaddr.sun_family = AF_UNIX; snprintf(cliaddr.sun_path, sizeof(cliaddr.sun_path), "%s", CLI_LSN); ret = bind(sckid, (struct sockaddr *)&cliaddr, sizeof(cliaddr)); if(ret < 0) { fprintf(stderr, "Bind failed! errmsg:[%d]%s\n", errno, strerror(errno)); return -1; } /* Connect to server */ svraddr.sun_family = AF_UNIX; snprintf(svraddr.sun_path, sizeof(svraddr.sun_path), "%s", SVR_LSN); ret = connect(sckid, (void *)&svraddr, sizeof(svraddr)); if(ret < 0) { fprintf(stderr, "Connect failed! errmsg:[%d]%s\n", errno, strerror(errno)); return -1; } /* Send log information */ for(idx=0; idx<LOOP; idx++) { snprintf(buf, sizeof(buf), "This is just a test![%d]\n", idx); ret = write(sckid, buf, strlen(buf)); if(ret < 0) { if(EINTR == errno) { --idx; continue; } fprintf(stderr, "Write failed! errmsg:[%d]%s\n", errno, strerror(errno)); break; } } close(sckid); return 0; }
5.3 测试结果
客户端写100W条日志的测试结果如下图所示:[注:请注意红线区域的系统调用情况]图4 U-UDP日志系统测试结果
6 同步日志系统[无锁]
6.1 代码实现
该函数是打开文件后,直接将日志写入指定文件中。[注:此日志系统适合在日志文件不共用的系统中]int main(int argc, const char *argv[]) { int ret = 0, fd = 0, idx = 0; char buf[BUFLEN] = {0}; fd = open("test.log", O_CREAT|O_APPEND|O_WRONLY, 0666); if(fd < 0) { fprintf(stderr, "Open failed! errmsg:[%d]%s\n", errno, strerror(errno)); return -1; } /* Write log information */ for(idx=0; idx<LOOP; idx++) { snprintf(buf, sizeof(buf), "This is just a test![%d]\n", idx); ret = write(fd, buf, strlen(buf)); if(ret < 0) { if(EINTR == errno) { --idx; continue; } fprintf(stderr, "Write failed! errmsg:[%d]%s\n", errno, strerror(errno)); break; } } close(fd); return 0; }
6.2 测试结果
客户端写100W条日志的测试结果如下图所示:[注:请注意红线区域的系统调用情况]图5 同步日志系统(无锁)测试结果
7 同步日志系统[加锁]
7.1 代码实现
该函数打开文件后,再往文件中写入日志信息之前,需要加锁并重新调整文件流的位置![注:此日志系统适合在日志文件共用的系统中]int main(int argc, const char *argv[]) { int ret = 0, fd = 0, idx = 0; char buf[BUFLEN] = {0}; for(idx=0; idx<LOOP; idx++) { fd = open("test.log", O_CREAT|O_APPEND|O_WRONLY, 0666); if(fd < 0) { fprintf(stderr, "Open failed! errmsg:[%d]%s\n", errno, strerror(errno)); return -1; } snprintf(buf, sizeof(buf), "This is just a test![%d]\n", idx); lockf(fd, F_LOCK, 0); lseek(fd, 0, SEEK_END); ret = write(fd, buf, strlen(buf)); if(ret < 0) { if(EINTR == errno) { --idx; continue; } fprintf(stderr, "Write failed! errmsg:[%d]%s\n", errno, strerror(errno)); break; } close(fd); } return 0; }
7.2 测试结果
客户端写100W条日志的测试结果如下图所示:[注:请注意红线区域的系统调用情况]图6 同步日志系统(加锁)测试结果
8 其他日志系统
其他日志系统包括使用共享内存、消息队列等等方式实现的日志系统,因其过程相对较为复杂,在此不做实现!感兴趣的可以自己去实现,并对比一下各自的性能情况!9 性能分析
以上系统调用的结果是通过strace -c ./proc-name进行统计的,通过对比可知性能排序如下所示:(依次递减)名次 | 日志系统 | 时间提高 (t1/t0) | 性能对比 (t0-t1)/t1 |
---|---|---|---|
01 | 同步日志[无锁] | 1 (参照t0) | +285% |
02 | U-UPD异步日志 | 1.58 | +143.7% |
03 | UDP异步日志 | 1.70 | +126.5% |
04 | U-TCP异步日志 | 2.10 | +83.3% |
05 | TCP异步日志 | 2.10 | +83.3% |
06 | 同步日志[加锁] | 3.85 | 0% (参照t0) |
总结:以上6种日志系统中,同步日志系统(无锁)的性能比其他5种日志系统的性能明显优异,而使用加锁的日志系统性能明显比其他的差很多!
注意:使用共享内存的日志缓存+无锁机制+同步机制+SVR进程的日志系统的性能在同步日志系统[无锁]的基础上提高150%以上,关于此日志系统的设计我将在后续的博文中给出设计思路。
相关文章推荐
- 日志文件系统及性能分析
- linux系统——日志文件系统及性能分析
- 5个日志数据:让你轻松分析系统性能
- 负载均衡 性能优化,网络安全,https,分布式系统,日志分析,离线数据分析视频教程
- 转 Linux日志文件系统及性能分析
- Linux日志文件系统及性能分析
- 五大车载操作(VOS)系统优劣对比,车载系统架构分析-QNX系统性能分析
- [置顶] ELK日志分析系统通过shield实现权限管理
- 《Spark商业案例与性能调优实战100课》第14课:商业案例之纯粹通过DataSet进行电商交互式分析系统中特定时段购买金额Top10 和访问次数增长Top10
- Android日志系统第三方库------Logger 源码分析
- linux系统性能调优第一步——性能分析(vmstat)
- Ubuntu14.04搭建ELK日志分析系统
- Linux系统性能分析命令二:vmstat
- 用户日志分析系统实战(四)
- Linux下使用NMON监控、分析系统性能
- linux系统性能分析工具
- elk 日志分析系统Logstash+ElasticSearch+Kibana4
- ELK日志分析系统部署
- nmon与nmonanalyser 系统性能分析(图表)利器
- 搭建ELK日志分析系统(一)-Elasticsearch安装