从一个GenServer以主动模式管理多个TCP连接



我通常使用此模式与单个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不需要重新启动

虽然我同意@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

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

希望这有帮助。

我建议你使用Supervisor而不是GenServer。管理引擎将允许您自动重新启动失败的连接,其中包括:

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

我意识到上面的段落是在讨论"注册表",但概念是相同的。

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

最新更新