对不起,我是一个Erlang新手,可能会提出愚蠢的问题.但请帮我解决这个问题.
我已经编写了一个Erlang服务器来替换我正在使用的Node.js,它占用了我所有的记忆,我正在祈祷Erlang可能是一个出路.服务器在单元测试和内部测试下正常工作,但在压力测试中面临高CPU使用率.
在减少之后,我发现CPU突发是由于客户端的TCP接收造成的.
receiveClientPacket(Sock) ->
inet:setopts(Sock, [{active, once}, {buffer, ?CLIENTHEARTBEATSIZE}]),
receive
{tcp, Sock, Data} ->
{ok, Data};
{tcp_closed, Sock} ->
{error, closed}
after ?CLIENTRECCEIVETIMEOUT ->
{error, timeout}
end.
Run Code Online (Sandbox Code Playgroud)
我试着在函数开始时让进程休眠10个小时(以防止它调用receive),CPU根本没有爆发.因此我得出结论,CPU的突发是由于TCP接收造成的.(如果我犯了任何错误,请纠正我)
以下是有关压力测试的信息:
我正在为Erlang服务器使用Amazon Linux AMI(大型实例,64位).是由于Linux的爆发?因为我不知道系统将如何耗尽CPU.或者这是我糟糕的代码问题?(我相信是这样...)
在实际情况中,我们的服务器不仅接收乒乓球,还接收消息,这是更多的负载...这只是第一步......
数以百万计的感谢任何能救我的人.
梅艳芳〜*
~~~~~~~~~~~~~~~~~~~~~~~
有关大型实例的信息(供参考):
我正在尝试学习Erlang来做一些简单但可扩展的网络编程.我基本上想要编写一个程序来完成互联网主干上的服务器 - 但规模较小.我想尝试建立一个内部网,其中包含可访问Web的服务器,这些服务器将充当内部网[sic]的网关,并将数据路由到连接的客户端和/或其他网关.
高流量来自这样一个事实:数据不仅会从客户端流向网关而是流向客户端,而是可能需要在几个网关周围反弹才能到达目的地(就像数据在互联网上传播一样).这意味着网关不仅要处理来自客户端的流量,还要处理来自其他网关客户端的流量.
我认为这会导致异常高的流量,即使对于中等数量的客户端和网关也是如此.
来自Python的背景,以及其他脚本语言的较小程度,我习惯于挖掘自定义模块来解决我的问题.我知道Erlang是专为高流量网络编程而设计的,但我能找到的所有这些东西的库/模块都是gen_tcp.
这是否意味着Erlang已经针对这种事情进行了优化,您可以使用其最基本的模块来启动它并期望它可以很好地扩展?
我有一个问题:我想创建一个可以保持1M同时打开TCP连接的Erlang服务器.我调整了我的操作系统(Oracle Linux 7)来提高文件描述符.在服务器上我做gen_tcp:listen
// point_1
Socket = gen_tcp:accept
spawn(handle(Socket))//另一个线程
返回point_1
如果我按顺序连接没问题,在100秒内连接100K客户端; 但我没有更多的信仰.
如果我想以一种连续的方式连接它们,例如,只有大约80个连接来自100.
这就是我运行一切的方式:
erlc *.erl
erl +Q 134217727 +P 1000000 -env ERL_MAX_PORTS 40960000 -env ERTS_MAX_PORTS 40960000
Run Code Online (Sandbox Code Playgroud)
//启动一个将侦听端口9999的服务器
ex:start(1, 9999)
Run Code Online (Sandbox Code Playgroud)
// 100个客户端尝试在端口9999上连接
ex:connect_clients(100, 9999)
Run Code Online (Sandbox Code Playgroud)
我来告诉你一些代码:
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); …
Run Code Online (Sandbox Code Playgroud) 我正在使用本教程中的socket_server 以及客户端和服务器的以下代码:
服务器:
-module(echo_server).
-export([start/0, loop/1]).
% echo_server specific code
start() ->
spawn(socket_server, start, [?MODULE, 7000, {?MODULE, loop}]).
loop(Socket) ->
case gen_tcp:recv(Socket, 0) of
{ok, Message} ->
Msg = binary_to_term(Message),
case Msg of
start ->
io:format("Got start message on socket ~p.~n", [Socket]),
send_count(Socket, 10),
gen_tcp:close(Socket);
Other ->
io:format("Got message on socket ~p: ~p~n",
[Socket, Other])
end;
{error, closed} ->
io:format("Got closed message on socket ~p.~n", [Socket]),
ok;
Error ->
io:format("Got bad message: ~p on socket ~p.~n", [Error, Socket]) …
Run Code Online (Sandbox Code Playgroud) 我正在尝试做的是让gen_server进程接受一个新客户端并立即生成一个新子进程来处理下一个客户端.我看到的问题是,当套接字完成并因此终止时,它也会关闭监听套接字,我无法找出原因,即使它不再引用它.
知道我做错了什么吗?
gen_server:
-module(simple_tcp).
-behaviour(gen_server).
%% API
-export([start_link/1, stop/0, start/0, start/1]).
%% gen-server callbacks
-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]).
-define(SERVER, ?MODULE).
-define(DEFAULT_PORT, 1055).
-record(state, {port, lsock}).
start_link({port, Port}) ->
gen_server:start_link(?MODULE, [{port, Port}], []);
start_link({socket, Socket}) ->
gen_server:start_link(?MODULE, [{socket, Socket}], []).
start({port, Port}) ->
simple_tcp_sup:start_child({port, Port});
start({socket, Socket}) ->
simple_tcp_sup:start_child({socket, Socket}).
start() ->
start({port, ?DEFAULT_PORT}).
stop() ->
gen_server:cast(?SERVER, stop).
% Callback functions
init([{port, Port}]) ->
{ok, LSock} = gen_tcp:listen(Port, [{active, true},{reuseaddr, true}]),
init([{socket, LSock}]);
init([{socket, Socket}]) ->
io:fwrite("Starting server with …
Run Code Online (Sandbox Code Playgroud) 使用半连接的连接,我的意思是客户端的调用connect()
成功返回的连接,但服务器调用accept()
没有.这可以通过以下方式发生:客户端调用connect()
,从而生成SYN
到服务器的数据包.服务器进入状态SYN-RECEIVED
并向SYN-ACK
客户端发送数据包.这会导致客户端回复ACK
,进入状态ESTABLISHED
并从connect()
呼叫返回.如果最终ACK
丢失(或忽略,由于在服务器上,这可能是更可能的情况全接受队列),服务器仍处于状态SYN-RECEIVED
和accept()
不返回.由于与SYN-RECEIVED
状态相关的超时SYN-ACK
将重新发送,允许客户端重新发送ACK
.如果服务器能够ACK
最终处理,它也将进入状态ESTABLISHED
.否则它最终将重置连接(即发送RST
到客户端).
您可以通过在单个侦听套接字上启动大量连接来创建此方案(如果不调整积压工作tcp_max_syn_backlog
).有关更多详细信息,请参阅此问题和本文.
我进行了几次实验(使用此代码的变体)并观察了一些我无法解释的行为.所有使用Erlang gen_tcp
和当前Linux 执行的实验,但我强烈怀疑答案并非特定于此设置,所以我试图在这里保持更一般.
connect()
- >等 - > send()
- >receive()
我的出发点是从客户端建立连接,等待1到5秒,向服务器发送"Ping"消息并等待回复.通过这种设置,我观察到当我有一个半连接时receive()
失败并出现错误closed
.在send()
半连接的连接期间从未出现过错误.您可以在此处找到有关此设置的更详细说明.
connect()
- >漫长的等待 - > send()
要看,如果我在半连接的连接上发送数据时可能会出错,我会在发送数据前等待4分钟.4分钟应涵盖与半连接相关的所有超时和重试.仍然可以发送数据,即send() …
我想用 Erlang 管理 HTTP 或 RTSP 会话。
例如,RTSP 协议的标准会话如下所示:
OPTIONS rtsp://192.168.1.55/test/ RTSP/1.0\r\n
CSeq: 1\r\n
User-Agent: VLC media player (LIVE555 Streaming Media v2008.07.24)\r\n
...
PLAY rtsp://192.168.1.55/test/ RTSP/1.0\r\n
CSeq: 5\r\n
Session: 1\r\n
Range: npt=0.000-\r\n
User-Agent: VLC media player (LIVE555 Streaming Media v2008.07.24)\r\n
Run Code Online (Sandbox Code Playgroud)
每条消息的长度是不同的。对于 erlang,gen_server:listen
使用一个选项{active, true}
(允许获取无限数量的数据)或{active, false}
(获取固定长度的数据)。
是否有推荐的方法来获取和解析此类长度可变的消息?
在我输入gen_
然后在 erlang shell 中输入 Tab 之后,我得到gen_server
, gen_event
,但我没有得到gen_tcp
. 我如何gen_tcp
在 Erlang shell 中使用?
我有一个客户端创建N个进程,所有连接到服务器,如下所示:
send(State = #state{low = Low, high = Low}) ->
NewState = receive_sockets(0, Low, State),
NewState;
send(State = #state{low = Low}) ->
N = Low rem 10,
Dest = lists:nth(N + 1, State#state.dest),
spawn(?MODULE, loop, [self(), Dest, Low]),
NewState = State#state{low = Low + 1},
send(NewState).
loop(From, {IP, Port}, Low) ->
case gen_tcp:connect(IP, Port, [binary]) of
{ok, Socket} ->
gen_tcp:send(Socket, integer_to_binary(Low)),
From ! {Low, Socket},
loop1(Socket);
%%timer:sleep(infinity);
_Else ->
io:format("The connection failed ~n"),
loop(From, {IP, Port}, Low)
end.
loop1(Socket) -> …
Run Code Online (Sandbox Code Playgroud) 我正在阅读编程Erlang 2E.在第17章的主动和被动套接字中,它说:
您可能认为对所有服务器使用被动模式是正确的方法.不幸的是,当我们处于被动模式时,我们只能等待来自一个套接字的数据.这对于编写必须等待来自多个套接字的数据的服务器毫无用处.
幸运的是,我们可以采用混合方法,既不阻塞也不阻塞.我们使用选项{active,once}打开套接字.在此模式下,套接字处于活动状态,但仅适用于一条消息.在控制进程发送消息后,它必须显式调用inet:setopts以重新启用下一条消息的接收.系统将阻止,直到发生这种情况.这是两全其美的.
相关代码:
% passive mode
loop(Socket) ->
?case? gen_tcp:recv(Socket, N) ?of??
{ok, B} ->?
... do something with the data ...?
loop(Socket);?
{error, closed}?
...?
?end?.
% once mode
loop(Socket) ->
?receive??
{tcp, Socket, Data} ->?
... do something with the data ...?
?%% when you're ready enable the next message??
inet:setopts(Sock, [{active, once}]),?
loop(Socket);?
{tcp_closed, Socket} ->?
...?
?end?.
Run Code Online (Sandbox Code Playgroud)
我没有看到两者之间有任何真正的区别.gen_tcp:recv
在passive
模式本质上是做同样的事情receive
在once
模式.once
模式如何解决此passive
模式问题: …
erlang ×10
gen-tcp ×10
sockets ×4
erlang-otp ×2
tcp ×2
asynchronous ×1
connect ×1
cpu ×1
erlang-shell ×1
gen-server ×1
http ×1
recv ×1
rtsp ×1
web-services ×1