具有大量并发客户端的慢速 QTcpServer
Slow QTcpServer with lots of simultaneous clients
我正在用Qt编写TCP服务器,它将为大文件提供服务。应用程序逻辑如下:
- 我已经子类化了QTcpServer并重新实现了incomingConnection(int)
- 在传入连接中,我正在创建"流"类的实例
- "Streamer"正在使用QTcpSocket,该套接字使用来自传入连接的setSocketDescriptor初始化
- 当来自客户端的数据到达时,我从 readyRead() 插槽中发回初始响应,然后将套接字的信号字节写入(qint64) 连接到流处理器的插槽字节写入()
bytesWrite 看起来像这样:
Streamer.h:
...
private:
QFile *m_file;
char m_readBuffer[64 * 1024];
QTcpSocket *m_socket;
...
Streamer.cpp
...
void Streamer::bytesWritten() {
if (m_socket->bytesToWrite() <= 0) {
const int bytesRead = m_file->read(m_readBuffer, 64 * 1024);
m_socket->write(m_readBuffer, bytesRead);
}
}
...
所以基本上我只在所有待处理的数据都完全写入时才写入新数据。我认为这是最异步的方式。
一切都工作正常,除了当有很多同时客户端时它很慢。
大约有 5 个客户端 - 我从该服务器下载的速度约为 1 MB/s(我家庭互联网连接的最大速度)
大约有 140 个客户端 - 下载速度约为 100-200 KB/s。
服务器的互联网连接是10 Gbps,有140个客户端,它的使用量约为100 Mbps,所以我认为这不是问题所在。
140 个客户端的服务器内存使用情况 - 100 MB 的 2GB 可用
空间服务器的 CPU 使用率 - 最大 20%
我正在使用端口 800。
当端口 800 上有 140 个客户端并且通过它的下载速度约为 100-200 KB/s 时,我在端口 801 上运行了单独的副本并以 1 MB/s 的速度下载而没有问题。
我的猜测是,不知何故,Qt的事件调度(或套接字通知器?)太慢了,无法处理所有这些事件。
我试过:
- 使用 -O3 编译整个 Qt 和我的应用程序
- 安装 libglib2.0-dev 并重新编译 Qt(因为 QCoreApplication 使用 QEventDispatcherGlib 或 QEventDispatcherUNIX,所以我想看看是否有任何区别)
- 生成一些线程并使用 streamer->moveToThread() 在 incomingConnection(int) 中,具体取决于当前特定线程中的客户端数量 - 这没有进行任何更改(尽管我观察到速度变化更大)
- 生成工作进程使用
法典:
main.cpp:
#include <sched.h>
int startWorker(void *argv) {
int argc = 1;
QCoreApplication a(argc, (char **)argv);
Worker worker;
worker.Start();
return a.exec();
}
in main():
...
long stack[16 * 1024];
clone(startWorker, (char *)stack + sizeof(stack) - 64, CLONE_FILES, (void *)argv);
然后在主进程中启动 QLocalServer 并将套接字描述符从 incomingConnection(int socketDescriptor) 传递到工作进程。它工作正常,但下载速度仍然很慢。
还尝试过:
- incomingConnection() 中的 fork()-ing 进程 - 几乎杀死了服务器:)
- 为每个客户端创建单独的线程 - 速度降至 50-100 KB/s
- 将 QThreadPool 与 QRunnable 一起使用 - 没有区别
我正在使用Qt 4.8.1
我没主意了。
是与Qt相关还是与服务器配置有关?
或者也许我应该使用不同的语言/框架/服务器?我需要提供文件的TCP服务器,但我还需要在数据包之间执行一些特定的任务,所以我需要自己实现该部分。
您的磁盘读取正在阻止操作,它们将停止任何处理,包括处理新的网络连接等。您的磁盘的 I/O 吞吐量也是有限的,您可以使其饱和。您可能不希望磁盘停止应用程序的其余部分。我不认为Qt在这里有什么问题 - 直到你运行一个分析器并显示Qt的CPU消耗过多,或者Qt以某种方式在事件队列上遇到锁争用(这些是唯一重要的)。
您应该将处理拆分到 QObject,如下所示:
-
接受传入连接。
-
处理套接字的写入和读取。
-
处理传入的网络数据并发出任何非文件回复。
-
从磁盘读取和写入网络。
1 和 #2 是现有的 Qt 类。
你必须写#3和#4。您可能可以将 #1 和 #2 移动到它们之间共享的一个线程中。#3 和 #4 应该分布在多个线程周围。应为每个活动连接创建一个 #3 实例。然后,当需要发送文件数据时,#3 实例化 #4。#4 可用的线程数应该是可调的,您可能会发现它针对特定工作负载有一个最佳设置。您可以以循环方式跨其线程实例化 #3 和 #4。由于磁盘访问是阻塞的,因此用于 #4 的线程应该是独占的,而不是用于其他任何内容。
当写入缓冲区中剩余的数据少于一定量时,#4 对象应执行磁盘读取。此数量可能不应该为零 - 如果可能的话,您希望始终保持这些网络接口繁忙,并且要发送的数据用完是空闲它们的可靠方法。
因此,我至少看到了您需要对其进行基准测试的以下可调参数:
minNetworkWatermark - 套接字传输缓冲区中的最小水位。从磁盘读取并在要写入的字节数少于该字节数时写入套接字。
minReadSize - 最小磁盘读取的大小。文件读取将是qMax(minNetworkWatermark - socket->bytesToWrite(),minReadSize)。
numDiskThreads - #4 对象移动到的线程数。
numNetworkThreads - #3 对象移动到的线程数。
您需要在不同的机器上进行基准测试,以了解事情可以进行多快以及调整的效果。从开发计算机(台式机或笔记本电脑)开始基准测试。由于它是您的日常主力,因此您可能会很快注意到它的性能是否有问题。
- "unknown ca"自生成的 CA、证书和客户端/服务器
- 如何将函数集合传递给客户端类,以便将它们当作客户端类本身的成员使用
- 使用调试/崩溃报告将应用程序部署到客户端
- 如何在本地机器上运行c++和javascript客户端代码(hackerbank风格)
- 如何通过套接字将文本文件的内容从服务器发送到客户端
- 从服务器传输到客户端的消息不会出现
- OpenSSL TLS服务器-使用客户端证书白名单
- 当服务中的事件被触发时,如何将响应从服务发送回客户端?
- 我可以与 python 服务器而不是 c++ 客户端建立 tcp/ip 套接字吗?
- 提升 Asio TCP 服务器 处理多个客户端
- boost::asio UDP 广播客户端仅接收"fast"数据包
- 如何绑定 C++ gRPC 客户端的网络接口
- C++套接字客户端到 Python 服务器未创建连接
- 用于解析 win64 堆栈跟踪的命令行客户端(可以访问符号服务器)
- 将相机数据从服务器实时流式传输到客户端
- 如何将 Firebase 与基于 Linux 的客户端应用配合使用,以便与服务器进行双向消息通信
- GRPC C++ TLS 客户端 grpc::SslCredentials() 方法不返回
- 具有大量并发客户端的慢速 QTcpServer
- 使用QTcpSocket和QTcpServer的Qt双向客户端服务器
- 如何知道连接到QTcpServer的客户端是否已关闭连接