标签: gen-server

如何使用Erlang的gen_server定期执行操作?

我想启动一个gen_server,另外,每分钟都会执行一个动作.

安排这个的最佳方法是什么?

erlang gen-server

34
推荐指数
2
解决办法
1万
查看次数

Elixir是否提供了更简单的方法来获取GenServer进程的当前状态?

给定一个简单的GenServer过程.

defmodule KVServer do
  use GenServer

  def start do
    GenServer.start(__MODULE__, %{}, name: :kv_server)
  end

  def store(k, v) do
    GenServer.cast(:kv_server, {:store, k, v})
  end

  def handle_cast({:store, k, v}, state) do
    {:noreply, Map.put(state, k, v)}
  end
end
Run Code Online (Sandbox Code Playgroud)

我可以使用当前的进程状态 :sys.get_status/1

iex(1)> {:ok, pid} = KVServer.start
{:ok, #PID<0.119.0>}
iex(2)> KVServer.store(:a, 1)
:ok
iex(3)> KVServer.store(:b, 2)
:ok
iex(4)> {_,_,_,[_,_,_,_,[_,_,{_,[{_,state}]}]]} = :sys.get_status(pid)
...
iex(5)> state
%{a: 1, b: 2}
Run Code Online (Sandbox Code Playgroud)

只是想知道Elixir提供了一种更简单的方法来获得GenServer流程的当前状态吗?

elixir gen-server

17
推荐指数
1
解决办法
3083
查看次数

正常关闭GenServer

我用GenServer编写了一个Elixir应用程序,它在启动时启动外部应用程序并关闭它并在退出时进行其他清理.我在init/1回调中的回调和清理代码中添加了启动功能terminate/2.

init当GenServer开始代码工作正常,并且terminate也被称为方法中,当:stop被手动发送信号,但在意外停机和中断的情况下(如在按下Ctrl + C的情况下)在IEx标志,该终止代码是不叫.


目前,我已经完成了大量的论坛帖子,博客文章和文档,包括:

来自Elixir Docs - GenServers:

如果在没有捕获退出时从任何进程GenServer接收到退出信号(即不是:normal),它将以相同的原因突然退出,因此不会调用terminate/2.请注意,过程确实NOT默认陷阱退出,并且当链接的过程退出或它的节点断开发送一个退出信号.

因此,不保证terminate/2GenServer退出时调用 .出于这些原因,我们通常建议通过使用监控或链接本身在分离的流程中实现重要的清理规则.

但我完全不知道如何获得:init.stop,linked processes或其他任何与此相关的工作(因为这是我第一次使用GenServers).


这是我的代码:

defmodule MyAwesomeApp do
  use GenServer

  def start do
    GenServer.start_link(__MODULE__, nil)
  end

  def init(state) do
    # Do Bootup stuff

    IO.puts "Starting: #{inspect(state)}"
    {:ok, state}
  end

  def terminate(reason, state) do …
Run Code Online (Sandbox Code Playgroud)

elixir gen-server

17
推荐指数
2
解决办法
7489
查看次数

gen_server与dict vs mnesia table vs ets

我正在构建一个erlang服务器.用户向服务器发送http请求以更新其状态.服务器上的http请求进程将用户状态消息保存在内存中.服务器每隔一分钟将所有消息发送到远程服务器并清除内存.如果用户在一分钟内多次更新其状态,则最后一条消息将覆盖前一条消息.重要的是,在读取所有消息并清除它们之间,没有其他进程能够写入状态消息.

实施它的最佳方法是什么?

  1. 带有字典的gen_server.密钥将是用户ID.dict:store/3将更新或创建状态.gen_server解决了"交易"问题.

  2. mnesia表与ram_copies.处理事务,我不需要实现gen_server.这个解决方案有太多的开销吗?

  3. ETS表更轻,并具有gen_server.是否可以在ETS中进行交易?在读取所有消息并清除它们之间锁定表格?

谢谢

erlang dictionary gen-server ets

13
推荐指数
1
解决办法
3427
查看次数

如何处理poolboy中的超时?

我有一个长时间消耗迁移的问题,我希望在并行运行(它可以在并行运行).实际上,迁移是关于获取数据库中的所有记录,并对每个记录执行耗费时间和资源的操作.

有时单个记录迁移挂出,所以我给10分钟完成.如果迁移没有完成,我希望它正常关闭,没有任何例外(见下文)

我也使用poolboy erlang包来并行化实现,因为迁移不仅消耗时间,而且消耗资源.问题是我不知道如何在发生超时并且代码将要中断时处理错误.我的监督树是:

defmodule MyReelty.Repo.Migrations.MoveVideosFromVimeoToB2 do
  use Ecto.Migration

  alias MyReelty.Repo
  alias MyReelty.Repo.Migrations.MoveVideosFromVimeoToB2.Migrator

  # parallel nature of migration force us to disable transaction
  @disable_ddl_transaction true

  @migrator_waiting_time 10 * 60 * 1000 # timeout
  @poolboy_waiting_time @migrator_waiting_time + 10 * 1000 # give a time for graceful shutdown

  @pool_name :migrator
  @pool_size 3
  @pool_config [
    { :name, { :local, @pool_name }},
    { :worker_module, Migrator },
    { :size, @pool_size },
    { :max_overflow, 0 },
    { :strategy, :fifo }
  ]

  def up do
    children …
Run Code Online (Sandbox Code Playgroud)

erlang elixir gen-server actor

12
推荐指数
1
解决办法
1443
查看次数

获取gen_server/gen_fsm状态以进行调试

是否有可能获得gen_server进程的当前状态(可能是通过发送一些系统消息)?它在调试时很有用.

当然,我可以添加一条消息,将当前状态返回到handle_call:

get_state(Server) -> gen_server:call(Server, '$get_state').

%% in every gen_server I want to debug
...
handle_call('$get_state', _From, State) ->
  {reply, State, State};
...
Run Code Online (Sandbox Code Playgroud)

但是内置了什么(即使它有点hacky)?

erlang gen-server

11
推荐指数
2
解决办法
4205
查看次数

在GenServer.start_link/3中使用{:via,module,term}注册名称有什么好处?

GenServer.start_link/3我可以使用原子在本地注册一个名称,用于这样的过程:

defmodule Worker do
  use GenServer

  def start_link do
    GenServer.start_link(__MODULE__, nil, name: :worker)
  end
end
Run Code Online (Sandbox Code Playgroud)

然后我可以开始一个主管来监督这个过程:

defmodule Boss do
  use Supervisor

  def init(_) do
    children = [worker(Worker, [])]
    supervise(children, strategy: :one_for_one)
  end
end
Run Code Online (Sandbox Code Playgroud)

现在我想让主管监督3个Worker进程,所以我需要为这3个进程提供唯一的名称,这样当supervisor重新启动进程时,它将始终使用相同的符号名称.

我可以简单地使用字符串插值作为唯一的Worker进程名称,如下所示:

defmodule Worker do
  use GenServer

  def start_link(id) do
    GenServer.start_link(__MODULE__, nil, name: :"worker_#{id}")
  end
end
Run Code Online (Sandbox Code Playgroud)

然后监督3个这样的过程:

defmodule Boss do
  use Supervisor

  def init(_) do
    children = for id <- 1..3 do
      worker(Worker, [id], id: id)
    end
    supervise(children, strategy: :one_for_one)
  end …
Run Code Online (Sandbox Code Playgroud)

elixir erlang-otp gen-server erlang-supervisor

11
推荐指数
2
解决办法
1952
查看次数

何时使用Gen_Fsm以及何时使用Gen_Server?

在检查Gen_Fsm和Gen_Server文档后,我发现它们或多或少地表现为类似的行为.在我看来,如果有一个循环函数用于发送广播或监听tcp sock,最好使用Gen_Fsm,否则使用gen_server.我想知道它是否正确?

erlang gen-server gen-fsm

10
推荐指数
1
解决办法
2509
查看次数

Erlang:使用gen_server:cast/2和标准消息传递之间的区别

我正在解决一个问题并注意到一些代码,其中一位前任程序员使用PID的标准约定传递消息!信息.我一直在使用gen_server:cast/2.我想知道是否有人可以向我解释两者之间选择时的关键差异和考虑因素?

erlang asynchronous gen-server

10
推荐指数
2
解决办法
1275
查看次数

构建GenServer调用self的正确方法

我知道让GenServer进程调用本身几乎是不可能的,因为你基本上遇到了死锁.但是,我很好奇是否有一种首选方式来做这种事情.

假设以下场景:我有一个队列,我正在弹出的东西.如果队列是空的,我想重新填充它.我可以像这样构造它:

def handle_call(:refill_queue, state) do
  new_state = put_some_stuff_in_queue(state)
  {:reply, new_state}
end

def handle_call(:pop, state) do
  if is_empty_queue(state) do
    GenServer.call(self, :refill_queue)
  end

  val,new_state = pop_something(state)

  {:reply, val, new_state}
end
Run Code Online (Sandbox Code Playgroud)

这里的一个大问题是,当我们尝试重新填充队列时,这将会死锁.我过去使用的一个解决方案是使用cast更多,因此它不会死锁.像这样(的变化callcast进行补充)

def handle_cast(:refill_queue, state) do
Run Code Online (Sandbox Code Playgroud)

但在这种情况下,我认为它不起作用,因为pop在实际填充队列之前,异步转换重新填充队列可能会返回,这意味着我将尝试弹出一个空队列.

无论如何,核心问题是:处理这个问题的最佳方法什么?我假设答案是put_some_stuff_in_queue直接在pop通话中打电话,但我想检查一下.换句话说,它似乎是做正确的事是使handle_callhandle_cast尽可能简单,基本上只是包装来在实际工作情况等功能.然后,创建尽可能多的handle_*函数来覆盖您将要处理的所有可能情况,而不是handle_call(:foo)依次调用handle_call(:bar).

elixir gen-server

10
推荐指数
1
解决办法
2272
查看次数