2015-09-22 24 views
7

Ho un problema: voglio creare un server Erlang in grado di contenere una connessione TCP simultanea aperta 1M. Ho ottimizzato il mio sistema operativo (Oracle Linux 7) per aumentare i descrittori di file. Sul server faccio gen_tcp: ascoltareErlang collega contemporaneamente client 1M

// point_1
Attacco = gen_tcp: accettare
spawn (maniglia (zoccolo)) // un altro thread
torna a point_1

se collego sequenzialmente non è un problema, in 100 secondi ho collegato client 100K; ma non ho avuto legami per di più.

Se voglio connettere quelli in un modo conncurent, solo circa 80 connessioni sono fatte da 100, per esempio.

Ecco come ho eseguito il tutto:

erlc *.erl 
erl +Q 134217727 +P 1000000 -env ERL_MAX_PORTS 40960000 -env ERTS_MAX_PORTS 40960000 

// avviare un server che sarà in ascolto sulla porta 9999

ex:start(1, 9999) 

// 100 clienti cercano di connettersi sulla porta 9999

ex:connect_clients(100, 9999) 

Lasciami mostrare un po 'di codice:

start(Num,LPort) -> 
    case gen_tcp:listen(LPort,[{active, false},{packet,2}]) of 
    {ok, ListenSock} -> 
     start_servers(Num,ListenSock), 
     {ok, Port} = inet:port(ListenSock), 
     Port; 
    {error,Reason} -> 
     {error,Reason} 
    end. 

start_servers(0,_) -> 
    ok; 
start_servers(Num,LS) -> 
    spawn(?MODULE,server,[LS,0]), 
    start_servers(Num-1,LS). 

server(LS, Nr) -> 
    io:format("before accept ~w~n",[Nr]), 
    case gen_tcp:accept(LS) of 
    {ok,S} -> 
     io:format("after accept ~w~n",[Nr]), 
     spawn(ex,loop,[S]), 
     server(LS, Nr+1); 
    Other -> 
     io:format("accept returned ~w - goodbye!~n",[Other]), 
     ok 
    end. 

loop(S) -> 
    inet:setopts(S,[{active,once}]), 
    receive 
    {tcp,S, _Data} -> 
     Answer = 1, 
     gen_tcp:send(S,Answer), 
     loop(S); 
    {tcp_closed,S} -> 
     io:format("Socket ~w closed [~w]~n",[S,self()]), 
     ok 
    end. 

client(PortNo) -> 
    {ok,Sock} = gen_tcp:connect("localhost", PortNo, 
    []). 

connect_clients(Number, Port) -> 
    spawn(ex, client, [Port]), 
    case Number of 
    0 -> ok; 
    _ -> connect_clients(Number-1, Port) 
    end. 

risposta

8

vedo almeno due questioni qui:

  • È necessario alzare la ascolta arretrato; il valore predefinito è 5. È possibile aumentarlo impostando {backlog, N} nelle opzioni di ascolto, ad esempio {backlog, 1024}.

  • La vostra funzione server/2 è difettoso perché accetta una connessione, quindi genera un nuovo processo per l'esecuzione loop/1 ma non fa che il nuovo processo controlling process per il socket accettato. La funzione loop/1 tenta di impostare la modalità {active,once} sul socket nel tentativo di ricevere messaggi in arrivo, ma poiché non è in esecuzione nel processo di controllo, non funzionerà. (Si consiglia di verificare il valore di ritorno di inet_setopts/2 dicendo ok = inet:setopts(S,[{active,once}]), lì, invece.)

Invece di deposizione delle uova del ciclo, si dovrebbe invece generare una nuova accettore, in questo modo:

server(LS, Nr) -> 
    io:format("before accept ~w~n",[Nr]), 
    case gen_tcp:accept(LS) of 
    {ok,S} -> 
     io:format("after accept ~w~n",[Nr]), 
     spawn(ex,server,[LS,Nr+1]), 
     loop(S); 
    Other -> 
     io:format("accept returned ~w - goodbye!~n",[Other]), 
     ok 
    end. 

Con questo approccio , il processo che ha accettato il socket viene eseguito loop/1 e quindi non è necessario modificare il processo di controllo del socket.

+0

In effetti, avevi ragione su questi due problemi. Ho risolto quelli. Ora quando chiamo connect_clients (1000, 9999) si connette circa 100 al secondo e va a 800; poi si ferma. Il server non si arresta in modo anomalo, quindi posso richiamarlo di nuovo, collegando un client o il numero desiderato. Ma non riesco a collegarmi più di circa 800 per chiamata. Vorrei poter chiamare connect_clients (1000000, 9999), ma poi la mia VM si blocca. qualche idea? Grazie per quello che mi hai già aiutato. –

+1

Inoltre, ricorda, puoi avere diversi processi acceptor che chiamano 'gen_tcp: accept/1' sullo stesso socket di ascolto per una migliore velocità effettiva al momento dell'accettazione. –

+0

@ ŞtefanStan difficile dire quali nuovi problemi stai colpendo. Sei in esecuzione con 'sasl' abilitato? Potresti avviarlo per assicurarti di non avere problemi che potrebbero passare inosservati. Sei sicuro che il tuo sistema operativo sia impostato correttamente per consentire il numero di connessioni necessarie? Cosa indica 'ulimit -n' nella shell? Inoltre, quale versione di Erlang/OTP stai usando? –