在前面几篇讨论里我们都提到过:Akka-http是一项系统集成工具库。它是以数据交换的形式进行系统集成的。所以,Akka-http的核心功能应该是数据交换的实现了:应该能通过某种公开的数据格式和传输标准比较方便的实现包括异类系统之间通过网上进行的数据交换。覆盖包括:数据编码、发送和数据接收、解析全过程。Akka-http提供了许多网上传输标准数据的概括模型以及数据类型转换方法,可以使编程人员很方便的构建网上往来的Request和Response。但是,现实中的数据交换远远不止针对request和response操作能够满足的。系统之间数据交换经常涉及文件或者数据库表类型的数据上传下载。虽然在Http标准中描述了如何通过MultiPart消息类型进行批量数据的传输,但是这个标准涉及的实现细节包括数据内容描述、数据分段方式、消息数据长度计算等等简直可以立即令人却步。Akka-http是基于Akka-stream开发的:不但它的工作流程可以用Akka-stream来表达,它还支持stream化的数据传输。我们知道:Akka-stream提供了功能强大的FileIO和Data-Streaming,可以用Stream-Source代表文件或数据库数据源。简单来说:Akka-http的消息数据内容HttpEntity可以支持理论上无限长度的data-stream。最可贵的是:这个Source是个Reactive-Stream-Source,具备了back-pressure机制,可以有效应付数据交换参与两方Reactive端点不同的数据传输速率。

Akka-http的stream类型数据内容是以Source[T,_]类型表示的。首先,Akka-stream通过FileIO对象提供了足够多的file-io操作函数,其中有个fromPath函数可以用某个文件内容数据构建一个Source类型:

/**
* Creates a Source from a files contents.
* Emitted elements are `chunkSize` sized [[akka.util.ByteString]] elements,
* except the final element, which will be up to `chunkSize` in size.
*
* You can configure the default dispatcher for this Source by changing the `akka.stream.blocking-io-dispatcher` or
* set it for a given Source by using [[akka.stream.ActorAttributes]].
*
* It materializes a [[Future]] of [[IOResult]] containing the number of bytes read from the source file upon completion,
* and a possible exception if IO operation was not completed successfully.
*
* @param f the file path to read from
* @param chunkSize the size of each read operation, defaults to 8192
*/
def fromPath(f: Path, chunkSize: Int = ): Source[ByteString, Future[IOResult]] =
fromPath(f, chunkSize, startPosition = )

这个函数构建了Source[ByteString,Future[IOResult]],我们需要把ByteString转化成MessageEntity。首先需要在implicit-scope内提供Marshaller[ByteString,MessageEntity]类型的隐式实例:

trait JsonCodec extends Json4sSupport {
import org.json4s.DefaultFormats
import org.json4s.ext.JodaTimeSerializers
implicit val serilizer = jackson.Serialization
implicit val formats = DefaultFormats ++ JodaTimeSerializers.all
}
object JsConverters extends JsonCodec object ServerStreaming extends App {
import JsConverters._
...

我们还需要Json-Streaming支持:

  implicit val jsonStreamingSupport = EntityStreamingSupport.json()
.withParallelMarshalling(parallelism = , unordered = false)

FileIO是blocking操作,我们还可以选用独立的线程供blocking操作使用:

   FileIO.fromPath(file, )
.withAttributes(ActorAttributes.dispatcher("akka.http.blocking-ops-dispatcher"))

现在我们可以从在server上用一个文件构建Source然后再转成Response:

  val route =
get {
path("files"/Remaining) { name =>
complete(loadFile(name))
}
}
def loadFile(path: String) = {
// implicit val ec = httpSys.dispatchers.lookup("akka.http.blocking-ops-dispatcher")
val file = Paths.get("/Users/tiger/"+path)
FileIO.fromPath(file, )
.withAttributes(ActorAttributes.dispatcher("akka.http.blocking-ops-dispatcher"))
.map(_.utf8String)
}

同样,我们也可以把数据库表内数据转成Akka-Stream-Source,然后再实现到MessageEntity的转换。转换过程包括用Query读取数据库表内数据后转成Reactive-Publisher,然后把publisher转成Akka-Stream-Source,如下:

object SlickDAO {
import slick.jdbc.H2Profile.api._
val dbConfig: slick.basic.DatabaseConfig[slick.jdbc.H2Profile] = slick.basic.DatabaseConfig.forConfig("slick.h2")
val db = dbConfig.db case class CountyModel(id: Int, name: String)
case class CountyTable(tag: Tag) extends Table[CountyModel](tag,"COUNTY") {
def id = column[Int]("ID",O.AutoInc,O.PrimaryKey)
def name = column[String]("NAME",O.Length())
def * = (id,name)<>(CountyModel.tupled,CountyModel.unapply)
}
val CountyQuery = TableQuery[CountyTable] def loadTable(filter: String) = {
// implicit val ec = httpSys.dispatchers.lookup("akka.http.blocking-ops-dispatcher")
val qry = CountyQuery.filter {_.name.toUpperCase like s"%${filter.toUpperCase}%"}
val publisher = db.stream(qry.result)
Source.fromPublisher(publisher = publisher)
.withAttributes(ActorAttributes.dispatcher("akka.http.blocking-ops-dispatcher"))
}
}

然后进行到MessageEntity的转换:

  val route =
get {
path("files"/Remaining) { name =>
complete(loadFile(name))
} ~
path("tables"/Segment) { t =>
complete(SlickDAO.loadTable(t))
}
}

下面是本次示范的完整源代码:

import java.nio.file._
import akka.actor._
import akka.stream._
import akka.stream.scaladsl._
import akka.http.scaladsl.Http
import akka.http.scaladsl.server.Directives._
import akka.http.scaladsl.common._
import de.heikoseeberger.akkahttpjson4s.Json4sSupport
import org.json4s.jackson object SlickDAO {
import slick.jdbc.H2Profile.api._
val dbConfig: slick.basic.DatabaseConfig[slick.jdbc.H2Profile] = slick.basic.DatabaseConfig.forConfig("slick.h2")
val db = dbConfig.db case class CountyModel(id: Int, name: String)
case class CountyTable(tag: Tag) extends Table[CountyModel](tag,"COUNTY") {
def id = column[Int]("ID",O.AutoInc,O.PrimaryKey)
def name = column[String]("NAME",O.Length())
def * = (id,name)<>(CountyModel.tupled,CountyModel.unapply)
}
val CountyQuery = TableQuery[CountyTable] def loadTable(filter: String) = {
// implicit val ec = httpSys.dispatchers.lookup("akka.http.blocking-ops-dispatcher")
val qry = CountyQuery.filter {_.name.toUpperCase like s"%${filter.toUpperCase}%"}
val publisher = db.stream(qry.result)
Source.fromPublisher(publisher = publisher)
.withAttributes(ActorAttributes.dispatcher("akka.http.blocking-ops-dispatcher"))
}
} trait JsonCodec extends Json4sSupport {
import org.json4s.DefaultFormats
import org.json4s.ext.JodaTimeSerializers
implicit val serilizer = jackson.Serialization
implicit val formats = DefaultFormats ++ JodaTimeSerializers.all
}
object JsConverters extends JsonCodec object ServerStreaming extends App {
import JsConverters._ implicit val httpSys = ActorSystem("httpSystem")
implicit val httpMat = ActorMaterializer()
implicit val httpEC = httpSys.dispatcher implicit val jsonStreamingSupport = EntityStreamingSupport.json()
.withParallelMarshalling(parallelism = , unordered = false) val (port, host) = (,"localhost") val route =
get {
path("files"/Remaining) { name =>
complete(loadFile(name))
} ~
path("tables"/Segment) { t =>
complete(SlickDAO.loadTable(t))
}
} def loadFile(path: String) = {
// implicit val ec = httpSys.dispatchers.lookup("akka.http.blocking-ops-dispatcher")
val file = Paths.get("/Users/tiger/"+path)
FileIO.fromPath(file, )
.withAttributes(ActorAttributes.dispatcher("akka.http.blocking-ops-dispatcher"))
.map(_.utf8String)
} val bindingFuture = Http().bindAndHandle(route,host,port) println(s"Server running at $host $port. Press any key to exit ...") scala.io.StdIn.readLine() bindingFuture.flatMap(_.unbind())
.onComplete(_ => httpSys.terminate()) }

Akka(35): Http:Server side streaming的更多相关文章

  1. Scalaz(35)- Free :运算-Trampoline,say NO to StackOverflowError

    在前面几次讨论中我们介绍了Free是个产生Monad的最基本结构.它的原理是把一段程序(AST)一连串的运算指令(ADT)转化成数据结构存放在内存里,这个过程是个独立的功能描述过程.然后另一个独立运算 ...

  2. js中this和回调方法循环-我们到底能走多远系列(35)

    我们到底能走多远系列(35) 扯淡: 13年最后一个月了,你们在13年初的计划实现了吗?还来得及吗? 请加油~ 主题: 最近一直在写js,遇到了几个问题,可能初入门的时候都会遇到吧,总结下. 例子: ...

  3. Windows Phone开发(35):使用Express Blend绘图

    原文:Windows Phone开发(35):使用Express Blend绘图 上一节中我们简单扯了一下绘图指令,然而那也不是最简单的绘图法,今天,我再向大家推荐一种更好的绘图方案--Express ...

  4. Akka(8): 分布式运算:Remoting-远程查找式

    Akka是一种消息驱动运算模式,它实现跨JVM程序运算的方式是通过能跨JVM的消息系统来调动分布在不同JVM上ActorSystem中的Actor进行运算,前题是Akka的地址系统可以支持跨JVM定位 ...

  5. Akka(17): Stream:数据流基础组件-Source,Flow,Sink简介

    在大数据程序流行的今天,许多程序都面临着共同的难题:程序输入数据趋于无限大,抵达时间又不确定.一般的解决方法是采用回调函数(callback-function)来实现的,但这样的解决方案很容易造成“回 ...

  6. Qt 学习之路 2(35):文件

    Qt 学习之路 2(35):文件 豆子 2013年1月5日 Qt 学习之路 2 12条评论 文件操作是应用程序必不可少的部分.Qt 作为一个通用开发库,提供了跨平台的文件操作能力.从本章开始,我们来了 ...

  7. web服务器专题:tomcat(二)模块组件与server.xml 配置文件

    web服务器专题:tomcat(二)模块组件与server.xml 配置文件 回顾: Web服务器专题:tomcat(一) 基础模块 一个Server.xml的实例 <?xml version= ...

  8. Python接口测试实战4(下) - 框架完善:用例基类,用例标签,重新运行上次失败用例

    如有任何学习问题,可以添加作者微信:lockingfree 课程目录 Python接口测试实战1(上)- 接口测试理论 Python接口测试实战1(下)- 接口测试工具的使用 Python接口测试实战 ...

  9. nodeJS(2)深了解: nodeJS 项目架构详解(app.js + Express + Http)

    简略了解:nodeJS 深了解(1): Node.js + Express 构建网站预备知识 环境: 环境: win7 + nodeJS 版本(node): 新建 nodeJS 项目: 名称为: te ...

随机推荐

  1. css常用属性1

    1  背景相关 背景颜色 background-color     = 颜色名称/rgb值/十六进制值 背景图片 background-image = url('') 背景图片平铺方式 backgro ...

  2. DotNetCore跨平台~linux上还原自主nuget包需要注意的问题

    问题的产生的背景 由于我们使用了jenkins进行部署(jenkins~集群分发功能和职责处理),而对于.net core项目来说又是跨平台的,所以对它的项目拉取,包的还原,项目的编译和项目的发布都是 ...

  3. babel的使用详解

    由于es6的很多特性在旧的浏览器下支持不好,我们在使用的时候需要将其转化为es5,下面介绍babel解析器的使用 一:独立使用babel的方法 1.本地安装babel-cli npm install ...

  4. Webx项目的获取与验证

    在JavaWeb环境配置后就可进行Webx实例项目的获取与研读了. 1.创建一个初始的Demo工程. 1)下载 Webx Maven 项目的目录结构Artifact插件. archetype-webx ...

  5. PyCharm基本操作

    1.1 PyCharm基本使用 视频学习连接地址:http://edu.51cto.com/course/9043.html 1.1.1 在Pycharm下为你的Python项目配置Python解释器 ...

  6. Spark版wordcount,并根据词频进行排序

    import org.apache.spark.{SparkConf, SparkContext}/** * Created by loushsh on 2017/10/9. */object Wor ...

  7. PHP,Mysql根据经纬度计算距离并排序

    网上给出的计算公式是这样的: Lng1表示A点纬度和经度,Lat2 Lng2 表示B点纬度和经度 a = Lat1 – Lat2为两点纬度之差 b = Lng1 -Lng2 为两点经度之差 6378. ...

  8. springMVC中的redirect和forward区别?

    1.forward在跳转后可以取到message值,redirect在跳转后无法取到message值. 2.forward跳转后地址栏URL不会改变,而redirect会改变.

  9. Linux下安装jdk8的详细步骤

    一.登录Linux,切换到root用户 sudo su 二.在usr目录下建立java安装目录 cd /usr mkdir java 三.下载jdk 登录网址:http://www.oracle.co ...

  10. python web框架之Tornado

    说Tornado之前分享几个前端不错的网站: -- Bootstrap http://www.bootcss.com/ -- Font Awesome http://fontawesome.io/ - ...