Akka(35): Http:Server side streaming
在前面几篇讨论里我们都提到过: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的更多相关文章
- Scalaz(35)- Free :运算-Trampoline,say NO to StackOverflowError
在前面几次讨论中我们介绍了Free是个产生Monad的最基本结构.它的原理是把一段程序(AST)一连串的运算指令(ADT)转化成数据结构存放在内存里,这个过程是个独立的功能描述过程.然后另一个独立运算 ...
- js中this和回调方法循环-我们到底能走多远系列(35)
我们到底能走多远系列(35) 扯淡: 13年最后一个月了,你们在13年初的计划实现了吗?还来得及吗? 请加油~ 主题: 最近一直在写js,遇到了几个问题,可能初入门的时候都会遇到吧,总结下. 例子: ...
- Windows Phone开发(35):使用Express Blend绘图
原文:Windows Phone开发(35):使用Express Blend绘图 上一节中我们简单扯了一下绘图指令,然而那也不是最简单的绘图法,今天,我再向大家推荐一种更好的绘图方案--Express ...
- Akka(8): 分布式运算:Remoting-远程查找式
Akka是一种消息驱动运算模式,它实现跨JVM程序运算的方式是通过能跨JVM的消息系统来调动分布在不同JVM上ActorSystem中的Actor进行运算,前题是Akka的地址系统可以支持跨JVM定位 ...
- Akka(17): Stream:数据流基础组件-Source,Flow,Sink简介
在大数据程序流行的今天,许多程序都面临着共同的难题:程序输入数据趋于无限大,抵达时间又不确定.一般的解决方法是采用回调函数(callback-function)来实现的,但这样的解决方案很容易造成“回 ...
- Qt 学习之路 2(35):文件
Qt 学习之路 2(35):文件 豆子 2013年1月5日 Qt 学习之路 2 12条评论 文件操作是应用程序必不可少的部分.Qt 作为一个通用开发库,提供了跨平台的文件操作能力.从本章开始,我们来了 ...
- web服务器专题:tomcat(二)模块组件与server.xml 配置文件
web服务器专题:tomcat(二)模块组件与server.xml 配置文件 回顾: Web服务器专题:tomcat(一) 基础模块 一个Server.xml的实例 <?xml version= ...
- Python接口测试实战4(下) - 框架完善:用例基类,用例标签,重新运行上次失败用例
如有任何学习问题,可以添加作者微信:lockingfree 课程目录 Python接口测试实战1(上)- 接口测试理论 Python接口测试实战1(下)- 接口测试工具的使用 Python接口测试实战 ...
- nodeJS(2)深了解: nodeJS 项目架构详解(app.js + Express + Http)
简略了解:nodeJS 深了解(1): Node.js + Express 构建网站预备知识 环境: 环境: win7 + nodeJS 版本(node): 新建 nodeJS 项目: 名称为: te ...
随机推荐
- servlet文件上传2——复合表单提交(数据获取和文件上传)
上传文件时表单enctype属性必须要更改为<enctype='multipart/form-data'>:采用post提交表单,元素需要有name属性: 利用第三方jar包(common ...
- hibernate学习手记(1)
1. java.sql.SQLException: The server time zone value '?й???????' is unrecognized or represents more ...
- 1.在CentOS 6.4安装python3
CentOS安装Python3.X 1.系统环境说明 [root@Python ~]# uname -r 2.6.32-431.el6.i686 [root@Python ~]# uname -m i ...
- Tomcat 设置自启动时遇到的错误问题与解决方案
首先,今天在做tomcat开机自启动时,原本很简单的一个问题,但却浪费了很长时间: 首先系统环境采用的是Window10,设置Tomcat自启动过程当中需要注意的是:JDK的版本和Tomcat的位数必 ...
- java猜数字(实验任务五)
1.程序设计思想: 先随机获取1-100之内的数字i: 在建立让用户输入数字的对话框,然后判断猜高了.低了还是猜对了: 用循环直到用户猜对了. 2.程序流程图: 3.源代码: package 实验任务 ...
- Docker到底是什么
简单讲docker和vm虚拟机类似,都是在同一硬件上虚拟化出多个服务器应用实例的功能,据Bottomley声称,借助经过全面调优的容器系统,你就可以在同一硬件上拥有数量比使用Xen虚拟机或KVM虚拟机 ...
- DNS—正、反向解析;委派;主从;子域;转发;智能dns等的实现
前言:DNS,耳熟能详的东西,内容太多,小编也不太好讲清,只能写几个实验详解,供大家参考. 一.简单介绍 1.DNS:通过主机名,最终得到该主机名对应的IP地址的过程叫做域名解析(或主机名解析). 端 ...
- Java面向对象 线程技术--上篇
Java面向对象 线程 知识概要: (1)线程与进程 (2)自定义线程的语法结构 (3)多线程概念理解 (4)多线程状态图 (5)多线程--卖票 (6)同 ...
- 初学者易上手的SSH-struts2 04值栈与ognl表达式
什么是值栈?struts2里面本身提供的一种存储机制,类似于域对象,值栈,可以存值和取值.,特点:先进后出.如果将它当做一个容器的话,而这个容器有两个元素,那么最上面的元素叫做栈顶元素,也就是所说的压 ...
- WPF 如何画出1像素的线
如何有人告诉你,请你画出1像素的线,是不是觉得很简单,实际上在 WPF 上还是比较难的. 本文告诉大家,如何让画出的线不模糊 画出线的第一个方法,创建一个 Canvas ,添加一个线 界面代码 < ...