简体   繁体   English

Elixir GenServer投射和start_link

[英]Elixir GenServer cast and start_link

These are my first 2 hours of Elixir so it might be a noobish question but I have some issues with spawning up a GenServer and casting it. 这是我开始GenServer Elixir的前2个小时,所以这可能是一个讨厌的问题,但是在生成GenServer并进行投射时,我遇到了一些问题。 I'm using this module which watches for changes in a file in my system and, each time a new entry is added in that file, I want to cast the log to a gen server which analyzes it and does whatever is supposed to do based on it's findings. 我正在使用这个模块来监视系统中文件的更改,并且每次在该文件中添加新条目时,我都希望将日志投射到gen服务器上,该服务器对其进行分析并根据在它的发现上。

Here's the module that watches the changes: 这是监视更改的模块:

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

The watcher works very well and it receives the new logs but I'm having a problem with Test.Receiver which uses GenServer . 观察者工作得很好,它接收新的日志,但我有一个问题Test.Receiver它使用GenServer

My first question is...where do I start this gen server? 我的第一个问题是...在哪里启动该发电服务器? ExFSWatch has it's own start method and I can't override that. ExFSWatch有它自己的start方法,我无法覆盖它。 Do I call start_link each time a new log passes in (I doubt that but I had to ask)? 每次传入新日志时,我是否都要调用start_link (我对此表示怀疑,但我不得不问)?

Ny all the examples I read I'm supposed to start it somewhere else, grab it's pid and pass it as a param to the analyze_log method so, if I'm right, my only remaining problem is finding out a good place to start the Test.Receiver Genserver and grabbing it's pid so I can use it inside the callback method. 我读过的所有示例都应该从其他地方开始,抓取它的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

Ok, to expand on @Dogbert's answer and clear it out for others who are starting with Elixir I will provide the solution that worked out for me. 好的,要扩展@Dogbert的答案并将其清除给其他以Elixir开头的人,我将提供为我解决的解决方案。 It may not be the most elegant one as I lack the knowledge yet but works. 它可能不是最优雅的,因为我还没有知识,但是可以工作。

As Dogbert said, you need to register your GenServer with a name and call it by that name. 正如Dogbert所说,您需要使用一个名称注册GenServer并使用该名称进行调用。 So I went to my main file and spawned a supervisor which keeps these listeners up and active like so: 因此,我进入主文件并生成了一个主管,该主管可以使这些侦听器保持活动状态,如下所示:

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

As you can see, the second parameter passed onto the supervisor call is the name under which I want to register this module. 如您所见,传递给超级用户调用的第二个参数是我要用来注册该模块的名称。 It may be anything you like I presume since I'm not very sure on that. 因为我不太确定,所以您可能会喜欢我想的任何东西。 This second parameter goes into my receiver's start_link and registers under that name: 第二个参数进入接收者的start_link并以该名称注册:

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

With this in place now remember this method on the receiver that I had problems regarding the pid which I didn't know where to get from: 现在有了这个,请记住在接收器上使用此方法时,我遇到了关于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

Works for me, open to discussion and improvements. 为我工作,欢迎讨论和改进。 It's my 2nd day now of Elixir and I started to grasp it's ways of ticking. 现在是Elixir的第二天,我开始掌握它的滴答作响方式。 Coming from Python, Ruby, PHP myself it's a little hard different grabbing and passing information as parameters and state all the time but..I see the sun now. 来自Python,Ruby,PHP本身,在获取和传递信息作为参数和状态方面一直存在一些 困难 ,但是..我现在看到了阳光。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM