【c1000k,单机百万并发测试】

c1000k,单机百万并发测试

使用epoll实现的reactor反应堆服务端,开启一个进程多个端口,然后使用多个客户端 ip 多端口来连接

​ 1、在16g内存的笔记本上

​ 2、用四台虚拟机(一台服务器 三台客户端)

​ 3、使用epoll写的reactor反应堆服务端,源码在最下面

​ 4、多线程的客户端,源码在最下面

​ 并发:一个服务器能够同时承载客户端的数量

​ 服务器能同时建立连接的数量 是并发的基础

​ 承载 100w 在200ms内返回

​ 这里回声服务器 没有下列多余影响

​ 1、数据库 2、网络带宽 3、内存操作 4、日志

一、一个连接的定义

1.1 服务器理论最大并发数

​ 一个连接包含五元组,源ip(sip)、目的ip(dip)源端口(sport)、目的端、(dport) 协议

2 的 32 次方(源ip数)× 2的 16 次方(源port数)× 2 的 32 次方(目的ip数)× 2的 16 次方(目的port数)大约等于四百多亿亿亿

(不过每条连接都会消耗服内存,实践中绝不可能达到这个理论数字。)

1.2 测试实验配置

这里使用一个服务端ip * 100服务端端口 * 60000客户端端口 * 3 个客户端 理论上可以达到200w连接 奈何笔记本内存不够

sip 多个客户端 这里采用三个虚拟机(三个ip)

dip 服务器IP只有一个 使用一个虚拟机(一个ip)

sport 客户端数量 使用60000个端口

dport 服务的端口 使用100个端口

proto tcp 使用tcp连接

1.3 linux系统的连接相关默认配置

看看ubuntu20.04的默认配置

root@luo:~# ulimit -n
1024
root@luo:~# sysctl -a |grep mem
...
net.ipv4.tcp_mem = 92880	123843	185760 # 92880* 4k 300m 500m 700m 当协议栈占用空间500m时优化 大于700m时不再分配
net.ipv4.tcp_rmem = 4096	131072	6291456
net.ipv4.tcp_wmem = 4096	16384	4194304
...
net.ipv4.ip_local_port_range = 32768	60999
fs.file-max = 9223372036854775807
net.nf_conntrack_max = 65536

1.4下面解释配置的作用

1、net.ipv4.tcp_mem TCP使用了内存页面数

​ net.ipv4.tcp_rmem 为TCP socket预留用于接收缓冲的内存
​ net.ipv4.tcp_wmem 为TCP socket预留用于发送缓冲的内存

​ tcp_wmem tcp_rmem服务器传输大文件就调大 传输字符 调小(但可能ssh 都会很慢) 一个fd就是tcp_wmem tcp_rmem所占用空间 使用更少的内存

2、ulimit -n 和 net.nf_conntrack_max fd 限制 ulimit 限制fd 数量

​ 默认单个进程打开的fd为1000多个 可以修改ulimit -n 1048576(临时修改) 或 /etc/security/limits.conf(永久修改)

accept: Too many open files

3、net.ipv4.ip_local_port_range 默认用户只能用30000以上的端口建立连接 要自己改

​ 建立连接时 服务端端口从三万多开始挨个遍历 找到一个没有被占用的

connections: 27999, sockfd:28002, time_used:3193
connect: Cannot assign requested address
error : Cannot assign requested address

4、net.nf_conntrack_max 服务端建立连接的syn连接数

​ iptables基于netfilter的应用程序 iptables会调用netfilrer的接口

​ 报文sk_buff从网卡上到达协议栈时会经过netfilter

conntion timeout

二、实验开始

2.1 使用sysctl临时修改配置(重启后失效)

#send buffer 服务器传输大文件就调大  传输字符 调小(但可能ssh 都会很慢) 一个fd就是tcp_wmem  tcp_rmem所占用空间 使用更少的内存
sysctl -w net.ipv4.tcp_wmem="2048 2048 4096"
sysctl -w net.ipv4.tcp_rmem="2048 2048 4096"

sysctl -w net.ipv4.tcp_mem="262144 524288 786432" # 262144*4k 1g 2g 3g 当协议栈占用空间2g时优化 大于3g时不再分配
sysctl -w net.ipv4.ip_local_port_range="1025 64000"
ulimit -n 1048576
modprobe ip_conntrack
sysctl -w net.nf_conntrack_max=1048576

2.2 编译最下方源码 开启c/s回声服务器看效果

send[fd=980328], [32]Hello Server: client --> 329478

1、服务端启动后看一下内存

root@luo:~# free
              total        used        free      shared  buff/cache   available
Mem:        8117280     2376860     5343540        1620      396880     5485128
Swap:       4001788           0     4001788

2、服务端维持980000连接时的内存

root@luo:~# free
              total        used        free      shared  buff/cache   available
Mem:        8117280     6973192      103992           0     1040096      913852
Swap:       4001788     1090996     2910792

3、计算单条连接消耗内存

6973192 - 2376860 = 4596332 ÷ 800000= 4.69

由于笔记本开了4台虚拟机一起跑 连接总是到90多万的时候虚拟机崩溃 最多980000

平均计算一个连接4k左右

三、测试中的一些问题以及优化方向

文件描述符fd不够

​ 端口数量不够

​ accept 全连接队列满了

​ 客户端超时 服务端不会ack 连接队列满了?

半连接全连接 连接队列的问题 满了后需要排队

会导致网络抖动 accept线程池竞争 多线程accept

多线程的网络模型 redis memcached nginx

​ 1、accept与recv/send的fd分开

​ 2、多线程accept

​ nginx memcached多进程更符合业务处理 固定多线程接入 多线程业务处理

源码

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>


#define BUFFER_LENGTH       1024
#define MAX_EPOLL_EVENTS    1024*1024 //connection 
#define MAX_EPOLL_ITEM      102400 //con
#define SERVER_PORT         8888

#define LISTEN_PORT_COUNT   100

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; // 1024 * 1024
};


int recv_cb(int fd, int events, void *arg);
int send_cb(int fd, int events, void *arg);


void 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 ;
    
}


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 =
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值