2015-09-22 12 views
7

Mam problem: chcę utworzyć serwer Erlang, który może utrzymywać jednoczesne otwarte połączenie TCP 1M. Dostroiłem system operacyjny (Oracle Linux 7), aby podnieść deskryptory plików. Na serwerze zrobić gen_tcp: ListenErlang jednocześnie łączą klientów 1M

// point_1
Socket = gen_tcp: przyjąć
ikry (rękojeść (Socket)) // inny wątek
pleców do point_1

Jeśli łączę sekwencyjnie nie ma problemu, w 100 s łączyłem klientów 100K; ale nie miałem więcej cierpliwości.

Jeśli chcę połączyć te w sposób conncurent, tylko około 80 połączeń jest na przykład ze 100.

ten sposób uruchomić wszystko:

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

// uruchomić jeden serwer, który będzie nasłuchiwał na porcie 9999

ex:start(1, 9999) 

// 100 klientów starają się łączyć na porcie 9999

ex:connect_clients(100, 9999) 

Pozwól mi pokazać jakiś kod:

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. 

Odpowiedz

8

widzę przynajmniej dwie kwestie tutaj:

  • Trzeba podnieść słuchać zaległości; wartość domyślna to 5. Możesz ją zwiększyć, ustawiając {backlog, N} w opcjach słuchania, np. {backlog, 1024}.

  • Twoja funkcja server/2 jest wadliwy, ponieważ akceptuje połączenie, a następnie ikra nowy proces uruchamiania loop/1 ale nie sprawiają, że nowy proces controlling process dla przyjętego gniazda. Funkcja próbuje ustawić tryb {active,once} na gnieździe w celu odebrania wiadomości przychodzących, ale ponieważ nie działa w procesie kontrolowania, nie zadziała. (Należy sprawdzić wartość zwracaną inet_setopts/2 mówiąc ok = inet:setopts(S,[{active,once}]), tam zamiast.)

Zamiast tarła pętlę, należy zamiast tarło nowy akceptor, tak:

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. 

Dzięki takiemu podejściu , proces, który zaakceptował gniazdo, działa loop/1 i dlatego nie ma potrzeby zmiany procesu kontrolowania gniazda.

+0

Rzeczywiście, miałeś rację w tych dwóch sprawach. Naprawiłem te. Teraz, gdy wywołuję connect_clients (1000, 9999), łączy on około 100 na sekundę i przechodzi do 800; to się zatrzymuje. Serwer nie ulega awarii, więc mogę zadzwonić ponownie, łącząc jednego klienta lub numer, który chcę. Ale nie mogę połączyć więcej niż około 800 na połączenie. Chciałbym móc wywołać connect_clients (1000000, 9999), ale wtedy moja VM zawiesza się. jakieś pomysły? Dzięki za to, co mi pomogłeś. –

+1

Pamiętaj też, że możesz mieć kilka procesów akceptujących wywoływanie 'gen_tcp: accept/1' na tym samym gnieździe nasłuchu, aby uzyskać lepszą przepustowość podczas akceptowania. –

+0

@ ŞtefanStan ciężko powiedzieć, jakie nowe problemy napotykasz. Czy używasz opcji 'sasl'? Możesz go uruchomić, aby upewnić się, że nie trafisz na żadne problemy, które mogą pozostać niezauważone. Czy jesteś pewien, że twój system operacyjny jest odpowiednio skonfigurowany, aby umożliwić liczbę połączeń, których potrzebujesz? Co oznacza "ulimit -n" w twojej powłoce? Jakiej wersji Erlanga/OTP używasz? –