在之前的博客中提到的一個服務端,在之前壓力測試的過程當中,發現單核CPU最多能達到1000TPSgit
還覺得是服務端性能不夠好,因此一直想着怎麼去優化它。github
但優化的思路明顯很少,因此就考慮換一種壓力測試的方法,事實證實這個想法是對的。併發
之前的壓力測試方法 :socket
for((i=0;i<$3;i++));do for((j=0;j<$4;j++));do cmd="./client $1 $2 4 $a $a $a $a" echo `time $cmd` done& done
思路是用多進程來實現併發,代碼如上所示;高併發
如今的壓力測試方法:性能
//客戶端程序 /******* 客戶端程序 client.c ************/ #include <stdio.h> #include <stdlib.h> #include <sys/types.h> #include <sys/socket.h> #include <string.h> #include <netdb.h> #include <netinet/in.h> #include <errno.h> #include <arpa/inet.h> #include <fcntl.h> #include <unistd.h> #include <sys/epoll.h> static struct sockaddr_in server_addr; static int epoll_fd; static int currency,total_req,total_read; static struct epoll_event* events; int setnonblock(int fd) { int flags; flags = fcntl(fd, F_GETFL); flags |= O_NONBLOCK; fcntl(fd, F_SETFL, flags); } void new_conn() { if(--total_req < 0)return; int sockfd; /* 客戶程序開始創建 sockfd描述符 */ if((sockfd=socket(AF_INET,SOCK_STREAM,0))==-1) { fprintf(stderr,"Socket Error:%s\a\n",strerror(errno)); return ; } setnonblock(sockfd); //讓epoll接管 struct epoll_event event; event.data.fd=sockfd; event.events = EPOLLOUT|EPOLLIN; epoll_ctl(epoll_fd,EPOLL_CTL_ADD, sockfd,&event); /* 客戶程序發起鏈接請求 */ if(connect(sockfd,(struct sockaddr *)(&server_addr),sizeof(struct sockaddr))==-1) { if(errno == EINPROGRESS) return; fprintf(stderr,"Connect Error:%s\a\n",strerror(errno)); return; } } int main(int argc, char *argv[]) { struct hostent *host; if((host=gethostbyname(argv[1]))==NULL) { fprintf(stderr,"Gethostname error\n"); exit(1); } int portnumber; if((portnumber=atoi(argv[2]))<0) { fprintf(stderr,"Usage:%s hostname portnumber\a\n",argv[0]); exit(1); } /* 客戶程序填充服務端的資料 */ bzero(&server_addr,sizeof(server_addr)); server_addr.sin_family=AF_INET; server_addr.sin_port=htons(portnumber); server_addr.sin_addr=*((struct in_addr *)host->h_addr); //併發數和總的請求數 currency = atoi(argv[3]); total_req = total_read = currency * atoi(argv[4]); if((epoll_fd=epoll_create(1000))==-1) { fprintf(stderr,"epoll create Error:%s\a\n",strerror(errno)); exit(1); } events = calloc(1000,sizeof(struct epoll_event)); //初始化併發數個鏈接 int i; for(i=0;i<currency;i++)new_conn(); while(1) { fprintf(stderr,"while\n"); int n,j; n = epoll_wait(epoll_fd, events, 1000, -1); for(j=0;j<n;j++) { if(events[j].events & EPOLLOUT) { fprintf(stderr, "can write\n",n); int fd = events[j].data.fd; int optval; socklen_t optlen = sizeof(optval); if(getsockopt(fd,SOL_SOCKET,SO_ERROR,&optval, &optlen) == -1) { fprintf(stderr, "getsockopt error\n",n); } else if(optval != 0) { fprintf(stderr, "connect error\n",n); continue; }; struct epoll_event event; event.data.fd=fd; event.events = EPOLLIN; epoll_ctl(epoll_fd,EPOLL_CTL_MOD, fd,&event); char buffer2[100]; memset(buffer2,100,0); int type,score,time; unsigned long oid; type= htonl(atoi(argv[5])); oid= htonl(atol(argv[6])); score= htonl(atoi(argv[7])); char*pass="220106aa"; char*loc = buffer2; memcpy((void*)loc,(void*)(pass),8); loc+=8; memcpy((void*)loc,(void*)(&type),4); loc+=4; memcpy((void*)loc,(void*)(&oid),8); loc+=8; memcpy((void*)loc,(void*)(&score),4); write(fd, buffer2, 24); /* 鏈接成功了 */ } else if(events[j].events & EPOLLIN) { fprintf(stderr, "can read\n",n); int fd = events[j].data.fd; char buf[100]; int n=read(fd,buf,100); close(fd); new_conn(); if(n==-1) { fprintf(stderr,"read Error:%s\a\n",strerror(errno)); continue; } buf[n]=0; fprintf(stderr, "return %s\n",buf); fprintf(stderr, "total_read %d\n",total_read); if (--total_read <= 0)return; } } } }
思路就是用單進程來測試,用EPOOL來實現併發
代碼也可下載:
百度網盤:http://pan.baidu.com/s/1vdQqB
github : https://github.com/hxdoit/real_time_rank/blob/master/server1-1.0/epoll.c測試
性能對比:
環境:單核CPU,兩臺機器,一臺服務端,一臺客戶端
多進程:690TPS
EPOLL:6435TPS
性能提升了十倍!優化
分析:
在單個機器上,使用多進程,資源消耗很大,不可能達到太大的併發
而使用EPOLL,單進程同時監聽多個socket,能夠達到較高併發spa