原文地址:Resource Resolution
鑒于很少-如果存在-真實的生活應用可以不依賴外部資源工作,環境識別資源解決方案成為應用基礎設施的一個關鍵部分。squbs通過ResolverRegistry
提供資源解決方案,并允許任何類型的資源通過名稱解決,環境指的是在開發環境、測試環境、研發環境下使用不同的資源。
依賴
這個解析器在 squbs-ext
。在你的build.sbt
或scala構建文件中加入以下依賴:
"org.squbs" %% "squbs-ext" % squbsVersion
用法
Resolver
的基礎用法來發現資源。類型由注冊表生產,可以擁有多種類型的資源。在我們的例子中,我們使用 URI
類型在本篇文檔中。查找調用如下所示:
Scala
//在指定的環境下處理資源
val resource: Option[URI] = ResolverRegistry(system).resolve[URI]("myservice", QA)
Java
//在指定的環境下處理資源
val resource: Optional<URI> = ResolverRegistry.get(system).resolve(URI.class, "myservice", QA.value());
ResolverRegistry
ResolverRegistry
是一個Akka的擴展,并在Scala和Java中遵循Akka擴展使用模式。它可以托管各種類型的資源解釋器,因此通過將它傳遞給register
調用在注冊時提供資源類型。同類型或不同類型的多個解釋器可以被注冊。
發現鏈
資源發現遵循后進后出模型。最近注冊的解釋器優先于先前注冊的。ResolverRegistry逐一走鏈直到有一個解釋器兼容該類型并生產資源,或者將鏈走完。在那種情況下,這個注冊表將返回None
在Scala API中,在Java API中返回Optional.empty()
。
類型兼容
在調用 resolve
時, ResolverRegistry
檢查請求類型。如果這個注冊的解釋器的類型與請求類型是同類型或自類型,這個解釋器(resolver )將被用于嘗試通過名稱解釋該資源。
由于JVM類型擦除,注冊類型參數或請求類型不計算在內。舉個例子,一個注冊類型 java.util.ArrayList<String>
可能被resolve
調用匹配上java.util.List<Int>
,因為String
和Int
類型在運行時被擦除了。因為這個限制,使用參數類型來注冊和查找通常不建議使用。結果是未定義的-你可能獲得一個錯誤的資源。
簡單的來說,這里不建議使用類型的層次結構。所有注冊的類型應該是唯一類型。
注冊解釋器
這里有兩套解釋器注冊的API風格。一個是一個快捷的API通過傳遞一個閉包/lambda作為解釋器。這個閉包 /lambda返回 Option[T]
類型在Scala中,Optional<T>
在Java中。另一個,完全的API使用 Resolver[T]
在Scala中,AbstractResolver<T>
在Java中。T
為資源類型。參見如下:
Scala
// To register a new resolver for type URI using a closure. Note the return
// type of the closure must be `Option[T]` or in this case `Option[URI]`
ResolverRegistry(system).register[URI]("MyResolver") { (svc, env) =>
(svc, env) match {
case ("myservice", QA) => Some(URI.create("http://myservice.qa.mydomain.com"))
case ("myservice", Default) => Some(URI.create("http://myservice.mydomain.com"))
case ("myservice2", QA) => Some(URI.create("http://myservice2.qa.mydomain.com"))
case ("myservice2", Default) => Some(URI.create("http://myservice2.mydomain.com"))
case _ => None
}
}
// To register a new resolver for type URI by extending the `Resolver` trait
class MyResolver extends Resolver[URI] {
def name: String = "MyResolver"
def resolve(svc: String, env: Environment = Default): Option[URI] = {
(svc, env) match {
case ("myservice", QA) => Some(URI.create("http://myservice.qa.mydomain.com"))
case ("myservice", Default) => Some(URI.create("http://myservice.mydomain.com"))
case ("myservice2", QA) => Some(URI.create("http://myservice2.qa.mydomain.com"))
case ("myservice2", Default) => Some(URI.create("http://myservice2.mydomain.com"))
case _ => None
}
}
}
//然后只需要注冊解釋器
ResolverRegistry(system).register[URI](new MyResolver)
Java
// To register a new resolver for type URI using a lambda. Note the return
// type of the lambda must be `Optional<T>` or in this case `Optional<URI>`
ResolverRegistry.get(system).register("MyResolver", (svc, env) -> {
if ("myservice".equals(svc)) {
if (QA.value().equals(env)) {
return Optional.of(URI.create("http://myservice.qa.mydomain.com"));
} else {
return Optional.of(URI.create("http://myservice.mydomain.com"));
}
} else if ("myservice2".equals(svc)) {
if (QA.value().equals(env)) {
return Optional.of(URI.create("http://myservice2.qa.mydomain.com"));
} else {
return Optional.of(URI.create("http://myservice2.mydomain.com"));
}
} else {
return Optional.empty();
}
});
// To register a new resolver for type URI by extending an abstract class
public class MyResolver extends AbstractResolver<URI> {
@Override
public String name() {
return "MyResolver";
}
@Override
public Optional<URI> resolve(String svc, Environment env) {
if ("myservice".equals(svc)) {
if (QA.value().equals(env)) {
return Optional.of(URI.create("http://myservice.qa.mydomain.com"));
} else {
return Optional.of(URI.create("http://myservice.mydomain.com"));
}
} else if ("myservice2".equals(svc)) {
if (QA.value().equals(env)) {
return Optional.of(URI.create("http://myservice2.qa.mydomain.com"));
} else {
return Optional.of(URI.create("http://myservice2.mydomain.com"));
}
} else {
return Optional.empty();
}
}
}
// Then register MyResolver.
ResolverRegistry.get(system).register(URI.class, new MyResolver());
解釋資源(Resolving for a Resource)
類似于注冊,解決方案需要一個類型兼容注冊類型;注冊類型必須相同或為解決類型的子類。
Scala
// To resolve a resource with `Default` environment.
val resource: Option[URI] = ResolverRegistry(system).resolve[URI]("myservice")
// To resolve a resource for a specific environment.
val resource: Option[URI] = ResolverRegistry(system).resolve[URI]("myservice", QA)
Java
val resource: Optional<URI> = ResolverRegistry.get(system).resolve(URI.class, "myservice", QA.value());
取消注冊解析器
取消注冊解析器通過名稱使用以下API完成
Scala
ResolverRegistry(system).unregister("MyResolver")
Java
ResolverRegistry.get(system).unregister("MyResolver");
并發考慮
注冊解析器和取消注冊解析器以非并發方式完成,在初始化時間。這里沒有對并發注冊進行保護,因此并發注冊的結果是未定義的。你的解釋器可能,亦或是不可能在一個并發注冊解析器上注冊或取消注冊。
然而,在ResolverRegistry
級別上,Resolve調用是線程安全的并且可以無限次的并發訪問。每個注冊解釋器需要線程安全。