2015-09-27 69 views
2

任務是通過服務對服務認證機制來查詢其中一項Google服務,該機制假定每60分鐘獲取一次新的訪問令牌。所以需要主動刷新或刷新請求失敗。如何在併發環境中刷新訪問令牌?

最新似乎是一個防禦技術,不符合藥劑哲學。由於訪問令牌過期或由於提供的憑證(電子郵件和密鑰)通常無效 - 它將是相同的文本和相同的401代碼,因此無法找出請求是否失敗。

也許任何人都可以就實施策略提供建議?這將是主機應用程序使用的庫,並且假設在令牌刷新期間(也許是90ms?)將創建新的請求,並且通常對於他們等待新令牌而不是使用即將到來的令牌,過時的一個。

回答

1

一般來說,在藥劑/二郎您有4種方式來存儲和使用共享數據:

  1. GenServer或類似的 - 你實現的過程,把握關鍵,也查詢API。您向它發送消息並返回數據。你不會打擾數據來自哪裏,使用哪些鍵以及如何使用。請求被序列化(並行完成的而不是),這可能是你不想要的東西。

  2. 代理 - 僅保存數據(密鑰)。您可以調用Agent.get/3並獲取密鑰。無論何時您發現密鑰已過期,都可以調用Agent.update/3將新密鑰添加到代理中。或者你總是調用update/3。

  3. ETS表 - 最後的手段。如果你沒有足夠的理由,不要使用它。

  4. 任何外部來源 - 你可以從磁盤,網絡等讀取甚至最後呃;)度假村。

對於您的用例,第一種和第二種解決方案可能幾乎相同。但我會因爲平順化而使用第二個。你可以寫類似:

defmodule TokenHolder do 

    def start_link(user,passwd) do 
    Agent.start_link(fn -> 
     tok_time = get_token user, passwd 
     {user,passwd,tok_time} 
    end, name: __MODULE__) 
    end 

    # refresh the token if older that one hour 
    @max_age 60*60*1000000 

    def token do 
    Agent.get_and_update(__MODULE__, fn state={user,passwd,{token,retrieved}} -> 
     now = :os.timestamp 
     if(:timer.now_diff(now, retrieved) < @max_age) do 
     # return old token and old state 
     {token,state} 
     else 
     # retrieve new token, return it and return changed state 
     tok_time = {token,_} = get_token user, passwd 
     {token,{user,passwd,tok_time}} 
     end 
    end) 
    end 

    defp get_token(user,passwd) do 
    token = ... # retrieve token somehow... 
    {token,:os.timestamp} 
    end 

end 

然後你只需要做:

{:ok,_} = TokenHolder.start_link("user","secret") 
token = TokenHolder.token 
+0

米羅斯拉夫,你能否詳細說明有關要求的系列化一點點? – lessless

+0

GenServer等待請求並回答它們。一個接一個,在一個過程中。請參閱http://www.erlang.org/doc/man/gen_server.html#call-2 如果您希望並行回答您的請求,則必須使用特殊技術,例如爲服務器提供PID,在其中產生新進程服務器,並等待「接收」的答案。但是這樣你就不能在請求結束時改變服務器狀態。 如果您使用上面給出的代碼,您只需在序列化代碼中獲取令牌,然後在您的過程中提出請求 - 即並行。 –

+0

您可以在這裏看到示例代碼:https://gist.github.com/mprymek/0a8f175d6c9201a81c62 嘗試將一些「:timer.sleep」放入「TokenHolder.token」中 - 您將看到KeyHolder中的操作被序列化。但對MyAPI.users的調用是並行完成的。 P.S.你可以運行「iex api_test.exs」的例子 –

相關問題