我正在尝试使用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"
每当客户端访问或重新加载页面时,并增加所有用户每次访问的计数器。
这里有很多需要修复/改进的地方。
① 不应该在运行时内调用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
等。