我们知道 Elixir 有一个从 Erlang 那里继承来的重要特性 —— immutable, 所以我们不能像 OO语言那样简单地用变量存储临时数据. 因此, Agent 出现了.

Agent 是关于 state 的简单抽象. state 可以解释为数据的某个状态. 在 Elixir 里经常需要在不同进程之间, 或者是同一个进程在不同的时间点, 分享或存储 state.

Agent 是一个基础的服务器实现, 它提供了简单的 API, 帮助我们对 state 进行获取与更新.

所有传送给 agent 的函数都会在 agent 里执行. 所以要避免在 agent 中进行昂贵的操作.

对比这两个例子:

# Compute in the agent/server
def get_something(agent) do
  Agent.get(agent, fn state -> do_something_expensive(state) end)
end

# Compute in the agent/client
def get_something(agent) do
  Agent.get(agent, &(&1)) |> do_something_expensive()
end

第一个例子阻塞了 agent, 第二个例子将所有的 state 复制到客户端执行. 选择哪种方式取决于数据的大小, 是否大到需要在服务器执行, 或是小到可以传送到客户端.

agent 提供了两种 API, 一种是匿名函数, 另一种是确定的模块函数和参数. 使用分布式 agent 的时候最好选择后者.

agent 可以热更新代码, 只需要简单地传送一个模块,函数和参数的元组到更新命令里. 例如:

{:update, :sample, {:advanced, {Enum, :into, [%{}]}}}

agent 的 state 会被当做第一个参数添加到参数列表里.

Agent 模块里的主要函数

@spec start_link((() -> term), GenServer.options) :: on_start
@spec start_link(module, atom, [any], GenServer.options) :: on_start
启动一个包含了给定的函数, 链接到当前进程的 agent.

@spec start((() -> term), GenServer.options) :: on_start
@spec start(module, atom, [any], GenServer.options) :: on_start
启动一个没有链接的 agent. (在监督树之外)

@spec get(agent, (state -> a), timeout) :: a when a: var
@spec get(agent, module, atom, [term], timeout) :: any
通过给定的函数获取 agent 的值.

@spec get_and_update(agent, (state -> {a, state}), timeout) :: a when a: var
@spec get_and_update(agent, module, atom, [term], timeout) :: any
获取并更新 agent state.

@spec update(agent, (state -> state), timeout) :: :ok
@spec update(agent, module, atom, [term], timeout) :: :ok
更新 agent state.

@spec cast(agent, (state -> state)) :: :ok
@spec cast(agent, module, atom, [term]) :: :ok
在 agent state 中执行一个 cast 操作.

@spec stop(agent, reason :: term, timeout) :: :ok
以给定的原因关闭 agent.


Ljzn
399 声望102 粉丝

网络安全;函数式编程;数字货币;人工智能