【仿Mudou库one thread per loop式并发服务器实现】服务器边缘测试+性能测试

【仿Mudou库one thread per loop式并发服务器实现】服务器边缘测试+性能测试

服务器边缘测试+性能测试

#include"httpserver.hpp"#defineWWWROOT"./wwwroot" std::string RequestStr(const HttpRequest &req){ std::stringstream ss; ss << req._method <<" "<< req._path <<" "<< req._version <<"\r\n";for(auto&it : req._params){ ss << it.first <<": "<< it.second <<"\r\n";}for(auto&it : req._headers){ ss << it.first <<": "<< it.second <<"\r\n";} ss <<"\r\n"; ss << req._body;return ss.str();}voidHello(const HttpRequest &req, HttpResponse *rsp){ rsp->SetContent(RequestStr(req),"text/plain");//sleep(15);}voidLogin(const HttpRequest &req, HttpResponse *rsp){ rsp->SetContent(RequestStr(req),"text/plain");}voidPutFile(const HttpRequest &req, HttpResponse *rsp){ std::string path = WWWROOT + req._path;Until::WriteFile(path,req._body);//rsp->SetContent(RequestStr(req), "text/plain");}voidDelFile(const HttpRequest &req, HttpResponse *rsp){ rsp->SetContent(RequestStr(req),"text/plain");}intmain(){ HttpServer server(8080); server.SetThreadCount(3); server.SetBaseDir(WWWROOT);//设置静态资源根目录,告诉服务器有静态资源请求到来,需要到哪里去找资源文件 server.Get("/hello", Hello); server.Post("/login", Login); server.Put("/1234.txt", PutFile); server.Delete("/1234.txt", DelFile); server.Start();return0;}

1. 长连接连续请求测试

长连接测试1:创建一个客户端持续给服务器发送数据,直到超过超时时间看看是否正常

#include"../source/server.hpp"intmain(){ Socket cli_sock; cli_sock.CreateClient(8080,"127.0.0.1"); std::string req ="GET /hello HTTP/1.1\r\nConnection: keep-alive\r\nContent-Length: 0\r\n\r\n";while(1){assert(cli_sock.Send(req.c_str(), req.size())!=-1);char buf[1024]={0};assert(cli_sock.Recv(buf,1023));LOG_DEBUG("[%s]", buf);sleep(3);} cli_sock.Close();return0;}

2. 超时连接释放测试1

超时连接测试1:创建一个客户端,给服务器发送一次数据后,不动了,查看服务器是否会正常的超时关闭连接

#include"../source/server.hpp"intmain(){ Socket cli_sock; cli_sock.CreateClient(8080,"127.0.0.1"); std::string req ="GET /hello HTTP/1.1\r\nConnection: keep-alive\r\nContent-Length: 0\r\n\r\n";while(1){assert(cli_sock.Send(req.c_str(), req.size())!=-1);char buf[1024]={0};assert(cli_sock.Recv(buf,1023));LOG_DEBUG("[%s]", buf);sleep(15);} cli_sock.Close();return0;}

3. 超时连接释放测试2

给服务器发送一个数据,告诉服务器要发送1024字节的数据,但是实际发送的数据不足1024,查看服务器处理结果

  1. 如果数据只发送一次,服务器将得不到完整请求,就不会进行业务处理,客户端也就得不到响应,最终超时关闭连接
  2. 连着给服务器发送了多次 小的请求, 服务器会将后边的请求当作前边请求的正文进行处理,而后面处理的时候有可能就会因为处理错误而关闭连接
#include"../source/server.hpp"intmain(){ Socket cli_sock; cli_sock.CreateClient(8080,"127.0.0.1"); std::string req ="GET /hello HTTP/1.1\r\nConnection: keep-alive\r\nContent-Length: 100\r\n\r\nbitejiuyeke";while(1){assert(cli_sock.Send(req.c_str(), req.size())!=-1);assert(cli_sock.Send(req.c_str(), req.size())!=-1);assert(cli_sock.Send(req.c_str(), req.size())!=-1);assert(cli_sock.Send(req.c_str(), req.size())!=-1);assert(cli_sock.Send(req.c_str(), req.size())!=-1);char buf[1024]={0};assert(cli_sock.Recv(buf,1023));LOG_DEBUG("[%s]", buf);sleep(3);} cli_sock.Close();return0;}

4. 超时连接释放测试3

业务处理超时,查看服务器的处理情况。

当服务器达到了一个性能瓶颈,在一次业务处理中花费了太长的时间(超过了服务器设置的非活跃超时时间)。

在一次业务处理中耗费太长时间,导致其他的连接也被连累超时,其他的连接有可能会被拖累超时释放。

假设现在 12345描述符就绪了, 在处理1的时候花费了30s处理完,超时了,导致2345描述符因为长时间没有刷新活跃度

  1. 如果接下来的2345描述符都是通信连接描述符,如果都就绪了,则并不影响,因为接下来就会进行处理并刷新活跃度
  2. 如果接下来的2号描述符是定时器事件描述符,定时器触发超时,执行定时任务,就会将345描述符给释放掉。这时候一旦345描述符对应的连接被释放,接下来在处理345事件的时候就会导致程序崩溃(内存访问错误。 因此这时候,在本次事件处理中,如果有释放连接的操作,并不能直接对连接进行释放,而应该将释放操作压入到任务池中, 等到事件处理完了执行任务池中的任务的时候,再去释放
#include"../source/server.hpp"intmain(){signal(SIGCHLD, SIG_IGN);for(int i =0; i <10; i++){ pid_t pid =fork();if(pid <0){LOG_DEBUG("FORK ERROR");return-1;}elseif(pid ==0){ Socket cli_sock; cli_sock.CreateClient(8080,"127.0.0.1"); std::string req ="GET /hello HTTP/1.1\r\nConnection: keep-alive\r\nContent-Length: 0\r\n\r\n";while(1){assert(cli_sock.Send(req.c_str(), req.size())!=-1);char buf[1024]={0};assert(cli_sock.Recv(buf,1023));LOG_DEBUG("[%s]", buf);} cli_sock.Close();exit(0);}}while(1)sleep(1);return0;}

现在把所有事件处理结束了,释放连接的操作都先压入到任务队列中。等到所有就绪时间处理完成后,在去执行任务队列中的任务。因此Channel中Handevent函数内,不用每个事件执行前先去执行任意事件回调。而是在最后执行一次任意事件回调。反正在处理就绪事件不会释放连接,不用担心因为释放连接销毁Connection对象而导致调用任意事件回调导致程序奔溃。

5. 数据中多条请求处理测试

一次性给服务器发送多条数据,然后查看服务器的处理结果
预期结果:每一条请求都应该得到正常处理

#include"../source/server.hpp"intmain(){ Socket cli_sock; cli_sock.CreateClient(8080,"127.0.0.1"); std::string req ="GET /hello HTTP/1.1\r\nConnection: keep-alive\r\nContent-Length: 0\r\n\r\n"; req +="GET /hello HTTP/1.1\r\nConnection: keep-alive\r\nContent-Length: 0\r\n\r\n"; req +="GET /hello HTTP/1.1\r\nConnection: keep-alive\r\nContent-Length: 0\r\n\r\n";while(1){assert(cli_sock.Send(req.c_str(), req.size())!=-1);char buf[1024]={0};assert(cli_sock.Recv(buf,1023));LOG_DEBUG("[%s]", buf);sleep(3);} cli_sock.Close();return0;}

6. PUT大文件上传测试

大文件传输测试,给服务器上传一个大文件,服务器将文件保存下来,观察处理结果

预期结果: 上传的文件,和服务器保存的文件一致

dd if=/dev/zero of=./hello.txt bs=100M count=3 #创建一个大小为300M的文件
#include"../source/http/httpserver.hpp"intmain(){ Socket cli_sock; cli_sock.CreateClient(8080,"127.0.0.1"); std::string req ="PUT /1234.txt HTTP/1.1\r\nConnection: keep-alive\r\n"; std::string body;Until::ReadFile("./hello.txt",&body); req +="Content-Length: "+ std::to_string(body.size())+"\r\n\r\n";assert(cli_sock.Send(req.c_str(), req.size())!=-1);assert(cli_sock.Send(body.c_str(), body.size())!=-1);char buf[1024]={0};assert(cli_sock.Recv(buf,1023));LOG_DEBUG("[%s]", buf);sleep(3); cli_sock.Close();return0;}

7. 服务器性能测试

采用webbench进行服务器性能测试。
Webbench是知名的网站压li测试⼯具,它是由Lionbridge公司(http://www.lionbridge.com)开发。

webbench的标准测试可以向我们展示服务器的两项内容: 每秒钟相应请求数 和 每秒钟传输数据量。

webbench测试原理是,创建指定数量的进程,在每个进程中不断创建套接字向服务器发送请求,并通过管道最终将每个进程的结果返回给主进程进行数据统计。

./webbench -c 1000 -t 60 http://127.0.0.1:8080/hello 

-c: 指定客户端数量
-t:指定时间

性能测试的两个重点衡量标准:并发量 & QPS

并发量:可以同时处理多少客户端的请求而不会出现连接失败
QPS:每秒钟处理的包的数量

抛开环境说性能测试都是无知的!!!

测试环境:
服务器是2核2G带宽4M的云服务器
客户端是ubentu环境
使用webbench以1000并发量,向服务器发送请求,进行了60秒测试
最终得到的结果是:并发量当前是1000包,QPM1分钟处理215188个包,QPS:一秒钟处理3586个包。

在这里插入图片描述

Read more

Flutter for OpenHarmony:cli_util 告别手写 print,用专业级日志系统构建你的 Dart 命令行工具 深度解析与鸿蒙适配指南

Flutter for OpenHarmony:cli_util 告别手写 print,用专业级日志系统构建你的 Dart 命令行工具 深度解析与鸿蒙适配指南

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.ZEEKLOG.net 前言 随着 Flutter 和 Dart 生态的爆发,越来越多的开发者开始使用 Dart 编写命令行工具(CLI)。从官方的 flutter 工具链,到社区的 melos、very_good_cli,Dart 因其 AOT 编译出的独立二进制文件(无需安装运行时)和极快的启动速度,已成为编写跨平台 CLI 的首选语言。 但在开发 CLI 时,我们经常面临一些底层痛点: * SDK 哪里找? 如何准确找到当前运行环境的 Dart SDK 路径?(用于调用 dart format 或 dart pub)。 * 日志怎么打? 简单的

By Ne0inhk
Flutter for OpenHarmony:stream_transform 响应式编程的瑞士军刀(Dart Stream 扩展操作符) 深度解析与鸿蒙适配指南

Flutter for OpenHarmony:stream_transform 响应式编程的瑞士军刀(Dart Stream 扩展操作符) 深度解析与鸿蒙适配指南

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.ZEEKLOG.net 前言 Dart 的 Stream 是处理一步事件流的基石(类似于 RxJava 或 RxJS)。 虽然原生 SDK 提供了一些可以转换 Stream 的方法(如 map, where),但在处理复杂交互时(如:防抖、节流、合并流、自动关闭流)依然显得捉襟见肘。 stream_transform 是 Dart 官方维护的一个 Stream 操作符扩展库。它补全了 Stream API 中缺失的高级功能,让你在不引入庞大的 rxdart 的情况下,也能轻松应对复杂的异步流处理。 对于 OpenHarmony 开发者,UI 交互(点击、

By Ne0inhk
Flutter 组件 satisfied_version 的适配 鸿蒙Harmony 实战 - 驾驭语义化版本约束、实现鸿蒙端精细化兼容性审计与分发策略动态对齐方案

Flutter 组件 satisfied_version 的适配 鸿蒙Harmony 实战 - 驾驭语义化版本约束、实现鸿蒙端精细化兼容性审计与分发策略动态对齐方案

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.ZEEKLOG.net Flutter 组件 satisfied_version 的适配 鸿蒙Harmony 实战 - 驾驭语义化版本约束、实现鸿蒙端精细化兼容性审计与分发策略动态对齐方案 前言 在鸿蒙(OpenHarmony)生态系统的快速迭代中,我们作为开发者,时刻面临着“版本碎裂”的挑战。不同的鸿蒙 API Level、不同的插件补丁版本、甚至是热更新包与主程序之间的语义化版本(SemVer)约束匹配,都直接决定了 App 在用户指尖的稳定性。 当你需要判断当前的系统版本是否满足 >=5.0.0 <6.0.0 这一严苛的运行范围,或者需要验证某一个从 Atomgit 下载的插件包是否兼容应用当前的宿主版本时,如果仅仅靠手动进行字符串切割和数字对比,不仅效率极低,更由于无法处理修正版本(Patch)

By Ne0inhk
Flutter 组件 ninja_prime 的适配 鸿蒙Harmony 实战 - 驾驭极致高性能数值计算、实现鸿蒙端加密基石与逻辑加速审计方案

Flutter 组件 ninja_prime 的适配 鸿蒙Harmony 实战 - 驾驭极致高性能数值计算、实现鸿蒙端加密基石与逻辑加速审计方案

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.ZEEKLOG.net Flutter 组件 ninja_prime 的适配 鸿蒙Harmony 实战 - 驾驭极致高性能数值计算、实现鸿蒙端加密基石与逻辑加速审计方案 前言 在鸿蒙(OpenHarmony)生态的极速加密通讯协议开发、基于区块链的分布式政务存证系统以及需要执行海量数据完整性指纹校验的各类专业级应用中,“数值计算的处理效能”是决定应用能否通过 0307 批次严苛性能红线的关键命门。面对涉及 2048 位以上的大数哈希(Large BigInt Hashing)、秒级内的超大规模素数判定(Primality Testing)或者是需要为复杂的 0307 批次资产金融模型执行高精度的科学运算。如果仅仅依靠 Dart 原生的数值类型或未经过度优化的通用算法库。不仅会导致在处理大型数值时产生令人难以忍受的 UI 线程阻塞,更会因为计算效率过低,引发严重的系统功耗异常。 我们需要一种“逻辑纯粹、计算爆发”的数值艺术。 ninja_prime

By Ne0inhk