手撕测试tcp服务器效率工具——以epoll和io_uring对比为例

简介: 手撕测试tcp服务器效率工具——以epoll和io_uring对比为例

服务器性能测试介绍

服务器的性能测试主要包括2部分:

  1. 并发量。能容纳多大的连接
  2. 效率。在不崩坏的情况下能对报文的处理效率。

本文主要进行效率测试,看看基于epoll模型和io_uring模型的tcp服务器,谁的效率更高。

测试思路

客户端(一个或多个)大量地向服务器发送报文,测试服务器的处理效率(tps:transaction per second,qps:queries per second)。这个或这些客户端也被成为测试工具。

测试工具需求

1、  基于tcp

2、  可以设置请求、线程与连接的数量。-n req -t threadnum -c connection。

在本文中,为了方便,我们为一个连接建立一个线程,也就是线程和连接一一对应。

getopt是一个解析命令行参数的函数,它不是一个线程安全的函数,尽量只在1个线程中使用,建议提前了解。

测试工具代码

代码有详细地注释,主要步骤为:

1、解析命令行参数,看看服务器的IP和port,以及要建立多少连接、发送多少数据等。

2、根据线程数建立线程,在线程里建立一个连接,连接服务器,并按每个线程的平均发送数据任务不间断地发送数据和接收数据。本案例中每笔报文的大小为64*8。

3、计算从开始发送到结束接收的耗时,并计算相关指标。

服务器的功能是:接收到什么数据就返回什么数据。

详细的服务器代码可看前文:

与epoll媲美的io_uring_io_uring tcp服务器-CSDN博客

用反应器模式和epoll构建百万并发服务器_如何设计一个支持百万并发的服务器-CSDN博客

#include<stdio.h>
#include<string.h>
#include<sys/socket.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/time.h>
#include<pthread.h>
#include<arpa/inet.h>
//设置该结构体用于存储线程函数所需的参数
typedef struct test_context_s{
  
  char serverip[16]; //服务器ip
  int port; //服务器端口
  int threadnum;  //线程数量
  int connection; //连接数量,此案例中与线程数量一致
  int requestion; //请求数量,也就是报文数量
  
  int failed; //统计发送失败的次数,有个大概的数就行,所以没用原子变量
}test_context_t;
typedef struct test_context_s test_context_t;
//与服务器建立tcp连接,常规的socket然后connect
int connect_tcpserver(const char* ip,unsigned short port){
  
  int connfd = socket(AF_INET,SOCK_STREAM,0);
  struct sockaddr_in tcpserver_addr;
  memset(&tcpserver_addr,0,sizeof(struct sockaddr_in));
  tcpserver_addr.sin_family = AF_INET;
  tcpserver_addr.sin_addr.s_addr = inet_addr(ip);
  tcpserver_addr.sin_port = htons(port);
  int ret = connect(connfd,(struct sockaddr*)&tcpserver_addr,sizeof(struct sockaddr_in));
  if(ret){
    perror("connect");
    return -1;
  }
  return connfd;
}
#define TIME_SUB_MS(tv1,tv2) ((tv1.tv_sec - tv2.tv_sec)*1000 + (tv1.tv_usec - tv2.tv_usec)/1000)
//要发送给客户端的数据的基本单位
#define TEST_MESSAGE   "ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890abcdefghijklmnopqrstuvwxyz\r\n"
#define RBUFFER_LENGTH 2048 //读数据的空间的大小,不一定用满
#define WBUFFER_LENGTH 2048 //写数据的空间的大小,不一定用满
//客户端发送并接收数据
int send_recv_tcppkt(int fd){
  char wbuffer[WBUFFER_LENGTH] = {0};
  //设置每次发送的报文包含多少个基本单位
  int i = 0;
  for(i=0;i<8;i++){
    strcpy(wbuffer+i *strlen(TEST_MESSAGE),TEST_MESSAGE);
  }
  //发送报文
  int res = send(fd,wbuffer,strlen(wbuffer),0);
  if(res<0){
    exit(1);
  }
  //接收报文
  char rbuffer[RBUFFER_LENGTH] ={0};
  res = recv(fd,rbuffer,RBUFFER_LENGTH,0);
  if(res<=0){
    exit(1);
  }
  if(strcmp(rbuffer,wbuffer)!=0){
    return -1;
  }
  
}
//线程函数,主要作用是建立连接、发送接收报文
static void *test_qps_entry(void* arg){
  
  test_context_t *pctx = (test_context_t*)arg;
  //建立连接
  int connfd = connect_tcpserver(pctx->serverip,pctx->port);
  if(connfd<0){
    printf("connect_tcpserver failed!\n");
    return NULL;
  } 
  //每个线程要发送的报文数量
  int count = pctx->requestion/pctx->threadnum;
  //发送报文
  int i=0;
  int res;
  while(i++<count){
    res = send_recv_tcppkt(connfd);
    if(res!=0){
      printf("send_recv_tcppkt failed\n");
      pctx->failed++;
      continue;
    }
  }
  return NULL;
}
int main(int argc,char *argv[]){
  
  int ret =0;
  test_context_t ctx ={0};
  
  int opt;
  
  //getopt函数可以一次解析出带有名称的输入参数
  //注意这个函数是线程不安全的,
  while((opt = getopt(argc,argv,"s:p:t:c:n:?"))!=-1){
    switch(opt){
      case 's':
        printf("-s:%s\n",optarg);
        strcpy(ctx.serverip,optarg);//服务器IP
        break;    
       case 'p':
        printf("-p:%s\n",optarg);
        ctx.port = atoi(optarg);//服务器端口
        break;
      case 't':    
        printf("-t:%s\n",optarg);
        ctx.threadnum = atoi(optarg);//线程数
        break;
      case 'c':
        printf("-c:%s\n",optarg);
        ctx.connection = ctx.threadnum;//还是和线程数一致吧  
        break;
      case 'n':
        printf("-n:%s\n",optarg);
        ctx.requestion = atoi(optarg);
        break;
      default:
        return -1;          
    }
  }
  //线程数组
  pthread_t *ptid = malloc(ctx.threadnum *sizeof("pthread_t"));
  //开始大规模发送发送报文
  struct timeval tv_begin;  //记录报文开始发送的时间
  gettimeofday(&tv_begin,NULL);
  int i = 0;
  for(i=0;i<ctx.threadnum;i++){ //建立线程运行线程函数
    pthread_create(&ptid[i],NULL,test_qps_entry,&ctx);
  }
  for(i=0;i<ctx.threadnum;i++){
    pthread_join(ptid[i],NULL);
  }
  struct timeval tv_end;  //记录报文全部发送并接收完毕的时间
  gettimeofday(&tv_end,NULL);
  int time_used = TIME_SUB_MS(tv_end,tv_begin);//计算用时
  printf("success: %d, failed: %d, time_used: %d, qps: %d\n", ctx.requestion-ctx.failed, 
    ctx.failed, time_used, ctx.requestion * 1000 / time_used);
  return 0;
}

测试结果

epoll服务器

发送了100w数据,qps为33243

io_uring服务器

发送了100w数据,qps为43305

结论

在本机、本案例的情况下,io_uring服务器的效率比epoll服务器的效率高约30%。

目录
相关文章
|
2月前
|
运维 Prometheus 监控
如何在测试环境中保持操作系统、浏览器版本和服务器配置的稳定性和一致性?
如何在测试环境中保持操作系统、浏览器版本和服务器配置的稳定性和一致性?
|
4天前
|
存储 弹性计算 固态存储
阿里云服务器ESSD Entry系统盘测评IOPS、IO读写和时延性能参数
阿里云ESSD Entry云盘是新一代企业级云盘,具备高IOPS、低延迟特性,适合开发与测试场景。它提供10~32,768 GiB容量范围,最大IOPS达6,000,吞吐量150 MB/s,时延1~3 ms。支持按量付费和包年包月,性价比高,特别适合个人开发者和中小企业。详情及价格参考阿里云官网。
|
1月前
|
缓存 网络协议 Java
【JavaEE】——TCP回显服务器(万字长文超详细)
ServerSocket类,Socket类,PrintWriter缓冲区问题,Socket文件释放问题,多线程问题
|
2月前
|
缓存 Ubuntu Linux
Linux环境下测试服务器的DDR5内存性能
通过使用 `memtester`和 `sysbench`等工具,可以有效地测试Linux环境下服务器的DDR5内存性能。这些工具不仅可以评估内存的读写速度,还可以检测内存中的潜在问题,帮助确保系统的稳定性和性能。通过合理配置和使用这些工具,系统管理员可以深入了解服务器内存的性能状况,为系统优化提供数据支持。
80 4
|
3月前
|
存储 监控 网络协议
服务器压力测试是一种评估系统在极端条件下的表现和稳定性的技术
【10月更文挑战第11天】服务器压力测试是一种评估系统在极端条件下的表现和稳定性的技术
204 32
|
2月前
|
存储 关系型数据库 MySQL
查询服务器CPU、内存、磁盘、网络IO、队列、数据库占用空间等等信息
查询服务器CPU、内存、磁盘、网络IO、队列、数据库占用空间等等信息
1115 2
|
2月前
|
存储 弹性计算 固态存储
阿里云服务器ESSD Entry系统盘测评IOPS、IO读写和时延性能参数
ESSD Entry云盘是阿里云推出的新一代云盘,具备高IOPS、低延迟和企业级数据保护能力。适用于开发与测试场景,支持按量付费和包年包月计费模式。99元和199元的ECS经济型e实例和通用算力型u1实例均采用ESSD Entry系统盘,性价比高。详细性能参数和价格请参考阿里云官方页面。
125 0
|
3月前
|
弹性计算 网络协议 Linux
云服务器评估迁移时间与测试传输速度
云服务器评估迁移时间与测试传输速度
|
20天前
|
弹性计算 数据挖掘 应用服务中间件
阿里云轻量应用服务器68元与云服务器99元和199元区别及选择参考
目前阿里云有三款特惠云服务器,第一款轻量云服务器2核2G68元一年,第二款经济型云服务器2核2G3M带宽99元1年,第三款通用算力型2核4G5M带宽199元一年。有的新手用户并不是很清楚他们之间的区别,因此不知道如何选择。本文来介绍一下它们之间的区别以及选择参考。
320 87
|
13天前
|
存储 弹性计算 应用服务中间件
阿里云轻量应用服务器出新品通用型实例了,全球26个地域可选
近日,阿里云再度发力,推出了首款全新升级的轻量应用服务器——通用型实例。这款服务器实例不仅标配了200Mbps峰值公网带宽,更在计算、存储、网络等基础资源上进行了全面优化,旨在为中小企业和开发者提供更加轻量、易用、普惠的云计算服务,满足其对于通用计算小算力的迫切需求。目前,这款新品已在全球26个地域正式上线,为全球用户提供了更加便捷、高效的上云选择。
113 27