Akka-http针对Connection的两头都提供了方便编程的Api,分别是Server-Side-Api和Client-Side-Api。通过这两个Api可以大大提高编程效率。当然,上期我们提到过,Http-Server是Akka-http的核心部分,所有系统集成功能都是在Server-Side实现的。Akka-http-Server-Side-Api可以说是最先进的Http-Server编程工具,支持:

  • Full support for HTTP persistent connections
  • Full support for HTTP pipelining
  • Full support for asynchronous HTTP streaming including “chunked” transfer encoding accessible through an idiomatic API
  • Optional SSL/TLS encryption
  • WebSocket support

Server-Side-Api又分两个层次:Low-level-Server-Side-Api和High-level-Server-Side-Api。Low-level-server-api支持HTTP/1.1Server所有功能,包括:

  • Connection management
  • Parsing and rendering of messages and headers
  • Timeout management (for requests and connections)
  • Response ordering (for transparent pipelining support)

其它Server功能如请求解析request routing,文件服务file serving,数据压缩compression等都放在了High-level-server-api里。Akka-http是基于Akka-stream编写的,所以我们需要从Akka-stream运算模式来理解Akka-http的类型表现形式。

一个Http-Server是绑定在一个Socket上来接收客户端上传的request进行相关的服务提供的。Server对Socket的绑定在Akka-http里的可以Stream形式来表现:

val serverSource: Source[Http.IncomingConnection, Future[Http.ServerBinding]] =
Http().bind(interface = "localhost", port = )

Server-Side Socket绑定实际上就是一个Akka-stream-source:Source[IncomingConnection]:

  /**
* Creates a [[akka.stream.scaladsl.Source]] of [[akka.http.scaladsl.Http.IncomingConnection]] instances which represents a prospective HTTP server binding
* on the given `endpoint`.
*
* If the given port is 0 the resulting source can be materialized several times. Each materialization will
* then be assigned a new local port by the operating system, which can then be retrieved by the materialized
* [[akka.http.scaladsl.Http.ServerBinding]].
*
* If the given port is non-zero subsequent materialization attempts of the produced source will immediately
* fail, unless the first materialization has already been unbound. Unbinding can be triggered via the materialized
* [[akka.http.scaladsl.Http.ServerBinding]].
*
* If an [[ConnectionContext]] is given it will be used for setting up TLS encryption on the binding.
* Otherwise the binding will be unencrypted.
*
* If no `port` is explicitly given (or the port value is negative) the protocol's default port will be used,
* which is 80 for HTTP and 443 for HTTPS.
*
* To configure additional settings for a server started using this method,
* use the `akka.http.server` config section or pass in a [[akka.http.scaladsl.settings.ServerSettings]] explicitly.
*/
def bind(interface: String, port: Int = DefaultPortForProtocol,
connectionContext: ConnectionContext = defaultServerHttpContext,
settings: ServerSettings = ServerSettings(system),
log: LoggingAdapter = system.log)(implicit fm: Materializer): Source[Http.IncomingConnection, Future[ServerBinding]] = {
val fullLayer = fuseServerBidiFlow(settings, connectionContext, log) tcpBind(interface, choosePort(port, connectionContext), settings)
.map(incoming ⇒ {
val serverFlow = fullLayer.addAttributes(prepareAttributes(settings, incoming)) join incoming.flow
IncomingConnection(incoming.localAddress, incoming.remoteAddress, serverFlow)
})
.mapMaterializedValue(materializeTcpBind)
}

run这个Source[IncomingConnection]产生一串连接Connection:

  /**
* Represents one accepted incoming HTTP connection.
*/
final case class IncomingConnection(
localAddress: InetSocketAddress,
remoteAddress: InetSocketAddress,
flow: Flow[HttpResponse, HttpRequest, NotUsed]) { /**
* Handles the connection with the given flow, which is materialized exactly once
* and the respective materialization result returned.
*/
def handleWith[Mat](handler: Flow[HttpRequest, HttpResponse, Mat])(implicit fm: Materializer): Mat =
flow.joinMat(handler)(Keep.right).run() /**
* Handles the connection with the given handler function.
*/
def handleWithSyncHandler(handler: HttpRequest ⇒ HttpResponse)(implicit fm: Materializer): Unit =
handleWith(Flow[HttpRequest].map(handler)) /**
* Handles the connection with the given handler function.
*/
def handleWithAsyncHandler(handler: HttpRequest ⇒ Future[HttpResponse], parallelism: Int = )(implicit fm: Materializer): Unit =
handleWith(Flow[HttpRequest].mapAsync(parallelism)(handler))
}

IncomingConnection类型提供了个handleWith这样的streaming函数进行request到response的转换。用户可以下面的方式提供自定义的转换方法:

调用handleWith传入Flow[HttpRequest,HttpResponse,_],如:

  def req2Resp: HttpRequest => HttpResponse = _ => HttpResponse(entity=
HttpEntity(ContentTypes.`text/html(UTF-)`,"<h> Hello World! </h>"))
val flow = Flow.fromFunction(req2Resp)

提供HttpRequest=>HttpResponse函数传人handleWithSyncHandler:

def syncHandler: HttpRequest => HttpResponse = {
case HttpRequest(HttpMethods.GET,Uri.Path("/"),_headers,_entiy,_protocol) =>
HttpResponse(entity=
HttpEntity(ContentTypes.`text/html(UTF-)`,"<h> Hello World! </h>")) case req: HttpRequest =>
req.discardEntityBytes() // important to drain incoming HTTP Entity stream
HttpResponse(, entity = "Unknown resource!")
}

提供HttpRequest=>Future[HttpResponse]函数传人handleWithASyncHandler:

  def asyncHandler: HttpRequest => Future[HttpResponse] = {
case HttpRequest(HttpMethods.GET,Uri.Path("/"),_headers,_entiy,_protocol) => Future {
HttpResponse(entity=
HttpEntity(ContentTypes.`text/html(UTF-)`,"<h> Hello World! </h>")) } case req: HttpRequest => Future {
req.discardEntityBytes() // important to drain incoming HTTP Entity stream
HttpResponse(, entity = "Unknown resource!")
}
}

run Source[IncomingConnection,Future[ServerBinding]]返回结果为Future[ServerBinding]:

  val futBinding: Future[Http.ServerBinding] =
connSource.to { Sink.foreach{ connection =>
println(s"client address ${connection.remoteAddress}")
// connection handleWith flow
// connection handleWithSyncHandler syncHandler
connection handleWithAsyncHandler asyncHandler
}}.run()

我们可以通过ServerBinding来释放绑定的Socket:

 /**
* Represents a prospective HTTP server binding.
*
* @param localAddress The local address of the endpoint bound by the materialization of the `connections` [[akka.stream.scaladsl.Source]]
*
*/
final case class ServerBinding(localAddress: InetSocketAddress)(private val unbindAction: () ⇒ Future[Unit]) { /**
* Asynchronously triggers the unbinding of the port that was bound by the materialization of the `connections`
* [[akka.stream.scaladsl.Source]]
*
* The produced [[scala.concurrent.Future]] is fulfilled when the unbinding has been completed.
*/
def unbind(): Future[Unit] = unbindAction()
}

我们可以调用这个unbind():

  futBinding.flatMap(_.unbind())

整个示范源代码如下:

import akka.actor._
import akka.stream._
import akka.stream.scaladsl._
import akka.http.scaladsl.Http
import akka.http.scaladsl.model._
import scala.concurrent._ object LowLevelServerApi extends App {
implicit val httpSys = ActorSystem("actorSystem")
implicit val httpMat = ActorMaterializer()
implicit val httpEc = httpSys.dispatcher val (interface,port) = ("localhost",)
val connSource: Source[Http.IncomingConnection,Future[Http.ServerBinding]] =
Http().bind(interface,port) def req2Resp: HttpRequest => HttpResponse = _ => HttpResponse(entity=
HttpEntity(ContentTypes.`text/html(UTF-)`,"<h> Hello World! </h>"))
val flow = Flow.fromFunction(req2Resp) def syncHandler: HttpRequest => HttpResponse = {
case HttpRequest(HttpMethods.GET,Uri.Path("/"),_headers,_entiy,_protocol) =>
HttpResponse(entity=
HttpEntity(ContentTypes.`text/html(UTF-)`,"<h> Hello World! </h>")) case req: HttpRequest =>
req.discardEntityBytes() // important to drain incoming HTTP Entity stream
HttpResponse(, entity = "Unknown resource!")
} def asyncHandler: HttpRequest => Future[HttpResponse] = {
case HttpRequest(HttpMethods.GET,Uri.Path("/"),_headers,_entiy,_protocol) => Future {
HttpResponse(entity=
HttpEntity(ContentTypes.`text/html(UTF-)`,"<h> Hello World! </h>")) } case req: HttpRequest => Future {
req.discardEntityBytes() // important to drain incoming HTTP Entity stream
HttpResponse(, entity = "Unknown resource!")
}
} val futBinding: Future[Http.ServerBinding] =
connSource.to { Sink.foreach{ connection =>
println(s"client address ${connection.remoteAddress}")
// connection handleWith flow
// connection handleWithSyncHandler syncHandler
connection handleWithAsyncHandler asyncHandler
}}.run() println(s"Server running at $interface $port. Press any key to exit ...") scala.io.StdIn.readLine() futBinding.flatMap(_.unbind())
.onComplete(_ => httpSys.terminate()) }

Akka(29): Http:Server-Side-Api,Low-Level-Api的更多相关文章

  1. Identity Server 4 原理和实战(完结)_建立Identity Server 4项目,Client Credentials 授权实例

    创建项目 dotnet new -i IdentityServer4.Templates 多出来的这些模板 adminUI用来测试,想要用再生产环境,需要交钱 结合core的 Identity来使用 ...

  2. Scalaz(29)- Free :Coyoneda - Functor for free

    很多时候我们会遇到一些高阶类型F[_],但又无法实现它的map函数,也就是虽然形似但F不可能成为Functor.看看下面的例子: trait Interact[A] case class Ask(pr ...

  3. JMS生产者+单线程发送-我们到底能走多远系列(29)

    我们到底能走多远系列(29) 扯淡: “然后我俩各自一端/望着大河弯弯/终于敢放胆/嘻皮笑脸/面对/人生的难”      --- <山丘> “迎着风/迎向远方的天空/路上也有艰难/也有那解 ...

  4. Akka(2):Actor生命周期管理 - 监控和监视

    在开始讨论Akka中对Actor的生命周期管理前,我们先探讨一下所谓的Actor编程模式.对比起我们习惯的行令式(imperative)编程模式,Actor编程模式更接近现实中的应用场景和功能测试模式 ...

  5. Akka(33): Http:Marshalling,to Json

    Akka-http是一项系统集成工具.这主要依赖系统之间的数据交换功能.因为程序内数据表达形式与网上传输的数据格式是不相同的,所以需要对程序高级结构化的数据进行转换(marshalling or se ...

  6. Windows Phone开发(29):隔离存储C

    原文:Windows Phone开发(29):隔离存储C 本文是隔离存储的第三节,大家先喝杯咖啡放松,今天的内容也是非常简单,我们就聊一件东东--用户设置. 当然了,可能翻译为应用程序设置合适一些,不 ...

  7. Akka(6): become/unbecome:运算行为切换

    通过一段时间的学习了解,加深了一些对Akka的认识,特别是对于Akka在实际编程中的用途方面.我的想法,或者我希望利用Akka来达到的目的是这样的:作为传统方式编程的老兵,我们已经习惯了直线流程方式一 ...

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

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

  9. Akka(0):聊聊对Akka的初步了解和想法

    前一段时间一直沉浸在函数式编程模式里,主要目的之一是掌握一套安全可靠的并发程序编程方法(concurrent programming),最终通过开源项目FunDA实现了单机多核CPU上程序的并行运算. ...

随机推荐

  1. 201521123071 《JAVA程序设计》第七周学习总结

    第7周-集合 1. 本周学习总结 以你喜欢的方式(思维导图或其他)归纳总结集合相关内容. 1.1 Iterator<E> iterator(); //iterator()返回一个实现了It ...

  2. 201521123122 《java程序设计》 第五周实验总结

    1. 本周学习总结 1.1 尝试使用思维导图总结有关多态与接口的知识点. 2. 书面作业 1.代码阅读:Child压缩包内源代码 1.1 com.parent包中Child.java文件能否编译通过? ...

  3. 201521123003《Java程序设计》第4周学习总结

    1. 本章学习总结 你对于本章知识的学习总结 1.1 尝试使用思维导图总结有关继承的知识点. 参考资料: 百度脑图 XMind 1.2 使用常规方法总结其他上课内容. (1)了解了类型转换(cast) ...

  4. 201521123018 《Java程序设计》第10周学习总结

    1. 本章学习总结 你对于本章知识的学习总结 2. 书面作业 一.inally 题目4-2 1.1 截图你的提交结果(出现学号) 1.2 4-2中finally中捕获异常需要注意什么? 答: 4-2中 ...

  5. eclipse 项目引入第三方jar包 3种方法

    我们在用Eclipse开发程序的时候,经常要用到第三方jar包.引入jar包不是一个小问题,由于jar包位置不清楚,而浪费时间.下面配图说明3种Eclipse引入jar包的方式. 1.最常用的普通操作 ...

  6. Oracle总结第三篇【PLSQL】

    PLSQL介绍 PLSQL是Oracle对SQL99的一种扩展,基本每一种数据库都会对SQL进行扩展,Oracle对SQL的扩展就叫做PLSQL- SQL99是什么 (1)是操作所有关系型数据库的规则 ...

  7. mysql truncate、delete与drop区别

    相同点: 1.truncate和不带where子句的delete.以及drop都会删除表内的数据. 2.drop.truncate都是DDL语句(数据定义语言),执行后会自动提交. 不同点: 1. t ...

  8. 关于sublime3的使用

    一.安装Package Control 使用Ctrl+`快捷键或者通过View->Show Console菜单打开命令行,粘贴如下代码: import urllib.request,os; pf ...

  9. 【Spring】Spring的bean装配

    前言 bean是Spring最基础最核心的部分,Spring简化代码主要是依赖于bean,下面学习Spring中如何装配bean. 装配bean Spring在装配bean时非常灵活,其提供了三种方式 ...

  10. Spring 使用AspectJ的三种方式

    Spring 使用AspectJ 的三种方式 一,使用JavaConfig 二,使用注解隐式配置 三,使用XML 配置 背景知识: 注意 使用AspectJ 的 时候 要导入相应的Jar 包 嗯 昨天 ...