我正在尝试在我的Akka演员系统中实现content-based router,根据this document,ConsistentHashingRouter是要走的路。阅读完官方文档后,我仍然对如何使用这个内置的哈希路由器感到困惑。我认为这是因为路由器本身是基于散列/密钥的,Akka doc作者选择使用的示例是涉及基于键值的缓存的场景......所以我无法分辨缓存使用哪些密钥以及哪些密钥使用路由器使用它!
我们举一个简单的例子。假设我们有以下消息:
interface Notification {
// Doesn’t matter what’s here.
}
// Will eventually be emailed to someone.
class EmailNotification implements Notification {
// Doesn’t matter what’s here.
}
// Will eventually be sent to some XMPP client and on to a chatroom somewhere.
class ChatOpsNotifications implements Notification {
// Doesn’t matter what’s here.
}
等。从理论上讲,我们可能会有20 Notification
次。我希望能够在运行时向演员/路由器发送Notification
并让路由器将其路由到正确的NotificationPubisher
:
interface NotificationPublisher<NOTIFICATION implements Notification> {
void send(NOTIFICATION notification)
}
class EmailNotificationPublisher extends UntypedActor implements NotificationPubisher<EmailNotification> {
@Override
void onReceive(Object message) {
if(message instanceof EmailNotification) {
send(message as EmailNotification)
}
}
@Override
void send(EmailNotification notification) {
// Use Java Mail, etc.
}
}
class ChatOpsNotificationPublisher extends UntypedActor implements NotificationPubisher<ChatOpsNotification> {
@Override
void onReceive(Object message) {
if(message instanceof ChatOpsNotification) {
send(message as ChatOpsNotification)
}
}
@Override
void send(ChatOpsNotification notification) {
// Use XMPP/Jabber client, etc.
}
}
现在我可以自己做这个路由,手动:
class ReinventingTheWheelRouter extends UntypedActor {
// Inject these via constructor
ActorRef emailNotificationPublisher
ActorRef chatOpsNotificationPublisher
// ...20 more publishers, etc.
@Override
void onReceive(Object message) {
ActorRef publisher
if(message instanceof EmailNotification) {
publisher = emailNotificationPublisher
} else if(message instanceof ChatOpsNotification) {
publisher = chatOpsNotificationPublisher
} else if(...) { ... } // 20 more publishers, etc.
publisher.tell(message, self)
}
}
或者我可以使用Akka-Camel module来定义基于Camel的路由器并将Notifications
发送到Camel路由器,但似乎Akka aready已经建立了 - 在解决方案中,为什么不使用它呢?我只是想弄清楚如何将Cache
示例从那些Akka文档转换为我的Notification
示例。 ConsistentHashingRouter
中“关键”的目的是什么?代码看起来会是什么样的呢?
当然,我很感激能帮助我解决这个问题的任何答案,但如果可能的话,我会非常喜欢基于Java的代码片段。 Scala看起来像是象形文字。
我同意Custom Router比ConsistentHashingRouter
更合适。在阅读自定义路由器上的文档后,似乎我会:
GroupBase
impl并直接向其发送消息(notificationGroup.tell(notification, self)
);然后GroupBase
impl,比如,NotificationGroup
会提供一个Router
实例,该实例是使用我的自定义RoutingLogic
impl注入的NotificationGroup
收到消息时,它会执行我的自定义RoutingLogic#select
方法,该方法确定将Routee
(我假定某种类型的演员?)发送给如果这是正确的(如果我错了请纠正我),那么路由选择魔法会在这里发生:
class MessageBasedRoutingLogic implements RoutingLogic {
@Override
Routee select(Object message, IndexedSeq<Routee> candidates) {
// How can I query the Routee interface and deterine whether the message at-hand is in fact
// appropriate to be routed to the candidate?
//
// For instance I'd like to say "If message is an instance of
// an EmailNotification, send it to EmailNotificationPublisher."
//
// How do I do this here?!?
if(message instanceof EmailNotification) {
// Need to find the candidate/Routee that is
// the EmailNotificationPublisher, but how?!?
}
}
}
但是你可以看到我有一些心理实施障碍要跨越。 Routee
接口并没有真正给我任何我可以智能地用来决定特定Routee
(候选)对于手头的消息是否正确的信息。
所以我问:(1)如何将消息映射到Routees
(有效地执行路由选择/逻辑)? (2)如何首先将我的发布者添加为路由? (3)我的NotificationPublisher
impls仍然需要延长UntypedActor
还是现在应该实施Routee
?
答案 0 :(得分:2)
这是Scala中的一个简单的小A / B路由器。我希望这有帮助,即使你想要一个基于Java的答案。首先是路由逻辑:
class ABRoutingLogic(a:ActorRef, b:ActorRef) extends RoutingLogic{
val aRoutee = ActorRefRoutee(a)
val bRoutee = ActorRefRoutee(b)
def select(msg:Any, routees:immutable.IndexedSeq[Routee]):Routee = {
msg match{
case "A" => aRoutee
case _ => bRoutee
}
}
}
这里的关键是我在构造函数中传入我的a
和b
actor refs,然后那些是我在select
方法中路由到的那些。然后,Group
用于此逻辑:
case class ABRoutingGroup(a:ActorRef, b:ActorRef) extends Group {
val paths = List(a.path.toString, b.path.toString)
override def createRouter(system: ActorSystem): Router =
new Router(new ABRoutingLogic(a, b))
val routerDispatcher: String = Dispatchers.DefaultDispatcherId
}
在这里同样的事情,我正在通过构造函数创建我想要路由到的actor。现在是一个简单的actor类,可以充当a
和b
:
class PrintingActor(letter:String) extends Actor{
def receive = {
case msg => println(s"I am $letter and I received letter $msg")
}
}
我将创建两个这样的实例,每个实例都有不同的字母赋值,因此我们可以验证正确的实例是否按路由逻辑获取了正确的消息。最后,一些测试代码:
object RoutingTest extends App{
val system = ActorSystem()
val a = system.actorOf(Props(classOf[PrintingActor], "A"))
val b = system.actorOf(Props(classOf[PrintingActor], "B"))
val router = system.actorOf(Props.empty.withRouter(ABRoutingGroup(a,b)))
router ! "A"
router ! "B"
}
如果你跑了这个,你会看到:
I am A and I received letter A
I am B and I received letter B
这是一个非常简单的例子,但它显示了一种方法来做你想做的事情。我希望您可以将此代码桥接到Java中并使用它来解决您的问题。