压力测试程序有很多种实现方式,比如I/O复用方式,多线程并发编程方式以及这些方式的结合使用。不过,淡出的I/O复用方式的施压程度是最高的,因为现场和进程的调度本身也是要占用一定的CPU时间的。因此我们使用epoll来实现一个通用的服务器压力测试程序。
C++ Code
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 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 |
#include <stdlib.h>
#include <stdio.h> #include <assert.h> #include <unistd.h> #include <sys/types.h> #include <sys/epoll.h> #include <fcntl.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <string.h> //每个客户连接不停地向服务器发送这个请求。 static const char *request = "GET http://localhost/index.html HTTP/ 1.1\r\nConnection: keep-alive\r\n\r\nxxxxxxxxxxxx "; int setnonblocking( int fd ) { int old_option = fcntl( fd, F_GETFL ); int new_option = old_option | O_NONBLOCK; fcntl( fd, F_SETFL, new_option ); return old_option; } void addfd( int epoll_fd, int fd ) { epoll_event event; event.data.fd = fd; event.events = EPOLLOUT | EPOLLET | EPOLLERR; epoll_ctl( epoll_fd, EPOLL_CTL_ADD, fd, &event ); setnonblocking( fd ); } //向服务器写入len字节的数据 bool write_nbytes( int sockfd, const char *buffer, int len ) { int bytes_write = 0; printf( "write out %d bytes to socket %d\n", len, sockfd ); while( 1 ) { bytes_write = send( sockfd, buffer, len, 0 ); if ( bytes_write == - 1 ) { return false; } else if ( bytes_write == 0 ) { return false; } len -= bytes_write; buffer = buffer + bytes_write; if ( len <= 0 ) { return true; } } } //从服务器读取数据 bool read_once( int sockfd, char *buffer, int len ) { int bytes_read = 0; memset( buffer, '\0', len ); bytes_read = recv( sockfd, buffer, len, 0 ); if ( bytes_read == - 1 ) { return false; } else if ( bytes_read == 0 ) { return false; } printf( "read in %d bytes from socket %d with content: %s\n", bytes_read, sockfd, buffer ); return true; } //向服务器发起num个TCP连接,我么可以通过改变num来调整测试压力 void start_conn( int epoll_fd, int num, const char *ip, int port ) { int ret = 0; struct sockaddr_in address; bzero( &address, sizeof( address ) ); address.sin_family = AF_INET; inet_pton( AF_INET, ip, &address.sin_addr ); address.sin_port = htons( port ); for ( int i = 0; i < num; ++i ) { sleep( 1 ); int sockfd = socket( PF_INET, SOCK_STREAM, 0 ); printf( "create 1 sock\n" ); if( sockfd < 0 ) { continue; } if ( connect( sockfd, ( struct sockaddr * )&address, sizeof( address ) ) == 0 ) { printf( "build connection %d\n", i ); addfd( epoll_fd, sockfd ); } } } void close_conn( int epoll_fd, int sockfd ) { epoll_ctl( epoll_fd, EPOLL_CTL_DEL, sockfd, 0 ); close( sockfd ); } int main( int argc, char *argv[] ) { assert( argc == 4 ); int epoll_fd = epoll_create( 100 ); start_conn( epoll_fd, atoi( argv[ 3 ] ), argv[ 1], atoi( argv[ 2] ) ); epoll_event events[ 10000 ]; char buffer[ 2048 ]; while ( 1 ) { int fds = epoll_wait( epoll_fd, events, 10000, 2000 ); //阻塞epoll for ( int i = 0; i < fds; i++ ) { int sockfd = events[i].data.fd; if ( events[i].events & EPOLLIN ) //接收数据 { if ( ! read_once( sockfd, buffer, 2048 ) ) { close_conn( epoll_fd, sockfd ); } struct epoll_event event; event.events = EPOLLOUT | EPOLLET | EPOLLERR; event.data.fd = sockfd; epoll_ctl( epoll_fd, EPOLL_CTL_MOD, sockfd, &event ); } else if( events[i].events & EPOLLOUT ) //发送数据 { if ( ! write_nbytes( sockfd, request, strlen( request ) ) ) { close_conn( epoll_fd, sockfd ); } struct epoll_event event; event.events = EPOLLIN | EPOLLET | EPOLLERR; event.data.fd = sockfd; epoll_ctl( epoll_fd, EPOLL_CTL_MOD, sockfd, &event ); } else if( events[i].events & EPOLLERR ) //出错 { close_conn( epoll_fd, sockfd ); } } } } |
$ ./stress_test 192.168.1.108 12345 1000
如果web服务器足够稳定的话,那么web服务器和stress_test 这两个程序将一直运行下去,并不断的交换数据。