【问题标题】:Managing multiple TCP Connections in active-mode from one GenServer从一个 GenServer 以主动模式管理多个 TCP 连接
【发布时间】:2016-12-06 00:47:36
【问题描述】:

我通常使用这种模式与单个 TCP 资源进行交互,在active-mode

  def connect(ip, port) do
    t = System.system_time(1000)
    case :gen_tcp.connect(ip, port, [:binary, active: :once, keepalive: true, nodelay: true]) do
      {:ok, socket} ->
        log "Connected to #{ip}:#{port} in #{System.system_time(1000) - t}ms"
        socket
      {:error, err} ->
        log "Connect Error - #{ip}: #{port} [#{inspect err}]"
        Process.send_after(self(), :retry_connect, 3000)
        nil
    end
  end

  def handle_info({:tcp, _, data}, s) do
    s = proc_raw(s.extra <> data, %{s | extra: ""})

    :inet.setopts(s.socket, active: :once)

    {:noreply, s}
  end

如何扩展它以处理同一GenServer 中的多个 TCP 连接?

到目前为止,这对于active-mode 中的单个 TCP 套接字非常有效

更新

每个 GenServer 都由主管管理。另外,每个 GenServer 代表一个客户端,每个客户端可能有 3-5 个 TCP 连接到一些外部资源。

TCP 连接会不时发生故障/重置,每次失败都会尝试重新连接,但主机 GenServer 不需要重新启动

【问题讨论】:

  • 为什么不希望每个连接一个进程?隔离连接是有意义的,因为单个连接可能会关闭,并且不应影响其他连接。
  • 每个 GenServer 用于单个客户端实例。每个客户端实例最多管理 5 个到相同 TCP 资源的 tcp 连接
  • @MartinSvalin 关闭或失败的 TCP 连接将被管理并重新启动,它不应使进程崩溃,因为这是预期的行为
  • 我还是会使用监督树。我不是每个客户端一个 GenServer,而是每个客户端一个主管,每个 tcp 连接一个 GenServer。我认为以这种方式进行故障处理会更容易推理。
  • @MartinSvalin 嗯,我会考虑的

标签: tcp elixir


【解决方案1】:

虽然我同意@Onorio Catenacci 的观点,但多个 GenServers 可能是更好的选择。不过,我还是会回答你提出的问题。

传递给handle_info 的元组的第二个位置是正在接收数据的套接字。因此,您已经知道它来自哪个套接字。

handle_info({:tcp, socket, data}, s) do
  # do something with the socket here ... 
end

我怀疑唯一需要的其他更改是保留对所有打开套接字的引用,这可以通过修改连接函数的 handle_call 和 GenServer 的 init 函数来完成:

def init(_) do
  {:ok, []}
end

def handle_call({:connect, ip, port}, _from, sockets) do
  s = connect(ip, port)
  {:reply, :ok, [s|sockets]}
end

注意,您可能还需要以类似的方式更改 handle_call for :retry_connect 函数。

希望这会有所帮助。

【讨论】:

  • 谢谢,我没有意识到套接字也被返回了handle_info({:tcp, socket, data}, s)
  • 对于独立模式,我目前为每个连接使用一个名为 GenServer。我希望运行多个 GenServer,然后让每个 Genserver 管理一些 TCP 连接。 @Onorio-Catenacci 的建议在我的场景中并不理想
【解决方案2】:

我建议您使用 Supervisor 而不是 GenServer。主管将允许您自动重新启动失败的连接:

当事情失败时,你的第一反应可能是:“让我们拯救那些 错误”。但是在 Elixir 中,我们避免了防御性编程习惯 拯救异常,这在其他语言中很常见。相反,我们 说“让它崩溃”。如果有错误导致我们的注册表 崩溃,我们没有什么可担心的,因为我们要建立一个 将启动注册表的新副本的主管。

我意识到上面的段落是在讨论“注册表”,但概念是一样的。

我也意识到我的回答有些笼统。但是,由于您的问题也很笼统,因此我很难为您提供更具体的内容。如果您有更具体的问题,我建议您编辑您的问题,我们可以为您提供更具体的答案。

【讨论】:

  • 每个 GenServer 都受到监督
  • 好的,那么如果每个 GenServer 都受到监督,为什么您希望每个 GenServer 处理多个连接?只需启动更多的 GenServer。我的意思是,如果添加更多 GenServer 有一些限制,那么您需要将其添加到您的问题中,因为您似乎正在尝试解决一个不存在的问题。
  • 我已经用一些上下文更新了这个问题,给出了 GenServer 和 Connections 之间的关系
猜你喜欢
  • 2011-11-06
  • 1970-01-01
  • 2015-03-01
  • 2013-04-27
  • 1970-01-01
  • 2011-06-25
  • 2019-05-08
  • 1970-01-01
  • 2016-11-19
相关资源
最近更新 更多