零、概述
做服务器性能测试的时候,并发量、QPS(每秒处理多少请求)、时迟(延迟,每个请求多长时间返回)、测试用例(业务代码,发什么数据和回什么数据)这四组都是重要的参数。
并发 → 网络io的并发,是服务端最基础的技能。备注:研发和运营的数据不相同。
一、报错信息
server ip: 192.168.80.129
client ip: 192.168.80.130
线程: 1
端口: 2000
(1)客户端出现Too many open files“”,而服务端出现“core dumped”
答:通过在“客户端”输入“ulimit -a”看到“open files (-n) 1024”。
对于客户端: 输入ulimit -n 1048576。也可直接修改文件“vim /etc/security/limits.conf”
对于服务端,原因是脚本里没有在相应位置做返回值判断,在以下2个地方加判断:
(2)客户端出现报错信息“Cannot assign requested address”,服务端出现“Segmentation fault (core dumped)”
答:第1部分
出现不能分配address,这个说明“5元组”不够。每个TCP连接由“5元”组成,包括
源ip (sip) 目的ip (dip) 源端口 (sport) 目的端口 (dport) 协议 (protocol)
local ip remote ip local port remote port TCP
192.168.80.130 192.168.80.129 1024-65535 1个(2000)
所以要在server端建立多个端口,从react.c文件里加sockfd的循环。
第2部分
输入命令“cat /proc/sys/net/ipv4/ip_local_port_range” 得到动态端口是从32768到60999共28232个。可通过root权限下的“sudo sysctl -w net.ipv4.ip_local_port_range="4000 65535"”命令临时修改动态端口数量。
(3)客户端fd不再增加
(4)在25万个并发请求后,客户端出现打开文件的上限,原因是什么?
答:这是系统级别的能够打开的文件的数量受限,这是对整个系统的限制。通过命令“sysctl -a | grep 'fs.file-max'”看到结果是“fs.file-max = 259827”的限制。有2种修改限制的方式,
方式1,临时生效,重启恢复默认
“echo 6553560 > /proc/sys/fs/file-max”或“sysctl -w "fs.file-max=655356"”
方式2,永久生效
“ulimit -HSn 6553560” 或
$ vim /etc/security/limits.conf
* soft nofile 65535
* hard nofile 65535
// 如果需要设置当前用户 session 立即生效,可以执行:
$ ulimit -n 65535
(5)客户端直接kill掉,原因是什么?
答:大概率是内存不足,客户端加内存。
(6)如何不让server端kill掉?
答:做一个信号注册函数,每次遇到信息做处理。
(7)出问题后,server端的CPU暴增原因?
答:此时客户端会有CPU里有大量的连接接收final信息,服务端会启用大量定时器,CPU在处理数据过程中出现雪崩的情况。
我们只能把CPU暴增的时间减短,但无法完全消除。
sigaction
handler(int sig){
printf("", 9, 11);
}
二、最终代码
#include <stdio.h>
#include <errno.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <string.h>
#include <pthread.h>
#include <unistd.h>
#include <poll.h>
#include <sys/epoll.h>
#include <sys/time.h>
#define BUFFER_LENGTH 1024
#define CONNECTION_SIZE 1048576 // 1024
#define MAX_PORTS 50
#define TIME_SUB_MS(tv1, tv2) ((tv1.tv_sec - tv2.tv_sec) * 1000 + (tv1.tv_usec - tv2.tv_usec) / 1000)
typedef int (*RCALLBACK)(int fd);
int accept_cb(int fd);
int recv_cb(int fd);
int send_cb(int fd);
int epfd = 0;
struct timeval begin;
struct conn{
int fd;
char rbuffer[BUFFER_LENGTH];
int rlength;
char wbuffer[BUFFER_LENGTH];
int wlength;
RCALLBACK send_callback;
union{
RCALLBACK recv_callback;
RCALLBACK accept_callback;
} r_action;
};
struct conn conn_list[CONNECTION_SIZE] = {0};
int set_event(int fd, int event, int flag){ // fd的epoll注册
if (flag){ // non-zero, than add
struct epoll_event ev;
ev.events = event; //只监听是否可读这种时间,EPOLLOUT等不被检测
ev.data.fd = fd;
epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &ev);
}else { // zero, then modify
struct epoll_event ev;
ev.events = event; //只监听是否可读这种时间,EPOLLOUT等不被检测
ev.data.fd = fd;
epoll_ctl(epfd, EPOLL_CTL_MOD, fd, &ev);
}
}
int event_register(int fd, int event){
if (fd < 0) return -1;
/// 把clientfd注册进回调函数 /
conn_list[fd].fd = fd; //
conn_list[fd].r_action.recv_callback = recv_cb; //
conn_list[fd].send_callback = send_cb; //
//
memset(conn_list[fd].rbuffer, 0, BUFFER_LENGTH); //为了后面正常,这边要清空 //
conn_list[fd].rlength = 0; //
//
memset(conn_list[fd].wbuffer, 0, BUFFER_LENGTH); //为了后面正常,这边要清空 //
conn_list[fd].wlength = 0;
set_event(fd, event, 1);
//
}
// accpept()函数
int accept_cb(int fd){
struct sockaddr_in clientddr;
socklen_t len = sizeof(clientddr);
int clientfd = accept(fd, (struct sockaddr*)&clientddr, &len);
//printf("accept finished: %d\n", clientfd);
if(clientfd < 0){
printf("accept errno: %d ---> %s\n", errno, strerror(errno));
return -1;
}
event_register(clientfd, EPOLLIN);
//每10000条打印一次信息
if ((clientfd % 1000) == 0){
#if 0
struct timeval current;
gettimeofday(¤t, NULL); //gettimeofday
int time_used = TIME_SUB_MS(current, begin);
memcpy(&begin, ¤t, sizeof(struct timeval));
printf("accept finished: %d, time_used is %d\n", clientfd, time_used);
#endif
printf("accept finished: %d\n", clientfd);
}
return 0;
}
int recv_cb(int fd){
int count = recv(fd, conn_list[fd].rbuffer, BUFFER_LENGTH, 0);
if (count == 0){ // disconnect
printf("client disconnect, clientfd is: %d\n", fd); //
close(fd); //通知操作系统释放资源
epoll_ctl(epfd, EPOLL_CTL_DEL, fd, NULL); //unfinished
return 0;
}
conn_list[fd].rlength = count;
//printf("RECV: %s\n", conn_list[fd].rbuffer);
#if 1 //echo
conn_list[fd].wlength = conn_list[fd].rlength;
memcpy(conn_list[fd].wbuffer, conn_list[fd].rbuffer, conn_list[fd].wlength);
//void *memcpy(void *dest, const void *src, size_t n)
#endif
set_event(fd, EPOLLOUT, 0);
return count;
}
int send_cb(int fd){
int count = send(fd, conn_list[fd].wbuffer, conn_list[fd].wlength, 0);
set_event(fd, EPOLLIN, 0);
return count;
}
int init_server(unsigned short port){
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
struct sockaddr_in servaddr;
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(port);
if (-1 == bind(sockfd, (struct sockaddr*)&servaddr, sizeof(struct sockaddr))){
printf("bind failed: %s\n", strerror(errno));
}
listen(sockfd, 10);
//printf("Listen finished: %d\n", sockfd);
return sockfd;
}
int main(){
/ 01_初始化socket到listen
unsigned short port = 2000; ///
/ 02_把sockfd注册到epoll
epfd = epoll_create(1); ///
int i = 0;
for (i = 0; i < MAX_PORTS; i ++){
int sockfd = init_server(port + i);
conn_list[sockfd].fd = sockfd;
conn_list[sockfd].r_action.recv_callback = accept_cb;
set_event(sockfd, EPOLLIN, 1);
}
//
// gettimeofday(&begin, NULL);
while(1){
struct epoll_event events[1024] = {0}; //定义口袋大小
int nready = epoll_wait(epfd, events, 1024,-1); //返回值是
int i = 0;
for (i = 0; i < nready;i ++){
int connfd = events[i].data.fd;
if (events[i].events & EPOLLIN){
conn_list[connfd].r_action.recv_callback(connfd);
}
if (events[i].events & EPOLLOUT){
conn_list[connfd].send_callback(connfd);
}
}
}
return 0;
}
标签:Reactor,int,list,fd,报错,2.1,event,conn,struct
From: https://blog.csdn.net/qsq_Ai/article/details/144459932