2015-09-27 20 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”的例子 –

相关问题