通过基于代理的 IP 地址列表循环工作人员
Looping workers trough an Agent-based list of IP addresses
我正在研究基于 Elixir 的网络扫描仪。
我的目标是能够将 IP 地址列表存储在代理中(简单),并在该代理上循环工作人员,以便他们获取列表的头部并启动 scanning/probing 函数(不太容易)。
虽然这听起来像是某种理论上的东西,但如果有帮助,我可以提供代码片段。
编辑:显然我设法弄清楚了该怎么做(我想这就是这样做的方式)。
defmodule Find3r.Worker do
use GenServer
require Logger
def start_link, do: GenServer.start_link(__MODULE__, :ok, name: :worker)
def init(:ok) do
Logger.debug("Worker activated.")
{:ok, :ok}
end
def scan_range(range), do: GenServer.call(__MODULE__, {:scan_range, range})
def handle_call({:scan_range, range}, _from, :ok) do
Logger.debug("Scanning #{range}")
{:ok, pid} = Agent.start(Find3r.Utils, :addresses_for, [range])
Agent.get_and_update(pid, fn(state) -> foo(state) end) |> loop(pid)
{:reply, :ok, :ok}
end
# Avoids an exception when the Agent runs out of addresses
defp foo([ip|rest]), do: {ip, rest}
defp foo(_), do: {[], []}
defp loop(ip, pid) when is_tuple(ip) do
spawn(Find3r.Utils, :scan, [ip])
Agent.get_and_update(pid, fn(state) -> foo(state) end) |> loop(pid)
end
defp loop(_, _), do: nil
end
项目已移至GitHub。
再次感谢 o/
一种简单的(hack-ish)方法来完成[我认为]您所描述的事情:
defmodule MultiScanner do
def scan(range) do
main_proc = self
Enum.each(range, fn(ip) ->
spawn_link(fn ->
res = Find3r.Utils.scan(ip)
send main_proc, {:ip_results, ip, res}
end)
end)
ip_map = Enum.map(range, fn(ip) -> {ip, nil} end)
|> Enum.into(%{})
wait_for_results(ip_map)
end
defp wait_for_results(ip_map, acc \ %{})
defp wait_for_results(ip_map, acc) when map_size(ip_map) == 0, do: acc
defp wait_for_results(ip_map, acc) do
receive do
{:ip_results, ip, res} ->
acc = Map.put(acc, ip, res)
ip_map = Map.delete(ip_map, ip)
wait_for_results(ip_map, acc)
end
end
end
这会为每个 IP 地址生成一个进程,该进程运行扫描,并将结果发送回调用进程,调用进程又将结果折叠为 ip => 结果的映射。
这种方法显然存在很多问题 - 最明显的是您可能不希望所有扫描同时开始。
有很多方法可以解决这个问题,但除了像评论中建议的 poolboy
或 :gproc
这样的固定解决方案——如果我要实际实施对于这种方法的稳健解决方案,我可能会把这样的监督树放在一起:
Scan.Supervisor
-ScanWorker.Supervisor (simple-one-for-one)
-ScanWorker (anon GenServer, executing a single scan)
-ScanWorker
...
-ScanWorker.Manager (Named GenServer, coordinates the ScanWorkers)
我的信封架构的背面有上面的 4 个模块,加上一个顶层模块 Scan
来容纳 public API。我还认为需要一个小时左右的时间来充实它,以至于我不会不好意思向同事展示它。
我开始为您勾勒出这个轮廓 - 但坦率地说,您的问题含糊不清且写得不好。你似乎没有花太多时间写它,甚至懒得校对它。我在这里整理的答案可能比你的问题应得的更慷慨:我认为如果你花更多的时间和精力来写你的问题,你会在 SO 上找到更好的成功。
我设法想出了如何做到这一点(我想这就是人们这样做的方式)。
defmodule Find3r.Worker do
use GenServer
require Logger
def start_link, do: GenServer.start_link(__MODULE__, :ok, name: :worker)
def init(:ok) do
Logger.debug("Worker activated.")
{:ok, :ok}
end
def scan_range(range), do: GenServer.call(__MODULE__, {:scan_range, range})
def handle_call({:scan_range, range}, _from, :ok) do
Logger.debug("Scanning #{range}")
{:ok, pid} = Agent.start(Find3r.Utils, :addresses_for, [range])
Agent.get_and_update(pid, fn(state) -> foo(state) end) |> loop(pid)
{:reply, :ok, :ok}
end
# Avoids an exception when the Agent runs out of addresses
defp foo([ip|rest]), do: {ip, rest}
defp foo(_), do: {[], []}
defp loop(ip, pid) when is_tuple(ip) do
spawn(Find3r.Utils, :scan, [ip])
Agent.get_and_update(pid, fn(state) -> foo(state) end) |> loop(pid)
end
defp loop(_, _), do: nil
end
我正在研究基于 Elixir 的网络扫描仪。 我的目标是能够将 IP 地址列表存储在代理中(简单),并在该代理上循环工作人员,以便他们获取列表的头部并启动 scanning/probing 函数(不太容易)。 虽然这听起来像是某种理论上的东西,但如果有帮助,我可以提供代码片段。
编辑:显然我设法弄清楚了该怎么做(我想这就是这样做的方式)。
defmodule Find3r.Worker do
use GenServer
require Logger
def start_link, do: GenServer.start_link(__MODULE__, :ok, name: :worker)
def init(:ok) do
Logger.debug("Worker activated.")
{:ok, :ok}
end
def scan_range(range), do: GenServer.call(__MODULE__, {:scan_range, range})
def handle_call({:scan_range, range}, _from, :ok) do
Logger.debug("Scanning #{range}")
{:ok, pid} = Agent.start(Find3r.Utils, :addresses_for, [range])
Agent.get_and_update(pid, fn(state) -> foo(state) end) |> loop(pid)
{:reply, :ok, :ok}
end
# Avoids an exception when the Agent runs out of addresses
defp foo([ip|rest]), do: {ip, rest}
defp foo(_), do: {[], []}
defp loop(ip, pid) when is_tuple(ip) do
spawn(Find3r.Utils, :scan, [ip])
Agent.get_and_update(pid, fn(state) -> foo(state) end) |> loop(pid)
end
defp loop(_, _), do: nil
end
项目已移至GitHub。
再次感谢 o/
一种简单的(hack-ish)方法来完成[我认为]您所描述的事情:
defmodule MultiScanner do
def scan(range) do
main_proc = self
Enum.each(range, fn(ip) ->
spawn_link(fn ->
res = Find3r.Utils.scan(ip)
send main_proc, {:ip_results, ip, res}
end)
end)
ip_map = Enum.map(range, fn(ip) -> {ip, nil} end)
|> Enum.into(%{})
wait_for_results(ip_map)
end
defp wait_for_results(ip_map, acc \ %{})
defp wait_for_results(ip_map, acc) when map_size(ip_map) == 0, do: acc
defp wait_for_results(ip_map, acc) do
receive do
{:ip_results, ip, res} ->
acc = Map.put(acc, ip, res)
ip_map = Map.delete(ip_map, ip)
wait_for_results(ip_map, acc)
end
end
end
这会为每个 IP 地址生成一个进程,该进程运行扫描,并将结果发送回调用进程,调用进程又将结果折叠为 ip => 结果的映射。
这种方法显然存在很多问题 - 最明显的是您可能不希望所有扫描同时开始。
有很多方法可以解决这个问题,但除了像评论中建议的 poolboy
或 :gproc
这样的固定解决方案——如果我要实际实施对于这种方法的稳健解决方案,我可能会把这样的监督树放在一起:
Scan.Supervisor
-ScanWorker.Supervisor (simple-one-for-one)
-ScanWorker (anon GenServer, executing a single scan)
-ScanWorker
...
-ScanWorker.Manager (Named GenServer, coordinates the ScanWorkers)
我的信封架构的背面有上面的 4 个模块,加上一个顶层模块 Scan
来容纳 public API。我还认为需要一个小时左右的时间来充实它,以至于我不会不好意思向同事展示它。
我开始为您勾勒出这个轮廓 - 但坦率地说,您的问题含糊不清且写得不好。你似乎没有花太多时间写它,甚至懒得校对它。我在这里整理的答案可能比你的问题应得的更慷慨:我认为如果你花更多的时间和精力来写你的问题,你会在 SO 上找到更好的成功。
我设法想出了如何做到这一点(我想这就是人们这样做的方式)。
defmodule Find3r.Worker do
use GenServer
require Logger
def start_link, do: GenServer.start_link(__MODULE__, :ok, name: :worker)
def init(:ok) do
Logger.debug("Worker activated.")
{:ok, :ok}
end
def scan_range(range), do: GenServer.call(__MODULE__, {:scan_range, range})
def handle_call({:scan_range, range}, _from, :ok) do
Logger.debug("Scanning #{range}")
{:ok, pid} = Agent.start(Find3r.Utils, :addresses_for, [range])
Agent.get_and_update(pid, fn(state) -> foo(state) end) |> loop(pid)
{:reply, :ok, :ok}
end
# Avoids an exception when the Agent runs out of addresses
defp foo([ip|rest]), do: {ip, rest}
defp foo(_), do: {[], []}
defp loop(ip, pid) when is_tuple(ip) do
spawn(Find3r.Utils, :scan, [ip])
Agent.get_and_update(pid, fn(state) -> foo(state) end) |> loop(pid)
end
defp loop(_, _), do: nil
end