編輯:最初的解決方案將信息放置在10秒之間,因爲建議使用burmajam。編輯提供了一個更合適的解決方案。
編輯
由於事實GenServer的handle_ *功能並不會真正從隊列中接收消息,但只是對其進行處理,我們不能利用模式匹配接收選擇只從每10秒處理隊列。
因此,由於我們按照它們的到達順序來拾取消息,因此我們需要將內部隊列作爲GenServer狀態的一部分。
defmodule RateLimited do
use GenServer
def start_link do
GenServer.start_link(__MODULE__, %{queue: []})
end
def init(state) do
allow_work()
{:ok, state}
end
def handle_cast({:call_api, data}, %{"queue" => queue} = state) do
{:noreply, %{state | queue: queue ++ [data]}}
end
def handle_info(:work, %{"queue" => [data | queue]} = state) do
send_to_external_api(data)
allow_work()
{:noreply, %{state | queue: queue}}
end
defp allow_work() do
Process.send_after(self(), :work, 10000) # after 10s
end
defp send_to_external_api (data) do end
end
所以我們僅僅是從進程隊列移動郵件到狀態隊列和我們處理的頭,當我們發出信號自己,10秒過去了。
但最終,我們實際上達到了同樣的結果,就好像我們讓進程睡了10秒一樣。你的解決方案似乎更容易,並達到相同的結果。
該解決方案是基於How to run some code every few hours in Phoenix framework?
首先,讓你的GenServer商店標誌在其狀態(工作= TRUE/FALSE)。
然後讓GenServer使用Process.send_after
來表明它可以工作。您收到handle_info
中的信號,您將work
狀態標誌設置爲true
。
現在注意handle_cast
函數中狀態的模式匹配:當work
狀態標誌等於true時,它只會選取消息。否則,消息將被放入隊列中等待。
你將郵件發送給外部服務後,你再次運行Process.send_after
計劃下一次信號,並返回其具有work
標誌設置爲false
以防止立刻拿起旁邊的郵件狀態。
defmodule RateLimited do
use GenServer
def start_link do
GenServer.start_link(__MODULE__, %{work: false})
end
def init(state) do
allow_work()
{:ok, state}
end
def handle_cast({:call_api, data}, %{"work" => true} = state) do
send_to_external_api(data)
allow_work()
{:noreply, %{state | work = false}}
end
def handle_info(:work, state) do
{:noreply, %{state | work = true}}
end
defp allow_work() do
Process.send_after(self(), :work, 10000) # after 10s
end
end
您還可以利用'Process.send_after'。看看這個答案:http://stackoverflow.com/questions/32085258/how-to-run-some-code-every-few-hours-in-phoenix-framework – edmz
@edmz嗨,所以這不是真的是一個週期性的任務,因爲它是一個每10秒執行一次的任務。但是它是一個可以被其他進程調用的進程,但是它必須以10秒爲間隔執行*最多*。 - 目的僅限於將API調用限制在閾值以下。 –
既然這是'handle_cast',這種方法對我來說很好。如果您想發送「每10秒最多1個請求」而不是「請求之間10秒的間隔」,您可能需要減去'send_to_external_api(data)'所用的時間從10000開始。 – Dogbert