我正在考虑一个实现,我可以从Play框架Web应用程序中流式传输一些事件。有一组物联网设备可以发出事件和警报。这些设备由其ID标识。我有一个HTTP端点,我可以通过它获取这些设备的遥测信号。现在我想为警报和事件做同样的事情。所以我从这个简单的方法开始,首先在我的控制器中定义我的终点,如下所示:
def events = WebSocket.accept[String, String] { request =>
ActorFlow.actorRef { out =>
EventsActor.props(out)
}
}
我的EventsActor:
class EventsActor(out: ActorRef) extends Actor {
def receive = {
case msg: String =>
out ! ("I received your message: " + msg)
}
}
object EventsActor {
def props(out: ActorRef) =
Props(new EventsActor(out))
}
现在,我对我的EventsActor做的不多,但是稍后这个Actor会将事件和警报消息推送到其中,然后将其路由到WebSocket端点。
现在我的要求是在WebSocket端点,当客户端建立连接时,他应该能够为他希望连接的IoT设备指定一个id,我应该能够将这个id传递给EventsActor我可以在哪里筛选包含传入ID的事件。
有关如何执行此操作的任何线索?
答案 0 :(得分:0)
我做了一个快速的例子,告诉你如何解决这个问题。它还有很多不足之处,但我希望它对你有所启发!
你真正想要的是一个协调器/路由器,它可以跟踪哪些websocket actor正在监听哪些事件类型。您可以将该集线器注入所有已创建的actor,并将事件分派给它以将这些websocket actor注册到事件集。
object TelemetryHub {
/** This is the message external sensors could use to stream the data in to the hub **/
case class FreshData(eventId: UUID, data: Int)
def props = Props(new TelemetryHub)
}
class TelemetryHub extends Actor {
type EventID = UUID
private var subscriptions =
mutable.Map.empty[EventID, Set[ActorRef]].withDefault(_ => Set())
override def receive = {
/** we can use the sender ref to add the requesting actor to the set of subscribers **/
case SubscribeTo(topic) => subscriptions(topic) = subscriptions(topic) + sender()
/** Now when the hub receives data, it can send a message to all subscribers
* of that particular topic
*/
case FreshData(incomingDataTopicID, data) =>
subscriptions.find { case (topic, _) => topic == incomingDataTopicID } match {
case Some((_, subscribers)) => subscribers foreach { _ ! EventData(data) }
case None => println("This topic was not yet subscribed to")
}
}
}
现在我们有了上面的结构,你的websocket端点可能如下所示:
object WebsocketClient {
/**
* Some messages with which we can subscribe to a topic
* These messages could be streamed through the websocket from the client
*/
case class SubscribeTo(eventID: UUID)
/** This is an example of some data we want to go back to the client. Uses int for simplicity **/
case class EventData(data: Int)
def props(out: ActorRef, telemetryHub: ActorRef) = Props(new WebsocketClient(out, telemetryHub))
}
/** Every client will own one of these actors. **/
class WebsocketClient(out: ActorRef, telemetryHub: ActorRef) extends Actor {
def receive = {
/** We can send these subscription request to a hub **/
case subscriptionRequest: SubscribeTo => telemetryHub ! subscriptionRequest
/** When we get data back, we can send it right to the client **/
case EventData(data) => out ! data
}
}
/** We can make a single hub which will be shared between all the connections **/
val telemetryHub = actorSys actorOf TelemetryHub.props
def events = WebSocket.accept[String, String] { _ =>
ActorFlow.actorRef { out => {
WebsocketClient.props(out, telemetryHub)
}}
}
或者你可以使用Akka提供的内部事件总线来实现同样的目标而不那么麻烦!