卡夫卡致websocket的信息

卡夫卡致websocket的信息,websocket,akka-stream,akka-http,Websocket,Akka Stream,Akka Http,我正在尝试使用反应式Kafka、akka http和akka流将Kafka使用者写入websocket流 val publisherActor = actorSystem.actorOf(CommandPublisher.props) val publisher = ActorPublisher[String](publisherActor) val commandSource = Source.fromPublisher(publisher) map toMessage def

我正在尝试使用反应式Kafka、akka http和akka流将Kafka使用者写入websocket流

  val publisherActor = actorSystem.actorOf(CommandPublisher.props)
  val publisher = ActorPublisher[String](publisherActor)
  val commandSource = Source.fromPublisher(publisher) map toMessage
  def toMessage(c: String): Message = TextMessage.Strict(c)

  class CommandPublisher extends ActorPublisher[String] {
    override def receive = {
      case cmd: String =>
        if (isActive && totalDemand > 0)
          onNext(cmd)
    }
  }

  object CommandPublisher {
    def props: Props = Props(new CommandPublisher())
  }

  // This is the route 
  def mainFlow(): Route = {
    path("ws" / "commands" ) {
       handleWebSocketMessages(Flow.fromSinkAndSource(Sink.ignore, commandSource))
    } 
  }
从卡夫卡消费者(此处省略),我做了一个
发布者!commandString
可动态向websocket添加内容

但是,当我启动websocket的多个客户端时,在后端遇到此异常:

[ERROR] [03/31/2016 21:17:10.335] [KafkaWs-akka.actor.default-dispatcher-3][akka.actor.ActorSystemImpl(KafkaWs)] WebSocket handler failed with can not subscribe the same subscriber multiple times (see reactive-streams specification, rules 1.10 and 2.12)
java.lang.IllegalStateException: can not subscribe the same subscriber multiple times (see reactive-streams specification, rules 1.10 and 2.12)
  at akka.stream.impl.ReactiveStreamsCompliance$.canNotSubscribeTheSameSubscriberMultipleTimesException(ReactiveStreamsCompliance.scala:35)
  at akka.stream.actor.ActorPublisher$class.aroundReceive(ActorPublisher.scala:295)
  ...
一个流不能用于所有websocket客户端吗?还是应该为每个客户端创建流/发布者参与者


在这里,我打算向所有websocket客户端发送“当前”/“实时”通知。通知的历史记录是不相关的,需要对新客户忽略。

我很抱歉要告诉你一个坏消息,但这似乎是akka关于通知的明确设计。不能按照您的意愿为所有客户端重用流实例。由于Rx模型,扇出必须是“明确的”

我遇到的示例使用特定于路由的

  // The flow from beginning to end to be passed into handleWebsocketMessages
  def websocketDispatchFlow(sender: String): Flow[Message, Message, Unit] =
    Flow[Message]
      // First we convert the TextMessage to a ReceivedMessage
      .collect { case TextMessage.Strict(msg) => ReceivedMessage(sender, msg) }
      // Then we send the message to the dispatch actor which fans it out
      .via(dispatchActorFlow(sender))
      // The message is converted back to a TextMessage for serialization across the socket
      .map { case ReceivedMessage(from, msg) => TextMessage.Strict(s"$from: $msg") }

  def route =
    (get & path("chat") & parameter('name)) { name =>
      handleWebsocketMessages(websocketDispatchFlow(sender = name))
    }
以下是关于它的讨论:

这正是我不喜欢Akka Stream的地方,这很明显 散开。当我从某个我想要的地方收到数据源时 过程(例如,可观察或源),我只想订阅它 我不想关心天气是冷是热还是冷 是否已被其他认购人认购。这是我的河流类比。 这条河不应该关心谁喝了它,也不应该关心饮酒者 不应该关心这条河的源头或其他多少 那里的饮酒者很多。我的样本,相当于Mathias的样本 提供的,确实共享数据源,但它只是引用 你可以有2个订户,也可以有100个订户,不是吗 重要。在这里,我得到了幻想,但引用计数没有 如果你不想失去比赛或者你想确保 这条小溪一直开着。但随后使用的是
ConnectableObservable
它有
connect():Cancelable
,非常适合说。。。 游戏的生命周期插件。以及您可以使用 如果要重复上一步,请选择BehaviorSubject或ReplaySubject 新订阅者的值。然后事情就开始了,没有手册 需要绘制连接图。 ... ... (这是来自) ... 对于接受一个可观测值并返回一个可观测值的函数,我们 确实有升力,这是最接近有升力的东西 名称和可在Monifu中用于
主题
或其他 由于LiftoOperators1(和2)的可观察类型,这是什么 可以在不丢失其类型的情况下变换观察值- 与RxJava使用
lift
所做的相比,这是一个面向对象的改进

但是,这些函数并不等同于
处理器
/
主题
。这个 不同之处在于,
主体
同时是消费者和消费者 制作人这意味着订阅者无法精确控制 当数据源启动并且数据源本质上是 热(意味着多个订阅者共享同一个数据源)。在Rx中,如果您对冷观测值进行建模(即 每个个体启动一个新数据源的可观测值 订户)。另一方面,在Rx(通常)中,使用 只能订阅一次的数据源,仅此而已。这个 Monifu中这一规则的唯一例外是由 GroupBy运算符,但这类似于确认 规则

这意味着什么,特别是再加上 Monifu和反应流协议的合同(应 不与同一消费者多次订阅),这是
主题
处理器
实例不可重用。为了这样 作为一个可重用的实例,Rx模型需要一个
处理器
。此外,这意味着无论何时您想要使用
主题
/
处理器
,您的数据源必须自动处于热状态 (可在多个订阅者之间共享)


有趣的是,我采用了我的代码,它的工作原理出人意料。它使用Source.actorPublisher而不是Source.fromPublisher-这是唯一的区别。我不明白为什么会这样,我也不知道。我很确定我引用的帖子是准确的。。。因此,这意味着,您必须像普通参与者一样使用它,而不是从
ActorPublisher
连接到一个ReactiveStreams Compliant,它说:/***创建一个[[org.ReactiveStreams.Publisher]]由[[ActorPublisher]]参与者支持的[[org.ReactiveStreams.Publisher]]。它可以*附加到[[org.reactivestreams.Subscriber]]或用作[[akka.stream.scaladsl.Flow]]的输入源*/