在 Scala 中存储和共享用于服务访问的 cookie



我有一个 Scalatra 2.3 应用程序,它使用 Dispatch 联系旧版服务以获取某些数据。某些 API 调用使用基于 cookie 的身份验证。我想避免为对这些安全端点的每个请求单独登录。

我的第一次尝试是一个伴侣对象,它有一个用于cookie的var和一个函数getCookie。这将从 var 返回存储的 cookie 或执行身份验证,将收到的 cookie 存储到 var 并返回它。如果 cookie 无效,逻辑将清除 cookie,检索它并重试调用。

object LegacyService {
  var cookie : Option[Cookie] = None
  def getCookie() :  Option[Cookie] = {
    this.synchronized {
    if (!cookie.isDefined) {
      def loginRequest = dispatch.url...
      val result = (for (r <- Http(loginRequest)) yield r.getCookies).apply()
      if (result.isEmpty) {
        cookie = None
      } else {
        cookie = Some(result.get(0))
      }
    }
    cookie
  }
}

事情似乎工作正常,直到我执行多个并行异步调用,需要对旧服务进行cookie。出于某种原因,这导致了某种死锁,直到应用程序重新启动,因此随着截止日期的临近,我最终扔掉了这个存储代码的cookie。

有什么建议如何正确做到这一点吗?

  1. 我会尝试使用番石榴缓存进行此cookie缓存

https://code.google.com/p/guava-libraries/wiki/CachesExplained

    LoadingCache<Key, Cookie> graphs = CacheBuilder.newBuilder()
       .expireAfterAccess(10, TimeUnit.MINUTES)
       .build(
           new CacheLoader<Key, Cookie>() {
             public Cookie load(Key key) { // no checked exception
                def loginRequest = dispatch.url...
                val result = (for (r <- Http(loginRequest)) yield r.getCookies).apply()
                if (result.isEmpty) {
                    throw new RuntimeException();
                }
                return result.get(0)
            };
       });

它可能会解决死锁问题,因为所有同步都将由番石榴处理。

  1. 另一种选择是使用一些 Scala 包装缓存,例如芒果(包装番石榴缓存)

https://github.com/feijoas/mango

所以这看起来像:

import org.feijoas.mango.common.base.Suppliers._
// a supplier is just a function () => Cookie
val supplier = () => getCookie ...          //> supplier  : () => Int 
                                            //= function0
// convert to a Guava supplier
val gSupplier = supplier.asJava             //> gSupplier  : com.google.common.base.Supplier[Cookie] 
                                            //= AsGuavaSupplier(function0)
// create s supplier that memoize its return
// value for 10 seconds
val memSupplier = memoizeWithExpiration(supplier, 10, TimeUnit.SECONDS)
                                              //> memSupplier  : () => Cookie  
                                              //= Suppliers.memoizeWithExpiration(function0, 10, SECONDS)

基本上,我想说的是,与其解决代码中的死锁,不如切换到常用的经过测试的缓存库,如番石榴,它应该没有任何并发问题。

最新更新