[erlang-questions] Comet server broadcasting messages to 40k clients

Adriano Bonat adrianob@REDACTED
Wed Jan 21 04:20:57 CET 2009


Hi,

I implemented a simple Comet server using gen_tcp (code follows below
this text), and I'm doing tests with 40k connected clients. With a
certain event, a message will be broadcasted to all connected clients,
in my current implementation you just have to access the URI "/send".

The current problem is that the first connected client will receive
the message after ~5s (in my implementation a new client goes in the
list's head), and this time is so high compared to a C version of the
server. I tried to understand where time is being spent using tools
like eprof and fprof, but I'm not that experienced trying to
understand their results.

This makes me think if I'm doing something wrong, if I can optimize it
in some way... or if this is the normal overhead imposed by running on
top of a VM...

Any ideas?

Thanks.


-module(myhttpd).

-compile(export_all).

-define(LISTEN_PORT, 8000).
-define(LISTEN_OPTS, [list, {packet, 0}, {keepalive, true}, {active, true}]).

start() ->
    register(router, spawn(fun handle_connections/0)),

    {ok, Listen} = gen_tcp:listen(?LISTEN_PORT, ?LISTEN_OPTS),
    spawn(fun() -> accept_connection(Listen) end).

accept_connection(Listen) ->
    {ok, Socket} = gen_tcp:accept(Listen),
    router ! {accepted, Socket},
    gen_tcp:controlling_process(Socket, whereis(router)),
    accept_connection(Listen).

handle_connections() ->
    handle_connections([]).

handle_connections(Sockets) ->
    receive
        {accepted, Socket} ->
            io:format("Just accepted ~p~n", [Socket]),
            handle_connections([Socket|Sockets]);
        {tcp, Socket, "GET /send" ++ _T} ->
            io:format("Sending messages.. ~n", []),
            TStart = now(),
            spawn(fun() ->
                lists:foreach(fun(S) -> send_chunk(S,
io_lib:format("(~b usecs) Hello world!<br>", [timer:now_diff(now(),
TStart)])) end, Sockets)
            end),
            gen_tcp:send(Socket, "HTTP/1.1 200 OK\r\n\r\nDone."),
            gen_tcp:close(Socket),
            handle_connections(Sockets -- [Socket]);
        {tcp_closed, Socket} ->
            io:format("Closed connection ~p~n", [Socket]),
            handle_connections(Sockets -- [Socket]);
        {tcp, Socket, X} ->
            io:format("Rcved: ~p~n", [X]),
            start_response(Socket),
            handle_connections(Sockets);
        {numclients} ->
            io:format("Num clients: ~p~n", [length(Sockets)]),
            handle_connections(Sockets);
        Any ->
            io:format("Just got ~p~n", [Any]),
            handle_connections(Sockets)
    end.

start_response(Socket) ->
    gen_tcp:send(Socket, "HTTP/1.1 200 OK\r\nContent-type:
text/html;charset=utf-8\r\nTransfer-encoding: chunked\r\n\r\n").

end_response(Socket) ->
    gen_tcp:send(Socket, "0\r\n\r\n").

send_chunk(Socket, Chunk) ->
    gen_tcp:send(Socket, io_lib:format("~.16b\r\n~s\r\n",
[iolist_size(Chunk), Chunk])).



More information about the erlang-questions mailing list