这几天上海快下了五天的雨☔️☔️☔️☔️,淅淅沥沥,郁郁沉沉。

    一共存在四个api:

  • Source.actorRef,返回actorRef,该actorRef接收到的消息,将被下游消费者所消费。
  • Sink.actorRef,接收actorRef,做为数据流下游消费节点。
  • Source.actorPublisher,返回actorRef,使用于reactive stream的Publisher。
  • Sink.actorSubscriber,使用于reactive stream的Subscriber。

Source.actorRef

  1. val stringSourceinFuture=Source.actorRef[String](100,OverflowStrategy.fail) // 缓存最大为100,超出的话,将以失败告终
  2. val hahaStrSource=stringSourceinFuture.filter(str=>str.startsWith("haha")) //source数据流中把不是以"haha"开头的字符串过滤掉
  3. val actor=hahaStrSource.to(Sink.foreach(println)).run()
  4. actor!"asdsadasd"
  5. actor!"hahaasd"
  6. actor!Success("ok")// 数据流成功完成并关闭

    "how to create a Source that can receive elements later via a method call?"在akka-http中经常遇见Source[T,N]的地方就是对文件上传和下载的功能的编码(文件IO)中,完成file=>Source[ByteString,_]的转化,或者Source(List(1,2,3,4,5))这种hello-world级别的玩具代码中,这些代码中在定义Source时,就已经确定流中数据是什么了。那么如何先定义流,而后给流传递数据呢?答案就是Source.actorRef。郑重说明:Source.actorRef没有背压策略(背压简单说就是生产者的生成速率大于消费者处理速率,导致数据积压)。

Sink.actorRef

  1. class MyActor extends Actor{
  2. override def receive: Receive = {
  3. case "FIN"=>
  4. println("完成了哇!!!")
  5. context.stop(self)
  6. case str:String =>
  7. println("msgStr:"+str)
  8. }
  9. }
  10. ......
  11. val actor=system.actorOf(Props[MyActor],"myActor")
  12. val sendToActor=Sink.actorRef(actor,onCompleteMessage = "FIN")
  13. val hahaStringSource=Source.actorRef[String](100,OverflowStrategy.dropHead).filter(str=>str.startsWith("haha"))
  14. val actorReceive=hahaStringSource.to(sendToActor).run()
  15. actorReceive!"hahasdsadsa1"
  16. actorReceive!"hahasdsadsa2"
  17. actorReceive!"hahasdsadsa3"
  18. actorReceive!"hahasdsadsa4"
  19. actorReceive!Success("ok")
  20. //output
  21. msgStr:hahasdsadsa1
  22. msgStr:hahasdsadsa2
  23. msgStr:hahasdsadsa3
  24. msgStr:hahasdsadsa4
  25. 完成了哇!!!

    Sink作为数据流终端消费节点,常见用法比如Sink.foreach[T](t:T=>Unit)Sink.fold[U,T](z:U)((u:U,t:T)=>U)等等。Sink.actorRef用于指定某个actorRef实例,把本该数据流终端处理的数据全部发送给这个actorRef实例去处理。解释上述程序,Sink,actorRef需要说明哪一个actorRef来接收消息,并且在数据流上游完成时,这个actorRef会接收到什么样的消息作为完成的信号。我们可以看到onCompleteMessage这条消息并没有受到str=>str.startsWith("haha")这过滤条件的作用(同样的,Sink.actorRef没有处理背压功能,数据挤压过多只能按某些策略舍弃,或者直接失败)。

背压处理

以上Source.actorRefSink.actorRef均不支持背压策略。我们可以借助Source.actorPublisher或者Sink.actorPublisher在数据流的上游或者下游处理背压问题,但是需要去继承ActorPublisher[T]ActorSubscriber实现了处理逻辑。

Source.actorPublisher

在数据流上游处自己手动实现背压处理逻辑:

  1. case object JobAccepted
  2. case object JobDenied
  3. case class Job(msg:String)
  4. ...
  5. class MyPublisherActor extends ActorPublisher[Job]{
  6. import akka.stream.actor.ActorPublisherMessage._
  7. val MAXSize=10
  8. var buf=Vector.empty[Job]
  9. override def receive: Receive = {
  10. case job:Job if buf.size==MAXSize =>
  11. sender()!JobDenied //超出缓存 拒绝处理
  12. case job:Job =>
  13. sender()!JobAccepted //确认处理该任务
  14. buf.isEmpty&&totalDemand>0 match {
  15. case true =>
  16. onNext(job)
  17. case false=>
  18. buf:+=job //先向缓存中存放job
  19. deliverBuf() //当下游存在需求时,再去从缓存中消费job
  20. }
  21. case req@Request(n)=>
  22. deliverBuf()
  23. case Cancel=>
  24. context.stop(self)
  25. }
  26. def deliverBuf():Unit= totalDemand>0 match {
  27. case true =>
  28. totalDemand<=Int.MaxValue match {
  29. case true =>
  30. val (use,keep)=buf.splitAt(totalDemand.toInt) //相当于(buf.take(n),buf.drop(n))
  31. buf=keep
  32. use.foreach(onNext(_)) //把buf一份两半,前一半发送给下游节点消费,后一半保留
  33. case false=>
  34. buf.take(Int.MaxValue).foreach(onNext(_))
  35. buf=buf.drop(Int.MaxValue)
  36. deliverBuf() //递归
  37. }
  38. case false=>
  39. }
  40. }
  41. ...
  42. val jobSource=Source.actorPublisher[Job](Props[MyPublisherActor])
  43. val jobSourceActor=jobSource.via(Flow[Job].map(job=>Job(job.msg*2))).to(Sink.foreach(println)).run()
  44. jobSourceActor!Job("ha")
  45. jobSourceActor!Job("he")

    actorPublisher的函数签名def actorPublisher[T](props: Props): Source[T, ActorRef]。上述代码中totalDemand是由下游消费节点确定。onNext(e)方法在ActorPublisher中定义,作用是将数据传输给下游节点。当然还有onComplete()onError(ex)函数,也是用于通知下游节点作出相应处理。

Sink.actorSubscriber

  1. case class Reply(id:Int)
  2. ...
  3. class Worker extends Actor{
  4. override def receive: Receive = {
  5. case (id:Int,job:Job)=>
  6. println("finish job:"+job)
  7. sender()!Reply(id)
  8. }
  9. }
  10. ...
  11. class CenterSubscriber extends ActorSubscriber{
  12. val router={ //路由组
  13. val routees=Vector.fill(3){ActorRefRoutee(context.actorOf(Props[Worker]))}
  14. Router(RoundRobinRoutingLogic(),routees)
  15. }
  16. var buf=Map.empty[Int,Job]
  17. override def requestStrategy: RequestStrategy = WatermarkRequestStrategy.apply(100)
  18. import akka.stream.actor.ActorSubscriberMessage._
  19. override def receive: Receive = {
  20. case OnNext(job:Job)=>
  21. val temp=(Random).nextInt(10000)->job
  22. buf+=temp //记录并下发任务
  23. router.route(temp,self)
  24. case OnError(ex)=>
  25. println("上游发生错误了::"+ex.getMessage)
  26. case OnComplete=>
  27. println("该数据流完成使命..")
  28. case Reply(id)=>
  29. buf-=id//当处理完成时,删去记录
  30. }
  31. }
  32. ...
  33. val actor=Source.actorPublisher[Job](Props[MyPublisherActor]).to(Sink.actorSubscriber[Job](Props[CenterSubscriber])).run()
  34. actor!Job("job1")
  35. actor!Job("job2")
  36. actor!Job("job3")

    ActorSubscriber可以接收如下几种消息类型:OnNext上游来的新消息、OnComplete上游已经结束数据流、OnError上游发生错误以及其他普通类型的消息。继承ActorSubscriber的子类都需要覆写requestStrategy以此来提供请求策略去控制数据流的背压(围绕requestDemand展开,何时向上游请求数据,一次请求多少数据等等问题)。

akka-stream与actor系统集成以及如何处理随之而来的背压问题的更多相关文章

  1. Akka Stream文档翻译:Motivation

    动机 Motivation The way we consume services from the internet today includes many instances of streami ...

  2. 报错:Flink Could not resolve substitution to a value: ${akka.stream.materializer}

    报错现象: Exception in thread "main" com.typesafe.config.ConfigException$UnresolvedSubstitutio ...

  3. Akka Stream之Graph

    最近在项目中需要实现图的一些操作,因此,初步考虑使用Akka Stream的Graph实现.从而学习了下: 一.介绍 我们知道在Akka Stream中有三种简单的线性数据流操作:Source/Flo ...

  4. Lagom学习 六 Akka Stream

    lagom中的stream 流数据处理是基于akka stream的,异步的处理流数据的.如下看代码: 流式service好处是: A: 并行:  hellos.mapAsync(8, name -& ...

  5. Akka系列(二):Akka中的Actor系统

    前言......... Actor模型作为Akka中最核心的概念,所以Actor在Akka中的组织结构是至关重要,本文主要介绍Akka中Actor系统. 1.Actor系统 Actor作为一种封装状态 ...

  6. Akka Stream文档翻译:Quick Start Guide: Reactive Tweets

    Quick Start Guide: Reactive Tweets 快速入门指南: Reactive Tweets (reactive tweets 大概可以理解为“响应式推文”,在此可以测试下GF ...

  7. akka实现的actor

    定义一个 Actor 类 要定义自己的Actor类,需要继承 Actor 并实现receive 方法. receive 方法需要定义一系列 case 语句(类型为 PartialFunction[An ...

  8. Akka简介与Actor模型

    Akka是一个构建在JVM上,基于Actor模型的的并发框架,为构建伸缩性强,有弹性的响应式并发应用提高更好的平台.本文主要是个人对Akka的学习和应用中的一些理解. Actor模型 Akka的核心就 ...

  9. akka设计模式系列-actor锚定

    actor锚定模式是指使用actorSelection对acor进行锚定的设计模式,也可以说是一个对actor的引用技巧.在某些情况下,我们可能需要能够根据Actor的path锚定对应的实例.简单来说 ...

随机推荐

  1. 《Java并发编程》之线程中断与终止线程运行

    Java中启动一个线程很容易,通常情况下我们都是等到任务运行结束后让线程自行停止.但有时需要在任务正在运行时取消他们,使得线程快速结束.对此Java并没有提供任何机制.但是我们可以通过Java提供的线 ...

  2. (转载) Java子类与父类之间的对象转换

    在使用Java的多态机制时,常常使用的一个特性便是子类和父类之间的对象转换.从子类向父类的转换称为向上转换(upcasting),通过向上转换,我们能够在编写程序时采用通用程序设计的思想,在需要使用子 ...

  3. RibbonForm使用技巧

    Ribbon右侧显示Logo 方法 重写RibbonControl的Paint事件 效果 代码 private void _ribbonControl_Paint(object sender, Pai ...

  4. python利用scrapy框架爬取起点

    先上自己做完之后回顾细节和思路的东西,之后代码一起上. 1.Mongodb 建立一个叫QiDian的库,然后建立了一个叫Novelclass(小说类别表)Novelclass(可以把一级类别二级类别都 ...

  5. TestLink使用

  6. Vue单页面骨架屏实践

    github 地址: VV-UI/VV-UI 演示地址: vv-ui 文档地址:skeleton 关于骨架屏介绍 骨架屏的作用主要是在网络请求较慢时,提供基础占位,当数据加载完成,恢复数据展示.这样给 ...

  7. 浏览器出现Cannot set property 'onclick' of null的问题

    Part1: 当js文件放在head里面时,如果绑定了onclick事件,就会出现这样的错误, 是因为W3School的写法是浏览器先加载完按钮节点才执行的js,所以当浏览器自顶向下解析时,找不到on ...

  8. mvc/mvvm小小的总结

    mvc/mvvm 阮大神博客 mvc 分为三层,其实M层是数据模型层,它是真正的后端数据在前端js中的一个映射模型,他们的关系是:数据模型层和视图层有映射关系,model改变,view展示也会更改,当 ...

  9. Coursera课程 Programming Languages, Part A 总结

    Coursera CSE341: Programming Languages 感谢华盛顿大学 Dan Grossman 老师 以及 Coursera . 碎言碎语 这只是 Programming La ...

  10. Sagit.Framework For IOS 开发框架入门开发教程1:框架下载与环境配置

    背景: 前天开源了框架:开源:Sagit.Framework For IOS 开发框架 所以注定要追补一套开发教程了,所以尽量抽空了!!! 步骤 1:下载框架源码 GitHub:https://git ...