Mi sto impegnando a ridisegnare un'applicazione del server Linux c ++. L'applicazione agisce come relayer di file sotto forma di ricezione di pacchetti di file (inclusi pacchetti di controllo e pacchetti di dati) dal client A, scrivendo questi pacchetti in un file di dati locale quindi crea e aggiorna il file di indice, infine avvia molti thread per leggere i dati del file e inoltrare questi pacchetti di dati ai suoi destinatari come client B, client C, client D, ecc. Il diagramma di flusso è simile al seguente: Client A - > Server - > Client B, Client C, Client D, ...
Attualmente l'applicazione utilizza il pattern Producer-Consumer per passare pacchetti di dati tra i moduli, ecco alcuni moduli che abbiamo: SocketModule (contiene un thread per ascoltare e leggere i dati in arrivo di tutti i socket), SessionModule (per la gestione delle sessioni di tcp dell'utente) , FileRelayModule (contiene molti thread per fare lavori come l'elaborazione di pacchetti in entrata e la scrittura in file di dati locali, la lettura di file di dati locali e l'aggiornamento di file indice e l'inoltro di questi pacchetti di dati ai ricevitori tramite socket TCP).
Il problema è che le sue prestazioni sono così brutte, prendi un esempio, ci vogliono circa 15 minuti per il client A per inviare un file di dimensioni 200M al client B, ma ho fatto un file p2p che invia e riceve tra macchine diverse che serve solo 20 secondi.
Riteniamo che la causa principale delle cattive prestazioni sia la memorizzazione delle informazioni sui socket in una globale std :: map, quindi ogni thread che desidera inviare i dati (ai client) deve attendere il blocco per std :: map, quindi l'applicazione agisce come un'applicazione a thread singolo.
Poiché il design originale di questa applicazione non segue i principi del modello di progettazione e questo si traduce in molti problemi, quindi vogliamo riprogettarlo usando il pattern MVC, aggiungendo ruoli di gestore di thread e file manager. Il primo elemento che penso è di utilizzare un ThreadPool, quindi inviare ogni socket accettato nel thread che ha il minor numero di client (numero di socket). Questo è possibile o ragionevole per questa applicazione? Cosa faresti se tu fossi il progettista di questo progetto?
[update]
1. Controllerò il progetto e capirò quale parte è il vero collo di bottiglia di
prestazioni
2. Non importa quale parte è il vero collo di bottiglia, abbiamo bisogno di ricostruire il
applicazione per gestire le risorse come thread e memoria e
essere facile da mantenere
Farò cose come il seguente:
1. Crea molte classi: CSessionManager per la gestione dei dati della sessione tcp (contiene
un thread), CFileManager per l'inoltro di file e la gestione di tutti i file di dati e di indice (contiene un ThreadPool con 32 thread di cui contiene una mappa socket e un socket di ascolto), CFileServer (contiene CSessionManager e CFileManager). Qui CFileServer funge da Controller, CFileManager funge da modello.
2. CFileManager crea un pool di thread con 32 thread inizializzati all'avvio. CFileServer crea un socket di ascolto e inserisce tutti i socket accettati in CFileManager dove il nuovo socket accettato è impostato su un thread nel threadpool interrogando chi ha i socket minimi.
[Update2]
Il threadpool sarà progettato come segue (è ragionevole?):
std::list<CThread*> workers;
//each thread would be
class CThread{
public:
PostMsg(CMessage*);
static void Run();
private:
map<int, socketinfo*> sockets;
std::deque<CMessage*> messageQueue;
};
...
Other objects will call PostMsg to inform message
to this CThread and in the run function:
void CThread::Run(){
....
while(1){
....
//firstly process all messages
//these messages includes adding socket,
//deleting socket, or go offline
CMessage* msg = messageQueue.pop_front;
if (msg != NULL) {
process(msg);
}
//after processing the messages,
//now read/write the sockets data
//set readfd and writefd
ret = select(max, readfd, writefd, NULL, timeout);
//now process readfd and writefd
//read data from readfd, then write to local
//file read data from local file then write
//to wirtefd or post a message to controller
// to remove fd from array.
......
}
}