我有一个 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。
有什么建议如何正确做到这一点吗?
- 我会尝试使用番石榴缓存进行此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)
};
});
它可能会解决死锁问题,因为所有同步都将由番石榴处理。
- 另一种选择是使用一些 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)
基本上,我想说的是,与其解决代码中的死锁,不如切换到常用的经过测试的缓存库,如番石榴,它应该没有任何并发问题。