初始化pos機參數

 新聞資訊2  |   2023-08-12 09:55  |  投稿人:pos機之家

網上有很多關于初始化pos機參數,百萬并發(fā)服務器系統(tǒng)參數調優(yōu)的知識,也有很多人為大家解答關于初始化pos機參數的問題,今天pos機之家(www.shbwcl.net)為大家整理了關于這方面的知識,讓我們一起來看下吧!

本文目錄一覽:

1、初始化pos機參數

初始化pos機參數

一、本文目的在編寫服務器時,如果服務器的設計初衷是要可以承擔百萬、千萬的客戶端連接,那么默認的情況下,Linux操作系統(tǒng)提供的相關配置參數(比如說進程可分配的文件數目等)是不能夠滿足我們的程序需求的,因此需要自己調整系統(tǒng)的相關參數

并發(fā)的概念是什么?什么是并發(fā)?

對于服務器并發(fā)的概念,下面幾點是錯誤的定義:①服務器處理客戶端請求的數量:沒有時間、空間等限制,因此不能作為并發(fā)的概念②單位時間內,能夠處理請求的數量:這也是不正確的定義,因為這個定義是針對于服務器吞吐量(qps)的,而不是并發(fā)其他等......下面幾點組成在一起可以作為服務器“并發(fā)”的概念:①服務器能夠同時承擔的客戶端數量(最基本要求)②能處理指定數量以上的相應請求③能夠對數據庫進行操作④有磁盤的操作⑤CPU的占用率最好不要超過60%⑥內存占用率最好不要超過80%

視頻資料詳解:

實現單機百萬連接丨優(yōu)化三次握手四次揮手

揭秘百萬并發(fā)服務器的奧秘

要C/C++ Linux服務器架構師學習資料后臺私信“1”獲取(資料包括C/C++,Linux,golang技術,Nginx,ZeroMQ,MySQL,Redis,fastdfs,MongoDB,ZK,流媒體,CDN,P2P,K8S,Docker,TCP/IP,協(xié)程,DPDK,ffmpeg等),免費分享二、本文環(huán)境搭建本文準備了兩份代碼,作為測試環(huán)境:reactor.c:作為服務端,采用單進程reactor模式編寫,持續(xù)接收客戶端的連接,并且與客戶端有數據的讀寫(recv()、send())mul_port_client_epoll.c:作為客戶端,會向reactor.c服務端發(fā)起不超過340000的客戶端連接,并且每個客戶端都會與服務端有數據的讀寫(recv()、send())

reactor.c

Github源碼鏈接參閱:https://github.com/dongyusheng/csdn-code/blob/master/server-client/reactor.c

// reactor.c// 源碼鏈接: https://github.com/dongyusheng/csdn-code/blob/master/server-client/reactor.c#include <stdio.h>#include <stdlib.h>#include <string.h>#include <sys/socket.h>#include <sys/epoll.h>#include <arpa/inet.h>#include <fcntl.h>#include <unistd.h>#include <errno.h>#include <time.h> #define BUFFER_lenGTH4096#define MAX_EPOLL_EVENTS1024#define SERVER_PORT 8888 typedef int Ncallback(int ,int, void*); struct ntyevent { int fd; int events; void *arg; int (*callback)(int fd, int events, void *arg); int status; char buffer[BUFFER_LENGTH]; int length; long last_active;}; struct ntyreactor { int epfd; struct ntyevent *events;}; int recv_cb(int fd, int events, void *arg);int send_cb(int fd, int events, void *arg);int nty_event_set(struct ntyevent *ev, int fd, NCALLBACK callback, void *arg) {ev->fd = fd;ev->callback = callback;ev->events = 0;ev->arg = arg;ev->last_active = time(NULL); return 0;} int nty_event_add(int epfd, int events, struct ntyevent *ev) { struct epoll_event ep_ev = {0, {0}};ep_ev.data.ptr = ev;ep_ev.events = ev->events = events; int op; if (ev->status == 1) { op = EPOLL_CTL_MOD;} else {op = EPOLL_CTL_ADD; ev->status = 1;} if (epoll_ctl(epfd, op, ev->fd, &ep_ev) < 0) { printf("event add failed [fd=%d], events[%d]\", ev->fd, events); return -1;} return 0;} int nty_event_del(int epfd, struct ntyevent *ev) { struct epoll_event ep_ev = {0, {0}}; if (ev->status != 1) { return -1;} ep_ev.data.ptr = ev;ev->status = 0;epoll_ctl(epfd, EPOLL_CTL_DEL, ev->fd, &ep_ev); return 0;} int recv_cb(int fd, int events, void *arg) { struct ntyreactor *reactor = (struct ntyreactor*)arg; struct ntyevent *ev = reactor->events + fd; int len = recv(fd, ev->buffer, BUFFER_LENGTH, 0);nty_event_del(reactor->epfd, ev); if (len > 0) { ev->length = len;ev->buffer[len] = '\\0'; printf("C[%d]:%s\", fd, ev->buffer); nty_event_set(ev, fd, send_cb, reactor);nty_event_add(reactor->epfd, EPOLLOUT, ev); } else if (len == 0) { close(ev->fd); printf("[fd=%d] pos[%ld], closed\", fd, ev-reactor->events); } else { //if(errno == EAGAIN || errno == EWOULDBLOCK) //continue; return 0; close(ev->fd); printf("recv[fd=%d] error[%d]:%s\", fd, errno, strerror(errno)); } return len;} int send_cb(int fd, int events, void *arg) { struct ntyreactor *reactor = (struct ntyreactor*)arg; struct ntyevent *ev = reactor->events + fd; int len = send(fd, ev->buffer, ev->length, 0); if (len > 0) { printf("send[fd=%d], [%d]%s\", fd, len, ev->buffer);nty_event_del(reactor->epfd, ev);nty_event_set(ev, fd, recv_cb, reactor);nty_event_add(reactor->epfd, EPOLLIN, ev); } else {close(ev->fd);nty_event_del(reactor->epfd, ev); printf("send[fd=%d] error %s\", fd, strerror(errno));} return len;} int accept_cb(int fd, int events, void *arg) { struct ntyreactor *reactor = (struct ntyreactor*)arg; if (reactor == NULL) return -1; struct sockaddr_in client_addr; socklen_t len = sizeof(client_addr); int clientfd; if ((clientfd = accept(fd, (struct sockaddr*)&client_addr, &len)) == -1) { if (errno != EAGAIN && errno != EINTR) {} printf("accept: %s\", strerror(errno)); return -1;} int i = 0; do { for (i = 0;i < MAX_EPOLL_EVENTS;i ++) { if (reactor->events[i].status == 0) { break;}} if (i == MAX_EPOLL_EVENTS) { printf("%s: max connect limit[%d]\", __func__, MAX_EPOLL_EVENTS); break;} int flag = 0; if ((flag = fcntl(clientfd, F_SETFL, O_NONBLOCK)) < 0) { printf("%s: fcntl nonblocking failed, %d\", __func__, MAX_EPOLL_EVENTS); break;}nty_event_set(&reactor->events[clientfd], clientfd, recv_cb, reactor);nty_event_add(reactor->epfd, EPOLLIN, &reactor->events[clientfd]);} while (0); printf("new connect [%s:%d][time:%ld], pos[%d]\", inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port), reactor->events[i].last_active, i); return 0;} int init_sock(short port) { int fd = socket(AF_INET, SOCK_STREAM, 0);fcntl(fd, F_SETFL, O_NONBLOCK); struct sockaddr_in server_addr; memset(&server_addr, 0, sizeof(server_addr));server_addr.sin_family = AF_INET;server_addr.sin_addr.s_addr = htonl(INADDR_ANY);server_addr.sin_port = htons(port);bind(fd, (struct sockaddr*)&server_addr, sizeof(server_addr)); if (listen(fd, 20) < 0) { printf("listen failed : %s\", strerror(errno));} return fd;} int ntyreactor_init(struct ntyreactor *reactor) { if (reactor == NULL) return -1; memset(reactor, 0, sizeof(struct ntyreactor));reactor->epfd = epoll_create(1); if (reactor->epfd <= 0) { printf("create epfd in %s err %s\", __func__, strerror(errno)); return -2;}reactor->events = (struct ntyevent*)malloc((MAX_EPOLL_EVENTS) * sizeof(struct ntyevent)); if (reactor->events == NULL) { printf("create epfd in %s err %s\", __func__, strerror(errno));close(reactor->epfd); return -3;} return 0;} int ntyreactor_destroy(struct ntyreactor *reactor) {close(reactor->epfd); free(reactor->events);} int ntyreactor_addlistener(struct ntyreactor *reactor, int sockfd, NCALLBACK *acceptor) { if (reactor == NULL) return -1; if (reactor->events == NULL) return -1;nty_event_set(&reactor->events[sockfd], sockfd, acceptor, reactor);nty_event_add(reactor->epfd, EPOLLIN, &reactor->events[sockfd]); return 0;} int ntyreactor_run(struct ntyreactor *reactor) { if (reactor == NULL) return -1; if (reactor->epfd < 0) return -1; if (reactor->events == NULL) return -1; struct epoll_event events[MAX_EPOLL_EVENTS+1]; int checkpos = 0, i; while (1) { long now = time(NULL); for (i = 0;i < 100;i ++, checkpos ++) { if (checkpos == MAX_EPOLL_EVENTS) {checkpos = 0;} if (reactor->events[checkpos].status != 1) { continue;} long duration = now - reactor->events[checkpos].last_active; if (duration >= 60) {close(reactor->events[checkpos].fd); printf("[fd=%d] timeout\", reactor->events[checkpos].fd);nty_event_del(reactor->epfd, &reactor->events[checkpos]);}} int nready = epoll_wait(reactor->epfd, events, MAX_EPOLL_EVENTS, 1000); if (nready < 0) { printf("epoll_wait error, exit\"); continue;} for (i = 0;i < nready;i ++) { struct ntyevent *ev = (struct ntyevent*)events[i].data.ptr; if ((events[i].events & EPOLLIN) && (ev->events & EPOLLIN)) {ev->callback(ev->fd, events[i].events, ev->arg);} if ((events[i].events & EPOLLOUT) && (ev->events & EPOLLOUT)) {ev->callback(ev->fd, events[i].events, ev->arg);}}}} int main(int argc, char *argv[]) { unsigned short port = SERVER_PORT; if (argc == 2) {port = atoi(argv[1]);} int sockfd = init_sock(port); struct ntyreactor *reactor = (struct ntyreactor*)malloc(sizeof(struct ntyreactor));ntyreactor_init(reactor);ntyreactor_addlistener(reactor, sockfd, accept_cb);ntyreactor_run(reactor);ntyreactor_destroy(reactor);close(sockfd); return 0;}

mul_port_client_epoll.c

Github源碼鏈接參閱:https://github.com/dongyusheng/csdn-code/blob/master/server-client/mul_port_client_epoll.c

// mul_port_client_epoll.c// 源碼鏈接: https://github.com/dongyusheng/csdn-code/blob/master/server-client/mul_port_client_epoll.c#include <stdio.h>#include <string.h>#include <stdlib.h>#include <sys/types.h>#include <sys/socket.h>#include <sys/epoll.h>#include <errno.h>#include <netinet/tcp.h>#include <arpa/inet.h>#include <netdb.h>#include <fcntl.h>#include <unistd.h>#include <time.h>#include <sys/time.h> #define MAX_BUFFER128#define MAX_EPOLLSIZE(384*1024)#define MAX_PORT1 #define TIME_SUB_MS(tv1, tv2) ((tv1.tv_sec - tv2.tv_sec) * 1000 + (tv1.tv_usec - tv2.tv_usec) / 1000) int isContinue = 0; static int ntySetNonblock(int fd) { int flags; flags = fcntl(fd, F_GETFL, 0); if (flags < 0) return flags;flags |= O_NONBLOCK; if (fcntl(fd, F_SETFL, flags) < 0) return -1; return 0;} static int ntySetReUseAddr(int fd) { int reuse = 1; return setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *)&reuse, sizeof(reuse));} int main(int argc, char **argv) { if (argc <= 2) { printf("Usage: %s ip port\", argv[0]); exit(0);} // 獲取要連接的服務端的ip和端口 const char *ip = argv[1]; int port = atoi(argv[2]); int connections = 0; char buffer[128] = {0}; int i = 0, index = 0; // 創(chuàng)建epoll struct epoll_event events[MAX_EPOLLSIZE]; int epoll_fd = epoll_create(MAX_EPOLLSIZE); strcpy(buffer, " Data From MulClient\"); // 初始化服務器地址 struct sockaddr_in addr; memset(&addr, 0, sizeof(struct sockaddr_in));addr.sin_family = AF_INET;addr.sin_addr.s_addr = inet_addr(ip); struct timeval tv_begin;gettimeofday(&tv_begin, NULL); while (1) { if (++index >= MAX_PORT) index = 0; struct epoll_event ev; int sockfd = 0; // 如果連接數小于340000,繼續(xù)連接服務器 if (connections < 340000 && !isContinue) {sockfd = socket(AF_INET, SOCK_STREAM, 0); if (sockfd == -1) {perror("socket"); goto err;} //ntySetReUseAddr(sockfd);addr.sin_port = htons(port + index); // 連接服務器 if (connect(sockfd, (struct sockaddr*)&addr, sizeof(struct sockaddr_in)) < 0) {perror("connect"); goto err;} ntySetNonblock(sockfd); // 將套接字設置為非阻塞ntySetReUseAddr(sockfd); // 設置可重用本地地址 // 向服務器發(fā)送數據 sprintf(buffer, "Hello Server: client --> %d\", connections);send(sockfd, buffer, strlen(buffer), 0); // 將套接字設置為可讀可寫,然后加入到epoll_wait()中ev.data.fd = sockfd;ev.events = EPOLLIN | EPOLLOUT;epoll_ctl(epoll_fd, EPOLL_CTL_ADD, sockfd, &ev); connections ++;} // 如果每連接了一千個客戶端或者連接數超過340000,那么就執(zhí)行這個條件 if (connections % 1000 == 999 || connections >= 340000) { struct timeval tv_cur; memcpy(&tv_cur, &tv_begin, sizeof(struct timeval)); gettimeofday(&tv_begin, NULL); // 打印一下每連接1000個客戶端所消耗的時間 int time_used = TIME_SUB_MS(tv_begin, tv_cur); printf("connections: %d, sockfd:%d, time_used:%d\", connections, sockfd, time_used); // 進行epoll_wait() int nfds = epoll_wait(epoll_fd, events, connections, 100); for (i = 0;i < nfds;i ++) { int clientfd = events[i].data.fd; // 執(zhí)行寫 if (events[i].events & EPOLLOUT) { sprintf(buffer, "data from %d\", clientfd);send(sockfd, buffer, strlen(buffer), 0);} // 執(zhí)行讀 else if (events[i].events & EPOLLIN) { char rBuffer[MAX_BUFFER] = {0}; ssize_t length = recv(sockfd, rBuffer, MAX_BUFFER, 0); if (length > 0) { printf(" RecvBuffer:%s\", rBuffer); if (!strcmp(rBuffer, "quit")) {isContinue = 0;} } else if (length == 0) { printf(" Disconnect clientfd:%d\", clientfd);connections --;close(clientfd);} else { if (errno == EINTR) continue; printf(" Error clientfd:%d, errno:%d\", clientfd, errno);close(clientfd);}} else { printf(" clientfd:%d, errno:%d\", clientfd, errno);close(clientfd);}}} // 休眠1000微秒(0.01秒)usleep(1 * 1000);} return 0;err: printf("error : %s\", strerror(errno)); return 0;}三、第1次測試

測試如下

左側運行服務端程序reactor(111.229.177.161:8888),右側運行客戶端程序mul_port_client_epoll去連接服務器效果如下:左側服務端接收了1021個客戶端(除去描述符0、1、2)之后程序報錯,顯示無法繼續(xù)接收客戶端的連接右側客戶端也報錯,無法繼續(xù)連接服務端

原因分析

默認情況下,系統(tǒng)有限制,一個進程最多只能1024個文件(或文件描述符)。我們的客戶端發(fā)起來了超過1024個客戶端的連接,因此程序報錯通過ulimit命令可以查看,如下:

解決方案(修改最大文件數)

第一步通過ulimit命令或者修改/etc/security/limits.conf配置文件,修改用戶可打開的最大文件描述符個數。由于ulimit命令修改的結果不能永久保存,因此我們此處修改/etc/security/limits.conf配置文件(下面圖1)下面修改/etc/security/limits.conf,將所有用戶可打開的文件描述符個數修改為1048576(1024*1024)(下面圖2)修改完成之后退出當前會話(不必重啟),然后重新開啟會話,通過ulinit命令可以看到修改成功然后再把客戶端的機器也全部修改一下,別忘記了第二步:(下面圖1)上面我們雖然修改了/etc/security/limits.conf,但是該配置文件約束的是“用戶級別”的系統(tǒng)限制。還有兩個“內核級別”的配置參數與文件描述符有關。一個為nr_open(表示單個進程打開文件句柄數上限),另一個為file-max(表示系統(tǒng)范圍內所有進程可打開的文件句柄的數量限制)其中nr_open默認為1048576,我們就不需要修改了file-max默認為180566,因此我們需要將其修改為1048576(下面圖2)修改/etc/sysctl.conf文件,在里面修改file-max的值為1048576,然后保存退出(下面圖3)執(zhí)行sysctl的命令,將/etc/sysctl.conf文件的內容更新到/proc/sys/fs/file-max中生效,令執(zhí)行完之后查看/proc/sys/fs/file-max文件,更新成功然后再把客戶端的機器也全部修改一下,別忘記了第三步:上面我們將進程可分配描述符數量都增大了,那么還需要修改服務端程序reactor.c中的MAX_EPOLL_EVENTS宏,因為其代表的是epoll_wait()可以處理事件的數量,因為客戶端數量增加了,因此該宏也要增加下面我們將該宏設置為1024*512=524288(由于本人機器內存不足,只能修改這個大,如果修改再大程序就運行不了,因為無法為程序分配更多的內存。如果你的機器內存夠大,那么可以將這個數值再往上調;如果你的機器內存不足,那么就將這個值調小)修改完成之后重新編譯reactor.c

#define MAX_EPOLL_EVENTS (1024 * 512)

上面所有的內容修改完成之后,進入下面的第2次測試四、第2次測試

測試如下

左側運行服務端程序reactor(111.229.177.161:8888),右側運行兩個客戶端程序mul_port_client_epoll去連接服務器效果如下:左側服務端持續(xù)接收客戶端的連接右側兩個客戶端向服務端發(fā)起連接,其中一個客戶端只連接了兩萬多個就報錯退出了,另一個客戶端只連接了一萬多個就報錯退出了左側服務端沒有報錯,只是沒有客戶端再連接進來了

原因分析

此處要介紹“五元組”的概念了,這個概念在下面的幾次測試中都要用到一個套接字fd就對應一個“五元組”,一個五元組包含下面幾項內容:1.源IP地址2.目的IP地址3.源端口4.目的端口5.協(xié)議類型(TCP、UDP等)因此,我們上面的兩個客戶端加起來最多只能創(chuàng)建四萬個套接字就報錯的原因是:源IP地址、目的IP地址、協(xié)議類型(TCP)這三者都保持不變、目的端口只有一個也保持不變,唯一可以變化的就是源端口(也就是客戶端的端口)我們知道,一個系統(tǒng)的端口最多只能有65535個,其中有一些已經被其他服務使用了,因此客戶端可以使用的端口大概只有四五萬個,所以客戶端也就只能創(chuàng)建四五萬個套接字,所以上面我們兩個客戶端總共加起來只創(chuàng)建了四五萬個套接字就不能再繼續(xù)創(chuàng)建套接字了概括來說,你能有多少種“五元組”類型,那么你就能建立多少個套接字連接

解決方案

通過上面的分析我們知道,主要的限制原因就在端口的限制上,因此下面我們讓服務端程序監(jiān)聽在多個端口上,這樣一來可以使用的“五元組”組合就可以更多,那么我們可以創(chuàng)建的套接字fd也就更多第一步:修改服務端程序reactor.c,使其監(jiān)聽在5個端口上,也就是啟動了5個Tcp Server(隨意多少個,只要端口多了,就能承載更多的客戶端,此處我們就只設置為5個)代碼變化不多,只是新增了一個LISTEN_PORT_COUNT宏,然后修改了main()函數使其監(jiān)聽在5個端口上,其余代碼全部不變修改完成之后重新編譯

// reactor.c其余代碼不變, 只需要修改下面的內容即可#include <stdio.h>#include <stdlib.h>#include <string.h>#include <sys/socket.h>#include <sys/epoll.h>#include <arpa/inet.h>#include <fcntl.h>#include <unistd.h>#include <errno.h>#include <time.h> #define BUFFER_LENGTH4096#define MAX_EPOLL_EVENTS1024#define SERVER_PORT 8888 // 新增一個宏, 表示監(jiān)聽的端口數量#define LISTEN_PORT_COUNT 5 typedef int NCALLBACK(int ,int, void*); struct ntyevent { int fd; int events; void *arg; int (*callback)(int fd, int events, void *arg); int status; char buffer[BUFFER_LENGTH]; int length; long last_active;}; struct ntyreactor { int epfd; struct ntyevent *events;}; int recv_cb(int fd, int events, void *arg);int send_cb(int fd, int events, void *arg);int nty_event_set(struct ntyevent *ev, int fd, NCALLBACK callback, void *arg) {ev->fd = fd;ev->callback = callback;ev->events = 0;ev->arg = arg;ev->last_active = time(NULL); return 0;} int nty_event_add(int epfd, int events, struct ntyevent *ev) { struct epoll_event ep_ev = {0, {0}};ep_ev.data.ptr = ev;ep_ev.events = ev->events = events; int op; if (ev->status == 1) { op = EPOLL_CTL_MOD;} else {op = EPOLL_CTL_ADD; ev->status = 1;} if (epoll_ctl(epfd, op, ev->fd, &ep_ev) < 0) { printf("event add failed [fd=%d], events[%d]\", ev->fd, events); return -1;} return 0;} int nty_event_del(int epfd, struct ntyevent *ev) { struct epoll_event ep_ev = {0, {0}}; if (ev->status != 1) { return -1;} ep_ev.data.ptr = ev;ev->status = 0;epoll_ctl(epfd, EPOLL_CTL_DEL, ev->fd, &ep_ev); return 0;} int recv_cb(int fd, int events, void *arg) { struct ntyreactor *reactor = (struct ntyreactor*)arg; struct ntyevent *ev = reactor->events + fd; int len = recv(fd, ev->buffer, BUFFER_LENGTH, 0);nty_event_del(reactor->epfd, ev); if (len > 0) { ev->length = len;ev->buffer[len] = '\\0'; printf("C[%d]:%s\", fd, ev->buffer); nty_event_set(ev, fd, send_cb, reactor);nty_event_add(reactor->epfd, EPOLLOUT, ev); } else if (len == 0) { close(ev->fd); printf("[fd=%d] pos[%ld], closed\", fd, ev-reactor->events); } else { //if(errno == EAGAIN || errno == EWOULDBLOCK) //continue; return 0; close(ev->fd); printf("recv[fd=%d] error[%d]:%s\", fd, errno, strerror(errno)); } return len;} int send_cb(int fd, int events, void *arg) { struct ntyreactor *reactor = (struct ntyreactor*)arg; struct ntyevent *ev = reactor->events + fd; int len = send(fd, ev->buffer, ev->length, 0); if (len > 0) { printf("send[fd=%d], [%d]%s\", fd, len, ev->buffer);nty_event_del(reactor->epfd, ev);nty_event_set(ev, fd, recv_cb, reactor);nty_event_add(reactor->epfd, EPOLLIN, ev); } else {close(ev->fd);nty_event_del(reactor->epfd, ev); printf("send[fd=%d] error %s\", fd, strerror(errno));} return len;} int accept_cb(int fd, int events, void *arg) { struct ntyreactor *reactor = (struct ntyreactor*)arg; if (reactor == NULL) return -1; struct sockaddr_in client_addr; socklen_t len = sizeof(client_addr); int clientfd; if ((clientfd = accept(fd, (struct sockaddr*)&client_addr, &len)) == -1) { if (errno != EAGAIN && errno != EINTR) {} printf("accept: %s\", strerror(errno)); return -1;} int i = 0; do { for (i = 0;i < MAX_EPOLL_EVENTS;i ++) { if (reactor->events[i].status == 0) { break;}} if (i == MAX_EPOLL_EVENTS) { printf("%s: max connect limit[%d]\", __func__, MAX_EPOLL_EVENTS); break;} int flag = 0; if ((flag = fcntl(clientfd, F_SETFL, O_NONBLOCK)) < 0) { printf("%s: fcntl nonblocking failed, %d\", __func__, MAX_EPOLL_EVENTS); break;}nty_event_set(&reactor->events[clientfd], clientfd, recv_cb, reactor);nty_event_add(reactor->epfd, EPOLLIN, &reactor->events[clientfd]);} while (0); printf("new connect [%s:%d][time:%ld], pos[%d]\", inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port), reactor->events[i].last_active, i); return 0;} int init_sock(short port) { int fd = socket(AF_INET, SOCK_STREAM, 0);fcntl(fd, F_SETFL, O_NONBLOCK); struct sockaddr_in server_addr; memset(&server_addr, 0, sizeof(server_addr));server_addr.sin_family = AF_INET;server_addr.sin_addr.s_addr = htonl(INADDR_ANY);server_addr.sin_port = htons(port);bind(fd, (struct sockaddr*)&server_addr, sizeof(server_addr)); if (listen(fd, 20) < 0) { printf("listen failed : %s\", strerror(errno));} return fd;} int ntyreactor_init(struct ntyreactor *reactor) { if (reactor == NULL) return -1; memset(reactor, 0, sizeof(struct ntyreactor));reactor->epfd = epoll_create(1); if (reactor->epfd <= 0) { printf("create epfd in %s err %s\", __func__, strerror(errno)); return -2;}reactor->events = (struct ntyevent*)malloc((MAX_EPOLL_EVENTS) * sizeof(struct ntyevent)); if (reactor->events == NULL) { printf("create epfd in %s err %s\", __func__, strerror(errno));close(reactor->epfd); return -3;} return 0;} int ntyreactor_destroy(struct ntyreactor *reactor) {close(reactor->epfd); free(reactor->events);} int ntyreactor_addlistener(struct ntyreactor *reactor, int sockfd, NCALLBACK *acceptor) { if (reactor == NULL) return -1; if (reactor->events == NULL) return -1;nty_event_set(&reactor->events[sockfd], sockfd, acceptor, reactor);nty_event_add(reactor->epfd, EPOLLIN, &reactor->events[sockfd]); return 0;} int ntyreactor_run(struct ntyreactor *reactor) { if (reactor == NULL) return -1; if (reactor->epfd < 0) return -1; if (reactor->events == NULL) return -1; struct epoll_event events[MAX_EPOLL_EVENTS+1]; int checkpos = 0, i; while (1) { long now = time(NULL); for (i = 0;i < 100;i ++, checkpos ++) { if (checkpos == MAX_EPOLL_EVENTS) {checkpos = 0;} if (reactor->events[checkpos].status != 1) { continue;} long duration = now - reactor->events[checkpos].last_active; if (duration >= 60) {close(reactor->events[checkpos].fd); printf("[fd=%d] timeout\", reactor->events[checkpos].fd);nty_event_del(reactor->epfd, &reactor->events[checkpos]);}} int nready = epoll_wait(reactor->epfd, events, MAX_EPOLL_EVENTS, 1000); if (nready < 0) { printf("epoll_wait error, exit\"); continue;} for (i = 0;i < nready;i ++) { struct ntyevent *ev = (struct ntyevent*)events[i].data.ptr; if ((events[i].events & EPOLLIN) && (ev->events & EPOLLIN)) {ev->callback(ev->fd, events[i].events, ev->arg);} if ((events[i].events & EPOLLOUT) && (ev->events & EPOLLOUT)) {ev->callback(ev->fd, events[i].events, ev->arg);}}}} int main(int argc, char *argv[]) { unsigned short port = SERVER_PORT; if (argc == 2) {port = atoi(argv[1]);} struct ntyreactor *reactor = (struct ntyreactor*)malloc(sizeof(struct ntyreactor));ntyreactor_init(reactor); int listened[LISTEN_PORT_COUNT]; int i = 0; for(i = 0; i < LISTEN_PORT_COUNT; ++i) { listened[i] = init_sock(port + i); ntyreactor_addlistener(reactor, listened[i], accept_cb); } ntyreactor_run(reactor); ntyreactor_destroy(reactor); for(i = 0; i < LISTEN_PORT_COUNT; ++i) { close(listened[i]); } return 0;}第二步:修改客戶端程序mul_port_client_epoll.c,將其MAX_PORT宏修改為5即可,修改完成之后,其就會向5個服務端都發(fā)起連接修改完成之后重新編譯

#define MAX_PORT 5

備注(必看)

此處出錯的場景在實際開發(fā)中是不會出現的,因為在實際生活中,不會出現一個系統(tǒng)中有上萬的客戶端(IP都一致),因此出現了這種“五元組”組合用盡的情況在實際中,每個客戶端的IP地址都是不同的,因此它們的“五元組”類型都是不同的,因此就不會出現這種因為端口被用完從而導致“五元組”組合被用盡的情況修改完成之后進行下面的第3次測試五、第3次測試

測試如下

左側運行服務端程序reactor(111.229.177.161:8888),右側運行兩個客戶端程序mul_port_client_epoll去連接服務器效果如下:左側服務端持續(xù)接收客戶端的連接右側兩個客戶端向服務端發(fā)起連接,但是當兩個客戶端都連接到64999的時候,客戶端不動了,阻塞了(程序沒有報錯,也沒有退出)左側服務端也跟著阻塞了(程序沒有報錯,也沒有退出)過了一小會之后,客戶端顯示連接超時,然后程序退出

原因分析

nf_conntrack_max限制了每一個程序可以建立的TCP的連接的最大數目(是連接數目,而不是套接字描述符的個數)

解決方案

我們只需要修改客戶端的內核參數nf_conntrack_max就可以,可以將其修改為1048576第一步:nf_conntrack_max參數默認是不開啟的,需要先加載下面的內核模塊,加載完成之后其值默認為65536

sudo modprobe ip_conntrack

第二步:然后在客戶端的/etc/sysctl.conf配置文件中將nf_conntrack_max設置為1048576第三步:調用sysctl命令將/etc/sysctl.conf配置文件的內容更新到/proc/sys/net/nf_conntrack_max中

備注一下

與第3次測試類似,在實際過程中也不會出現這個問題,因為不可能有一個客戶端程序會發(fā)起上萬個連接,因為此處我們是測試,所以程序要發(fā)起上萬個測試,才修改這個參數修改完成之后進行第4次測試六、第4次測試

測試如下

左側運行服務端程序reactor(111.229.177.161:8888),右側運行兩個客戶端程序mul_port_client_epoll去連接服務器效果如下:左側服務端持續(xù)接收客戶端的連接右側兩個客戶端向服務端發(fā)起連接,但是當兩個客戶端都連接到140999個的時候,客戶端顯示無法分配地址,從而程序退出左側服務端沒有報錯,只是沒有客戶端再連接進來了

原因分析

在第2次測試中,我們讓服務端監(jiān)聽在5個端口上,這次錯誤的原因可能是因為所有分配端口可用的地址都用完了

解決方案

調整服務端reactor.c程序的LISTEN_PORT_COUNT宏,使其監(jiān)聽在更多的端口上,這樣就可以承載更多的客戶端。例如,此處我們將LISTEN_PORT_COUNT宏調整為50

#define LISTEN_PORT_COUNT 50

相應的,客戶端mul_port_client_epoll.c中的MAX_PORT也要調整為50,因為要連接50種服務端

#define MAX_PORT 50

修改完成之后進行下面第5次測試七、第5次測試

測試如下

左側運行服務端程序reactor(111.229.177.161:8888),右側運行兩個客戶端程序mul_port_client_epoll去連接服務器效果如下:左側服務端持續(xù)接收客戶端的連接右側兩個客戶端向服務端發(fā)起連接,每個客戶端都連接二十多萬的時候程序阻塞(加起來就是五十多萬)左側服務端接收到五十多萬客戶端的時候就阻塞了

原因分析

在第1次測試的時候,我們將服務端程序reactor.c中的MAX_EPOLL_EVENTS設置為1024*512=524288個,此處可以看到剛好服務端在接收到五十多萬客戶端的時候阻塞。因此分析應該是MAX_EPOLL_EVENTS宏達到上限也就是epoll_wait()處理的事件數組達到上限

解決方案

增加reactor.c中的MAX_EPOLL_EVENTS,使其可以處理的epoll事件數組變多,例如修改為1024*1024=1048576個因為第1次測試的時候,我們的MAX_EPOLL_EVENTS設置不能過大,如果過大程序運行時會顯示內存不足,現在想到一個辦法,那就是使用posix_memalign()函數來創(chuàng)建epoll的事件數組,posix_memalign()函數是專門用來分配大內存的修改完成之后下面進行第6次測試八、第6次測試下面我們不再測試了,也不再尋找客戶端或服務端的錯誤,而是來分析一下如何使客戶端更多的去連接服務端在前面的測試中,我們的幾臺機器都是在局域網內的,每連接1000個客戶端約耗時3秒。如果是在局域網之外,那么耗時更長

一種加快連接的思路

服務是調用accept()函數來接收客戶端的連接的,因此如果想要加快客戶端的連接,那么可以在accept()函數上下手提供的思路有:1.多個accept()放在一個線程中2.多個accept()分配在不同的線程3.多個accept()分配在不同的進程(Nginx為多進程服務器,它就是這樣做的,每個進程都有自己獨立的資源)為什么多進程比多線程好:1.多進程不需要加鎖2.多進程可以承載比多線程更多的fd,因為每個進程都有自己一份獨立的資源九、總結附加

用戶態(tài)協(xié)議棧設計

在服務器承載百萬客戶端的時候,這時候會有大量的數據在進行交互。對于默認的應用程序來說,其數據的傳輸都要經過協(xié)議棧緩沖區(qū),如下圖所示:客戶端發(fā)送數據到服務端時,數據先達到網卡,然后將數據拷貝到協(xié)議棧中,最后再把數據從協(xié)議棧中拷貝到服務端程序中服務端回送數據時是相反的順序,先把數據從服務端程序拷貝到協(xié)議棧,然后再把數據拷貝到網卡發(fā)送給客戶端從上圖可以了解,當服務端與百萬客戶端數據交互的時候會帶來下面的弊端:數據要進行大量的拷貝,從而造成程序的性能降低數據交互速度受限與緩慢用戶態(tài)協(xié)議棧設計:通過上面我們知道,數據要經過一層協(xié)議棧,會對性能帶來影響,因此可以自己設計“用戶態(tài)協(xié)議棧”,大致原理如下:數據交互時不再經過協(xié)議棧,將網卡與服務端的數據mmap映射到內存中,直接從內存中進行交互,從而減少了中間的拷貝這就是常說的“零拷貝”

以上就是關于初始化pos機參數,百萬并發(fā)服務器系統(tǒng)參數調優(yōu)的知識,后面我們會繼續(xù)為大家整理關于初始化pos機參數的知識,希望能夠幫助到大家!

轉發(fā)請帶上網址:http://www.shbwcl.net/newsone/96943.html
上一篇:pos機借記卡 下一篇:一般店鋪pos機刷卡

你可能會喜歡:

版權聲明:本文內容由互聯(lián)網用戶自發(fā)貢獻,該文觀點僅代表作者本人。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。如發(fā)現本站有涉嫌抄襲侵權/違法違規(guī)的內容, 請發(fā)送郵件至 babsan@163.com 舉報,一經查實,本站將立刻刪除。