【问题标题】:Elixir GenServer cast and start_linkElixir GenServer 演员表和 start_link
【发布时间】:2017-02-17 19:29:55
【问题描述】:

这是我使用 Elixir 的前 2 个小时,所以这可能是一个愚蠢的问题,但我在生成 GenServer 并施放它时遇到了一些问题。我正在使用此模块来监视系统中文件的更改,并且每次在该文件中添加新条目时,我都想将日志投射到生成服务器,该服务器对其进行分析并执行任何应该做的事情关于它的发现。

这是监视更改的模块:

defmodule Test.Watcher do
    use ExFSWatch, dirs: ["/var/log/"]

    import Test.Receiver, only: [analyze_log: 2]

    def callback(file_path, actions) do
        if file_path == "/var/log/syslog" do
            if :modified in actions do
                #~~~~ WHERE DO I GET THIS pid FROM?
                analyze_log pid, get_log
            end
        end
    end

    def get_log do
        {log, _} = System.cmd("tail", ["-n", "1", "/var/log/syslog"])
        log
    end
end

观察者工作得很好,它接收新的日志,但我遇到了Test.Receiver 的问题,它使用了GenServer

我的第一个问题是……我在哪里启动这个 gen 服务器? ExFSWatch 有它自己的 start 方法,我无法覆盖它。每次有新日志传入时我是否都会致电start_link(我对此表示怀疑,但我不得不问)?

我读到的所有示例都应该从其他地方开始,获取它的 pid 并将其作为参数传递给 analyze_log 方法,所以,如果我是对的,我唯一剩下的问题是找到找到一个启动 Test.Receiver Genserver 并获取它的 pid 的好地方,这样我就可以在 callback 方法中使用它。

defmodule Test.Receiver do
    use GenServer

    def start_link do
        GenServer.start_link(__MODULE__, :ok, [])
    end

    def analyze_log(pid, log) do
        GenServer.cast(pid, {:analyze_log, log})
    end

    def init(:ok) do
        {:ok, %{}}
    end

    def handle_cast({:analyze_log, log}, state) do
        IO.puts log
        {:noreply, state}
    end
end

【问题讨论】:

  • 大多数具有此类回调接口的模块还有一个额外的“状态”参数来携带此类信息,但由于某种原因,ExFSWatch 似乎没有。除此之外,我能想到的最简单的方法是使用名称生成和注册 Receiver 进程并按名称调用它。
  • 我根据您的评论形成了一个答案,因为它有帮助。谢谢。

标签: elixir erlang-otp gen-server


【解决方案1】:

好的,为了扩展@Dogbert 的答案并为其他开始使用 Elixir 的人清除它,我将提供适合我的解决方案。它可能不是最优雅的,因为我还缺乏知识,但很有效。

正如 Dogbert 所说,您需要为您的 GenServer 注册一个名称并使用该名称调用它。所以我去了我的主文件并产生了一个监督者,它让这些听众保持活跃和活跃,就像这样:

defmodule Test do
    use Application

    def start(_type, _args) do
        import Supervisor.Spec, warn: false

        Fail2ban.Watcher.start

        children = [
            # ... I have more here but let's keep it simple
            supervisor(Test.Receiver, [Test.Receiver]),
        ]

        opts = [strategy: :one_for_one, name: Test.Supervisor]
        Supervisor.start_link(children, opts)
    end
end

如您所见,传递给主管调用的第二个参数是我要注册此模块的名称。我认为这可能是您喜欢的任何东西,因为我对此不太确定。第二个参数进入我的接收者的start_link 并以该名称注册:

def start_link(name \\ nil) do
    GenServer.start_link(__MODULE__, nil, [name: name])
end

有了这个,现在记住接收器上的这个方法,我遇到了关于 pid 的问题,我不知道从哪里得到:

defmodule Test.Watcher do
    use ExFSWatch, dirs: ["/var/log/"]

    import Test.Receiver, only: [analyze_log: 2]

    def callback(file_path, actions) do
        if file_path == "/var/log/syslog" do
            if :modified in actions do
                # Instead of the pid, pass the name under which it was registered
                analyze_log Test.Receiver, get_log
            end
        end
    end

    def get_log do
        {log, _} = System.cmd("tail", ["-n", "1", "/var/log/syslog"])
        log
    end
end

适合我,欢迎讨论和改进。现在是我使用 Elixir 的第二天,我开始掌握它的滴答作响方式。来自 Python、Ruby、PHP

【讨论】:

    猜你喜欢
    • 2019-10-04
    • 2018-06-30
    • 2018-12-14
    • 2017-10-16
    • 2023-03-18
    • 2016-03-20
    • 2021-03-03
    • 2016-03-14
    • 2018-01-13
    相关资源
    最近更新 更多