Akka(37): Http:客户端操作模式
Akka-http的客户端连接模式除Connection-Level和Host-Level之外还有一种非常便利的模式:Request-Level-Api。这种模式免除了连接Connection的概念,任何时候可以直接调用singleRequest来与服务端沟通。下面我们用几个例子来示范singleRequest的用法:
- (for {
- response <- Http().singleRequest(HttpRequest(method=HttpMethods.GET,uri="http://localhost:8011/message"))
- message <- Unmarshal(response.entity).to[String]
- } yield message).andThen {
- case Success(msg) => println(s"Received message: $msg")
- case Failure(err) => println(s"Error: ${err.getMessage}")
- }.andThen {case _ => sys.terminate()}
这是一个GET操作:用Http().singleRequest直接把HttpRequest发送给服务端uri并获取返回的HttpResponse。我们看到,整组函数的返回类型都是Future[?],所以用for-comprehension来把所有实际运算包嵌在Future运算模式内(context)。下面这个例子是客户端上传数据示范:
- (for {
- entity <- Marshal("Wata hell you doing?").to[RequestEntity]
- response <- Http().singleRequest(HttpRequest(method=HttpMethods.PUT,uri="http://localhost:8011/message",entity=entity))
- message <- Unmarshal(response.entity).to[String]
- } yield message).andThen {
- case Success(msg) => println(s"Received message: $msg")
- case Failure(err) => println(s"Error: ${err.getMessage}")
- }.andThen {case _ => sys.terminate()}
以上是个PUT操作。我们需要先构建数据载体HttpEntity。格式转换函数Marshal也返回Future[HttpEntity],所以也可以包含在for语句内。关注一下这个andThen,它可以连接一串多个monadic运算,在不影响上游运算结果的情况下实现一些副作用计算。值得注意的是上面这两个例子虽然表现形式很简洁,但我们无法对数据转换过程中的异常及response的状态码等进行监控。所以我们应该把整个过程拆分成两部分:先获取response,再具体处理response,包括核对状态,处理数据等:
- case class Item(id: Int, name: String, price: Double)
- def getItem(itemId: Int): Future[HttpResponse] = for {
- response <- Http().singleRequest(HttpRequest(method=HttpMethods.GET,uri = s"http://localhost:8011/item/$itemId"))
- } yield response
- def extractEntity[T](futResp: Future[HttpResponse])(implicit um: Unmarshaller[ResponseEntity,T]) = {
- futResp.andThen {
- case Success(HttpResponse(StatusCodes.OK, _, entity, _)) =>
- Unmarshal(entity).to[T]
- .onComplete {
- case Success(t) => println(s"Got response entity: ${t}")
- case Failure(e) => println(s"Unmarshalling failed: ${e.getMessage}")
- }
- case Success(_) => println("Exception in response!")
- case Failure(err) => println(s"Response Failed: ${err.getMessage}")
- }
- }
- extractEntity[Item](getItem())
现在这个extractEntity[Item](getItem(13))可以实现全过程的监控管理了。用同样的模式实现PUT操作:
- def putItem(item: Item): Future[HttpResponse] =
- for {
- reqEntity <- Marshal(item).to[RequestEntity]
- response <- Http().singleRequest(HttpRequest(method=HttpMethods.PUT,uri="http://localhost:8011/item",entity=reqEntity))
- } yield response
- extractEntity[Item](putItem(Item(,"Item#23", 46.0)))
- .andThen { case _ => sys.terminate()}
当然,我们还是使用了前面几篇讨论里的Marshalling方式来进行数据格式的自动转换:
- import de.heikoseeberger.akkahttpjson4s.Json4sSupport
- import org.json4s.jackson
- ...
- 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
- ...
- import JsConverters._
- implicit val jsonStreamingSupport = EntityStreamingSupport.json()
- .withParallelMarshalling(parallelism = , unordered = false)
如果我们需要对数据交换过程进行更细致的管控,用Host-Level-Api会更加适合。下面我们就针对Host-Level-Api构建一个客户端的工具库:
- class PooledClient(host: String, port: Int, poolSettings: ConnectionPoolSettings)
- (implicit sys: ActorSystem, mat: ActorMaterializer) {
- import sys.dispatcher
- private val cnnPool: Flow[(HttpRequest, Int), (Try[HttpResponse], Int), Http.HostConnectionPool] =
- Http().cachedHostConnectionPool[Int](host = host, port = port, settings = poolSettings)
- //单一request
- def requestSingleResponse(req: HttpRequest): Future[HttpResponse] = {
- Source.single(req -> )
- .via(cnnPool)
- .runWith(Sink.head).flatMap {
- case (Success(resp), _) => Future.successful(resp)
- case (Failure(fail), _) => Future.failed(fail)
- }
- }
- //组串request
- def orderedResponses(reqs: Iterable[HttpRequest]): Future[Iterable[HttpResponse]] = {
- Source(reqs.zipWithIndex.toMap)
- .via(cnnPool)
- .runFold(SortedMap[Int, Future[HttpResponse]]()) {
- case (m, (Success(r), idx)) => m + (idx -> Future.successful(r))
- case (m, (Failure(f), idx)) => m + (idx -> Future.failed(f))
- }.flatMap { m => Future.sequence(m.values) }
- }
- }
下面是一种比较安全的模式:使用了queue来暂存request从而解决因发送方与接收方速率不同所产生的问题:
- class QueuedRequestsClient(host: String, port: Int, poolSettings: ConnectionPoolSettings)
- (qsize: Int = , overflowStrategy: OverflowStrategy = OverflowStrategy.dropNew)
- (implicit sys: ActorSystem, mat: ActorMaterializer) {
- import sys.dispatcher
- private val cnnPool: Flow[(HttpRequest,Promise[HttpResponse]),(Try[HttpResponse],Promise[HttpResponse]),Http.HostConnectionPool] =
- Http().cachedHostConnectionPool[Promise[HttpResponse]](host=host,port=port,settings=poolSettings)
- val queue =
- Source.queue[(HttpRequest, Promise[HttpResponse])](qsize, overflowStrategy)
- .via(cnnPool)
- .to(Sink.foreach({
- case ((Success(resp), p)) => p.success(resp)
- case ((Failure(e), p)) => p.failure(e)
- })).run()
- def queueRequest(request: HttpRequest): Future[HttpResponse] = {
- val responsePromise = Promise[HttpResponse]()
- queue.offer(request -> responsePromise).flatMap {
- case QueueOfferResult.Enqueued => responsePromise.future
- case QueueOfferResult.Dropped => Future.failed(new RuntimeException("Queue overflowed. Try again later."))
- case QueueOfferResult.Failure(ex) => Future.failed(ex)
- case QueueOfferResult.QueueClosed => Future.failed(new RuntimeException("Queue was closed (pool shut down) while running the request. Try again later."))
- }
- }
- }
下面是这些工具函数的具体使用示范:
- val settings = ConnectionPoolSettings(sys)
- .withMaxConnections()
- .withMaxOpenRequests()
- .withMaxRetries()
- .withPipeliningLimit()
- val pooledClient = new PooledClient("localhost",,settings)
- def getItemByPool(itemId: Int): Future[HttpResponse] = for {
- response <- pooledClient.requestSingleResponse(HttpRequest(method=HttpMethods.GET,uri = s"http://localhost:8011/item/$itemId"))
- } yield response
- extractEntity[Item](getItemByPool())
- def getItemsByPool(itemIds: List[Int]): Future[Iterable[HttpResponse]] = {
- val reqs = itemIds.map { id =>
- HttpRequest(method = HttpMethods.GET, uri = s"http://localhost:8011/item/$id")
- }
- val rets = (for {
- responses <- pooledClient.orderedResponses(reqs)
- } yield responses)
- rets
- }
- val futResps = getItemsByPool(List(,,))
- futResps.andThen {
- case Success(listOfResps) => {
- listOfResps.foreach { r =>
- r match {
- case HttpResponse(StatusCodes.OK, _, entity, _) =>
- Unmarshal(entity).to[Item]
- .onComplete {
- case Success(t) => println(s"Got response entity: ${t}")
- case Failure(e) => println(s"Unmarshalling failed: ${e.getMessage}")
- }
- case _ => println("Exception in response!")
- }
- }
- }
- case _ => println("Failed to get list of responses!")
- }
- val queuedClient = new QueuedRequestsClient("localhost",,settings)()
- def putItemByQueue(item: Item): Future[HttpResponse] =
- for {
- reqEntity <- Marshal(item).to[RequestEntity]
- response <- queuedClient.queueRequest(HttpRequest(method=HttpMethods.PUT,uri="http://localhost:8011/item",entity=reqEntity))
- } yield response
- extractEntity[Item](putItemByQueue(Item(,"Item#23", 46.0)))
- .andThen { case _ => sys.terminate()}
下面是本次讨论的示范源代码:
服务端代码:
- import akka.actor._
- import akka.stream._
- import akka.http.scaladsl.Http
- import akka.http.scaladsl.server.Directives._
- import de.heikoseeberger.akkahttpjson4s.Json4sSupport
- import org.json4s.jackson
- 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 TestServer extends App with JsonCodec {
- implicit val httpSys = ActorSystem("httpSystem")
- implicit val httpMat = ActorMaterializer()
- implicit val httpEC = httpSys.dispatcher
- import JsConverters._
- case class Item(id: Int, name: String, price: Double)
- val messages = path("message") {
- get {
- complete("hello, how are you?")
- } ~
- put {
- entity(as[String]) {msg =>
- complete(msg)
- }
- }
- }
- val items =
- (path("item" / IntNumber) & get) { id =>
- get {
- complete(Item(id, s"item#$id", id * 2.0))
- }
- } ~
- (path("item") & put) {
- entity(as[Item]) {item =>
- complete(item)
- }
- }
- val route = messages ~ items
- val (host, port) = ("localhost", )
- 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())
- }
客户端源代码:
- import akka.actor._
- import akka.http.scaladsl.settings.ConnectionPoolSettings
- import akka.stream._
- import akka.stream.scaladsl._
- import akka.http.scaladsl.Http
- import akka.http.scaladsl.model._
- import scala.util._
- import de.heikoseeberger.akkahttpjson4s.Json4sSupport
- import org.json4s.jackson
- import scala.concurrent._
- import akka.http.scaladsl.unmarshalling.Unmarshal
- import akka.http.scaladsl.unmarshalling._
- import akka.http.scaladsl.marshalling.Marshal
- import scala.collection.SortedMap
- import akka.http.scaladsl.common._
- 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
- class PooledClient(host: String, port: Int, poolSettings: ConnectionPoolSettings)
- (implicit sys: ActorSystem, mat: ActorMaterializer) {
- import sys.dispatcher
- private val cnnPool: Flow[(HttpRequest, Int), (Try[HttpResponse], Int), Http.HostConnectionPool] =
- Http().cachedHostConnectionPool[Int](host = host, port = port, settings = poolSettings)
- def requestSingleResponse(req: HttpRequest): Future[HttpResponse] = {
- Source.single(req -> )
- .via(cnnPool)
- .runWith(Sink.head).flatMap {
- case (Success(resp), _) => Future.successful(resp)
- case (Failure(fail), _) => Future.failed(fail)
- }
- }
- def orderedResponses(reqs: Iterable[HttpRequest]): Future[Iterable[HttpResponse]] = {
- Source(reqs.zipWithIndex.toMap)
- .via(cnnPool)
- .runFold(SortedMap[Int, Future[HttpResponse]]()) {
- case (m, (Success(r), idx)) => m + (idx -> Future.successful(r))
- case (m, (Failure(f), idx)) => m + (idx -> Future.failed(f))
- }.flatMap { m => Future.sequence(m.values) }
- }
- }
- class QueuedRequestsClient(host: String, port: Int, poolSettings: ConnectionPoolSettings)
- (qsize: Int = , overflowStrategy: OverflowStrategy = OverflowStrategy.dropNew)
- (implicit sys: ActorSystem, mat: ActorMaterializer) {
- import sys.dispatcher
- private val cnnPool: Flow[(HttpRequest,Promise[HttpResponse]),(Try[HttpResponse],Promise[HttpResponse]),Http.HostConnectionPool] =
- Http().cachedHostConnectionPool[Promise[HttpResponse]](host=host,port=port,settings=poolSettings)
- val queue =
- Source.queue[(HttpRequest, Promise[HttpResponse])](qsize, overflowStrategy)
- .via(cnnPool)
- .to(Sink.foreach({
- case ((Success(resp), p)) => p.success(resp)
- case ((Failure(e), p)) => p.failure(e)
- })).run()
- def queueRequest(request: HttpRequest): Future[HttpResponse] = {
- val responsePromise = Promise[HttpResponse]()
- queue.offer(request -> responsePromise).flatMap {
- case QueueOfferResult.Enqueued => responsePromise.future
- case QueueOfferResult.Dropped => Future.failed(new RuntimeException("Queue overflowed. Try again later."))
- case QueueOfferResult.Failure(ex) => Future.failed(ex)
- case QueueOfferResult.QueueClosed => Future.failed(new RuntimeException("Queue was closed (pool shut down) while running the request. Try again later."))
- }
- }
- }
- object ClientRequesting extends App {
- import JsConverters._
- implicit val sys = ActorSystem("sysClient")
- implicit val mat = ActorMaterializer()
- implicit val ec = sys.dispatcher
- implicit val jsonStreamingSupport = EntityStreamingSupport.json()
- .withParallelMarshalling(parallelism = , unordered = false)
- case class Item(id: Int, name: String, price: Double)
- def extractEntity[T](futResp: Future[HttpResponse])(implicit um: Unmarshaller[ResponseEntity,T]) = {
- futResp.andThen {
- case Success(HttpResponse(StatusCodes.OK, _, entity, _)) =>
- Unmarshal(entity).to[T]
- .onComplete {
- case Success(t) => println(s"Got response entity: ${t}")
- case Failure(e) => println(s"Unmarshalling failed: ${e.getMessage}")
- }
- case Success(_) => println("Exception in response!")
- case Failure(err) => println(s"Response Failed: ${err.getMessage}")
- }
- }
- (for {
- response <- Http().singleRequest(HttpRequest(method=HttpMethods.GET,uri="http://localhost:8011/message"))
- message <- Unmarshal(response.entity).to[String]
- } yield message).andThen {
- case Success(msg) => println(s"Received message: $msg")
- case Failure(err) => println(s"Error: ${err.getMessage}")
- } //.andThen {case _ => sys.terminate()}
- (for {
- entity <- Marshal("Wata hell you doing?").to[RequestEntity]
- response <- Http().singleRequest(HttpRequest(method=HttpMethods.PUT,uri="http://localhost:8011/message",entity=entity))
- message <- Unmarshal(response.entity).to[String]
- } yield message).andThen {
- case Success(msg) => println(s"Received message: $msg")
- case Failure(err) => println(s"Error: ${err.getMessage}")
- } //.andThen {case _ => sys.terminate()}
- def getItem(itemId: Int): Future[HttpResponse] = for {
- response <- Http().singleRequest(HttpRequest(method=HttpMethods.GET,uri = s"http://localhost:8011/item/$itemId"))
- } yield response
- extractEntity[Item](getItem())
- def putItem(item: Item): Future[HttpResponse] =
- for {
- reqEntity <- Marshal(item).to[RequestEntity]
- response <- Http().singleRequest(HttpRequest(method=HttpMethods.PUT,uri="http://localhost:8011/item",entity=reqEntity))
- } yield response
- extractEntity[Item](putItem(Item(,"Item#23", 46.0)))
- .andThen { case _ => sys.terminate()}
- val settings = ConnectionPoolSettings(sys)
- .withMaxConnections()
- .withMaxOpenRequests()
- .withMaxRetries()
- .withPipeliningLimit()
- val pooledClient = new PooledClient("localhost",,settings)
- def getItemByPool(itemId: Int): Future[HttpResponse] = for {
- response <- pooledClient.requestSingleResponse(HttpRequest(method=HttpMethods.GET,uri = s"http://localhost:8011/item/$itemId"))
- } yield response
- extractEntity[Item](getItemByPool())
- def getItemsByPool(itemIds: List[Int]): Future[Iterable[HttpResponse]] = {
- val reqs = itemIds.map { id =>
- HttpRequest(method = HttpMethods.GET, uri = s"http://localhost:8011/item/$id")
- }
- val rets = (for {
- responses <- pooledClient.orderedResponses(reqs)
- } yield responses)
- rets
- }
- val futResps = getItemsByPool(List(,,))
- futResps.andThen {
- case Success(listOfResps) => {
- listOfResps.foreach { r =>
- r match {
- case HttpResponse(StatusCodes.OK, _, entity, _) =>
- Unmarshal(entity).to[Item]
- .onComplete {
- case Success(t) => println(s"Got response entity: ${t}")
- case Failure(e) => println(s"Unmarshalling failed: ${e.getMessage}")
- }
- case _ => println("Exception in response!")
- }
- }
- }
- case _ => println("Failed to get list of responses!")
- }
- val queuedClient = new QueuedRequestsClient("localhost",,settings)()
- def putItemByQueue(item: Item): Future[HttpResponse] =
- for {
- reqEntity <- Marshal(item).to[RequestEntity]
- response <- queuedClient.queueRequest(HttpRequest(method=HttpMethods.PUT,uri="http://localhost:8011/item",entity=reqEntity))
- } yield response
- extractEntity[Item](putItemByQueue(Item(,"Item#23", 46.0)))
- .andThen { case _ => sys.terminate()}
- }
Akka(37): Http:客户端操作模式的更多相关文章
- HDFS的Java客户端操作代码(HDFS的查看、创建)
1.HDFS的put上传文件操作的java代码: package Hdfs; import java.io.FileInputStream; import java.io.FileNotFoundEx ...
- arm9的操作模式,寄存器,寻址方式
工作模式 Arm有7种工作模式: 名称 简称 简介 User Usr 正常用户程序执行的模式(linux下用户程序就是在这一模式执行的.) FIQ Fiq 快速中断模式 IRQ Irq 普通中断模式 ...
- IdentityServer4 (1) 客户端授权模式(Client Credentials)
写在前面 1.源码(.Net Core 2.2) git地址:https://github.com/yizhaoxian/CoreIdentityServer4Demo.git 2.相关章节 2.1. ...
- PYTHON工业互联网应用实战12—客户端操作
本章节我们将实现与admin里类似的列操作"下达"功能,演示客户端是如何实现操作功能,同时,演示也会强调一点,何时合并你的功能代码,避免相同功能使用不同的代码段来实现,在企业开发中 ...
- ListView多选操作模式——上下文操作模式
1.什么叫上下文操作模式 2.如何进入上下文操作模式 1.ListView自身带了单选.多选模式,可通过listview.setChoiceMode来设置: listview.setChoiceMod ...
- java web 获取客户端操作系统信息
package com.java.basic.pattern; import java.util.regex.Matcher; import java.util.regex.Pattern; /** ...
- Hadoop系列007-HDFS客户端操作
title: Hadoop系列007-HDFS客户端操作 date: 2018-12-6 15:52:55 updated: 2018-12-6 15:52:55 categories: Hadoop ...
- pytthon—day8 读写模式的结合、文件操作模式、with完成文本文件复制、游标操作
一.读写模式的结合 w:写指没有新建文件,有文件就清空 w=open('1.txt','w',encoding='utf-8') w.write('000\n') 在写入数据时,需要及时处理内存空间, ...
- 使用Java客户端操作elasticsearch(二)
承接上文,使用Java客户端操作elasticsearch,本文主要介绍 常见的配置 和Sniffer(集群探测) 的使用. 常见的配置 前面已介绍过,RestClientBuilder支持同时提供一 ...
随机推荐
- JavaScript面向对象(OOP)
前 言 JRedu 面向对象程序设计(简称OOP)是现在最流行的程序设计方法,这种方法有别于基于过程的程序设计方法.在写面向对象的WEB应用程序方面JavaScript是一种很好的选择.它能支持 ...
- Opencv处理鼠标事件-OpenCV步步精深
在图片上双击过的位置绘制一个 圆圈 鼠标事件就是和鼠标有关的,比如左键按下,左键松开,右键按下,右键松开,双击右键等等. 我们可以通过鼠标事件获得与鼠标对应的图片上的坐标.我们通过以下函数来调用查看所 ...
- linux_base_commond_one
1.cd commond a. cd usr 切换到该目录下usr目录 b. cd ../ 切换到上一层目录 c.cd / 切换到系统根目录 d. cd ~ 切换到用户主目录 e. cd - 切 ...
- 阿里巴巴Java规约插件试用
阿里Java开发规约Eclipse插件介绍 阿里巴巴集团配合<阿里巴巴Java开发手册>PDF终极版开发的IDE插件,目前包括IDEA插件.Eclipse插件. 安装 检查环境 插件要求: ...
- win10 uwp json
本文讲的是关于在uwp使用json的简单使用,json应用很多,因为我只是写简单使用,说的东西可能不对或者不符合每个人的预期.如果觉得我有讲的不对的,就多多包含,或者直接关掉这篇文章,但是请勿生气或者 ...
- Java IO(IO流)-1
IO流 第一部分 (outputStream/InputStream Writer/Redaer) IO流对象中输入和输出是相辅相成的,输出什么,就可以输入什么. IO的命名方式为前半部分能干什么,后 ...
- Amaze UI 是一个移动优先的跨屏前端框架。 http://amazeui.org/
http://amazeui.org/ Amaze UI 是一个移动优先的跨屏前端框架.... Amaze UI 以移动优先(Mobile first)为理念,从小屏逐步扩展到大屏,最终实现所有屏幕适 ...
- 浅析Java源码之LinkedList
可以骂人吗???辛辛苦苦写了2个多小时搞到凌晨2点,点击保存草稿退回到了登录页面???登录成功草稿没了???喵喵喵???智障!!气! 很厉害,隔了30分钟,我的登录又失效了,草稿再次回滚,不客气了,* ...
- chromium源码阅读--进程间通信(IPC)
第一篇就有提到Chromium是目前默认是采用多进程架构,当然,chromium有singe-process的版本. 多进程与多线程的区别,确实有很多可以讲的,我的另一篇博客也讲了一些,这里是从浏览器 ...
- Uncle Tom's Inherited Land*
Uncle Tom's Inherited Land* Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java ...