2

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

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

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

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

我有一个监督树,其中host_supervisor监视所有监视器,hostconnection_supervisor监视每个监视器。,都由应用程序主管监督,这意味着它们是监督树中的第一级子级。不过是下。connectionhost_agent_supervisoragenthost_supervisorconnection_supervisorhost_agent_supervisorconnection_supervisor

问题:

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

2 回答 2

3

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

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

  2. 一种简单的方法是每次“主机代理”启动时,它都会产生另一个进程,该进程除了链接到“主机代理”之外什么都不做,并从“主机”时从您拥有的任何存储中删除主机 ID 到代理 PID 映射代理”死了。另一种方法是使映射存储进程本身链接到您的主机代理 PID,这可能使您不必担心可能的竞争条件。

  3. 可能。当我阅读您的问题时,我留下了某些问题,并且总体感觉我选择的解决方案不会引导我找到您所询问的精确查找问题(即在收到“主机代理”的 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 的进程,诸如此类?

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

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

在这里,'map_server' 只维护主机 ID 到 PID 的映射hostssvc_listener具有侦听套接字,并且只接受连接并要求在新客户端连接时hosts_sup产生host新的连接,并且host进程(在 下hosts_sup)负责接受的套接字, 并map_server在它们启动时注册主机 ID 和它们的 PID 。

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

于 2016-01-21T14:58:59.753 回答
1

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

supervisor:which_children(SupRef) -> [{Id, Child, Type, Modules}]
于 2016-01-21T13:02:38.807 回答