我想启动一个gen_server,另外,每分钟都会执行一个动作.
安排这个的最佳方法是什么?
给定一个简单的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流程的当前状态吗?
我用GenServer编写了一个Elixir应用程序,它在启动时启动外部应用程序并关闭它并在退出时进行其他清理.我在init/1回调中的回调和清理代码中添加了启动功能terminate/2.
在init当GenServer开始代码工作正常,并且terminate也被称为方法中,当:stop被手动发送信号,但在意外停机和中断的情况下(如在按下Ctrl + C的情况下)在IEx标志,该终止代码是不叫.
目前,我已经完成了大量的论坛帖子,博客文章和文档,包括:
如果在没有捕获退出时从任何进程
GenServer接收到退出信号(即不是:normal),它将以相同的原因突然退出,因此不会调用terminate/2.请注意,过程确实NOT默认陷阱退出,并且当链接的过程退出或它的节点断开发送一个退出信号.因此,不保证
terminate/2在GenServer退出时调用 .出于这些原因,我们通常建议通过使用监控或链接本身在分离的流程中实现重要的清理规则.
但我完全不知道如何获得: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) 我正在构建一个erlang服务器.用户向服务器发送http请求以更新其状态.服务器上的http请求进程将用户状态消息保存在内存中.服务器每隔一分钟将所有消息发送到远程服务器并清除内存.如果用户在一分钟内多次更新其状态,则最后一条消息将覆盖前一条消息.重要的是,在读取所有消息并清除它们之间,没有其他进程能够写入状态消息.
实施它的最佳方法是什么?
带有字典的gen_server.密钥将是用户ID.dict:store/3将更新或创建状态.gen_server解决了"交易"问题.
mnesia表与ram_copies.处理事务,我不需要实现gen_server.这个解决方案有太多的开销吗?
ETS表更轻,并具有gen_server.是否可以在ETS中进行交易?在读取所有消息并清除它们之间锁定表格?
谢谢
我有一个长时间消耗迁移的问题,我希望在并行运行(它可以在并行运行).实际上,迁移是关于获取数据库中的所有记录,并对每个记录执行耗费时间和资源的操作.
有时单个记录迁移挂出,所以我给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) 是否有可能获得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)?
在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) 在检查Gen_Fsm和Gen_Server文档后,我发现它们或多或少地表现为类似的行为.在我看来,如果有一个循环函数用于发送广播或监听tcp sock,最好使用Gen_Fsm,否则使用gen_server.我想知道它是否正确?
我正在解决一个问题并注意到一些代码,其中一位前任程序员使用PID的标准约定传递消息!信息.我一直在使用gen_server:cast/2.我想知道是否有人可以向我解释两者之间选择时的关键差异和考虑因素?
我知道让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更多,因此它不会死锁.像这样(的变化call来cast进行补充)
def handle_cast(:refill_queue, state) do
Run Code Online (Sandbox Code Playgroud)
但在这种情况下,我认为它不起作用,因为pop在实际填充队列之前,异步转换重新填充队列可能会返回,这意味着我将尝试弹出一个空队列.
无论如何,核心问题是:处理这个问题的最佳方法是什么?我假设答案是put_some_stuff_in_queue直接在pop通话中打电话,但我想检查一下.换句话说,它似乎是做正确的事是使handle_call和handle_cast尽可能简单,基本上只是包装来在实际工作情况等功能.然后,创建尽可能多的handle_*函数来覆盖您将要处理的所有可能情况,而不是handle_call(:foo)依次调用handle_call(:bar).
gen-server ×10
erlang ×6
elixir ×5
actor ×1
asynchronous ×1
dictionary ×1
erlang-otp ×1
ets ×1
gen-fsm ×1