2012-04-22 48 views
5

Tôi đang viết máy chủ TCP trong Qt để phục vụ các tệp lớn. Ứng dụng logic là như sau:Máy chủ QTcpS chậm với nhiều khách hàng đồng thời

  1. Tôi đã subclassed QTcpServer và reimplemented incomingConnection (int)
  2. Trong incomingConnection, tôi là tạo ra thể hiện của "Streamer" class
  3. "Streamer" đang sử dụng QTcpSocket đó là khởi tạo với setSocketDescriptor từ incomingConnection
  4. Khi dữ liệu từ client đến, tôi gửi lại phản ứng ban đầu từ bên trong readyRead() khe, và sau đó tôi kết nối bytesWritten tín hiệu ổ cắm của (qint64) để bytesWritten khe Streamer của()

bytesWritten trông giống như sau:

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); 
    } 
} 
... 

Vì vậy, về cơ bản tôi chỉ ghi dữ liệu mới khi tất cả dữ liệu đang chờ được đầy đủ bằng văn bản. Tôi nghĩ đó là cách không đồng bộ nhất để làm điều đó.

Và mọi thứ hoạt động chính xác, ngoại trừ nó khá chậm khi có nhiều khách hàng đồng thời.

Với khoảng 5 khách hàng - Tôi đang tải về từ máy chủ với tốc độ khoảng 1 MB/s (tối đa kết nối internet của nhà tôi)

Với khoảng 140 khách hàng - tốc độ tải về là khoảng 100-200 KB/s .

Kết nối internet của máy chủ là 10 Gbps và với 140 khách hàng sử dụng nó là khoảng 100 Mb/giây, vì vậy tôi không nghĩ đó là vấn đề.

sử dụng bộ nhớ Server với 140 khách hàng - 100 MB bộ 2GB có sẵn

sử dụng CPU Server - tối đa 20%

Tôi đang sử dụng cổng 800.

Khi có được 140 khách hàng trên cổng 800 và tốc độ tải xuống thông qua nó là 100-200 KB/s, tôi đã chạy bản sao riêng biệt trên cổng 801 và tải xuống ở mức 1 MB/giây mà không gặp sự cố nào.

Tôi đoán là bằng cách nào đó, sự kiện gửi đi của Qt (hoặc bộ nhận dạng socket?) Quá chậm để xử lý tất cả các sự kiện đó.

Tôi đã thử:

  1. Biên soạn toàn bộ Qt và ứng dụng của tôi với O3
  2. Cài đặt libglib2.0-dev và biên dịch lại Qt (vì QCoreApplication sử dụng QEventDispatcherGlib hoặc QEventDispatcherUNIX, vì vậy tôi muốn xem có bất kỳ sự khác biệt nào không)
  3. Tạo ra một vài luồng và trongConnect (int) bằng streamer-> moveToThread() tùy thuộc vào số lượng khách hàng hiện tại chủ đề cụ thể - mà không thực hiện bất kỳ thay đổi (mặc dù tôi đã quan sát thấy rằng tốc độ là độ khác nhau nhiều hơn nữa)
  4. quá trình lao động Spawning sử dụng

Code:

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); 

và sau đó bắt đầu một QLoc alServer trong quá trình chính và truyền socketDescriptors từ incomingConnection (int socketDescriptor) đến các quy trình công nhân. Nó hoạt động chính xác, nhưng tốc độ tải xuống vẫn chậm.

Cũng thử:

  1. fork() - quá trình ing trong incomingConnection() - đó gần như bị giết server :)
  2. Tạo chủ đề riêng biệt cho từng khách hàng - tốc độ giảm xuống còn 50-100 KB/s
  3. sử dụng QThreadPool với QRunnable - có sự khác biệt

tôi đang sử dụng Qt 4.8.1

Tôi hết ý tưởng.

Có liên quan đến Qt hoặc có thể là điều gì đó có cấu hình máy chủ không?

Hoặc có lẽ tôi nên sử dụng ngôn ngữ/khuôn khổ/máy chủ khác nhau? Tôi cần máy chủ TCP sẽ phục vụ các tập tin, nhưng tôi cũng cần phải thực hiện một số nhiệm vụ cụ thể giữa các gói, vì vậy tôi cần phải thực hiện phần đó bản thân mình.

+1

Cách sử dụng đĩa máy chủ? Nó có thể là nút cổ chai? –

+0

Nó hoàn toàn có thể. Có vẻ như phần cứng máy chủ có thể bị lỗi. Tôi sẽ chắc chắn vào thứ hai, và tôi sẽ cho bạn biết. Cảm ơn! – AdrianEddy

+0

Nút cổ chai chắc chắn là hoạt động của đĩa IO. Trên 80 tệp đã mở dẫn đến tải trên máy chủ> 1 và tốc độ tải xuống khoảng 150 KB/s. Có bất cứ điều gì tôi có thể thay đổi trong chương trình của tôi, hoặc tôi phải chơi với cấu hình/phần cứng máy chủ? – AdrianEddy

Trả lời

3

Lần đọc đĩa của bạn đang chặn hoạt động, chúng sẽ ngừng xử lý bất kỳ, bao gồm việc xử lý các kết nối mạng mới và như vậy. Đĩa của bạn cũng có thông lượng I/O hữu hạn, và bạn có thể bão hòa nó. Bạn có thể không muốn đĩa của bạn dừng phần còn lại của ứng dụng của bạn. Tôi không nghĩ có bất cứ điều gì sai với Qt ở đây - không phải cho đến khi bạn chạy profiler và cho thấy mức tiêu thụ CPU của Qt quá mức, hoặc bằng cách nào đó Qt tấn công khóa trên hàng đợi sự kiện (đó là những người duy nhất quan trọng ở đây).

Bạn nên có chia xử lý của bạn trên QObjects, như sau:

  1. Chấp nhận kết nối đến.

  2. Xử lý văn bản và đọc từ ổ cắm.

  3. Xử lý dữ liệu mạng đến và phát hành bất kỳ thư trả lời không phải tệp nào.

  4. Đọc từ đĩa và ghi vào mạng.

Tất nhiên # 1 và # 2 là các lớp Qt hiện có.

Bạn phải viết # 3 và # 4. Bạn có thể di chuyển # 1 và # 2 vào một chuỗi được chia sẻ giữa chúng. # 3 và # 4 nên được lan truyền xung quanh một số chủ đề. Một ví dụ của # 3 nên được tạo ra cho mỗi kết nối hoạt động.Sau đó, khi thời gian đến để gửi dữ liệu tệp, # 3 instantiates # 4. Số lượng các chủ đề có sẵn cho # 4 nên được điều chỉnh, bạn có thể sẽ thấy rằng có một thiết lập tối ưu cho nó cho một khối lượng công việc cụ thể. Bạn có thể nhanh chóng # 3 và # 4 trên các chủ đề của họ theo kiểu vòng tròn. Kể từ khi truy cập đĩa bị chặn, các chủ đề được sử dụng cho # 4 nên độc quyền và không được sử dụng cho bất cứ điều gì khác.

Đối tượng # 4 nên đọc đĩa khi có ít hơn một lượng dữ liệu nhất định còn lại trong bộ đệm ghi. Số tiền này có thể không phải là số không - bạn muốn giữ cho các giao diện mạng đó bận rộn mọi lúc, nếu có thể, và hết dữ liệu để gửi là một cách chắc chắn để nhàn rỗi chúng.

Vì vậy, tôi thấy ít nhất các thông số du dương sau đó bạn sẽ cần phải chuẩn cho:

  1. minNetworkWatermark - mực nước tối thiểu trong bộ đệm ổ cắm truyền. Bạn đọc từ đĩa và ghi vào ổ cắm khi có ít hơn nhiều byte để ghi.

  2. minĐọc kích thước - Kích thước của một đĩa tối thiểu được đọc. Đọc tệp sẽ là qMax (minNetworkWatermark - socket-> bytesToWrite(), minReadSize).

  3. numDiskThreads - số lượng chủ đề mà các đối tượng # 4 được chuyển đến.

  4. numNetworkThreads - số lượng chủ đề mà đối tượng # 3 được chuyển đến.

Bạn sẽ muốn điểm chuẩn trên các máy khác nhau để có ý tưởng về tốc độ của những thứ nhanh và hiệu quả điều chỉnh. Bắt đầu các điểm chuẩn từ máy phát triển của bạn, cho dù máy tính để bàn hoặc máy tính xách tay. Vì đó là công việc hàng ngày của bạn, bạn có thể sẽ nhận thấy một cách nhanh chóng nếu có điều gì đó sai trái với hiệu suất của nó.

Các vấn đề liên quan