女人自慰AV免费观看内涵网,日韩国产剧情在线观看网址,神马电影网特片网,最新一级电影欧美,在线观看亚洲欧美日韩,黄色视频在线播放免费观看,ABO涨奶期羡澄,第一导航fulione,美女主播操b

0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

服務(wù)器并發(fā)的概念

科技綠洲 ? 來源:Linux開發(fā)架構(gòu)之路 ? 作者:Linux開發(fā)架構(gòu)之路 ? 2023-11-10 10:05 ? 次閱讀

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

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

對(duì)于服務(wù)器并發(fā)的概念,下面幾點(diǎn)是錯(cuò)誤的定義

  • ①服務(wù)器處理客戶端請(qǐng)求的數(shù)量:沒有時(shí)間、空間等限制,因此不能作為并發(fā)的概念
  • ②單位時(shí)間內(nèi),能夠處理請(qǐng)求的數(shù)量:這也是不正確的定義,因?yàn)檫@個(gè)定義是針對(duì)于服務(wù)器吞吐量(qps)的,而不是并發(fā)
  • 其他等......

下面幾點(diǎn)組成在一起可以作為服務(wù)器“并發(fā)”的概念:

  • ①服務(wù)器能夠同時(shí)承擔(dān)的客戶端數(shù)量(最基本要求)
  • ②能處理指定數(shù)量以上的相應(yīng)請(qǐng)求
  • ③能夠?qū)?shù)據(jù)庫進(jìn)行操作
  • ④有磁盤的操作
  • CPU的占用率最好不要超過60%
  • ⑥內(nèi)存占用率最好不要超過80%

二、本文環(huán)境搭建

本文準(zhǔn)備了兩份代碼,作為測(cè)試環(huán)境:

  • reactor.c:作為服務(wù)端,采用單進(jìn)程reactor模式編寫,持續(xù)接收客戶端的連接,并且與客戶端有數(shù)據(jù)的讀寫(recv()、send())
  • mul_port_client_epoll.c:作為客戶端,會(huì)向reactor.c服務(wù)端發(fā)起不超過340000的客戶端連接,并且每個(gè)客戶端都會(huì)與服務(wù)端有數(shù)據(jù)的讀寫(recv()、send())

reactor.c

Github源碼鏈接參閱:csdn-code/reactor.c at master · dongyusheng/csdn-code · GitHub

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

#define BUFFER_LENGTH 4096
#define MAX_EPOLL_EVENTS 1024
#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]n", 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] = '?';

printf("C[%d]:%sn", 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], closedn", fd, ev-reactor->events);

} else {
//if(errno == EAGAIN || errno == EWOULDBLOCK)
// continue;
return 0;

close(ev->fd);
printf("recv[fd=%d] error[%d]:%sn", 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]%sn", 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 %sn", 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: %sn", 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]n", __func__, MAX_EPOLL_EVENTS);
break;
}

int flag = 0;
if ((flag = fcntl(clientfd, F_SETFL, O_NONBLOCK)) < 0) {
printf("%s: fcntl nonblocking failed, %dn", __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]n",
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 : %sn", 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 %sn", __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 %sn", __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] timeoutn", 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, exitn");
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
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include

#define MAX_BUFFER 128
#define MAX_EPOLLSIZE (384*1024)
#define MAX_PORT 1

#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 portn", argv[0]);
exit(0);
}

// 獲取要連接的服務(wù)端的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 MulClientn");

// 初始化服務(wù)器地址
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;

// 如果連接數(shù)小于340000,繼續(xù)連接服務(wù)器
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);

// 連接服務(wù)器
if (connect(sockfd, (struct sockaddr*)&addr, sizeof(struct sockaddr_in)) < 0) {
perror("connect");
goto err;
}

ntySetNonblock(sockfd); // 將套接字設(shè)置為非阻塞
ntySetReUseAddr(sockfd); // 設(shè)置可重用本地地址

// 向服務(wù)器發(fā)送數(shù)據(jù)
sprintf(buffer, "Hello Server: client --> %dn", connections);
send(sockfd, buffer, strlen(buffer), 0);

// 將套接字設(shè)置為可讀可寫,然后加入到epoll_wait()中
ev.data.fd = sockfd;
ev.events = EPOLLIN | EPOLLOUT;
epoll_ctl(epoll_fd, EPOLL_CTL_ADD, sockfd, &ev);

connections ++;
}

// 如果每連接了一千個(gè)客戶端或者連接數(shù)超過340000,那么就執(zhí)行這個(gè)條件
if (connections % 1000 == 999 || connections >= 340000)
{
struct timeval tv_cur;
memcpy(&tv_cur, &tv_begin, sizeof(struct timeval));

gettimeofday(&tv_begin, NULL);

// 打印一下每連接1000個(gè)客戶端所消耗的時(shí)間
int time_used = TIME_SUB_MS(tv_begin, tv_cur);
printf("connections: %d, sockfd:%d, time_used:%dn", connections, sockfd, time_used);

// 進(jìn)行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 %dn", 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:%sn", rBuffer);

if (!strcmp(rBuffer, "quit")) {
isContinue = 0;
}

} else if (length == 0) {
printf(" Disconnect clientfd:%dn", clientfd);
connections --;
close(clientfd);
} else {
if (errno == EINTR) continue;

printf(" Error clientfd:%d, errno:%dn", clientfd, errno);
close(clientfd);
}
} else {
printf(" clientfd:%d, errno:%dn", clientfd, errno);
close(clientfd);
}
}
}

// 休眠1000微秒(0.01秒)
usleep(1 * 1000);
}
return 0;
err:
printf("error : %sn", strerror(errno));
return 0;
}

三、第1次測(cè)試

測(cè)試如下

左側(cè)運(yùn)行服務(wù)端程序reactor(111.229.177.161:8888),右側(cè)運(yùn)行客戶端程序mul_port_client_epoll去連接服務(wù)器

效果如下:

  • 左側(cè)服務(wù)端接收了1021個(gè)客戶端(除去描述符0、1、2)之后程序報(bào)錯(cuò),顯示無法繼續(xù)接收客戶端的連接
  • 右側(cè)客戶端也報(bào)錯(cuò),無法繼續(xù)連接服務(wù)端

圖片

原因分析

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

圖片

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

第一步:

  • 通過ulimit命令或者修改/etc/security/limits.conf配置文件,修改用戶可打開的最大文件描述符個(gè)數(shù)。由于ulimit命令修改的結(jié)果不能永久保存,因此我們此處修改/etc/security/limits.conf配置文件
  • (下面圖1)下面修改/etc/security/limits.conf,將所有用戶可打開的文件描述符個(gè)數(shù)修改為1048576(1024*1024)
  • (下面圖2)修改完成之后退出當(dāng)前會(huì)話(不必重啟),然后重新開啟會(huì)話,通過ulinit命令可以看到修改成功
  • 然后再把客戶端的機(jī)器也全部修改一下,別忘記了

圖片

圖片

第二步:

(下面圖1)上面我們雖然修改了/etc/security/limits.conf,但是該配置文件約束的是“用戶級(jí)別”的系統(tǒng)限制。還有兩個(gè)“內(nèi)核級(jí)別”的配置參數(shù)與文件描述符有關(guān)。一個(gè)為nr_open(表示單個(gè)進(jìn)程打開文件句柄數(shù)上限),另一個(gè)為file-max(表示系統(tǒng)范圍內(nèi)所有進(jìn)程可打開的文件句柄的數(shù)量限制)

  • 其中nr_open默認(rèn)為1048576,我們就不需要修改了
  • file-max默認(rèn)為180566,因此我們需要將其修改為1048576

(下面圖2)修改/etc/sysctl.conf文件,在里面修改file-max的值為1048576,然后保存退出

(下面圖3)執(zhí)行sysctl的命令,將/etc/sysctl.conf文件的內(nèi)容更新到/proc/sys/fs/file-max中生效,令執(zhí)行完之后查看/proc/sys/fs/file-max文件,更新成功

然后再把客戶端的機(jī)器也全部修改一下,別忘記了

圖片

圖片

圖片

第三步:

上面我們將進(jìn)程可分配描述符數(shù)量都增大了,那么還需要修改服務(wù)端程序reactor.c中的MAX_EPOLL_EVENTS宏,因?yàn)槠浯淼氖莈poll_wait()可以處理事件的數(shù)量,因?yàn)榭蛻舳藬?shù)量增加了,因此該宏也要增加

下面我們將該宏設(shè)置為1024*512=524288(由于本人機(jī)器內(nèi)存不足,只能修改這個(gè)大,如果修改再大程序就運(yùn)行不了,因?yàn)闊o法為程序分配更多的內(nèi)存。如果你的機(jī)器內(nèi)存夠大,那么可以將這個(gè)數(shù)值再往上調(diào);如果你的機(jī)器內(nèi)存不足,那么就將這個(gè)值調(diào)小)

修改完成之后重新編譯reactor.c

#define MAX_EPOLL_EVENTS (1024 * 512)

圖片

上面所有的內(nèi)容修改完成之后,進(jìn)入下面的第2次測(cè)試

四、第2次測(cè)試

測(cè)試如下

左側(cè)運(yùn)行服務(wù)端程序reactor(111.229.177.161:8888),右側(cè)運(yùn)行兩個(gè)客戶端程序mul_port_client_epoll去連接服務(wù)器

效果如下:

  • 左側(cè)服務(wù)端持續(xù)接收客戶端的連接
  • 右側(cè)兩個(gè)客戶端向服務(wù)端發(fā)起連接,其中一個(gè)客戶端只連接了兩萬多個(gè)就報(bào)錯(cuò)退出了,另一個(gè)客戶端只連接了一萬多個(gè)就報(bào)錯(cuò)退出了
  • 左側(cè)服務(wù)端沒有報(bào)錯(cuò),只是沒有客戶端再連接進(jìn)來了

圖片

圖片

原因分析

此處要介紹“五元組”的概念了,這個(gè)概念在下面的幾次測(cè)試中都要用到

一個(gè)套接字fd就對(duì)應(yīng)一個(gè)“五元組”,一個(gè)五元組包含下面幾項(xiàng)內(nèi)容:

  • 1.源IP地址
  • 2.目的IP地址
  • 3.源端口
  • 4.目的端口
  • 5.協(xié)議類型(TCP、UDP等)

圖片

因此,我們上面的兩個(gè)客戶端加起來最多只能創(chuàng)建四萬個(gè)套接字就報(bào)錯(cuò)的原因是:

  • 源IP地址、目的IP地址、協(xié)議類型(TCP)這三者都保持不變、目的端口只有一個(gè)也保持不變,唯一可以變化的就是源端口(也就是客戶端的端口)
  • 我們知道,一個(gè)系統(tǒng)的端口最多只能有65535個(gè),其中有一些已經(jīng)被其他服務(wù)使用了,因此客戶端可以使用的端口大概只有四五萬個(gè),所以客戶端也就只能創(chuàng)建四五萬個(gè)套接字,所以上面我們兩個(gè)客戶端總共加起來只創(chuàng)建了四五萬個(gè)套接字就不能再繼續(xù)創(chuàng)建套接字了
  • 概括來說,你能有多少種“五元組”類型,那么你就能建立多少個(gè)套接字連接

圖片

解決方案

通過上面的分析我們知道,主要的限制原因就在端口的限制上,因此下面我們讓服務(wù)端程序監(jiān)聽在多個(gè)端口上,這樣一來可以使用的“五元組”組合就可以更多,那么我們可以創(chuàng)建的套接字fd也就更多

圖片

第一步:

  • 修改服務(wù)端程序reactor.c,使其監(jiān)聽在5個(gè)端口上,也就是啟動(dòng)了5個(gè)Tcp Server(隨意多少個(gè),只要端口多了,就能承載更多的客戶端,此處我們就只設(shè)置為5個(gè))
  • 代碼變化不多,只是新增了一個(gè)LISTEN_PORT_COUNT宏,然后修改了main()函數(shù)使其監(jiān)聽在5個(gè)端口上,其余代碼全部不變
  • 修改完成之后重新編譯
// reactor.c其余代碼不變, 只需要修改下面的內(nèi)容即可
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include

#define BUFFER_LENGTH 4096
#define MAX_EPOLL_EVENTS 1024
#define SERVER_PORT 8888

// 新增一個(gè)宏, 表示監(jiān)聽的端口數(shù)量
#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]n", 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] = '?';

printf("C[%d]:%sn", 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], closedn", fd, ev-reactor->events);

} else {
//if(errno == EAGAIN || errno == EWOULDBLOCK)
// continue;
return 0;

close(ev->fd);
printf("recv[fd=%d] error[%d]:%sn", 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]%sn", 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 %sn", 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: %sn", 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]n", __func__, MAX_EPOLL_EVENTS);
break;
}

int flag = 0;
if ((flag = fcntl(clientfd, F_SETFL, O_NONBLOCK)) < 0) {
printf("%s: fcntl nonblocking failed, %dn", __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]n",
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 : %sn", 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 %sn", __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 %sn", __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] timeoutn", 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, exitn");
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即可,修改完成之后,其就會(huì)向5個(gè)服務(wù)端都發(fā)起連接
  • 修改完成之后重新編譯
#define MAX_PORT 5

圖片

備注(必看)

  • 此處出錯(cuò)的場(chǎng)景在實(shí)際開發(fā)中是不會(huì)出現(xiàn)的,因?yàn)樵趯?shí)際生活中,不會(huì)出現(xiàn)一個(gè)系統(tǒng)中有上萬的客戶端(IP都一致),因此出現(xiàn)了這種“五元組”組合用盡的情況
  • 在實(shí)際中,每個(gè)客戶端的IP地址都是不同的,因此它們的“五元組”類型都是不同的,因此就不會(huì)出現(xiàn)這種因?yàn)槎丝诒挥猛陱亩鴮?dǎo)致“五元組”組合被用盡的情況

修改完成之后進(jìn)行下面的第3次測(cè)試

五、第3次測(cè)試

測(cè)試如下

左側(cè)運(yùn)行服務(wù)端程序reactor(111.229.177.161:8888),右側(cè)運(yùn)行兩個(gè)客戶端程序mul_port_client_epoll去連接服務(wù)器

效果如下:

  • 左側(cè)服務(wù)端持續(xù)接收客戶端的連接
  • 右側(cè)兩個(gè)客戶端向服務(wù)端發(fā)起連接,但是當(dāng)兩個(gè)客戶端都連接到64999的時(shí)候,客戶端不動(dòng)了,阻塞了(程序沒有報(bào)錯(cuò),也沒有退出)
  • 左側(cè)服務(wù)端也跟著阻塞了(程序沒有報(bào)錯(cuò),也沒有退出)
  • 過了一小會(huì)之后,客戶端顯示連接超時(shí),然后程序退出

圖片

圖片

原因分析

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

解決方案

我們只需要修改客戶端的內(nèi)核參數(shù)nf_conntrack_max就可以,可以將其修改為1048576

第一步:nf_conntrack_max參數(shù)默認(rèn)是不開啟的,需要先加載下面的內(nèi)核模塊,加載完成之后其值默認(rèn)為65536

sudo modprobe ip_conntrack

圖片

第二步:然后在客戶端的/etc/sysctl.conf配置文件中將nf_conntrack_max設(shè)置為1048576

圖片

第三步:調(diào)用sysctl命令將/etc/sysctl.conf配置文件的內(nèi)容更新到/proc/sys/net/nf_conntrack_max中

圖片

備注一下

  • 與第3次測(cè)試類似,在實(shí)際過程中也不會(huì)出現(xiàn)這個(gè)問題,因?yàn)椴豢赡苡幸粋€(gè)客戶端程序會(huì)發(fā)起上完個(gè)連接,因?yàn)榇颂幬覀兪菧y(cè)試,所以程序要發(fā)起上萬個(gè)測(cè)試,才修改這個(gè)參數(shù)

修改完成之后進(jìn)行第4次測(cè)試

六、第4次測(cè)試

測(cè)試如下

左側(cè)運(yùn)行服務(wù)端程序reactor(111.229.177.161:8888),右側(cè)運(yùn)行兩個(gè)客戶端程序mul_port_client_epoll去連接服務(wù)器

效果如下:

  • 左側(cè)服務(wù)端持續(xù)接收客戶端的連接
  • 右側(cè)兩個(gè)客戶端向服務(wù)端發(fā)起連接,但是當(dāng)兩個(gè)客戶端都連接到140999個(gè)的時(shí)候,客戶端顯示無法分配地址,從而程序退出
  • 左側(cè)服務(wù)端沒有報(bào)錯(cuò),只是沒有客戶端再連接進(jìn)來了

圖片

原因分析

在第2次測(cè)試中,我們讓服務(wù)端監(jiān)聽在5個(gè)端口上,這次錯(cuò)誤的原因可能是因?yàn)樗蟹峙涠丝诳捎玫牡刂范加猛炅?/p>

解決方案

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

#define LISTEN_PORT_COUNT 50

相應(yīng)的,客戶端mul_port_client_epoll.c中的MAX_PORT也要調(diào)整為50,因?yàn)橐B接50種服務(wù)端

#define MAX_PORT 50

修改完成之后進(jìn)行下面第5次測(cè)試

七、第5次測(cè)試

測(cè)試如下

左側(cè)運(yùn)行服務(wù)端程序reactor(111.229.177.161:8888),右側(cè)運(yùn)行兩個(gè)客戶端程序mul_port_client_epoll去連接服務(wù)器

效果如下:

  • 左側(cè)服務(wù)端持續(xù)接收客戶端的連接
  • 右側(cè)兩個(gè)客戶端向服務(wù)端發(fā)起連接,每個(gè)客戶端都連接二十多萬的時(shí)候程序阻塞(加起來就是五十多萬)
  • 左側(cè)服務(wù)端接收到五十多萬客戶端的時(shí)候就阻塞了

圖片

原因分析

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

解決方案

  • 增加reactor.c中的MAX_EPOLL_EVENTS,使其可以處理的epoll事件數(shù)組變多,例如修改為1024*1024=1048576個(gè)
  • 因?yàn)榈?次測(cè)試的時(shí)候,我們的MAX_EPOLL_EVENTS設(shè)置不能過大,如果過大程序運(yùn)行時(shí)會(huì)顯示內(nèi)存不足,現(xiàn)在想到一個(gè)辦法,那就是使用posix_memalign()函數(shù)來創(chuàng)建epoll的事件數(shù)組,posix_memalign()函數(shù)是專門用來分配大內(nèi)存的

修改完成之后下面進(jìn)行第6此測(cè)試

八、第6次測(cè)試

下面我們不再測(cè)試了,也不再尋找客戶端或服務(wù)端的錯(cuò)誤,而是來分析一下如何使客戶端更多的去連接服務(wù)端

在前面的測(cè)試中,我們的幾臺(tái)機(jī)器都是在局域網(wǎng)內(nèi)的,每連接1000個(gè)客戶端約耗時(shí)3秒。如果是在局域網(wǎng)之外,那么耗時(shí)更長(zhǎng)

一種加快連接的思路

服務(wù)是調(diào)用accept()函數(shù)來接收客戶端的連接的,因此如果想要加快客戶端的連接,那么可以在accept()函數(shù)上下手

提供的思路有:

1.多個(gè)accept()放在一個(gè)線程中

2.多個(gè)accept()分配在不同的線程

3.多個(gè)accept()分配在不同的進(jìn)程(Nginx為多進(jìn)程服務(wù)器,它就是這樣做的,每個(gè)進(jìn)程都有自己獨(dú)立的資源)

為什么多進(jìn)程比多線程好:

1.多進(jìn)程不需要加鎖

2.多進(jìn)程可以承載比多線程更多的fd,因?yàn)槊總€(gè)進(jìn)程都有自己一份獨(dú)立的資源

九、總結(jié)附加

用戶態(tài)協(xié)議棧設(shè)計(jì)

在服務(wù)器承載百萬客戶端的時(shí)候,這時(shí)候會(huì)有大量的數(shù)據(jù)在進(jìn)行交互。對(duì)于默認(rèn)的應(yīng)用程序來說,其數(shù)據(jù)的傳輸都要經(jīng)過協(xié)議棧緩沖區(qū),如下圖所示:

  • 客戶端發(fā)送數(shù)據(jù)到服務(wù)端時(shí),數(shù)據(jù)先達(dá)到網(wǎng)卡,然后將數(shù)據(jù)拷貝到協(xié)議棧中,最后再把數(shù)據(jù)從協(xié)議棧中拷貝到服務(wù)端程序中
  • 服務(wù)端回送數(shù)據(jù)時(shí)是相反的順序,先把數(shù)據(jù)從服務(wù)端程序拷貝到協(xié)議棧,然后再把數(shù)據(jù)拷貝到網(wǎng)卡發(fā)送給客戶端

圖片

從上圖可以了解,當(dāng)服務(wù)端與百萬客戶端數(shù)據(jù)交互的時(shí)候會(huì)帶來下面的弊端:

  • 數(shù)據(jù)要進(jìn)行大量的拷貝,從而造成程序的性能降低
  • 數(shù)據(jù)交互速度受限與緩慢

用戶態(tài)協(xié)議棧設(shè)計(jì):通過上面我們知道,數(shù)據(jù)要經(jīng)過一層協(xié)議棧,會(huì)對(duì)性能帶來影響,因此可以自己設(shè)計(jì)“用戶態(tài)協(xié)議棧”,大致原理如下:

  • 數(shù)據(jù)交互時(shí)不再經(jīng)過協(xié)議棧,將網(wǎng)卡與服務(wù)端的數(shù)據(jù)mmap映射到內(nèi)存中,直接從內(nèi)存中進(jìn)行交互,從而減少了中間的拷貝
  • 這就是常說的“零拷貝”

圖片

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • 服務(wù)器
    +關(guān)注

    關(guān)注

    12

    文章

    9681

    瀏覽量

    87267
  • 參數(shù)
    +關(guān)注

    關(guān)注

    11

    文章

    1866

    瀏覽量

    32858
  • 磁盤
    +關(guān)注

    關(guān)注

    1

    文章

    388

    瀏覽量

    25645
  • 程序
    +關(guān)注

    關(guān)注

    117

    文章

    3820

    瀏覽量

    82390
收藏 人收藏

    評(píng)論

    相關(guān)推薦
    熱點(diǎn)推薦

    我國首款億級(jí)并發(fā)服務(wù)器系統(tǒng)實(shí)現(xiàn)量產(chǎn)

    我國高性能計(jì)算領(lǐng)軍企業(yè)中科曙光29日在天津宣布,曙光星河云服務(wù)器系統(tǒng)正式量產(chǎn)。這是我國首款億級(jí)并發(fā)服務(wù)器系統(tǒng),也是“十二五”期間國家863計(jì)劃信息技術(shù)領(lǐng)域“億級(jí)并發(fā)
    發(fā)表于 11-30 15:47 ?892次閱讀

    基于Select/Poll實(shí)現(xiàn)并發(fā)服務(wù)器(一)

    LWIP:2.0.2 ? 并發(fā)服務(wù)器支持多個(gè)客戶端的同時(shí)連接,最大可接入的客戶端數(shù)取決于內(nèi)核控制塊的個(gè)數(shù)。當(dāng)使用Socket API時(shí),要使服務(wù)器能夠同時(shí)支持多個(gè)客戶端的連接,必須引入多任務(wù)機(jī)制,為每個(gè)
    的頭像 發(fā)表于 06-20 00:20 ?4154次閱讀
    基于Select/Poll實(shí)現(xiàn)<b class='flag-5'>并發(fā)</b><b class='flag-5'>服務(wù)器</b>(一)

    網(wǎng)絡(luò)視頻服務(wù)器概念及其構(gòu)成

      網(wǎng)絡(luò)視頻服務(wù)器概念及其構(gòu)成 視頻服務(wù)器     隨著音視頻編碼技術(shù)的不斷發(fā)展和寬帶網(wǎng)絡(luò)技術(shù)的發(fā)展,視頻
    發(fā)表于 08-22 14:34

    在DragonBoard 410c上實(shí)現(xiàn)高并發(fā)處理TCP服務(wù)器

    服務(wù),讓傳感和相關(guān)的控制設(shè)備接入,為此,本期blog將向大家介紹如何使用gevent高性能的并發(fā)處理庫在draognbaord 410c上來實(shí)現(xiàn)一個(gè)高性能的TCP服務(wù)器。gevent
    發(fā)表于 09-25 15:53

    高性能高并發(fā)服務(wù)器架構(gòu)分享

    由于自己正在做一個(gè)高性能大用戶量的論壇程序,對(duì)高性能高并發(fā)服務(wù)器架構(gòu)比較感興趣,于是在網(wǎng)上收集了不少這方面的資料和大家分享。希望能和大家交流 msn: ——————————————————————————————————————— ? 初創(chuàng)網(wǎng)站與開源軟件 6 ? 談?wù)劥笮?/div>
    發(fā)表于 09-16 06:45

    如何利用多線程去構(gòu)建一種TCP并發(fā)服務(wù)器

    一、實(shí)驗(yàn)?zāi)康暮鸵?了解TCP/IP協(xié)議2掌握Socket編程,熟悉基于TCP和UDP的傳輸模型3掌握多線程編程4掌握基于TCP的并發(fā)服務(wù)器設(shè)計(jì)二、實(shí)驗(yàn)內(nèi)容和原理實(shí)驗(yàn)內(nèi)容:編寫C程序,利用多線程構(gòu)建
    發(fā)表于 12-22 08:03

    Linux環(huán)境并發(fā)服務(wù)器設(shè)計(jì)技術(shù)研究

    講述并發(fā)服務(wù)器設(shè)計(jì)的主要技術(shù),包括多進(jìn)程服務(wù)器、多線程服務(wù)器和I/ O 復(fù)用服務(wù)器,同時(shí)對(duì)以上服務(wù)器
    發(fā)表于 04-24 10:02 ?16次下載

    服務(wù)器概念及作用介紹

    服務(wù)器是指具有固定的地址,并為網(wǎng)絡(luò)用戶提供服務(wù)的節(jié)點(diǎn),它是實(shí)現(xiàn)資源共享的重要組成部分,服務(wù)器主要有網(wǎng)絡(luò)服務(wù)器、打印服務(wù)器、終端
    的頭像 發(fā)表于 10-15 14:09 ?6292次閱讀

    阿里云2核4G服務(wù)器租賃的并發(fā)怎樣算

    阿里云2核4G服務(wù)器租賃的并發(fā)怎樣算?我們要知道我們租用的服務(wù)器能支持多少人同時(shí)訪問,并發(fā)數(shù)是一個(gè)很重要的參考值。很多人不了解服務(wù)器
    的頭像 發(fā)表于 07-07 17:19 ?2152次閱讀

    單臺(tái)服務(wù)器支持的TCP并發(fā)連接數(shù)

    總之,65535只是Linux系統(tǒng)中可使用端口port數(shù)量的上限,端口port數(shù)量與TCP連接數(shù)量并非完全一一對(duì)應(yīng)的關(guān)系,服務(wù)器支持的TCP并發(fā)連接數(shù)量主要跟服務(wù)器的內(nèi)存以及允許單個(gè)進(jìn)程同時(shí)打開
    的頭像 發(fā)表于 11-06 19:36 ?1930次閱讀

    服務(wù)器的高并發(fā)能力如何提升?

    服務(wù)器的高并發(fā)能力如何提升? 服務(wù)器并發(fā)能力體現(xiàn)著服務(wù)器在單位時(shí)間內(nèi)的很強(qiáng)數(shù)據(jù)處理能力,一般來說,如果企業(yè)的互聯(lián)網(wǎng)業(yè)務(wù)需要面對(duì)大量的同時(shí)在
    的頭像 發(fā)表于 03-17 17:07 ?1212次閱讀

    存儲(chǔ)服務(wù)器在高并發(fā)環(huán)境下該如何優(yōu)化?

    在這個(gè)信息化時(shí)代,每時(shí)每刻都有人在訪問數(shù)據(jù),這就造成了存儲(chǔ)服務(wù)器的高并發(fā),使得存儲(chǔ)服務(wù)器工作的效率變低。
    的頭像 發(fā)表于 03-27 15:49 ?791次閱讀

    網(wǎng)站服務(wù)器并發(fā)數(shù)的計(jì)算方法是什么?

    并發(fā)數(shù)也就是指同時(shí)訪問服務(wù)器站點(diǎn)的連接數(shù),所以站長(zhǎng)為了后期避免主機(jī)服務(wù)器等資源出現(xiàn)過剩浪費(fèi)及資源不足等問題的出現(xiàn),都會(huì)對(duì)服務(wù)器并發(fā)數(shù)進(jìn)行計(jì)
    的頭像 發(fā)表于 04-12 15:22 ?3808次閱讀

    并發(fā)服務(wù)器的設(shè)計(jì)與實(shí)現(xiàn)

    并發(fā)服務(wù)器支持多個(gè)客戶端的連接,最大可接入的客戶端數(shù)取決于內(nèi)核控制塊的個(gè)數(shù)。當(dāng)使用Socket API時(shí),要使服務(wù)器能夠同時(shí)支持多個(gè)客戶端的連接,必須引入多任務(wù)機(jī)制,為每個(gè)連接創(chuàng)建一個(gè)單獨(dú)的任務(wù)來處理連接上的數(shù)據(jù),我們將這個(gè)設(shè)計(jì)
    的頭像 發(fā)表于 04-25 15:35 ?1068次閱讀
    <b class='flag-5'>并發(fā)</b><b class='flag-5'>服務(wù)器</b>的設(shè)計(jì)與實(shí)現(xiàn)

    RAKsmart服務(wù)器如何重塑AI高并發(fā)算力格局

    在AI大模型參數(shù)量突破萬億級(jí)、實(shí)時(shí)推理需求激增的當(dāng)下,傳統(tǒng)服務(wù)器架構(gòu)的并發(fā)處理能力已逼近物理極限。RAKsmart通過“硬件重構(gòu)+軟件定義”的雙引擎創(chuàng)新,推出新一代AI服務(wù)器解決方案。下面,AI部落小編為您解析RAKsmart
    的頭像 發(fā)表于 04-03 10:37 ?234次閱讀