如何跟踪erlang中的子进程?

时间:2016-01-21 12:13:26

标签: erlang child-process supervisor

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

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

主持人保留了主机信息。主机代理处理传入的数据,将主机信息保存到主机并决定以什么格式发送什么(例如,ack到具有主机ID和响应代码的客户端)。

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

我有一个监督树,其中host_supervisor监控所有hostconnection_supervisor监控每个connectionhost_agent_supervisor监控agenthost_supervisorconnection_supervisor都由应用程序主管监督,这意味着他们是监督树中的第一级孩子。但host_agent_supervisor位于connection_supervisor

问题:

  1. 使用host_id和map将地图存储到db中是个好主意 host_agent_pid对?
  2. 如果1.为true,则如何更新host_agent_pid 当出现问题并重启代理时?
  3. 有没有更好的想法来实施这个案子?似乎我的解决方案不遵循“erlang方式”。

2 个答案:

答案 0 :(得分:3)

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

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

  2. 这样做的一个简单方法是每次一个主机代理"开始时,它会生成另一个进程,它只会链接到"主机代理"并从主机代理商#34;中删除主机ID到代理PID映射。死亡。另一种方法是使映射存储过程本身链接到您的主机代理PID,这可能会减少对可能的竞争条件的关注。

  3. 可能。当我读到你的问题时,我留下了一些问题和一般的感觉,我选择的解决方案不会引导我查询你所询问的精确查找问题(即查找"主机的PID代理"收到TCP数据包后),但我不能确定这不是因为您已经努力将问题最小化为Stack Overflow。我有点不清楚你的主人,#34;主持人",#34; host_agent"的角色,责任和互动情况。和"连接"过程确实是,如果它们都应该存在和/或有单独的监督树。

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

    无论哪种方式......通常在这种情况下,我会期望一个新的过程("主机代理"通过它的声音在你的情况下)将产生处理新建立的TCP连接(通过动态(例如简单的一对一)监督),取得作为该连接的服务器端端点的套接字的所有权;适当地读取和写入套接字,并在连接关闭时终止。

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

    现在永远不会出现在收到TCP数据包时查找代理的PID的需要。

    如果您出于其他原因需要查找代理的PID,因为您的服务器有时需要主动将数据发送到可能已连接的主机",那么您必须获取一个列表所有受监督的主持人代理人#34;并挑出正确的一个(为此你将使用主管:which_children / 1,根据Hamidreza的回答)或者您将使用map,gb_trees,dict,ets等维护主机ID到PID的映射。哪个是正确的取决于有多少"主机"你可以拥有 - 如果它超过少数,那么你应该可以维持某种地图,这样查找时间就不会太大。

    最后评论,如果您认为它已经用于您的案例,您可以考虑查看gproc。它做了这样的事情。

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

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

    我不明白你主持人的观点是什么?过程,你说"主机保持主机信息"这听起来像只是一个包含主机名或主机ID的进程,类似的东西?

    你还说"它指定了源主机和目标主机,这意味着它由源主机发送并且应该由目标主机接收"这开始使这个声音有点像聊天服务器,或至少某种hub spoke / star network式通信协议。我不明白为什么你不能通过这样创建一个监督树来做你想做的一切:

            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进程(在map_server下)负责接受的套接字,并在启动时用map_server注册主机ID及其PID。

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

答案 1 :(得分:1)

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

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