Erlang同时连接1M客户端

Posted

技术标签:

【中文标题】Erlang同时连接1M客户端【英文标题】:Erlang simultaneously connect 1M clients 【发布时间】:2015-12-19 02:22:22 【问题描述】:

我有一个问题:我想创建一个可以同时保持 1M 开放 tcp 连接的 Erlang 服务器。我调整了我的操作系统(Oracle Linux 7)来提升文件描述符。 在我做的服务器上 gen_tcp:听

// point_1 套接字 = gen_tcp:accept spawn(handle(Socket)) // 另一个线程 返回点_1

如果我按顺序连接没问题,在 100 秒内我连接了 10 万个客户端;但我没有耐心等待更多。

如果我想以 conncurent 方式连接它们,例如,只有大约 80 个连接来自 100 个。

这就是我运行一切的方式:

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

// 启动一台服务器,监听 9999 端口

ex:start(1, 9999) 

// 100 个客户端尝试连接端口 9999

ex:connect_clients(100, 9999)

让我给你看一些代码:

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.

【问题讨论】:

【参考方案1】:

我在这里至少看到两个问题:

您需要提高收听积压;默认为 5。您可以通过在收听选项中设置 backlog, N 来提高它,例如,backlog, 1024

您的server/2 函数有问题,因为它接受了一个连接,然后生成了一个新进程来运行loop/1,但它不会使该新进程成为接受套接字的controlling process。 loop/1 函数尝试在套接字上设置active,once 模式以尝试接收传入的消息,但由于它没有在控制进程中运行,所以它不会工作。 (您应该在此处使用ok = inet:setopts(S,[active,once]), 来验证inet_setopts/2 的返回值。)

您应该生成一个新的接受器,而不是生成循环,如下所示:

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.

通过这种方法,接受套接字的进程运行loop/1,因此无需更改套接字的控制进程。

【讨论】:

确实,您在这两个问题上是对的。我修好了那些。现在,当我调用 connect_clients(1000, 9999) 时,它每秒连接大约 100 个,然后达到 800 个;然后它停止。服务器不会崩溃,所以我可以再次调用它,连接一个客户端或我想要的号码。但我每次通话不能连接超过 800 个左右。我希望我可以调用 connect_clients(1000000, 9999),但随后我的虚拟机冻结了。有什么想法吗?感谢您为我提供的帮助。 另外,请记住,您可以有多个接受者进程在同一个侦听套接字上调用gen_tcp:accept/1,以便在接受时获得更好的吞吐量。 @ȘtefanStan 很难说你遇到了什么新问题。您是否在启用sasl 的情况下运行?您可以启动它以确保您没有遇到任何可能被忽视的问题。您确定您的操作系统设置正确以允许您需要的连接数吗?您的外壳中的ulimit -n 表示什么?另外,您使用的是什么版本的 Erlang/OTP?

以上是关于Erlang同时连接1M客户端的主要内容,如果未能解决你的问题,请参考以下文章

Erlang 更改 VM 进程的初始大小。调整 Erlang 虚拟机

Erlang网络通信

Erlang 建立TCP连接后,怎么样从返回的Socket获取客户端ip和端口

Erlang 作为后端进程

XMPP 服务器的 Erlang 客户端

为啥代码版本控制器在 erlang 中不起作用?