Во-первых, я хотел бы извиниться, я даю так много информации, чтобы было как можно яснее понять, в чем проблема. Пожалуйста, дайте мне знать, если что-то еще нужно прояснить.
(работает erlang R13B04, ядро 2.6.18-194, сентос 5.5)
У меня очень странная проблема. У меня есть следующий код для прослушивания и обработки сокетов:
%Opts used to make listen socket
-define(TCP_OPTS, [binary, {packet, raw}, {nodelay, true}, {reuseaddr, true}, {active, false},{keepalive,true}]).
%Acceptor loop which spawns off sock processors when connections
%come in
accept_loop(Listen) ->
case gen_tcp:accept(Listen) of
{ok, Socket} ->
Pid = spawn(fun()->?MODULE:process_sock(Socket) end),
gen_tcp:controlling_process(Socket,Pid);
{error,_} -> do_nothing
end,
?MODULE:accept_loop(Listen).
%Probably not relevant
process_sock(Sock) ->
case inet:peername(Sock) of
{ok,{Ip,_Port}} ->
case Ip of
{172,16,_,_} -> Auth = true;
_ -> Auth = lists:member(Ip,?PUB_IPS)
end,
?MODULE:process_sock_loop(Sock,Auth);
_ -> gen_tcp:close(Sock)
end.
process_sock_loop(Sock,Auth) ->
try inet:setopts(Sock,[{active,once}]) of
ok ->
receive
{tcp_closed,_} ->
?MODULE:prepare_for_death(Sock,[]);
{tcp_error,_,etimedout} ->
?MODULE:prepare_for_death(Sock,[]);
%Not getting here
{tcp,Sock,Data} ->
?MODULE:do_stuff(Sock,Data);
_ ->
?MODULE:process_sock_loop(Sock,Auth)
after 60000 ->
?MODULE:process_sock_loop(Sock,Auth)
end;
{error,_} ->
?MODULE:prepare_for_death(Sock,[])
catch _:_ ->
?MODULE:prepare_for_death(Sock,[])
end.
Вся эта установка прекрасно работает и работает последние несколько месяцев. Сервер работает как сервер передачи сообщений с длительными tcp-соединениями и в среднем поддерживает около 100 тыс. Соединений. Однако сейчас мы пытаемся использовать сервер более интенсивно. Мы делаем два длительных соединения (в будущем, вероятно, больше) с сервером erlang и делаем несколько сотен команд каждую секунду для каждого из этих соединений. Каждая из этих команд, в общем случае, порождает новый поток, который, вероятно, будет выполнять какое-то чтение из mnesia и отправлять некоторые сообщения, основанные на этом.
Странность возникает, когда мы пытаемся проверить эти два командных соединения. Когда мы включаем поток команд, любое новое соединение имеет примерно 50% вероятности зависания. Например, при использовании netcat, если я должен был подключиться и отправить по строке «blahblahblah», сервер должен немедленно вернуть ошибку. При этом он не будет делать никаких вызовов вне потока (поскольку все, что он делает, это пытается проанализировать команду, что не удастся, потому что blahblahblah не является командой). Но примерно в 50% случаев (когда запущены два командных соединения) при наборе blahblahblah сервер просто простаивает 60 секунд, а затем возвращает эту ошибку.
Пытаясь отладить это, я поднял wireshark. Рукопожатие tcp всегда происходит немедленно, и когда первый пакет от клиента (netcat) отправляется, он сразу же получает подтверждение, сообщая мне, что стек tcp ядра не является узким местом. Мое единственное предположение, что проблема заключается в функции process_sock_loop. У него есть прием, который вернется к началу функции через 60 секунд и снова попытается получить больше из сокета. Я думаю, что происходит следующее:
- Соединение установлено, поток переходит к process_sock_loop
- {активно, один раз} установлено
- Поток получает, , но не получает данные, даже если он там есть
- Через 60 секунд нить возвращается к вершине process_sock_loop
- {активный, один раз} устанавливается снова
- На этот раз данные поступают, все идет как обычно
Почему это так, я понятия не имею, и когда мы отключаем эти два командных соединения, все возвращается в норму, и проблема исчезает.
Есть идеи?