我需要根据一些黑名单过滤我的流程,该流程可以在流程执行之外进行更改。因此,我看到了两种方法可以做到这一点:
将黑名单封装在单独的服务中
class Blacklist(init: Set[String]) {
@volatile private var set: Set[String] = init
def get: Set[String] = set
def update(newSet: Set[String]): Unit = {
set = newSet
}
}
val blacklist = new Blacklist(Set.empty)
Flow[String]
.filterNot(blacklist.get)
在演员中加入黑名单
class Blacklist extends Actor {
import Blacklist._
private var set = Set.empty[String]
override def receive: Receive = {
case UpdateBlacklist(newset: Set[String]) =>
set = newset
case GetBlacklist =>
sender ! set
}
}
object Blacklist {
case class UpdateBlacklist(set: Set[String])
case object GetBlacklist
}
val parallelism: Int = ???
val blacklist = system.actorOf(Props(new Blacklist()))
Flow[String]
.mapAsync(parallelism) { str =>
val ask = blacklist ? Blacklist.GetBlacklist
ask.mapTo[Set[String]] map { str -> _ }
} filterNot { case (str, exclude) =>
exclude(str)
}
恐怕mapAsync的参与者持有者解决方案会引入新的异步边界,从而阻止操作员进行融合。 那么,我应该选择哪一个呢?还是有一种更惯用的方式?
答案 0 :(得分:0)
我认为在您的情况下,单独的服务解决方案更为合理。这是一个干净的解决方案,并且避免了参与者的开销。
但是,您的代码无法正常工作。您正在将不可变的Set
传递给filterNot
而不是函数。您应该考虑直接使用同步集(如in this answer所述,基于ConcurrentHashMap
),或在黑名单上实现apply
,如下所示:
class Blacklist(init: Set[String]) {
val blacklist = new AtomicReference[Set[String]](init)
def apply(s: String) = (blacklist.get) (s)
def update(newSet: Set[String]): Unit = {
blacklist.set(newSet)
}
}
val blacklist = new Blacklist(Set.empty)
Flow[String].filterNot(blacklist)
我建议始终使用AtomicReference
而不是@volatile
,因为它更明确和易于理解。