使用Akka Stream从数据库中传输记录

使用Akka Stream从数据库中传输记录,akka,akka-stream,reactive-streams,Akka,Akka Stream,Reactive Streams,我有一个使用Akka的系统,该系统目前通过消息队列处理传入的流数据。当一条记录到达时,它将被处理,mq将被确认,记录将被传递到系统中以供进一步处理 现在我想添加对使用DBs作为输入的支持。 输入源能够处理DB的方法是什么(应该以接收器能够处理的速度流入>100M的记录-所以我假设是反应式/akka流?)Slick Library 通常是这样做的 稍微扩展一下slick文档,以包括akka streams: //SELECT Name from Coffees val q = for (c <

我有一个使用Akka的系统,该系统目前通过消息队列处理传入的流数据。当一条记录到达时,它将被处理,mq将被确认,记录将被传递到系统中以供进一步处理

现在我想添加对使用DBs作为输入的支持。

输入源能够处理DB的方法是什么(应该以接收器能够处理的速度流入>100M的记录-所以我假设是反应式/akka流?)

Slick Library

通常是这样做的

稍微扩展一下slick文档,以包括akka streams:

//SELECT Name from Coffees
val q = for (c <- coffees) yield c.name

val action = q.result

type Name = String

val databasePublisher : DatabasePublisher[Name] = db stream action

import akka.stream.scaladsl.Source

val akkaSourceFromSlick : Source[Name, _] = Source fromPublisher databasePublisher
当然,所有ResultSet实例都必须将光标移动到第一行之前:

val adjustResultSetBeforeFirst : (ResultSet) => Try[ResultSet] = 
  (resultSet) => Try(resultSet.beforeFirst()) map (_ => resultSet)
一旦我们开始遍历行,我们就必须从正确的列中提取值:

val getNameFromResultSet : ResultSet => Name = _ getString "Name"
现在我们可以实现
迭代器
接口,从结果集创建
迭代器[Name]

val convertResultSetToNameIterator : ResultSet => Iterator[Name] = 
  (resultSet) => new Iterator[Try[Name]] {
    override def hasNext : Boolean  = resultSet.next
    override def next() : Try[Name] = Try(getNameFromResultSet(resultSet))
   } flatMap (_.toOption)
最后,将所有部分粘合在一起,创建我们需要传递到
源代码的函数。from iterator

val resultSetGenToNameIterator : (() => Try[ResultSet]) => () => Iterator[Name] = 
  (_ : () => Try[ResultSet])
    .andThen(_ flatMap adjustResultSetBeforeFirst) 
    .andThen(_ map convertResultSetToNameIterator) 
    .andThen(_ getOrElse Iterator.empty)
此迭代器现在可以提供源:

val akkaSourceFromResultSet : Source[Name, _] = 
  Source fromIterator resultSetGenToNameIterator(resultSetGenerator)

这个实现一直到数据库都是被动的。由于ResultSet一次预取有限数量的行,因此数据只会在流
接收器
信号需要时通过数据库从硬盘驱动器中取出

我发现,与Java Publisher界面相比,Alpakka文档非常优秀,而且是一种更容易处理反应流的方法

Alpakka项目是一个开源项目,旨在为Java和Scala实现流感知、反应式的集成管道。它构建在Akka Streams之上,从一开始就被设计为理解本地流,并为反应式和面向流的编程提供DSL,内置背压支持

带滑膜的阿尔帕卡文件:


Alpakka Github:

我们应该如何进行一些清理操作,例如,如果我们想关闭
ResultSet
语句
?@user650167您可以在
resultSetGenerator
之前预先构建
ResultSet
,而不是在其内部。然后在关闭钩子上进行
源代码
呼叫关闭。谢谢,@Ramon J Romero y Vigil。因此,我需要进行SQL查询以在
源代码
之外创建
结果集
,并将其转换为
源代码
,对吗?源代码的关闭钩子在哪里?我想你不是说
onComplete
hook的
Future
@user650167通常,一旦
源代码被具体化,它就会返回一个hook。例如:如果您执行类似于
valhook:Future[Done]=mySource.runForeach(println)
的操作,则可以执行
hook.foreach(=>resultSet.close())
。这确保了只有在流处理完所有行之后,结果集才会关闭。现在,如果Akka stream web应用程序停止运行30分钟,然后恢复运行,会发生什么情况呢?它是否从30分钟间隔窗口中流式处理所有新数据更改?另外,我还有一个问题,这个库是在幕后使用变更数据捕获技术,还是在时间戳列或主键上使用带where条件的sql查询,并保持数据库池化?您的两个问题的答案都是,这个库仅仅是通过Slick与数据库的交互。你可以看看源代码,其实没那么多:Slick本身就是一个方便的数据库访问。因为您想要实现的目标取决于您的数据库。您可能需要DB触发器(可能比轮询DB更好)。例如,请查看以下内容:
val akkaSourceFromResultSet : Source[Name, _] = 
  Source fromIterator resultSetGenToNameIterator(resultSetGenerator)