Akka之Flow相关API总结

(1)viaMat[T, Mat2, Mat3](flow: Graph[FlowShape[Out, T], Mat2])(combine: (Mat, Mat2) ⇒ Mat3): ReprMat[T, Mat3]
通过附加给定的步骤转换此Flow。

combine函数用于组合此流和另外一个流的物化值,并放入Flow结果的物化值。

one of the values.建议使用内部优化 Keep.leftKeep.right 保持, 而不是手动编写传入其中一个值的函数。

(2)toMat[Mat2, Mat3](sink: Graph[SinkShape[Out], Mat2])(combine: (Mat, Mat2) ⇒ Mat3): Sink[In, Mat3]
Flow连接到Sink,连接两者的处理步骤。

combine函数用于组合此flow和Sink的物化值,并放入Sink结果的物化值。

     +----------------------------+
     | Resulting Sink[In, M2]     |
     |                            |
     |  +------+        +------+  |
     |  |      |        |      |  |
 In ~~> | flow | ~Out~> | sink |  |
     |  |   Mat|        |     M|  |
     |  +------+        +------+  |
     +----------------------------+

(3)to[Mat2](sink: Graph[SinkShape[Out], Mat2]): Sink[In, Mat]
toMat(sink)(Keep.left)。生成的Sink的物化值将是现在Flow的物化值(忽略提供的Sink的物化值);如果需要不同的策略,使用toMat

(4)mapMaterializedValue[Mat2](f: Mat ⇒ Mat2): ReprMat[Out, Mat2]
转换此Flow的物化值。

(5)joinMat[Mat2, Mat3](flow: Graph[FlowShape[Out, In], Mat2])(combine: (Mat, Mat2) ⇒ Mat3): RunnableGraph[Mat3]
Join this [[Flow]] to another [[Flow]], by cross connecting the inputs and outputs, creating a [[RunnableGraph]]
通过连接输入和输出, 将此Flow加入到另一个Flow, 创建 RunnableGraph。

 +------+        +-------+
 |      | ~Out~> |       |
 | this |        | other |
 |      | <~In~  |       |
 +------+        +-------+

例如:

val connection = Tcp().outgoingConnection(localhost)
      //#repl-client

      val replParser =
        Flow[String].takeWhile(_ != "q")
          .concat(Source.single("BYE"))
          .map(elem ⇒ ByteString(s"$elem\n"))

      val repl = Flow[ByteString]
        .via(Framing.delimiter(
          ByteString("\n"),
          maximumFrameLength = 256,
          allowTruncation = true))
        .map(_.utf8String)
        .map(text ⇒ println("Server: " + text))
        .map(_ ⇒ readLine("> "))
        .via(replParser)

      connection.join(repl).run()
      //#repl-client

(6)join[Mat2](flow: Graph[FlowShape[Out, In], Mat2]): RunnableGraph[Mat]
joinMat(flow)(Keep.left)

(7)joinMat[I2, O2, Mat2, M](bidi: Graph[BidiShape[Out, O2, I2, In], Mat2])(combine: (Mat, Mat2) ⇒ M): Flow[I2, O2, M]
将此 [[Flow]] 连接到 [[[BidiFlow]] 以关闭协议栈的 "顶部":

 +---------------------------+
 | Resulting Flow            |
 |                           |
 | +------+        +------+  |
 | |      | ~Out~> |      | ~~> O2
 | | flow |        | bidi |  |
 | |      | <~In~  |      | <~~ I2
 | +------+        +------+  |
 +---------------------------+

例如:

val bidi = BidiFlow.fromFlows(
    Flow[Int].map(x ⇒ x.toLong + 2).withAttributes(name("top")),
    Flow[ByteString].map(_.decodeString("UTF-8")).withAttributes(name("bottom")))
val f = Flow[String].map(Integer.valueOf(_).toInt).join(bidi)
val result = Source(List(ByteString("1"), ByteString("2"))).via(f).limit(10).runWith(Sink.seq)

结果应该是:Seq(3L, 4L)

(8)join[I2, O2, Mat2](bidi: Graph[BidiShape[Out, O2, I2, In], Mat2]): Flow[I2, O2, Mat]
joinMat(bidi)(Keep.left)

(9)withAttributes(attr: Attributes): Repr[Out]
将此 [[流]] 的属性更改为给定部分, 并密封属性列表。这意味着进一步的调用将无法删除这些属性, 而是添加新的。请注意, 此操作对空流没有影响 (因为这些属性只适用于已包含的处理阶段)。
例如:

val flow = Flow[Int]
      .filter(100 / _ < 50).map(elem ⇒ 100 / (5 - elem))
      .withAttributes(ActorAttributes.supervisionStrategy(decider))

(10)addAttributes(attr: Attributes): Repr[Out]
将给定的属性添加到此流中。进一步调用 "withAttributes" 不会删除这些属性。请注意, 此操作对空流没有影响 (因为这些属性只适用于已包含的处理阶段)。
例如:

val section = Flow[Int].map(_ * 2).async
      .addAttributes(Attributes.inputBuffer(initial = 1, max = 1)) // the buffer size of this map is 1
val flow = section.via(Flow[Int].map(_ / 2)).async // the buffer size of this map is the default

(11)named(name: String): Repr[Out]
addAttributes(Attributes.name(name))。给Flow增加一个name属性。

(12)async: Repr[Out]
即,addAttributes(Attributes.asyncBoundary)。在这个Flow周围放置一个异步边界。

(13)runWith[Mat1, Mat2](source: Graph[SourceShape[In], Mat1], sink: Graph[SinkShape[Out], Mat2])(implicit materializer: Materializer): (Mat1, Mat2)
将Source连接到Flow,然后连接到Sink并运行它。返回的元组包含了Source和Sink的物化值。

(14)toProcessor: RunnableGraph[Processor[In @uncheckedVariance, Out @uncheckedVariance]]
将此流转换为一个RunnableGraph(物化到一个响应式流的org.reactivestreams.Processor, 它实现了由此流程封装的操作)。每个物化都会产生一个新的Processor实例, 即返回的 RunnableGraph是可重用的。

(15)recover[T >: Out](pf: PartialFunction[Throwable, T]): Repr[T]
恢复允许在发生故障时发送最后一个元素,并优雅地完成流。由于底层故障信号onError到达带外(out-of-band),它可能跳过现有元素。 该阶段可以恢复故障信号,但不能恢复跳过的元素(它们将被丢弃)。

recover中抛出异常将自动记录日志在ERROR级别。

当上游元素可用或者上游失败偏函数pf返回一个元素时,发送元素。

当下游背压时背压。

当下游完成或者引起上游失败的异常pf能够处理,则完成。

当下游取消时,取消。

例如:

Source(0 to 6).map(n ⇒
      if (n < 5) n.toString
      else throw new RuntimeException("Boom!")
    ).recover {
      case _: RuntimeException ⇒ "stream truncated"
    }.runForeach(println)

输出是:

0
1
2
3
4
stream truncated

(16)recoverWithRetries[T >: Out](attempts: Int, pf: PartialFunction[Throwable, Graph[SourceShape[T], NotUsed]]): Repr[T]
RecoverWithRetries 允许Flow失败时,切换到备用Source。在故障尝试多次并恢复后, 它将继续有效, 这样每次出现故障时, 它就会被送入偏函数"pf", 而一个新的源可能会被物化。请注意, 如果传入0, 则根本不会尝试恢复。

attempts设为负数,被解释为无限。

由于底层故障信号onError到达带外(out-of-band),它可能跳过现有元素。 该阶段可以恢复故障信号,但不能恢复跳过的元素(它们将被丢弃)。

recover中抛出异常将自动记录日志在ERROR级别。

当上游元素可用或者上游失败偏函数pf返回一个元素时,发送元素。

当下游背压时背压。

当下游完成或者引起上游失败的异常pf能够处理,则完成。

当下游取消时,取消。

参数attempts 重试的最大值或者设为-1将无限次重试。

参数偏函数pf 接收失败原因并返回要物化的新Source, 如果有的话。

如果attempts是一个负数但不是-1抛出IllegalArgumentException 异常。
例如:

val planB = Source(List("five", "six", "seven", "eight"))

    Source(0 to 10).map(n ⇒
      if (n < 5) n.toString
      else throw new RuntimeException("Boom!")
    ).recoverWithRetries(attempts = 1, {
      case _: RuntimeException ⇒ planB
    }).runForeach(println)

输出结果是:

0
1
2
3
4
five
six
seven
eight

(17)mapError(pf: PartialFunction[Throwable, Throwable]): Repr[Out]
虽然类似 recover,但此阶段可用于将错误信号转换为另一种, 而不将其日志记录为过程中的错误。因此, 从这个意义上说, 它并不完全等同于 recover(t => throw t2),因为 recover将日志记录 t2错误。

由于底层故障信号 onError 到达带外, 它可能会跳过现有的元素。此阶段可以恢复故障信号, 但不能收回将被丢弃的已跳过的元素。

recover类似,在mapError中抛出异常将被记录。
例如:

val ex = new RuntimeException("ex") with NoStackTrace
val boom = new Exception("BOOM!") with NoStackTrace
Source(1 to 3).map { a ⇒ if (a == 2) throw ex else a }
        .mapError { case t: Exception ⇒ throw boom }

(18)map[T](f: Out ⇒ T): Repr[T]
通过将给定函数应用于每个元素, 使其通过此处理步骤来转换此流。

遵守ActorAttributes.SupervisionStrategy属性。

当映射函数返回一个元素时发出。

当下游背压时背压。

当下游完成时完成。

当下游取消时取消。

(19)statefulMapConcat[T](f: () ⇒ Out ⇒ immutable.Iterable[T]): Repr[T]
将每个输入元素转换为输出元素的 Iterable, 然后将其平坦化到输出流中。该转换意味着是有状态的, 这是通过为每个物化重新创建转换函数来启用的-返回的函数通常会掩盖可变对象以在调用之间存储状态。对于无状态变量, 请参见 [[FlowOps. mapConcat]]。

返回的Iterable不能包含null值, 因为它们是非法的流元素-根据反应流规范。

遵守ActorAttributes.SupervisionStrategy属性。

当映射函数返回一个元素或者先前计算的集合仍有剩余的元素时发出。

当下游背压或者先前计算的集合仍有剩余的元素时背压。

当下游完成并且剩余的元素已经发出时完成。

当下游取消时,取消。

例如:

"be able to restart" in {
      Source(List(2, 1, 3, 4, 1)).statefulMapConcat(() ⇒ {
        var prev: Option[Int] = None
        x ⇒ {
          if (x % 3 == 0) throw ex
          prev match {
            case Some(e) ⇒
              prev = Some(x)
              (1 to e) map (_ ⇒ x)
            case None ⇒
              prev = Some(x)
              List.empty[Int]
          }
        }
      }).withAttributes(ActorAttributes.supervisionStrategy(Supervision.restartingDecider))
        .runWith(TestSink.probe[Int])
        .request(2).expectNext(1, 1)
        .request(4).expectNext(1, 1, 1, 1)
        .expectComplete()
    }

    "be able to resume" in {
      Source(List(2, 1, 3, 4, 1)).statefulMapConcat(() ⇒ {
        var prev: Option[Int] = None
        x ⇒ {
          if (x % 3 == 0) throw ex
          prev match {
            case Some(e) ⇒
              prev = Some(x)
              (1 to e) map (_ ⇒ x)
            case None ⇒
              prev = Some(x)
              List.empty[Int]
          }
        }
      }).withAttributes(ActorAttributes.supervisionStrategy(Supervision.resumingDecider))
        .runWith(TestSink.probe[Int])
        .request(2).expectNext(1, 1)
        .requestNext(4)
        .request(4).expectNext(1, 1, 1, 1)
        .expectComplete()
    }

(20)mapConcat[T](f: Out ⇒ immutable.Iterable[T]): Repr[T]
statefulMapConcat(() ⇒ f)。这个转换函数没有内部状态,即没有掩盖可变对象。
例如:

val someDataSource = Source(List(List("1"), List("2"), List("3", "4", "5"), List("6", "7")))

//#flattening-seqs
val myData: Source[List[Message], NotUsed] = someDataSource
val flattened: Source[Message, NotUsed] = myData.mapConcat(identity)
//#flattening-seqs

Await.result(flattened.limit(8).runWith(Sink.seq), 3.seconds) should be(List("1", "2", "3", "4", "5", "6", "7"))

(21)mapAsync[T](parallelism: Int)(f: Out ⇒ Future[T]): Repr[T]
通过将给定函数应用于每个元素, 使其通过此处理步骤来转换此流。 函数返回一个Future并将Future的值发给下游。并行运行的Future数量作为mapAsync的第一个参数给出。这些Future可能以任何顺序完成,但下游发出的元素与从上游接收的元素相同。

如果函数`f'引发异常,或者如果Future完成失败,并且监管(supervision)决定是akka.stream.Supervision.Stop,流将完成失败。

如果函数`f'抛出异常,或者如果Future完成失败,监管(supervision)决定是akka.stream.Supervision.Resumeakka.stream.Supervision.Restart,则元素被丢弃,流继续运行。

函数`f'总是按照元素到达的顺序对它们进行调用。

遵守ActorAttributes.SupervisionStrategy属性。

当由提供的函数返回的Future按顺序完成下一个元素时发出。

当future的数量到达配置的并行数并且下游背压时,或者当第一个future没有完成时,背压。

当下游完成并且所有的future完成并且所有元素已经发出,则完成。

当下游取消时,取消。

例如:

//#mapAsync-ask
import akka.pattern.ask
implicit val askTimeout = Timeout(5.seconds)
val words: Source[String, NotUsed] =
      Source(List("hello", "hi"))

words
      .mapAsync(parallelism = 5)(elem ⇒ (ref ? elem).mapTo[String])
      // continue processing of the replies from the actor
      .map(_.toLowerCase)
      .runWith(Sink.ignore)
//#mapAsync-ask

(22)mapAsyncUnordered[T](parallelism: Int)(f: Out ⇒ Future[T]): Repr[T]
通过将给定函数应用于每个元素, 使其通过此处理步骤来转换此流。 函数返回一个Future并将Future的值发给下游。并行运行的Future数量作为mapAsyncUnordered的第一个参数给出。每个已处理的元素将在准备就绪后立即发送到下游, 也就是说, 元素可能不会按照从上游接收到的相同顺序发送到下游。

如果函数`f'引发异常,或者如果Future完成失败,并且监管(supervision)决定是akka.stream.Supervision.Stop,流将完成失败。

如果函数`f'抛出异常,或者如果Future完成失败,监管(supervision)决定是akka.stream.Supervision.Resumeakka.stream.Supervision.Restart,则元素被丢弃,流继续运行。

函数`f'总是按照元素到达的顺序对它们进行调用(即使 "f" 返回的Future的结果可能以不同的顺序发出)。

遵守ActorAttributes.SupervisionStrategy属性。

当任一由给定函数返回的Future完成时,发送。

当future的数量到达配置的并行数并且下游背压时背压。

当下游完成并且所有的future已经完成并且所有元素已经发送时,完成。

当下游取消时取消。

(23)filter(p: Out ⇒ Boolean): Repr[Out]
只传递满足给定谓词的元素。

遵守ActorAttributes.SupervisionStrategy属性。

当对于某元素,给定的谓词返回true,则发送该元素。

当对于某元素给定的谓词返回true并且下游背压时,背压。

当下游完成时完成。

当下游取消时取消。

(24)filterNot(p: Out ⇒ Boolean): Repr[Out]
只传递那些不满足给定谓词的元素。

(25)takeWhile(p: Out ⇒ Boolean, inclusive: Boolean): Repr[Out]
在谓词第一次返回 false 后终止处理 (并取消上游发布者), 包括第一个失败的元素 (如果inclusive是真的) 由于输入缓冲, 某些元素可能已从上游发布者请求, 然后此步骤的下游不处理。

如果第一个流元素的谓词为 false, 则该流将完成而不生成任何元素。

遵守ActorAttributes.SupervisionStrategy属性。

当谓词为true时,发送元素。

当下游背压时背压。

如果inclusive为false当谓词为false时或者当如果inclusive为true而谓词为false后第一个元素已发出并且下游完成时或者下游完成时,完成。
例如:

Source(1 to 10).takeWhile(_ < 3, true).runWith(TestSink.probe[Int])
        .request(4)
        .expectNext(1, 2, 3)
        .expectComplete()

(26)takeWhile(p: Out ⇒ Boolean): Repr[Out]
即takeWhile(p, false)
在谓词第一次返回false后,终止处理(并且取消上游发布者)。由于输入缓冲,一些元素可能已经从上游发布者请求,将不会被此步骤的下游处理。

如果第一个流元素的谓词为 false, 则该流将完成而不生成任何元素。

当谓词为true时,发送元素。

当下游背压时背压。

当谓词返回false或者下游取消时,则取消。

例如:

val flowUnderTest = Flow[Int].takeWhile(_ < 5)

val future = Source(1 to 10).via(flowUnderTest).runWith(Sink.fold(Seq.empty[Int])(_ :+ _))
val result = Await.result(future, 3.seconds)
assert(result == (1 to 4))

(27)dropWhile(p: Out ⇒ Boolean): Repr[Out]
例如:
在谓词为true时,丢弃该元素。
谓词第一次返回false后,所有元素都将被采用。

Source(1 to 4).dropWhile(_ < 3).runWith(TestSink.probe[Int])
        .request(2)
        .expectNext(3, 4)
        .expectComplete()

Source(1 to 4).dropWhile(a ⇒ if (a < 3) true else throw TE("")).withAttributes(supervisionStrategy(resumingDecider))
        .runWith(TestSink.probe[Int])
        .request(1)
        .expectComplete()

Source(1 to 4).dropWhile {
        case 1 | 3 ⇒ true
        case 4     ⇒ false
        case 2     ⇒ throw TE("")
 }.withAttributes(supervisionStrategy(restartingDecider))
        .runWith(TestSink.probe[Int])
        .request(1)
        .expectNext(4)
        .expectComplete()

(28)collect[T](pf: PartialFunction[Out, T]): Repr[T]
通过将给定的偏函数应用于每个元素, 以便通过此处理步骤来转换此流。 不匹配的元素被过滤掉。

遵守ActorAttributes.SupervisionStrategy属性。

如果某元素在偏函数中有定义,则发送该元素。

如果某元素在偏函数中有定义并且下游背压,则背压。

如果下游完成,则完成。

如果下游取消,则取消。

例如:

 val emailAddresses: Source[String, NotUsed] =
      authors
        .mapAsync(4)(author ⇒ addressSystem.lookupEmail(author.handle))
        .collect { case Some(emailAddress) ⇒ emailAddress }

(29)grouped(n: Int): Repr[immutable.Seq[Out]]
将该流块组合成给定大小的组,最后一组可能由于流的结束而小于请求。

n必须是正数, 否则抛出IllegalArgumentException异常。

已累积指定数量的元素或上游完成时发出元素。

已组装出一组并且下游背压时背压。

下游完成时,完成。

下游取消时,取消。

例如:

Source(1 to 4).grouped(2)
      .runWith(Sink.seq)

结果应该是:

Seq(Seq(1, 2), Seq(3, 4))

(30)limitWeighted[T](max: Long)(costFn: Out ⇒ Long): Repr[Out]
通过使用成本函数来评估传入元素的成本, 确保流有界性。到底有多少元素将被允许前往下游取决于每个元素的评估成本。如果累计成本超过最大值, 它将向上游发出故障 "StreamLimitException" 信号。

由于输入缓冲,可能已经从上游发布者请求了一些元素,然后将不会在该步骤的下游处理。

遵守ActorAttributes.SupervisionStrategy属性。

上游发出且已累计的成本没有达到最大值时发出。

下游背压时背压。

累计的成本超过最大值时报错。

下游取消时取消。

(31)limit(max: Long): Repr[Out]
limitWeighted(max)(_ ⇒ 1)
通过限制上游元素的数量来确保流的有界性。如果传入元素的数量超过最大值, 它将向上游发出故障 "StreamLimitException" 信号。

由于输入缓冲,可能已经从上游发布者请求了一些元素,然后将不会在该步骤的下游处理。

上游发出且已发出的元素数量没有达到最大值时发出。

下游背压时背压。

传入元素的总数超过最大值时报错。

下游取消时取消。

(32)sliding(n: Int, step: Int = 1): Repr[immutable.Seq[Out]]
在流上应用滑动窗口, 并将窗口作为元素组返回;因为流尾部,最后一个组可能小于请求。

n必须是正数, 否则抛出IllegalArgumentException异常。

step````必须是正数, 否则抛出IllegalArgumentException```异常。

当窗口内已收集足够的元素或上游已完成,发出元素。

当窗口已收集足够的元素且下游背压时背压。

当上游完成时,完成。

当下游取消时,取消。

(33)scan[T](zero: T)(f: (T, Out) ⇒ T): Repr[T]
Similar to fold but is not a terminal operation,

  • emits its current value which starts at zero and then
  • applies the current and next value to the given function f,
  • emitting the next current value.
    类似于fold, 但不是终端操作, 从zero 作为当前值开始, 然后将当前值和上游传入的元素应用于给定函数 f, 立即将结果(中间结果)发送到下游,并作为下次计算的当前值。

如果函数```f````抛出异常,并且监督决定是[[akka.stream.Supervision.Restart]],则当前值将再次从零开始,流将继续。

遵守ActorAttributes.SupervisionStrategy属性。

当扫描元素的函数返回一个新元素时发出元素。

当下游背压时背压。

当上游完成时完成。

当下游取消时取消。

例如:

val scan = Flow[Int].scan(0) { (old, current) ⇒
        require(current > 0)
        old + current
      }.withAttributes(supervisionStrategy(Supervision.restartingDecider))
Source(List(1, 3, -1, 5, 7)).via(scan)

注意,scan会将每一次计算结果都发给它的下游,此处下游会收到0, 1, 4, 0, 5, 12共六个元素。

(34)scanAsync[T](zero: T)(f: (T, Out) ⇒ Future[T]): Repr[T]
类似于scan,但使用异步函数,它发出其当前值 (从zero 开始), 然后将当前值和下一个数值应用于给定函数 f, 从而发出解析为下一个当前值的Future

如果函数f````抛出异常,并且监督决定是[[akka.stream.Supervision.Restart]],则当前值将再次从zero```开始,流将继续运行。

如果函数 f 抛出异常, 并且监视决策是akka.stream.Supervision.Resume当前值从上一个当前值开始, 或者当它没有一个时, 则为zero, 流将继续。

遵守ActorAttributes.SupervisionStrategy属性。

f返回future完成时,发送元素。

当下游背压时背压。

当上游完成并且最后一个f返回的future完成时,,完成。

下游取消时,取消。

(35)fold[T](zero: T)(f: (T, Out) ⇒ T): Repr[T]
类似于scan,但它只在上游传入元素完成,并计算出最终结果后,才将最终结果发送到下游。应用给定的函数将当前值和下一个值进行计算,其结果作为下一次计算的当前值(注意此中间结果不会发给下游)。

如果函数f````抛出异常,并且监督决定是[[akka.stream.Supervision.Restart]],则当前值将再次从zero```开始,流将继续运行。

遵守ActorAttributes.SupervisionStrategy属性。

当上游完成时,发送计算结果。

当下游背压时背压。

当上游完成时,完成。

下游取消时,取消。

例如:

val fold= Flow[Int].fold(0) { (old, current) ⇒
        require(current > 0)
        old + current
      }.withAttributes(supervisionStrategy(Supervision.restartingDecider))
Source(List(1, 3, -1, 5, 7)).via(fold)

注意下游只会收到一个元素12。

(36)foldAsync[T](zero: T)(f: (T, Out) ⇒ Future[T]): Repr[T]
类似于fold,但使用异步函数,应用给定的函数将当前值和下一个值进行计算,其结果作为下一次计算的当前值(注意此中间结果不会发给下游)。

遵守ActorAttributes.SupervisionStrategy属性。

如果函数f````抛出异常,并且监督决定是[[akka.stream.Supervision.Restart]],则当前值将再次从zero```开始,流将继续运行。

当上游完成时,发送计算结果。

当下游背压时背压。

当上游完成时,完成。

下游取消时,取消。

(37)reduce[T >: Out](f: (T, T) ⇒ T): Repr[T]
类似于fold但使用收到的第一个元素作为zero元素。应用给定的函数将当前值和下一个值进行计算,其结果作为下一次计算的当前值(注意此中间结果不会发给下游)。

如果流是空的 (即在发送任何元素之前完成), 则 reduce 阶段将以NoSuchElementException使下游失败, 这在语义上与 Scala 的标准库集合在这种情况下的状态一致。

遵守ActorAttributes.SupervisionStrategy属性。

当上游完成时,发送计算结果。

当下游背压时,背压。

当上游完成时,完成。

下游取消时,取消。

(38)intersperse[T >: Out](start: T, inject: T, end: T): Repr[T]以及intersperse[T >: Out](inject: T): Repr[T]
使用所提供元素的散流, 类似于scala.collection.immutable.List.mkString在List元素之间插入分隔符的方式。

此外, 还可以向流中注入起始和结束标记元素。
例如:

  val nums = Source(List(1,2,3)).map(_.toString)
  nums.intersperse(",")            //   1 , 2 , 3
  nums.intersperse("[", ",", "]")  // [ 1 , 2 , 3 ]

如果您只想预置或仅追加元素 (但是仍然使用intercept特性在元素之间插入一个分隔符,你可能需要使用下面的模式而不是3个参数版本的intersperse(参见Source .concat用于语义细节):

 Source.single(">> ") ++ Source(List("1", "2", "3")).intersperse(",")

 Source(List("1", "2", "3")).intersperse(",") ++ Source.single("END")

上游发出时发出(或者如果有提供start元素,先发送它)。

当下游背压时,背压。

当上游完成时,完成。

下游取消时,取消。

(39)groupedWithin(n: Int, d: FiniteDuration): Repr[immutable.Seq[Out]]
将流进行分组,或按照一个时间窗口内接收到的元素分组,或按照给定数量的限制进行分组,无论哪一个先发生。 如果没有从上游接收到元素,空组将不会被发出。 流结束之前的最后一个组将包含自先前发出的组以来的缓冲元素。

n必须是正数,d必须大于0秒,否则抛出IllegalArgumentException异常。

从上一个组发射后经过配置的时间或者缓冲n个元素后发送。

当下游背压且已经有n+1个元素缓冲,则背压。

当上游完成并且最后一组已发送,完成。

当下游取消,取消。

(40)groupedWeightedWithin(maxWeight: Long, d: FiniteDuration)(costFn: Out ⇒ Long): Repr[immutable.Seq[Out]]
将流进行分组,或按照一个时间窗口内接收到的元素分组,或按照元素权重的限制进行分组,无论哪一个先发生。 如果没有从上游接收到元素,空组将不会被发出。 流结束之前的最后一个组将包含自先前发出的组以来的缓冲元素。

maxWeight必须是正数,d必须大于0秒,否则抛出IllegalArgumentException异常。

从上一个组发射后经过配置的时间或者到达权重限制后发送。

当下游背压,并且缓冲组(+等待元素)权重大于maxWeight时,背压。

当上游完成并且最后一组已发送,完成。

当下游取消,取消。

(41)delay(of: FiniteDuration, strategy: DelayOverflowStrategy = DelayOverflowStrategy.dropTail): Repr[Out]
按一定时间间隔将元素的发射时间改变。在等待下一个元素被发送时,它允许存储元素在内部缓冲区。如果缓冲区内没有足够的空间,根据定义的akka.stream.DelayOverflowStrategy,可以丢弃元素或者背压上游。

延迟精度为 10ms, 以避免不必要的计时器调度周期。

内部缓冲区的默认容量为16。您可以通过调用addAttributes (inputBuffer)来设置缓冲区大小。

当缓冲区有等待的元素并且为这个元素配置的时间已到达,则发送元素。

OverflowStrategy策略介绍:

  • emitEarly:如果缓冲区已满,当新的元素可用时,这个策略不等待直接发送下一个元素到下游。
  • dropHead:如果缓冲区已满,当新的元素到达,丢弃缓冲区中最旧的元素,从而为新元素留出空间。
  • dropTail:如果缓冲区已满,当新的元素到达,丢弃缓冲区中最新的元素,从而为新元素留出空间。
  • dropBuffer:如果缓冲区已满,当新的元素到达,丢弃缓冲区中所有元素,从而为新元素留出空间。
  • dropNew:如果缓冲区已满,当新的元素到达,丢弃这个新元素。
  • backpressure:如果缓冲区已满,当新的元素到达,则背压上游发布者直到缓冲区内的空间可用。
  • fail:如果缓冲区已满,当新的元素到达,则以失败完成流。

当上游完成并且缓冲元素被耗尽时,完成。

当下游取消时,取消。

(42)drop(n: Long): Repr[Out]
在流的开始处丢弃给定数目的元素。
如果n为零或为负数, 则不会丢弃任何元素。

当已经丢弃了指定数目的元素,则发送元素。

当指定数目的元素已被丢弃并且下游背压时,背压。

当上游完成时,完成。

当下游取消时,取消。

(43)dropWithin(d: FiniteDuration): Repr[Out]
在流的开始处在给定的持续时间内,丢弃接收的元素。

当经过指定的时间后并且新的上游元素到达,发送元素。

当下游背压时,背压。

当上游完成时,完成。

下游取消时,取消。

(44)take(n: Long): Repr[Out]
在给定数量的元素之后终止处理 (并取消上游发布者)。由于输入缓冲, 有些元素可能已从上游发布者请求, 这些元素将不会在这一步的下游进行处理。

如果n是0或者负数,流将不产生任何元素完成。

指定的数目还没到达时,发送元素。

当下游背压时,背压。

当指定数目的元素已经处理或者上游完成时,完成。

当指定数目的元素已被处理或者下游取消时,取消。

(45)takeWithin(d: FiniteDuration): Repr[Out]
在给定的持续时间后,终止处理(并取消上游发布者)。由于输入缓冲, 有些元素可能已从上游发布者请求, 这些元素将不会在这一步的下游进行处理。

请注意,这可以与take结合来限制持续时间内的元素数量。

当上游元素到达时,发送元素。

当下游背压时,背压。

在上游完成或计时器触发时完成。

当下游取消或计时器触发时取消。

(46)conflateWithSeed[S](seed: Out ⇒ S)(aggregate: (S, Out) ⇒ S): Repr[S]
通过将元素合并到一个摘要中,允许更快的上游独立于较慢的订阅者,直到订阅者准备好接受它们。例如,如果上游的发布者速度更快,那么合并的步骤可能会匀化传入的数目。

此版本的合并允许从第一个元素派生种子, 并将聚合类型更改为与输入类型不同的类型。有关不更改类型的更简单版本, 请参见FlowOps.conflate

此元素仅在上游速度较快时聚合元素, 但如果下游速度较快, 则不会复制元素。

遵守ActorAttributes.SupervisionStrategy属性。

当下游停止背压并且有可用的已合并元素,则发送元素。

没有背压的时候。

当上游完成时,完成。

当下游取消时,取消。

参数seed,使用第一个未消耗的元素作为开始,为合并值提供第一个状态。
参数aggregate, 获取当前聚合的值和当前正在等待的元素,以生成一个新的聚合值。

也请参见FlowOps.conflate, FlowOps.limit, FlowOps.limitWeighted, FlowOps.batch, FlowOps.batchWeighted

(47)conflate[O2 >: Out](aggregate: (O2, O2) ⇒ O2): Repr[O2]
通过将元素合并到一个摘要中,允许更快的上游独立于较慢的订阅者,直到订阅者准备好接受它们。例如,如果上游的发布者速度更快,那么合并的步骤可能会匀化传入的数目。

此版本的合并不改变流的输出类型。请参见FlowOps.conflateWithSeed,一个更为复杂的版本,可以在聚合时使用种子函数并转换元素。

此元素仅在上游速度较快时聚合元素, 但如果下游速度较快, 则不会复制元素。

遵守ActorAttributes.SupervisionStrategy属性。

当下游停止背压并且有可用的已合并元素,则发送元素。

没有背压的时候。

当上游完成时,完成。

当下游取消时,取消。

参数aggregate, 获取当前聚合的值和当前正在等待的元素,以生成一个新的聚合值。

也请参见FlowOps.conflateWithSeed, FlowOps.limit, FlowOps.limitWeighted, FlowOps.batch, FlowOps.batchWeighted

(48)batch[S](max: Long, seed: Out ⇒ S)(aggregate: (S, Out) ⇒ S): Repr[S]
通过将元素聚合到批次中,允许更快的上游独立于较慢的订阅者,直到订阅者准备好接受它们。例如,如果上游发布者更快,一个batch步骤可以存储接收到的元素于一个数组直到最大限制值。

此元素仅在上游速度较快时聚合元素, 但如果下游速度较快, 则不会复制元素。

遵守ActorAttributes.SupervisionStrategy属性。

当下游停止背压并且有可用的已聚合元素,则发送元素。

当成批元素达到最大值而有一个待处理元素且下游背压时,背压。

当上游完成且没有成批或待处理元素等待,完成。

参数max 在背压上游前,可成批的最大元素数
(必须是非0正数)。
参数seed,使用第一个未消耗的元素作为开始,为合并值提供第一个状态。
参数aggregate, 获取当前成批的值和当前正在等待的元素,以生成一个新的聚合值。

(49)batchWeighted[S](max: Long, costFn: Out ⇒ Long, seed: Out ⇒ S)(aggregate: (S, Out) ⇒ S): Repr[S]
通过将元素聚合到批次中,允许更快的上游独立于较慢的订阅者,直到订阅者准备好接受它们。例如,如果上游发布者速度更快,则batch步骤可以将“ByteString”元素连接到允许的最大限制。

此元素仅在上游速度较快时聚合元素, 但如果下游速度较快, 则不会复制元素。

成批将应用于所有元素,即使一个单一元素成本比允许的最大值还大。这种情况下,先前成批的元素将被发送,然后这个"重"元素将被发送(在应用了种子函数后)而不用它与其它元素成批处理,然后其余传入的元素成批处理。

当下游停止背压并且有可用的已聚合元素,则发送元素。

当成批元素权重值到达最大值而有一个待处理元素且下游背压时,背压。

当上游完成且没有成批或待处理元素等待,完成。

当下游取消时,取消。

参数max 在背压上游前,成批元素的最大权重值(必须时非0正数)
参数costFn 用于计算单一元素权重值的函数
参数seed,使用第一个未消耗的元素作为开始,为成批值提供第一个状态。
参数aggregate, 获取当前成批的值和当前正在等待的元素,以生成一个新的成批值。

(50)expand[U](extrapolate: Out ⇒ Iterator[U]): Repr[U]
通过从较老的元素中外推元素,直到从上游来新元素,可以使更快地下游独立于较慢的发布者。例如,扩展步骤可能会重复给订阅者的最后一个元素,直到它从上游接收到更新。

这个元素永远不会“丢弃”上游元素,因为所有元素都经过至少一个外推步骤。

这意味着如果上游实际上比上游更快,它将被下游用户背压。

Expand不支持akka.stream.Supervision.Restartakka.stream.Supervision.Resume

来自seed或者extrapolate函数的异常将使流以失败完成。

当下游停止背压时,发送元素。

当下游背压或者迭代器运行为空时,背压。

当上游完成时,完成。

当下游取消时,取消。

(51)buffer(size: Int, overflowStrategy: OverflowStrategy): Repr[Out]
在流中添加一个固定大小的缓冲区, 允许从较快的上游存储元素, 直到它变为满的。如果没有可用的空间, 根据已定义的 akka.stream.OverflowStrategy, 它可能会丢弃元素或上背压上游。

当下游停止背压,并且在缓冲区有待处理元素,则发送元素。

OverflowStrategy策略介绍:

  • emitEarly:如果缓冲区已满,当新的元素可用时,这个策略不等待直接发送下一个元素到下游。
  • dropHead:如果缓冲区已满,当新的元素到达,丢弃缓冲区中最旧的元素,从而为新元素留出空间。
  • dropTail:如果缓冲区已满,当新的元素到达,丢弃缓冲区中最新的元素,从而为新元素留出空间。
  • dropBuffer:如果缓冲区已满,当新的元素到达,丢弃缓冲区中所有元素,从而为新元素留出空间。
  • dropNew:如果缓冲区已满,当新的元素到达,丢弃这个新元素。
  • backpressure:如果缓冲区已满,当新的元素到达,则背压上游发布者直到缓冲区内的空间可用。
  • fail:如果缓冲区已满,当新的元素到达,则以失败完成流。

当上游完成并且缓冲元素被耗尽时,完成。

当下游取消时,取消。

(52)prefixAndTail[U >: Out](n: Int): Repr[(immutable.Seq[Out], Source[U, NotUsed])]
从流中获取n个元素 (仅当上游在发出n个元素之前完成比n小) 并返回一个包含所取元素的严格序列和一个表示剩余元素的流的pair。如果 "n" 为零或负, 则返回一个空集合和一个流(将包含整个上游的流保持不变)的pair

如果上游出现错误, 则取决于当前状态

  • 如果在获取n个元素之前,主流标识错误,子流尚未发出。
  • 如果在获取n个元素之后,主流标识错误,子流已经发出(在那一刻,主流已经完成)

当达到配置的“前缀”元素数目时,发送“前缀”以及剩余部分组成的子流。

当下游背压或者子流背压时,背压。

当“前缀”元素和子流都已耗尽时,完成。

当下游取消或者子流取消时,取消。

(53)groupBy[K](maxSubstreams: Int, f: Out ⇒ K): SubFlow[Out, Mat, Repr, Closed]
此操作将输入流解复用为单独的输出流,每个元素键一个输出流。 使用给定函数为每个元素计算键。 当第一次遇到一个新的键时,一个新的子流被打开,并随后所有属于该键的元素输入到该流。

从这个方法返回的对象不是一个普通的Source或Flow,而是一个SubFlow。这意味着在此之后,所有的转换都将以相同的方式应用于所有遇到的子流。SubFlow模式通过关闭子流(即将其连接到一个Sink)或将子流合并在一起而退出;有关更多信息,请参见SubFlow中的to和mergeBack方法。

需要注意的是子流也像任何其他流一样传播背压,这意味着阻塞一个子流将阻塞“groupBy”运算符本身——从而阻塞所有子流——一旦所有的内部或显式缓冲区被填满。

如果 groupby 函数 f抛出一个异常, 并且监管策略是 akka.stream.Supervision.Stop, 则流和 substreams 将以失败完成。

如果 groupby 函数 f抛出一个异常, 并且监管策略是 akka.stream.Supervision.Resume或者akka.stream.Supervision.Restart, 该元素被丢弃,流和 substreams 将继续运行。

函数f不可以返回null。这将抛出异常并触发监管决策机制。

遵守ActorAttributes.SupervisionStrategy属性。

当分组函数返回尚未创建的组的元素时发出。发出新组。

当某个组有尚未处理的元素,而该组的子流背压时,背压。

当上游完成时,完成。

当下游取消并且所有子流取消时,取消。

参数maxSubstreams 配置支持的最大子流数/键数。如果遇到更多不同的键, 则流将失败。

(54)splitWhen(substreamCancelStrategy: SubstreamCancelStrategy)(p: Out ⇒ Boolean): SubFlow[Out, Mat, Repr, Closed]
此操作将给定谓词应用于所有传入元素, 并将它们发送到输出流中的一个流, 如果给定谓词返回 true, 则总是用当前元素开始新的一个子流。这意味着, 对于以下一系列谓词值, 将产生三个子流, 长度为1、2和 3:

    false,             // 元素进入第一个子流
    true, false,       // 元素进入第二个子流
    true, false, false // 元素进入第三个子流

如果流的 * 第一个 * 元素与谓词匹配, 则 splitWhen 发出的第一个流将从该元素开始。例如:

     true, false, false // 第一个流从拆分元素开始
     true, false        // 随后的子流操作方式相同

从这个方法返回的对象不是一个普通的Source或Flow,而是一个SubFlow。这意味着在此之后,所有的转换都将以相同的方式应用于所有遇到的子流。SubFlow模式通过关闭子流(即将其连接到一个Sink)或将子流合并在一起而退出;有关更多信息,请参见SubFlow中的to和mergeBack方法。

需要注意的是子流也像任何其他流一样传播背压,这意味着阻塞一个子流将阻塞“splitWhen”运算符本身——从而阻塞所有子流——一旦所有的内部或显式缓冲区被填满。

如果拆分谓词 p抛出一个异常, 并且监管策略是 akka.stream.Supervision.Stop, 则流和 substreams 将以失败完成。

如果拆分谓词 p抛出一个异常, 并且监管策略是 akka.stream.Supervision.Resume或者akka.stream.Supervision.Restart, 该元素被丢弃,流和 substreams 将继续运行。

当元素对于谓词为true时,为随后的元素打开并发出新的子流。

当某个子流有待处理的元素而之前的元素尚未完全消耗时,或子流背压时,背压。

当上游完成时,完成。

当下游取消并且子流以SubstreamCancelStrategy.drain取消时,或者下游取消或任何子流以SubstreamCancelStrategy.propagate取消时,取消。

也请参见FlowOps.splitAfter

(55)splitWhen(p: Out ⇒ Boolean): SubFlow[Out, Mat, Repr, Closed]
splitWhen(SubstreamCancelStrategy.drain)(p)

(56)splitAfter(substreamCancelStrategy: SubstreamCancelStrategy)(p: Out ⇒ Boolean): SubFlow[Out, Mat, Repr, Closed]
此操作将给定谓词应用于所有传入元素, 并将它们发送到输出流中的一个流, 如果给定谓词返回 true, 则结束当前子流。这意味着, 对于以下一系列谓词值, 将产生三个子流, 长度为2、2和 3:

     false, true,        // 元素进入第一个子流
     false, true,        // 元素进入第二个子流
     false, false, true  // 元素进入第三个子流

需要注意的是子流也像任何其他流一样传播背压,这意味着阻塞一个子流将阻塞“splitAfter”运算符本身——从而阻塞所有子流——一旦所有的内部或显式缓冲区被填满。

如果拆分谓词 p抛出一个异常, 并且监管策略是 akka.stream.Supervision.Stop, 则流和 substreams 将以失败完成。

如果拆分谓词 p抛出一个异常, 并且监管策略是 akka.stream.Supervision.Resume或者akka.stream.Supervision.Restart, 该元素被丢弃,流和 substreams 将继续运行。

当元素经过时发出。当提供的谓词为真时, 它发出元素并为后续元素打开一个新的流。

当某个子流有待处理的元素而之前的元素尚未完全消耗时,或子流背压时,背压。

当上游完成时完成。

当下游取消并且子流以SubstreamCancelStrategy.drain取消时,或者下游取消或任何子流以SubstreamCancelStrategy.propagate取消时,取消。

也请参见FlowOps.splitWhen

(57)splitAfter(p: Out ⇒ Boolean): SubFlow[Out, Mat, Repr, Closed]
splitAfter(SubstreamCancelStrategy.drain)(p)

(58)flatMapConcat[T, M](f: Out ⇒ Graph[SourceShape[T], M]): Repr[T]
将每个输入元素转换为输出元素的Source,然后通过串联将其平铺为输出流,从而一个接着一个的完全处理Source。

当当前消费的子流有一个元素可用时发出。

当下游背压时,背压。

当上游完成且所有子流完成时,完成。

当下游取消时,取消。

(59)flatMapMerge[T, M](breadth: Int, f: Out ⇒ Graph[SourceShape[T], M]): Repr[T]
将每个输入元素转换为输出元素的“Source”,然后通过合并将输出元素展平成输出流,其中在任何给定时间最大“广度”子流被处理。

当当前消费的子流有一个元素可用时发出。

当下游背压时,背压。

当上游完成且所有子流完成时,完成。

当下游取消时,取消。

例如:

val async = Flow[Int].map(_ * 2).async

Source(0 to 9)
        .map(_ * 10)
        .flatMapMerge(5, i ⇒ Source(i to (i + 9)).via(async))
        .grouped(1000)
        .runWith(Sink.head)
        .futureValue
        .sorted should ===(0 to 198 by 2)

(60)initialTimeout(timeout: FiniteDuration): Repr[Out]
如果在提供的超时之前,第一个元素还没有经过这个阶段,则流以scala.concurrent.TimeoutException失败。

当上游发出元素时,发出。

当下游背压时,背压。

当上游完成时完成或者在第一个元素到达前超时已过则失败。

当下游取消时取消。

(61)completionTimeout(timeout: FiniteDuration): Repr[Out]
当超时已过时,流尚未完成,则流以scala.concurrent.TimeoutException失败。

当上游发出元素时,发出。

当下游背压时,背压。

当上游完成时完成或者在上游完全前超时已过则失败。

当下游取消时取消。

(62)idleTimeout(timeout: FiniteDuration): Repr[Out]
如果两个处理的元素之间的时间超过了提供的超时时间,则流以scala.concurrent.TimeoutException失败。定期检查超时,所以检查的分辨率是一个周期(等于超时值)。

当上游发出元素时,发出。

当下游背压时,背压。

当上游完成时完成或者如果在两个发出的元素之间超时失败。

当下游取消时取消。

(63)backpressureTimeout(timeout: FiniteDuration): Repr[Out]
如果一个元素的发出和下一个下游需求之间的时间超过了提供的超时时间,则流以scala.concurrent.TimeoutException失败。定期检查超时,所以检查的分辨率是一个周期(等于超时值)。

当上游发出元素时,发出。

当下游背压时,背压。

当上游完成时完成或者如果一个元素的发出和下一个下游需求之间的时间超时失败。

当下游取消时取消。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,736评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,167评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,442评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,902评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,302评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,573评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,847评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,562评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,260评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,531评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,021评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,367评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,016评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,068评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,827评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,610评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,514评论 2 269

推荐阅读更多精彩内容