0

我正在尝试使用 GenServer 实现一个计数器。这个想法是每当客户端访问服务器时增加一个计数器,并在控制台中打印它。但是,每次访问后 GenServer 状态重置或 PID 终止。有没有办法让 GenServer 状态持久化?

这是代码:

lib/myapp_web/livecount.ex中的计数器文件

defmodule Counter do
    use GenServer
    # Client
  
    def start_link(integer \\ 0, opts \\ []) when is_integer(integer) do
      GenServer.start_link(__MODULE__, integer, opts)
    end

    def add(pid) do
        GenServer.call(pid, :add)    
    end

    # Server
    @impl true
    def handle_call(:add, _from, state) do
        value = state + 1
        {:reply, "Dashboard hit: #{value}", value}
    end

    @impl true
    def init(value) do
        {:ok, value}
    end
end 

我在lib/myapp_web/endpoint.ex中添加了以下代码


  def message(conn, _opts) do
    Code.require_file("lib/myapp_web/livecount.ex")
    {:ok, pid} = Counter.start_link()
    message = Counter.add(pid)
    IO.puts """
    Message: #{inspect(message)}
    """

    conn
  end 

这是控制台中的消息:

[info] Sent 200 in 30ms
Message: "Dashboard hit: 1"

[info] CONNECTED TO Phoenix.LiveView.Socket in 143µs
  Transport: :websocket
  Serializer: Phoenix.Socket.V2.JSONSerializer
  Parameters: %{"_csrf_token" => "YyUeC3lgIQ83AlU3VBdoSg9PAwgYGl4QVQW_1PMHOjfg1s72wvoYHW2T", "_mounts" => "0", "vsn" => "2.0.0"}
[info] CONNECTED TO Phoenix.LiveView.Socket in 105µs
  Transport: :websocket
  Serializer: Phoenix.Socket.V2.JSONSerializer
  Parameters: %{"_csrf_token" => "TToLLhxmCAVIXXsJLVIJOjNYC2QIJy0bxNBzTVdB05HYH6VBKag5XjA_", "_mounts" => "0", "vsn" => "2.0.0"}

我要打印

Message: "Dashboard hit: 1"
Message: "Dashboard hit: 2"
Message: "Dashboard hit: 3"

每当客户访问或重新加载页面时,每次所有用户访问都会增加计数器。

4

1 回答 1

0

这里有很多需要修复/改进的地方。

① 不应该Code.require_file/1从运行时调用,除非绝对确定这是唯一的方法;lib默认情况下,位于其中的常规文件是可用的
②不应从常规代码中启动不受监控(无监督)的进程;在③ 中的监督树中启动它,application.ex
为进程命名以避免跟踪的必要性pid
④ 您可能希望分别跟踪每个用户的访问;当前尝试将为所有用户提供一个计数器

也就是说,有点像下面的工作(假设该过程已application.ex在其监督树中开始。)

  use GenServer
 
  def start_link(integer \\ 0) when is_integer(integer) do
    GenServer.start_link(__MODULE__, integer, name: __MODULE__)
  end

  def add, do: GenServer.call(__MODULE__, :add)

  def message(conn, _opts) do
    message = Counter.add()
    IO.inspect(message, label: "Message:")
    conn
  end 

要跟踪每个用户的计数器,可能会conn => counter在该州或类似情况下持有一张地图。

于 2021-05-11T04:09:51.243 回答