如何跟踪erlang中的子进程?

Posted

技术标签:

【中文标题】如何跟踪erlang中的子进程?【英文标题】:How to keep track of children processes in erlang? 【发布时间】:2016-04-27 16:25:39 【问题描述】:

我有一个包含信息的“主机”静态列表,以及一个“主机代理”的动态列表。每台主机只要通过 TCP 连接连接到服务器,就只有一个代理。由于主机可能已连接或未连接,因此其代理进程可能会或可能不会启动。当带有主机 ID 的 TCP 数据包到达时,我需要确定该主机的“代理”是否已启动。

Connection 负责从 tcp 套接字接收和发送数据,解析数据以找出它应该发送到哪个主机并交付给它的主机代理处理。

主机保存主机信息。主机代理处理传入数据,将主机信息保存到主机并决定以什么格式发送什么(例如,使用主机 ID 和响应代码向客户端确认)。

并且在数据包中,它指定了源主机和目标主机,也就是说它是由源主机发送的,应该由目标主机接收。在这种情况下,目标主机可以连接到另一个连接中。这就是为什么需要所有连接的全局映射,以便于获取目标主机代理 pid。

我有一个监督树,其中host_supervisor 监控所有hostconnection_supervisor 监控每个connectionhost_agent_supervisor 监控agenthost_supervisorconnection_supervisor 都由应用程序主管监督,这意味着它们是监督树中的第一级子项。但是host_agent_supervisorconnection_supervisor 之下。

问题:

    使用 host_id 将地图存储到数据库中是个好主意吗? host_agent_pid 对? 如果1.为真,如何更新host_agent_pid 当出现问题并重新启动代理时? 有没有更好的办法来实现这个案例?看来我的解决方案不遵循“erlang 方式”。

【问题讨论】:

【参考方案1】:

对您的问题的简单或快速回答是:

    没关系,不过除了地图之外,您还可以使用 gb_trees、dict 或 ETS 表(当然,地图是所有这些中最不成熟的)。然而,尽管如此,PID 查找表的键/ID 原则上是可以的。 ETS 可能会比其他进程带来性能优势,因为您可以创建一个可以从其他进程访问的 ETS 表,从而消除了单个进程执行所有读取和写入的必要性。这可能重要也可能不重要和/或适当。

    执行此操作的一种简单方法是,每次“主机代理”启动时,它都会生成另一个进程,该进程除了链接到“主机代理”并从任何存储中删除主机 ID 到代理 PID 映射之外什么都不做当“主机代理”死亡时,您就有了。另一种方法是使映射存储进程本身链接到您的主机代理 PID,这可能会让您减少对可能的竞争条件的关注。

    可能。当我阅读您的问题时,我留下了某些问题,并且普遍认为我选择的解决方案不会引导我找到您所询问的精确查找问题(即在收到“主机代理”的 PID 时查找TCP 数据包),但我不能确定这不是因为您已经努力将您对 Stack Overflow 的问题最小化。我有点不清楚您的“主机”、“主机代理”和“连接”进程的角色、职责和交互到底是什么,以及它们是否都应该存在和/或有单独的监督树。

所以,寻找可能的替代方案...当您说“当 TCP 数据包到达时”时,我假设您的意思是当外部主机连接到侦听套接字或在已接受的现有套接字上发送一些数据时,并且主机ID 可以是主机名(和/或端口),也可以是外部主机在连接后发送给您的其他任意 ID。

无论哪种方式...通常在这种情况下,我希望会产生一个新进程(在您的情况下是“主机代理”)来处理新建立的 TCP 连接(通过一个动态的(例如简单的一对一的)监督者),取得作为该连接的服务器端端点的套接字的所有权;根据需要读写套接字,并在连接关闭时终止。

使用该模型,如果已经有连接,则应始终启动“主机代理”,如果没有连接,则始终不启动,并且任何传入的 TCP 数据包将自动落入正确的代理手中,因为它将被传递到代理正在处理的套接字,或者如果它是一个新连接,代理将被启动。

现在不再需要在收到 TCP 数据包时查找代理的 PID。

如果您出于其他原因需要查找代理的 PID,因为假设您的服务器有时需要主动向可能连接的“主机”发送数据,那么您要么必须获取所有受监管的列表“主机代理”并选择正确的代理(为此,您将使用 supervisor:which_children/1,根据 Hamidreza 的回答)或者您将使用 map、gb_trees、dict、ets 等维护主机 ID 到 PID 的映射。是否正确取决于您可以拥有多少“主机” - 如果它不止少数,那么您可能应该维护某种地图,以便查找时间不会变得太大。

最后的评论,如果你还没有考虑过gproc,你可以考虑看看,以防你认为它对你的情况有用。它会做这种事情。

编辑/添加(在问题编辑之后):

你的连接过程对我来说是多余的;如上所述,如果您将套接字提供给主机代理,那么连接的大部分责任就消失了。主机代理没有理由无法解析它接收到的数据,据我所知,让另一个进程解析它没有任何价值,然后将它传递给另一个进程。解析本身可能是一个确定性函数,因此为它设置一个单独的模块是明智的,但我认为单独的过程没有意义。

我不明白你的“主机”进程的意义,你说“主机保留主机信息”,这听起来像是一个持有主机名或主机 ID 的进程,诸如此类?

你还说“它指定了源主机和目标主机,这意味着它是由源主机发送的,应该由目标主机接收”,这开始让这听起来有点像聊天服务器,或者至少是某种@ 987654321@风格的通讯协议。我不明白为什么你不能通过创建这样的主管树来做你想做的一切:

        top_sup
           |
     .------------------------------.
     |             |                |
map_server    svc_listener      hosts_sup (simple one to one)
                                    |
                        .----------------------------->
                        |    |    |    |   |    |

这里的 'map_server' 只是维护一个主机 ID 到 hosts 的 PID 的映射,svc_listener 具有监听套接字,并且只接受连接并要求 hosts_sup 在新的 host 生成一个新的客户端连接,host 进程(在hosts_sup 下)负责接受的套接字,并在启动时将主机 ID 及其 PID 注册到map_server

如果map_server 链接到host PID,它可以在host 死亡时自动清理,并且它可以为任何进程提供合适的API,以通过主机ID 查找host PID。

【讨论】:

【参考方案2】:

为了获取主管的子进程列表,您可以使用supervisor:which_children/1 API。它获取对您的主管的引用,可以是其注册名称或 PID,并返回其子级列表。

supervisor:which_children(SupRef) -> [Id, Child, Type, Modules]

【讨论】:

为了具体到我的用例,我需要遍历所有连接上的所有 host_agent_sup 以查找其上是否存在相关主机代理。似乎有点复杂。有没有更好的办法来解决这种情况? @gy8409 同样使用地图来跟踪复杂场景中的特殊流程也不是一个坏习惯。此映射可能位于协调器进程或 ets 表的状态中。

以上是关于如何跟踪erlang中的子进程?的主要内容,如果未能解决你的问题,请参考以下文章

如何在其他网络命名空间中的子进程与父进程之间正确通信?

父进程全局变量如何复制到python多处理中的子进程

如何附加到 LLDB 中的子进程

如何从 QT(C++)中的子进程联系父进程以执行类中的方法?

Erlang:supervisor(3),添加子进程

subprocess:删除Windows中的子进程