我在elixir中有一个看起来像这样的Web应用程序
defmodule Test do
use Plug.Router
plug :match
plug :dispatch
def expensiveComputation() do
// performs an expensive computation and
// returns a list
end
get "/randomElement" do
randomElement = expensiveComputation() |> Enum.random
send_resp(conn, 200, randomElement)
end
end
每当我向GET
发出/randomElement
请求时,都会调用expensiveComputation
。 expensiveComputation
函数需要很长时间才能运行,但每次调用它都会返回相同的内容。缓存结果的最简单方法是什么,以便它在启动时只运行一次?
答案 0 :(得分:6)
您可以使用ETS来缓存昂贵的计算。这是我最近写的东西,它可能不是一个成熟的缓存解决方案,但对我来说效果很好:
defmodule Cache do
@table __MODULE__
def start do
:ets.new @table, [:named_table, read_concurrency: true]
end
def fetch(key, expires_in_seconds, fun) do
case lookup(key) do
{:hit, value} ->
value
:miss ->
value = fun.()
put(key, expires_in_seconds, value)
value
end
end
defp lookup(key) do
case :ets.lookup(@table, key) do
[{^key, expires_at, value}] ->
case now < expires_at do
true -> {:hit, value}
false -> :miss
end
_ ->
:miss
end
end
defp put(key, expires_in_seconds, value) do
expires_at = now + expires_in_seconds
:ets.insert(@table, {key, expires_at, value})
end
defp now do
:erlang.system_time(:seconds)
end
end
首先,你需要在某处调用Cache.start
,因此将创建ETS表(例如在你的应用程序的start
函数中)。然后你可以像这样使用它:
value = Cache.fetch cache_key, expires_in_seconds, fn ->
# expensive computation
end
例如:
Enum.each 1..100000, fn _ ->
message = Cache.fetch :slow_hello_world, 1, fn ->
:timer.sleep(1000) # expensive computation
"Hello, world at #{inspect :calendar.local_time}!"
end
IO.puts message
end
答案 1 :(得分:5)
在Elixir中,当你想要状态时,你几乎总是需要一个进程来保持这种状态。 Agent
模块特别适合您想要的操作 - 只需包装一些值并访问它。这样的事情应该有效:
defmodule Cache do
def start_link do
initial_state = expensive_computation
Agent.start_link(fn -> initial_state end, name: __MODULE__)
end
def get(f \\ &(&1)) do
Agent.get(__MODULE__, f)
end
defp expensive_computation do
# ...
end
end
然后,您可以正常将Cache
插入监管树,当您需要Cache.get
的结果时,只需expensive_computation
。
请注意,在启动监督树的过程中,这将在启动时运行expensive_computation
。如果非常昂贵 - 大约10秒或更长时间 - 您可能希望将计算移至Agent
进程:
def start_link do
Agent.start_link(fn -> expensive_computation end, name: __MODULE__)
end
在这种情况下,您需要处理缓存为空的情况,而在第一个示例中,阻止启动直到expensive_computation
完成。您可以根据启动顺序中的Cache
放置工作人员来使用它。