Playframework 基于“的BroadcastHub筛选”;资源“;连接的客户端正在使用?

Playframework 基于“的BroadcastHub筛选”;资源“;连接的客户端正在使用?,playframework,websocket,akka,broadcast,akka-stream,Playframework,Websocket,Akka,Broadcast,Akka Stream,我正在编写一个纯websocket web应用程序,这意味着在websocket升级之前没有用户/客户端步骤,更具体地说: 身份验证请求和其他通信一样通过WebSocket 有/有: /api/ws上正好有一个websocket端点 多个客户端连接到该端点 针对多个客户端的多个项目 现在,并不是每个客户机都可以访问每个项目——该项目的访问控制是在服务器端(ofc)实现的,与WebSocket本身无关 我的问题是,我想允许协作,这意味着N个客户可以一起处理一个项目 现在,如果其中一个客户机修改

我正在编写一个纯websocket web应用程序,这意味着在websocket升级之前没有用户/客户端步骤,更具体地说: 身份验证请求和其他通信一样通过WebSocket

有/有:

  • /api/ws上正好有一个websocket端点
  • 多个客户端连接到该端点
  • 针对多个客户端的多个项目
现在,并不是每个客户机都可以访问每个项目——该项目的访问控制是在服务器端(ofc)实现的,与WebSocket本身无关

我的问题是,我想允许协作,这意味着N个客户可以一起处理一个项目

现在,如果其中一个客户机修改了某些内容,我想通知正在处理该项目的所有其他客户机

这一点尤其重要,因为我是唯一一个对此进行研究和测试的人,这是我方面的主要疏忽,因为现在:

如果客户端A连接到项目X,客户端B连接到项目Y,如果其中任何一个更新了各自项目中的某些内容,则另一个会收到这些更改的通知

现在,我的WebsocketController相当简单,我基本上有:

private val fanIn=MergeHub.source[AllowedWSMessage].to(sink.run())
private val fanOut=source.toMat(BroadcastHub.sink[AllowedWSMessage])(Keep.right.run()
def handle:WebSocket=WebSocket.accept[AllowedWSMessage,AllowedWSMessage]
{
_=>来自SinkandSource的流量(扇入、扇出)
}
现在根据我的理解,我需要的是

1) 每个项目有多个websocket端点,如/api/{project_identifier}/ws

(十) 或

2) 基于WebSocket连接/连接的客户端正在工作的项目来拆分WebSocket连接/连接的客户端的一些方法

由于我不想走路线1),我将分享我对路线2的想法:

我现在还没有看到解决方法,问题是我可能很容易在服务器端创建一些集合,在其中存储在任何给定时刻哪个用户正在处理哪个项目(例如,如果他们选择/切换一个项目,客户机将其发送到服务器,而那个用户存储此信息)

但是我仍然有一个
扇形输出
,所以这并不能解决我关于WebSocket/AkkaStreams的问题

BroadcastHub
上是否有一些魔法(过滤)可以调用,这正是我想要的

编辑:在尝试应用@James Roper的好提示但失败后,现在在此处共享我的整个websocket逻辑:

class WebSocketController@Inject()(隐式cc:ControllerComponents,ec:ExecutionContext,system:ActorSystem,mat:Materializer)扩展了AbstractController(cc)
{ val logger:logger=logger(this.getClass())

键入WebSocketMessage=数组[字节]
导入scala.concurrent.duration_
val tickingSource:Source[WebSocketMessage,可取消]=
Source.tick(initialDelay=1秒,interval=10秒,tick=NotUsed)
.map(=>Wrapper().withKeepAlive(KeepAlive()).toByteArray)
private val generalector=system.actorOf(Props
{
新myActor(系统,“generalActor”)
}“总干事”)
private val serverMessageSource=Source
.queue[WebSocketMessage](10,OverflowStrategy.backpressure)
.mapMaterializedValue
{queue=>generalActor!InitTunnel(队列)}
private val sink:sink[WebSocketMessage,NotUsed]=sink.actorRefWithAck(generalActor,InternalMessages.Init(),InternalMessages.Acknowledged(),InternalMessages.Completed())
private val source:source[WebSocketMessage,可取消]=tickingSource.merge(serverMessageSource)
private val fanIn=MergeHub.source[WebSocketMessage].to(sink.run())
private val fanOut=source.toMat(BroadcastHub.sink[WebSocketMessage])(Keep.right.run()
//TODO切换到WebSocket.acceptOrResult
def handle:WebSocket=WebSocket.accept[WebSocketMessage,WebSocketMessage]
{
//_=>createFlow()
_=>来自SinkandSource的流量(扇入、扇出)
}
private val projectHubs=TrieMap.empty[字符串,(接收器[WebSocketMessage,未使用],源[WebSocketMessage,未使用]]
专用def buildProjectHub(项目名称:字符串)=
{
logger.info(s“为$projectName构建projectHub”)
val projectActor=system.actorOf(Props
{
新的myActor(系统,s“${projectName}Actor”)
},s“${projectName}Actor”)
val projectServerMessageSource=Source
.queue[WebSocketMessage](10,OverflowStrategy.backpressure)
.mapMaterializedValue
{queue=>projectActor!InitTunnel(queue)}
val projectSink:Sink[WebSocketMessage,NotUsed]=Sink.actorRefWithAck(projectActor,InternalMessages.Init(),InternalMessages.Acknowledged(),InternalMessages.Completed())
val projectSource:Source[WebSocketMessage,可取消]=勾选源。合并(projectServerMessageSource)
val projectFanIn=MergeHub.source[WebSocketMessage].to(projectSink.run())
val projectFanOut=projectSource.toMat(BroadcastHub.sink[WebSocketMessage])(Keep.right.run())
(projectFanIn,projectFanOut)
}
私有def getProjectHub(用户名:String,项目名称:String):流[WebSocketMessage,WebSocketMessage,NotUsed]=
{
logger.info(s“尝试获取$projectName的projectHub”)
val(sink,source)=projectHubs.getOrElseUpdate(projectName{
buildProjectHub(项目名称)
})
来自sink和source的流量耦合(sink,source)
}
私有def extractUserAndProject(msg:WebSocketMessage):(字符串,字符串)=
{
Wrapper.parseFrom(msg)。`type`match
{
案例m:MessageType=>
val消息=m.value
(message.userName、message.projectName)
大小写=>(“”,“”)
}
}
private def createFlow():流[WebSocketMessage,WebSocketMessage,NotUsed]=
{
//在这一个流中对多个聊天室进行解复用/多路复用的广播源和接收器
//稍后我们将在实现流程时提供它们
var broadcastSource:Source[WebSocketMessage,未使用]=null
var mergeSink:Sink[WebSocke
val generalFlow = {
  val (sink, source) = MergeHub.source[NonProjectSpecificEvent]
    .toMat(BroadcastHub.sink[NonProjectSpecificEvent])(Keep.both).run
  Flow.fromSinkAndSourceCoupled(sink, source)
}
} via {
  Flow.fromSinkAndSourceCoupledMat(BroadcastHub.sink[YourEvent], MergeHub.source[YourEvent]) { (source, sink) =>
    broadcastSource = source
    mergeSink = sink

    source.filter(_.isInstanceOf[NonProjectSpecificEvent])
      .via(generalFlow)
      .runWith(sink)

    NotUsed
  }
}