【问题标题】:One listen socket for many processes一个监听多个进程的套接字
【发布时间】:2021-12-05 00:14:25
【问题描述】:

我有一个非常简单的问题,我想创建 20 个子进程,每个子进程都有相同的监听套接字,有 2 种方法我只想知道两者之间有什么区别:

module(sup). 
.....
start() ->
supervisor:start_link({local,?MODULE},?MODULE,[]).
%%%%%%%%%%%%%%%%%%

-------第一种方法------

init([]) ->
Listen=gen_tcp:listen(....),
spawn(fun() ->start_children(20) end), 
{ok,{{simple_one_for_one,5,1},[{child,{ChildModule,start,[Listen]},....}]}}.
%%%%%%%%%%%%%%%%%
start_children(N) ->
[supervisor:start_child(?MODULE, [])||_ <-lists:seq[1,N]], 
ok. 

这是一个 simple_one_for_one 树,我只是创建一个监听套接字并将其设置为每个启动的进程的参数,这些进程将在稍后处理它,我产生了一个新进程来运行 start_children/1,因为这个函数调用主管,稍后再调用在它的init/1function 中,它不能在它自己的启动之前启动子进程,所以进程将等待 sup 的启动来调用它,让我们看看第二种方法: ---------第二种方法---------

init([]) ->
ChildSpecs=[{Id,{ChildModule,start,[fun createListenSocket/0]},....}||Id <-lists:seq[1,20]] 
{ok,{{one_for_one,5,1},ChildSpecs}}.
%%%%%%%%%%%%%%%%%%%%
createListenSocket() ->
gen_tcp:listen(....).

这是一个 one_for_one 树,sup 在开始时创建了 20 个子节点,有 20 个套接字:每个子节点一个套接字,所以问题是:这两种方法是相同的还是不同的?如果我们认为它们是相同的,则意味着侦听套接字只是一个变量,并且套接字中的特殊事物(侦听传入连接)在我们运行 gen_tcp:accept/1 时开始。 因为如果不是,我们会遇到第一种方法中 20 个进程共享同一个侦听套接字的情况。

编辑:

好吧,我认为 José 已经回答了我的问题,但他的回答给了我另一个问题:如何在 Erlang 中创建多个具有相同端口和 IP 地址的套接字?因为如果我想为每个节点运行 20 个套接字,则 ip 是本地 IP 地址,并且所有套接字都相同,并且在我只需要一个指定端口用于应用程序的情况下,端口也相同?有一个选项{reuseaddr, true} 作为gen_tcp:listen 的参数,但它可以在我们为不同的IP 地址使用相同的端口并且Erlang 中没有reuseport 时使用,那该怎么办?

【问题讨论】:

    标签: sockets tcp erlang elixir erlang-otp


    【解决方案1】:

    免责声明:这个答案是关于 Linux 的。

    不,这两种方法不一样,监听套接字是操作系统结构。一个创建一个监听套接字,另一个创建 20 个监听套接字(或者至少尝试这样做,因为最后 19 个会失败,除非您启用端口重用。您在 this question 中有关于此选项的精彩解释)。

    您可以使用sudo ss -punta | grep LISTEN检查监听套接字

    在具有多个侦听套接字的操作系统级别,连接的负载平衡由内核执行,忽略同级套接字是否有任何操作系统线程等待accept。因此,在拥有单个侦听套接字时,您可能会在接受队列中遇到争用,而拥有多个侦听套接字时,您可能会看到延迟差异,因为一个 OS 线程具有更多负载。 (在this cloudflare blog entry中有详细说明)

    编辑:

    reuseport 仅适用于newish low-level socket interface,如果您使用gen_tcp,则仅适用于reuseaddr

    话虽如此,请记住,当您在 erlang 中时,您处于上一层:阻塞 gen_tcp:accept 不需要(而且很可能不是 *)OS-level blocking accept。同样,我之前提到的 OS 线程不是 erlang 线程,而是 BEAM 调度程序。在大多数情况下,拥有一个监听套接字就可以了。

    *由于依赖于 erlang 代码的调度程序(操作系统线程)和任意数量的套接字数量有限,因此使用 blocking accept 而不是 epoll 将是一场噩梦。

    【讨论】:

    • 我认为上一篇博客回答了所有这些问题,但我对 Linux 不是很熟悉 iam Unix 粉丝
    • 现在我只想到 ONE Socket 和 MANY 接受者的模型:一个监听套接字gen_tcp:listen(....) 是一个可以被许多人同时使用的大窗口,服务器是家。接受者站在窗口,当连接请求到来时,接受者将接受它并打开一个新的小窗口,这将是客户端和服务器之间的通信方式,这个新窗口是 Accepted SocketSocket=gen_tcp:accept(Listen) 我只想知道我是对还是错。
    • @Michael 是的,就是这样,确切的行为取决于它是使用阻塞 accept 还是 epoll,但本质上就是这样。
    • 如果你想看的话,我已经发布了另一个关于套接字的问题
    猜你喜欢
    • 1970-01-01
    • 2010-10-14
    • 1970-01-01
    • 1970-01-01
    • 2014-02-07
    • 1970-01-01
    • 2021-02-12
    • 2013-06-17
    • 2018-11-23
    相关资源
    最近更新 更多