Akka(24): Stream:从外部系统控制数据流-control live stream from external system
在数据流应用的现实场景中常常会遇到与外界系统对接的需求。这些外部系统可能是Actor系统又或者是一些其它类型的系统。与这些外界系统对接的意思是在另一个线程中运行的数据流可以接收外部系统推送的事件及做出行为改变的响应。
如果一个外界系统需要控制一个运行中数据流的功能环节GraphStage,首先必须在这个GraphStage内部构建一个控制函数,这样才能接触并更改GraphStage的内部状态。外部系统可以通过调用这个控制函数来向GraphStage发送信息,控制GraphStage行为。akka-stream是多线程异步模式的程序,所以这个函数只能是一个异步运行的回调callback。akka-stream提供了一个函数getAsyncCallback函数,能够把一个函数放到另一个线程里并返回它的callback:
/**
* Obtain a callback object that can be used asynchronously to re-enter the
* current [[GraphStage]] with an asynchronous notification. The [[invoke()]] method of the returned
* [[AsyncCallback]] is safe to be called from other threads and it will in the background thread-safely
* delegate to the passed callback function. I.e. [[invoke()]] will be called by the external world and
* the passed handler will be invoked eventually in a thread-safe way by the execution environment.
*
* This object can be cached and reused within the same [[GraphStageLogic]].
*/
final def getAsyncCallback[T](handler: T ⇒ Unit): AsyncCallback[T] = {
new AsyncCallback[T] {
override def invoke(event: T): Unit =
interpreter.onAsyncInput(GraphStageLogic.this, event, handler.asInstanceOf[Any ⇒ Unit])
}
}
getAsyncCallback把一个函数T=>Unit变成了异步运算函数并通过AsyncCallback返回它的回调callback。下面是getAsyncCallback的一个用例:
//external system
object Injector {
var callback: AsyncCallback[String] = null
def inject(m: String) = {
if (callback != null)
callback.invoke(m)
}
}
class InjectControl(injector: Injector.type) extends GraphStage[FlowShape[String,String]] {
val inport = Inlet[String]("input")
val outport = Outlet[String]("output")
val shape = FlowShape.of(inport,outport) override def createLogic(inheritedAttributes: Attributes): GraphStageLogic =
new GraphStageLogic(shape) {
var extMessage = ""
override def preStart(): Unit = {
val callback = getAsyncCallback[String] { m =>
if (m.length > )
m match {
case "Stop" => completeStage()
case s: String => extMessage = s
} }
injector.callback = callback
} setHandler(inport, new InHandler {
override def onPush(): Unit =
if (extMessage.length > ) {
push(outport,extMessage)
extMessage=""
}
else
push(outport, grab(inport))
})
setHandler(outport, new OutHandler {
override def onPull(): Unit = pull(inport)
})
} }
上面这个例子里的object Injector模拟一个外部系统。我们重写了GraphStage InjectControl.createLogic里的preStart()函数,在这里把一个String=>Unit函数的callback登记在Injector里。这个callback函数能接受传入的String并更新内部状态extMessage,或者当传入String==“Stop"时终止数据流。在onPush()里extMessage最终会被当作流元素插入到数据流中。下面我们就构建这个GraphStage的测试运行程序:
object InteractWithStreams extends App {
implicit val sys = ActorSystem("demoSys")
implicit val ec = sys.dispatcher
implicit val mat = ActorMaterializer(
ActorMaterializerSettings(sys)
.withInputBuffer(initialSize = , maxSize = )
)
val source = Source(Stream.from()).map(_.toString).delay(.second,DelayOverflowStrategy.backpressure)
val graph = new InjectControl(Injector)
val flow = Flow.fromGraph(graph)
source.via(flow).to(Sink.foreach(println)).run()
Thread.sleep()
Injector.inject("hello")
Thread.sleep()
Injector.inject("world!")
Thread.sleep()
Injector.inject("Stop")
scala.io.StdIn.readLine()
sys.terminate()
}
试运行结果显示:
hello world! Process finished with exit code
正确地把"hello world!"插入了一个正在运行中的数据流中并在最后终止了这个数据流。
另外,一个GraphStage也可以被外界当作一种Actor来进行交流。我们可以在GraphStage内部构建一个(ActorRef,Any)=>Unit款式的函数,然后用getStageActor(func).ref以一种ActorRef形式返回这个函数:
/**
* Initialize a [[StageActorRef]] which can be used to interact with from the outside world "as-if" an [[Actor]].
* The messages are looped through the [[getAsyncCallback]] mechanism of [[GraphStage]] so they are safe to modify
* internal state of this stage.
*
* This method must (the earliest) be called after the [[GraphStageLogic]] constructor has finished running,
* for example from the [[preStart]] callback the graph stage logic provides.
*
* Created [[StageActorRef]] to get messages and watch other actors in synchronous way.
*
* The [[StageActorRef]]'s lifecycle is bound to the Stage, in other words when the Stage is finished,
* the Actor will be terminated as well. The entity backing the [[StageActorRef]] is not a real Actor,
* but the [[GraphStageLogic]] itself, therefore it does not react to [[PoisonPill]].
*
* @param receive callback that will be called upon receiving of a message by this special Actor
* @return minimal actor with watch method
*/
// FIXME: I don't like the Pair allocation :(
@ApiMayChange
final protected def getStageActor(receive: ((ActorRef, Any)) ⇒ Unit): StageActor = {
_stageActor match {
case null ⇒
val actorMaterializer = ActorMaterializerHelper.downcast(interpreter.materializer)
_stageActor = new StageActor(actorMaterializer, getAsyncCallback, receive)
_stageActor
case existing ⇒
existing.become(receive)
existing
}
}
...
/**
* The ActorRef by which this StageActor can be contacted from the outside.
* This is a full-fledged ActorRef that supports watching and being watched
* as well as location transparent (remote) communication.
*/
def ref: ActorRef = functionRef
下面是receive:((ActorRef,Any))=>Unit这个函数的实现例子:
def behavior(m:(ActorRef,Any)): Unit = {
val (sender, msg) = m
msg.asInstanceOf[String] match {
case "Stop" => completeStage()
case s@ _ => extMessage = s
}
}
这个函数的输入参数(sender,msg)代表发送消息的Actor和发送的消息。与上个例子一样,作为一个GraphStage的内部函数,它可以使用、更新GraphStage内部状态。GraphStage的实现如下:
class StageAsActor(extActor: ActorRef) extends GraphStage[FlowShape[String,String]] {
val inport = Inlet[String]("input")
val outport = Outlet[String]("output")
val shape = FlowShape.of(inport,outport)
override def createLogic(inheritedAttributes: Attributes): GraphStageLogic =
new GraphStageLogic(shape) {
var extMessage = ""
override def preStart(): Unit = {
extActor ! getStageActor(behavior).ref
}
def behavior(m:(ActorRef,Any)): Unit = {
val (sender, msg) = m
msg.asInstanceOf[String] match {
case "Stop" => completeStage()
case s@ _ => extMessage = s
}
}
setHandler(inport, new InHandler {
override def onPush(): Unit =
if (extMessage.length > ) {
push(outport,extMessage)
extMessage=""
}
else
push(outport, grab(inport))
})
setHandler(outport, new OutHandler {
override def onPull(): Unit = pull(inport)
})
}
}
参数extActor就是外部的控制Actor。在creatLogic.preStart()里我们先把StageActor传给extActor。外部系统就可以通过extActor来控制数据流行为:
class Messenger extends Actor with ActorLogging {
var stageActor: ActorRef = _
override def receive: Receive = {
case r: ActorRef =>
stageActor = r
log.info("received stage actorRef")
case s: String => stageActor forward s
log.info(s"forwarding message:$s")
}
}
object GetStageActorDemo extends App {
implicit val sys = ActorSystem("demoSys")
implicit val ec = sys.dispatcher
implicit val mat = ActorMaterializer(
ActorMaterializerSettings(sys)
.withInputBuffer(initialSize = , maxSize = )
)
val stageActorMessenger = sys.actorOf(Props[Messenger],"forwarder")
val source = Source(Stream.from()).map(_.toString).delay(.second,DelayOverflowStrategy.backpressure)
val graph = new StageAsActor(stageActorMessenger)
val flow = Flow.fromGraph(graph)
source.via(flow).to(Sink.foreach(println)).run()
Thread.sleep()
stageActorMessenger ! "Hello"
Thread.sleep()
stageActorMessenger ! "World!"
Thread.sleep()
stageActorMessenger ! "Stop"
scala.io.StdIn.readLine()
sys.terminate()
}
Messenger就是一个存粹的中介,把控制消息通过StageActor转发给运行中的数据流。
下面是本次示范的源代码:
GetAsyncCallBack.scala
import akka.actor._
import akka.stream._
import akka.stream.scaladsl._
import akka.stream.stage._
import scala.concurrent.duration._ //external system
object Injector {
var callback: AsyncCallback[String] = null
def inject(m: String) = {
if (callback != null)
callback.invoke(m)
}
}
class InjectControl(injector: Injector.type) extends GraphStage[FlowShape[String,String]] {
val inport = Inlet[String]("input")
val outport = Outlet[String]("output")
val shape = FlowShape.of(inport,outport) override def createLogic(inheritedAttributes: Attributes): GraphStageLogic =
new GraphStageLogic(shape) {
var extMessage = ""
override def preStart(): Unit = {
val callback = getAsyncCallback[String] { m =>
if (m.length > )
m match {
case "Stop" => completeStage()
case s: String => extMessage = s
} }
injector.callback = callback
} setHandler(inport, new InHandler {
override def onPush(): Unit =
if (extMessage.length > ) {
push(outport,extMessage)
extMessage=""
}
else
push(outport, grab(inport))
})
setHandler(outport, new OutHandler {
override def onPull(): Unit = pull(inport)
})
} } object GetAsyncCallbackDemo extends App {
implicit val sys = ActorSystem("demoSys")
implicit val ec = sys.dispatcher
implicit val mat = ActorMaterializer(
ActorMaterializerSettings(sys)
.withInputBuffer(initialSize = , maxSize = )
) val source = Source(Stream.from()).map(_.toString).delay(.second,DelayOverflowStrategy.backpressure)
val graph = new InjectControl(Injector)
val flow = Flow.fromGraph(graph) source.via(flow).to(Sink.foreach(println)).run()
Thread.sleep()
Injector.inject("hello")
Thread.sleep()
Injector.inject("world!")
Thread.sleep()
Injector.inject("Stop") scala.io.StdIn.readLine()
sys.terminate() }
GetStageActorDemo.scala
import akka.actor._
import akka.stream._
import akka.stream.scaladsl._
import akka.stream.stage._
import scala.concurrent.duration._ class StageAsActor(extActor: ActorRef) extends GraphStage[FlowShape[String,String]] {
val inport = Inlet[String]("input")
val outport = Outlet[String]("output")
val shape = FlowShape.of(inport,outport) override def createLogic(inheritedAttributes: Attributes): GraphStageLogic =
new GraphStageLogic(shape) {
var extMessage = ""
override def preStart(): Unit = {
extActor ! getStageActor(behavior).ref
} def behavior(m:(ActorRef,Any)): Unit = {
val (sender, msg) = m msg.asInstanceOf[String] match {
case "Stop" => completeStage()
case s@ _ => extMessage = s
}
} setHandler(inport, new InHandler {
override def onPush(): Unit =
if (extMessage.length > ) {
push(outport,extMessage)
extMessage=""
}
else
push(outport, grab(inport))
})
setHandler(outport, new OutHandler {
override def onPull(): Unit = pull(inport)
})
} } class Messenger extends Actor with ActorLogging {
var stageActor: ActorRef = _
override def receive: Receive = {
case r: ActorRef =>
stageActor = r
log.info("received stage actorRef")
case s: String => stageActor forward s
log.info(s"forwarding message:$s") }
}
object GetStageActorDemo extends App {
implicit val sys = ActorSystem("demoSys")
implicit val ec = sys.dispatcher
implicit val mat = ActorMaterializer(
ActorMaterializerSettings(sys)
.withInputBuffer(initialSize = , maxSize = )
) val stageActorMessenger = sys.actorOf(Props[Messenger],"forwarder") val source = Source(Stream.from()).map(_.toString).delay(.second,DelayOverflowStrategy.backpressure)
val graph = new StageAsActor(stageActorMessenger)
val flow = Flow.fromGraph(graph) source.via(flow).to(Sink.foreach(println)).run() Thread.sleep()
stageActorMessenger ! "Hello"
Thread.sleep()
stageActorMessenger ! "World!"
Thread.sleep()
stageActorMessenger ! "Stop" scala.io.StdIn.readLine()
sys.terminate() }
Akka(24): Stream:从外部系统控制数据流-control live stream from external system的更多相关文章
- 泛函编程(13)-无穷数据流-Infinite Stream
上节我们提到Stream和List的主要分别是在于Stream的“延后计算“(lazy evaluation)特性.我们还讨论过在处理大规模排列数据集时,Stream可以一个一个把数据元素搬进内存并且 ...
- java8 Stream的实现原理 (从零开始实现一个stream流)
1.Stream 流的介绍 1.1 java8 stream介绍 java8新增了stream流的特性,能够让用户以函数式的方式.更为简单的操纵集合等数据结构,并实现了用户无感知的并行计算. 1.2 ...
- Access control differentiation in trusted computer system
A trusted computer system that offers Linux® compatibility and supports contemporary hardware speeds ...
- Stream流中的常用方法foeEach和Stream流中的常用方法filter
延迟方法:返回值类型仍然是Stream接口自身类型的方法,因此支持链式调用.(除了中介方法外,其余方法均为延迟方法) 终结方法:返回值类型不再是Stream接口自身类型的方法,因此不再支持类似Stri ...
- FunDA(4)- 数据流内容控制:Stream data element control
上节我们探讨了通过scalaz-stream-fs2来驱动一套数据处理流程,用fs2的Pipe类型来实现对数据流的逐行操作.本篇讨论准备在上节讨论的基础上对数据流的流动和元素操作进行优化完善.如数据流 ...
- 数据流中位数 · data stream median
[抄题]: 数字是不断进入数组的,在每次添加一个新的数进入数组的同时返回当前新数组的中位数. [思维问题]: [一句话思路]: 左边x个元素,右边要有x+1个元素,因此利用maxheap把左边的最大值 ...
- Power control within a coherent multi-processing system
Within a multi-processing system including a plurality of processor cores 4, 6operating in accordanc ...
- php执行外部命令函数:exec()、passthru()、system()、shell_exec()对比
PHP提供了4种方法执行系统外部命令:exec().passthru().system().shell_exec(),下面分别介绍: 1.exec 原型:string exec ( string $c ...
- openstack搭建之-创建实例(13)
一. 创建flat网络的实例 #运行admin环境变量,创建网络类型为flat . admin-openrc openstack network create --share \ --provider ...
随机推荐
- 通过HtppWebRequest发送图片到服务器并保存
之前写的楼主没有测试,后来发现用起来有点小问题 就修改了一下,现在已经亲测可用 完全没有问题了 下面就开始贴代码了 首先将图片装换成功byte 数组 这个path是图片的路径 例如d:12.png ...
- 简单工厂设计模式--Java设计模式(一)
一 概念: 简单工厂模式就是通过一个工厂类根据参数动态创建对应的类. 二 案例 我们以汽车作为例子,在现实生活中汽车只是一个抽象的产品,其中有很多类型的汽车才是具体产品,如奔驰.宝马.保时捷等等(当然 ...
- java中方法调用
JAVA中方法的调用[基础] 一.调用本类中的方法 方法一.被调用方法声明为static ,可以在其他方法中直接调用.示例代码如下: public class HelloWord { /** * @p ...
- java集合框架的讲解
下面要开始java中相关集合框架的学习啦. Are you ready?Let's go~~ 今天要讲解的Java中的集合框架. 1) 首先查看jdk中Collection类的源码后会发现如下内容: ...
- Python模块----linecache
Python标准库提供了一个有趣的模块:linecache模块.该模块用来从文件中读取任何的行,并且将这些lines使用缓存进行优化,常见的情况是从个大文件中读取指定的行.不过由于此模块使用内存进行缓 ...
- 基于 Laravel、Vue.js开发的全新社交系统----ThinkSNS+
什么是ThinkSNS+ ThinkSNS(简称TS)始于2008年,一款全平台综合性社交系统,为国内外大中小企业和创业者提供社会化软件研发及技术解决方案,目前最新版本为ThinkSNS+.新的产品名 ...
- SpringMVC创建HelloWorld程序
1.IDE说明和依赖管理工具说明 开发工具:intellij idea 依赖管理使用:maven 2.创建maven工程 创建新的maven工程,并添加相应的文件夹,创建好的项目目录如下所示: 3.添 ...
- JavaScript入门(二)
一.JS中的运算符 1.算术运算(单目运算符) + 加.-减.*乘. /除. %取余.++自增运算符 .--自减运算符; >>>+:有两种作用,链接字符串/加法运算符.当+两边全为数 ...
- Javacript的变量和输出
一.js使用的三种方式 1.在HTML标签中,直接内嵌js(并不提倡使用): >>不符合W3C内容与表现分离的要求!!! 2.在HTML页面中使用<script></sc ...
- 汇编总结:mov指令
mov指令的作用: mov指令可能是汇编里用的最多的指令了,完成c语言里的赋值. mov指令种类: 1.普通的mov指令 2.做符号扩展的movs 3.做零扩展的movz 1.普通mov的种类有: m ...