我是erlang的初学者,我写了一个基本的gen服务器程序如下,我想知道,如何测试服务器让我知道它运行良好.
-module(gen_server_test).
-behaviour(gen_server).
-export([start_link/0]).
-export([alloc/0, free/1]).
-export([init/1, handle_call/3, handle_cast/2]).
start_link() ->
gen_server:start_link({local, gen_server_test}, ch3, [], []).
alloc() ->
gen_server:call(gen_server_test, alloc).
free(Ch) ->
gen_server:cast(gen_server_test, {free, Ch}).
init(_Args) ->
{ok, channels()}.
handle_call(alloc, _From, Chs) ->
{Ch, Chs2} = alloc(Chs),
{reply, Ch, Chs2}.
handle_cast({free, Ch}, Chs) ->
io:format(Ch),
io:format(Chs),
Chs2 = free(),
{noreply, Chs2}.
free() ->
io:format("free").
channels() ->
io:format("channels").
alloc(chs) ->
io:format("alloc chs").
Run Code Online (Sandbox Code Playgroud)
BTW:该程序可以编译,它不是一个好程序,我只想打印一些东西,以确保它的工作:)
在编写代码时,我会问自己应该使用call哪种类型的消息,应该使用哪种类型的消息info?
在这个问题下面,还有另一个长期怀疑info, cast, call消息之间是否存在优先级差异?这3种消息是否共享同一队列?
我的gen_server包含这样的方法:
handle_call(error, From, State) ->
io:format("Inside the handle_call error~n"),
1/0.
Run Code Online (Sandbox Code Playgroud)
它提供start(非start_link)功能:
start() ->
gen_server:start({local, ?MODULE}, ?MODULE, [], []).
normal_call() ->
gen_server:call(?MODULE, {normal}).
error_call() ->
gen_server:call(?MODULE, error).
Run Code Online (Sandbox Code Playgroud)
我start从shell 调用函数:
c(some_module).
some_module:start().
Run Code Online (Sandbox Code Playgroud)
然后我调用终止服务器进程的错误调用,因为除零错误,但它也终止了shell(调用进程).我不明白为什么?它们没有链接但仍然使用新的pid重新启动shell.这是gen_server的预期行为,还是我做错了什么?
更新:它仍然无法正常工作,以帮助我发布完整的代码.
-module(some_test).
-behaviour(gen_server).
-compile(export_all).
%% api functions, can be directly used by the user
start() ->
gen_server:start({local, ?MODULE}, ?MODULE, [], []).
normal_call() ->
gen_server:call(?MODULE, normal, infinity).
error_call() ->
gen_server:call(?MODULE, error, infinity).
%% service specific function shoule not be call directly
init(_) ->
io:format("Inside …Run Code Online (Sandbox Code Playgroud) 我目前正在以合理的方式学习Erlang,但对主管的gen_server有疑问.如果gen_server进程崩溃并由主管重新启动,它将收到一个新的pid.现在,如果我希望其他进程通过Pid引用该进程,该怎么办?在这些流程中"更新"Pid有哪些好的惯用方法?
作为一个实际应用的练习,我正在编写一个锁定服务器,客户端可以使用任意键请求锁定.理想情况下,我希望有一个单独的进程来处理特定锁的锁定和释放,这个想法是我可以使用gen_server中的timeout参数来终止进程,如果在N个时间之后没有人请求它,那么只有当前相关的锁将留在内存中.现在,我有一个目录进程,它将锁名称映射到锁进程.当锁定进程终止时,它会从目录中删除锁定.
我关心的是如何处理客户端在锁定进程正在终止时请求锁定的情况.它还没有关闭,所以嗅到pid活着是行不通的.锁定进程尚未到达从目录中删除它的子句.
有没有更好的方法来处理这个?
编辑
目前有两个gen_servers:'directory'维护一个来自LockName - > Lock Process的ETS表,以及'lock servers',它们使用start_child动态添加到监督树.理想情况下,我希望每个锁服务器直接处理与客户端的通话,但我担心当进程处于崩溃中时,获取/释放获取/释放请求的场景(因此将不会响应)消息).
从{local}或{global}开始将无效,因为可能有N个.
gen_server中的handle_call函数是:
Module:handle_call(Request, From, State) -> Result
Run Code Online (Sandbox Code Playgroud)
但是我遇到了一个这样的handle_call函数:
handle_call(info, _From, #yuv{decoder = undefined} = State) ->
{reply, [], State};
handle_call(info, _From, #yuv{decoder = Decoder} = State) ->
{reply, av_decoder:info(Decoder), State};
handle_call(_Request, _From, State) ->
{noreply, ok, State}.
Run Code Online (Sandbox Code Playgroud)
我想知道发生了什么?它超越了我的头脑
BTW:yuv记录是:
-record(yuv, {
host,
name,
media,
decoder,
consumer
}).
Run Code Online (Sandbox Code Playgroud) 我的一项服务与速率受限的外部 API 通信,因此我想确保每 10 秒发送不超过 1 个调用。
我天真的方法是拥有一个长时间运行的 API 服务,并在每次调用后超时:
def handle_cast({:call_api, data}, state) do
send_to_external_api(data)
:timer.sleep(10000)
{:noreply, state}
end
Run Code Online (Sandbox Code Playgroud)
我不确定是否有适当的方法来做到这一点。
为什么,在Erlang/Elixir中,是以?GenServer命名的异步处理程序handle_cast?该handle_部分是显而易见的,但为什么这个词投?
我能想到的唯一的事情是,它是类似于刚才扔的东西在那里(如在投荷兰国际集团的净)与呼叫荷兰国际集团出来的东西,等待答复.
我有一个使用 ETS 的原始缓存的 GenServer/客户端实现:
defmodule Cache do
use GenServer
def start_link() do
GenServer.start_link(__MODULE__, [])
end
def fetch(key, def_value) do
case get(key) do
{:not_found} -> set(key, def_value)
{:found, result} -> result
end
end
defp get(key) do
case GenServer.call(__MODULE__, {:get, key}) do
[] -> {:not_found}
[{_, result}] -> {:found, result}
end
end
defp set(key, value) do
GenServer.call(__MODULE__, {:set, key, value})
end
# GenServer callbacks
def handle_call({:get, key}, _from, state) do
result = :ets.lookup(:my_table, key)
{:reply, result, state}
end
def handle_call({:set, key, value}, …Run Code Online (Sandbox Code Playgroud) 设想:
GenServer的管理一些状态。map来管理我的 state。但随着我向该州添加更多数据,它正在增长。问题:
struct我的GenServer模块中可以有 a吗?可以通过从同一 内的另一个回调返回来调用handle_continuea 的回调。GenServer{:noreply, state, {:continue, :foo}GenServer
如果回调后我有一小部分单独的步骤GenServer.init:
defmodule MyGenServer do
use GenServer
def start_link(args \\ %{}) do
GenServer.start_link(__MODULE__, args, [])
end
@impl true
def init(args) do
{:ok, args, {:continue, :foo}}
end
@impl true
def handle_continue(:foo, state)
case foo(state) do
{:ok, state} ->
{:noreply, state, {:continue, :bar}}
{:error, :bar_not_set} ->
{:noreply, state}
end
@impl true
def handle_continue(:bar, state)
state = bar(state)
{:noreply, state}
end
defp foo(state = %{bar: _}) do
{:ok, state}
end
defp …Run Code Online (Sandbox Code Playgroud)