2015-09-22 10 views
7

Ich habe ein Problem: Ich möchte einen Erlang-Server erstellen, der 1M gleichzeitige offene TCP-Verbindung halten kann. Ich habe mein Betriebssystem (Oracle Linux 7) angepasst, um die Dateideskriptoren zu erhöhen. Auf dem Server tun i gen_tcp: hörenErlang verbinden Sie gleichzeitig 1M Clients

// Point_1
Sockel = gen_tcp: akzeptieren
spawn (Griff (Sockel)) // anderen Thread
zurück zu Point_1

Wenn ich eine Verbindung sequentiell ist es kein Problem, in 100 Sekunden habe ich 100K Clients verbunden; aber ich hatte keine Geduld mehr.

Wenn ich diese in einer conncurent Weise verbinden möchten, werden nur etwa 80 Verbindungen von 100 hergestellt, zum Beispiel. Diese

ist, wie ich das alles laufen:

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

// einen Server starten, der auf Port 9999

ex:start(1, 9999) 

// 100 Clients versuchen, eine Verbindung auf Port 9999

hören
ex:connect_clients(100, 9999) 

Lassen Sie mich Ihnen einen Code zeigen:

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. 

Antwort

8

Ich sehe zumindest zwei Fragen hier:

  • Sie müssen Bestand erhöhen Ihre hören; Es ist standardmäßig auf 5 eingestellt. Sie können es erhöhen, indem Sie in Ihren Listenoptionen {backlog, N} einstellen, z. B. {backlog, 1024}.

  • Ihre server/2 Funktion fehlerhaft ist, weil es eine Verbindung akzeptiert, spawnt dann einen neuen Prozess loop/1 zu laufen, aber es nicht, dass die neuen Verfahren für die akzeptierte Fassung der controlling process machen. Die loop/1 Funktion versucht, {active,once} Modus auf dem Socket in einem Versuch, eingehende Nachrichten zu empfangen, aber da es nicht im Controlling-Prozess ausgeführt wird, wird es nicht funktionieren. (Sie sollten den Rückgabewert von inet_setopts/2 überprüfen, indem ok = inet:setopts(S,[{active,once}]), es stattdessen zu sagen.)

Statt die Schleife von Laich, sollten Sie stattdessen einen neuen Akzeptor laichen, wie folgt aus:

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. 

Mit diesem Ansatz , der Prozess, der den Socket akzeptiert, läuft loop/1 und so gibt es keine Notwendigkeit, den Steuerungsprozess des Sockets zu ändern.

+0

In der Tat, Sie hatten Recht mit diesen beiden Fragen. Ich habe diese behoben. Jetzt, wenn ich connect_clients (1000, 9999) anrufe, verbindet es sich ungefähr 100 pro Sekunde und es geht zu 800; dann hört es auf. Der Server stürzt nicht ab, so dass ich ihn erneut anrufen kann, indem ich einen Client oder die Nummer, die ich möchte, anschließe. Aber ich kann nicht mehr als etwa 800 pro Anruf verbinden. Ich wünschte, ich könnte connect_clients (1000000, 9999) aufrufen, aber dann friert meine VM ein. Irgendwelche Gedanken? Danke für das, was Sie mir bereits geholfen haben. –

+1

Denken Sie auch daran, dass Sie mehrere Akzeptor-Prozesse haben können, die 'gen_tcp: accept/1' auf demselben Listen-Socket aufrufen, um einen besseren Durchsatz beim Akzeptieren zu erreichen. –

+0

@ ŞtefanStan schwer zu sagen, welche neuen Probleme Sie treffen. Laufen Sie mit 'sasl' aktiviert? Sie können es starten, um sicherzustellen, dass Sie keine Probleme bemerken, die unbemerkt bleiben. Sind Sie sicher, dass Ihr Betriebssystem richtig eingerichtet ist, um die Anzahl der benötigten Verbindungen zuzulassen? Was bedeutet "ulimit -n" in deiner Shell? Welche Version von Erlang/OTP benutzt du? –