Akka(38): Http:Entityof ByteString-数据传基础

文字

于信息的读取,图表可以快捷而无法就准确,这就是图形要辅以文字说明的意思所在。

文的可读性主要反映于书(衬线字体和非衬线字体),字号,配色和排版。以排版为条例,用户之读书习惯是由左往右,从上通往生,也是我们经常说的Z型阅读模式。这种模式下,左对同步之契排布就异常方便,用户可以无意识的回到段落左端,开始新一行的阅读。而在中以及不当对合使得段落每一样行左端的职位还无一样,用户每次都使发觉察的查找起来位置,阅读起来会杀辛苦。

方说到了用户浏览习惯,接下再吃大家享受另一个爱吃我们忽视用户浏览习惯。我们以设计饼状图的早晚,份额最特别的有些该放置于12点钟方向,因为用户还习惯于12点钟的职上马浏览。其余的片本自深及多少依次排,顺时针逆时针都可以。

客户端上传数范例:

科普图表种类

这就是说什么样才能够设计有用户满意的图样呢?要对是题材,首先我们设明白目前广大的图形种类有哪。

折线图,曲线图,饼图,环状图,条状图,雷达图,地图抵足以说凡是我们目前不过广泛的图形样式了。具体到每种图表适用于表现什么项目的数本身此虽无多说了,数学老师应该还说了,今天要来说图表设计。

咱俩可自以下四个维度来分析图表设计:可读性,一致性,视觉层级与美观性

老大扎眼,HttpEntity可以分点儿万分接近,一种植是Strict类型的,它的data是ByteString。另一样栽是UniversalEntity类型,它的数目dataBytes是Source[ByteString,Any]。无论如何最终在线上之要么ByteString。HttpEntity的ContentType注明了传输数据格式,有:

  def runService(request: HttpRequest, rentity: RequestEntity) = {
   val futResp = for {
     entity <- Future.successful(rentity)
     resp <- Http(sys).singleRequest(
       request.copy(entity = rentity)
     )
   } yield resp

   futResp
    .andThen {
      case Success(r@HttpResponse(StatusCodes.OK, _, entity, _)) =>
        entity.dataBytes.map(_.utf8String).runForeach(println)
      case Success(r@HttpResponse(code, _, _, _)) =>
        println(s"Download request failed, response code: $code")
        r.discardEntityBytes()
      case Success(_) => println("Unable to download rows!")
      case Failure(err) => println(s"Download failed: ${err.getMessage}")

    }
  }

一致性

同等悠悠产品遭所起的图肯定不止一种,为了打消不同门类图表给用户带来的咀嚼负担,我们可由此叫图表添加相同之筹划元素来起图表的一致性原则。这些平之设计因素得以是背景色,排版,标题样式等。

以咕咚为条例,这其中出现的几栽图表样式都是较统一之。

 

视觉层级

差的多寡有不同之重点程度,我们得以经过配色,群组,字体,间距等手法来如图表的视觉层次分开,有助于用户对于数据的轻重缓急有一个直观的判断。

对待于浅色,深色背景再会吸引用户之注意力。

 } ~ path("text") {
      get {
        complete("Hello World!")
      } ~

总结

当即虽是本人从可读性,一致性,视觉层级和美观性这四独点对图纸设计之下结论,希望可以对大家有所助。大家发其他问题和想法,欢迎留言来交流。

咱们于客户端或者用runService来分析传过来的entity。由于接到一个大型的Source,所以要改一下接收方代码:

美观性

事实上如一个图纸可以满足以上的老三只极既得以说凡是一定对了,但是咱只要重进一步,从美观性的角度重新对图纸进行升级。好的图纸应该是装有美感的,最好有有趣,独特等元素。这样才足以引发用户之注意力,让她们顾念看个究竟。看到特别美好的图形样式,用户还会见错过分享。虎扑做的“数读NBA”就是一个特别好的例证。

本咱们得以视有的图会引入一些动效,动效可以美化界面,有趣的动效还可于及戏用户之意图,但是动效会下降加载速度。比如下方的一个电子收据动画,这个动画看起老酷炫,但是它的留存叫用户需4秒钟才能够来看交易细节。这个等待时明显超过了用户之方寸预期。所以于动效的利用上,我们必将要找到一个平衡点。

 

图片的面世可以帮忙我们对数码进行可视化处理,提升了数据的可读性。因为相比于纯文本,用户处理图片信息的速而尽早得多。

  val numbers = Source.fromIterator(() =>
    Iterator.continually(Random.nextInt()))
    .map(n => ByteString(s"$n\n"))
  //make conform to withoutSizeLimit constrain
  val source = limitableByteSource(numbers)

用户以采取产品过程遭到,会触发到大气之数量。其实数据对用户来说就是文字和数字之结缘,大脑处理纯文本的快慢较缓慢。例如我们来拘禁一样出球队的技能统计,场均116.7区划,28.3助攻,45.0篮板…这些数量对咱们吧都是淡然的,我们死不便快速的了解其幕后的义。

    } ~ path("text") {
      get {
        complete("Hello World!")
      } ~
        post {
          withoutSizeLimit {
            extractDataBytes { bytes =>
              val data = bytes.runFold(ByteString())(_ ++ _)
              onComplete(data) { t =>
                complete(t)
              }
            }
          }
        }

 val numbers = Source.fromIterator(() =>
    Iterator.continually(Random.nextInt()))
    .map(n => ByteString(s"$n\n"))
  //make conform to withoutSizeLimit constrain
  val source = limitableByteSource(numbers)

  val bytes = HttpEntity(
    ContentTypes.`application/octet-stream`,
    // transform each number to a chunk of bytes
    source.take(10000)
  )
  val postRandom = HttpRequest(HttpMethods.POST,uri = s"http://localhost:8011/random")
  runService(postRandom,bytes)
    .andThen{case _ => sys.terminate()}

群组

群组的设计理念及卡片式设计互动近似,都是对准信息进行划分为用户更爱消化。以转账功能为条例,对于收款人信息,我们可行使左边的列表样式逐条展示,其实这么做问题吧不深,而且还省事。但是这种展示方式属于毫无主次的陈铺出有消息,用户无法明确优先级。在此界面被,用户最好关注的是收款人信息,而收款人信息遭用户之关心重点依次是收款人姓名>收款账号>开户实施。如果就此左手的列表样式,优先级是维度就无法体现。

那我们可对信息进行分组整合,将收款人姓名,账号,开户行整合交手拉手,还透过使用icon来支援用户快速稳定主要信息,提升阅读速度。

 

配色

图形的配色这里关键来说背景色,一般的话,图表的背景色我们可以分成深色和浅色。浅色背景的图样更便利用户阅读,可以有效的增高多少的可读性。

想必有人会咨询了,既然浅色背景再有益于阅读,那么我们就是可以直接生定论了“图表背景一律使用浅色”,为什么还要考虑深色呢?

这就是说是以有界面内容过少,为了使界面看起不那么干燥,我们会利用深色背景。因为界面内容己便少,用户一眼就能够看罢,所以深色背景对可读性影响不酷。但是咱设牢记,当数了多的时段,必须采取浅色背景。

 

于这边我们下的凡雷达图,球队各项主要统计一目了然。

我们领略Akka-http是依据Akka-Stream的,具备Reactive-Stream功能特色。下面我们便示范一下哪些开展stream的上传下载。首先定制一个Source:

可读性

图片设计的初衷就是是为着给用户多样的数额被摆脱出来,图表需要救助用户更好之”读取”数据,所以在这里自己以可读性放在图表设计之重要性位置。

图表的可读性主要注意以下三点。配色,文字和群组

  path("random") {
      get {
        complete(
          HttpEntity(
            ContentTypes.`application/octet-stream`,
            // transform each number to a chunk of bytes
            source.take(10000)
          )
        )
      } ~

起者讨论我们询问了于Marshal,Unmarshal下层才是ByteString的操作及转移。下面是此次讨论示范源代码:

 

       post {
          withoutSizeLimit {
            extractDataBytes { bytes =>
              val data = bytes.runFold(ByteString())(_ ++ _)
              onComplete(data) { t =>
                complete(t)
              }
            }
          }
        }
import akka.actor._
import akka.stream._
import akka.stream.scaladsl._
import akka.http.scaladsl.Http
import akka.http.scaladsl.model.HttpEntity.limitableByteSource
import akka.http.scaladsl.model._

import scala.concurrent.duration._
import akka.util.ByteString

import scala.concurrent._
import scala.util._

object ClientEntity extends App {

  implicit val sys = ActorSystem("ClientSys")
  implicit val mat = ActorMaterializer()
  implicit val ec = sys.dispatcher

  def runService(request: HttpRequest, rentity: RequestEntity) = {
   val futResp = for {
     entity <- Future.successful(rentity)
     resp <- Http(sys).singleRequest(
       request.copy(entity = rentity)
     )
   } yield resp

   futResp
    .andThen {
      case Success(r@HttpResponse(StatusCodes.OK, _, entity, _)) =>
        val futEnt = entity.dataBytes.map(_.utf8String).runForeach(println)
             Await.result(futEnt, Duration.Inf) // throws if binding fails
             println("End of stream!!!")
      case Success(r@HttpResponse(code, _, _, _)) =>
        println(s"Download request failed, response code: $code")
        r.discardEntityBytes()
      case Success(_) => println("Unable to download rows!")
      case Failure(err) => println(s"Download failed: ${err.getMessage}")

    }
  }

  val reqText = HttpRequest(uri = s"http://localhost:8011/text")
//  runService(reqText,HttpEntity.Empty)
//    .andThen{case _ => sys.terminate()}

  val postText = HttpRequest(HttpMethods.POST,uri = s"http://localhost:8011/text")
  val uploadText = HttpEntity(
    ContentTypes.`text/plain(UTF-8)`,
    // transform each number to a chunk of bytes
    ByteString("hello world again")
  )
//  runService(postText,uploadText)
//    .andThen{case _ => sys.terminate()}

  val reqRandom = HttpRequest(uri = s"http://localhost:8011/random")
 //   runService(reqRandom,HttpEntity.Empty)
 //    .andThen{case _ => sys.terminate()}

  val numbers = Source.fromIterator(() =>
    Iterator.continually(Random.nextInt()))
    .map(n => ByteString(s"$n\n"))
  //make conform to withoutSizeLimit constrain
  val source = limitableByteSource(numbers)

  val bytes = HttpEntity(
    ContentTypes.`application/octet-stream`,
    // transform each number to a chunk of bytes
    source.take(10000)
  )
  val postRandom = HttpRequest(HttpMethods.POST,uri = s"http://localhost:8011/random")
  runService(postRandom,bytes)
    .andThen{case _ => sys.terminate()}


}

 

  val reqRandom = HttpRequest(uri = s"http://localhost:8011/random")
    runService(reqRandom,HttpEntity.Empty)
     .andThen{case _ => sys.terminate()}
object ContentTypes {
  val `application/json` = ContentType(MediaTypes.`application/json`)
  val `application/octet-stream` = ContentType(MediaTypes.`application/octet-stream`)
  val `text/plain(UTF-8)` = MediaTypes.`text/plain` withCharset HttpCharsets.`UTF-8`
  val `text/html(UTF-8)` = MediaTypes.`text/html` withCharset HttpCharsets.`UTF-8`
  val `text/xml(UTF-8)` = MediaTypes.`text/xml` withCharset HttpCharsets.`UTF-8`
  val `text/csv(UTF-8)` = MediaTypes.`text/csv` withCharset HttpCharsets.`UTF-8`

  // used for explicitly suppressing the rendering of Content-Type headers on requests and responses
  val NoContentType = ContentType(MediaTypes.NoMediaType)
}
  val postText = HttpRequest(HttpMethods.POST,uri = s"http://localhost:8011/text")
  val uploadText = HttpEntity(
    ContentTypes.`text/plain(UTF-8)`,
    // transform each number to a chunk of bytes
    ByteString("hello world again")
  )
  runService(postText,uploadText)
    .andThen{case _ => sys.terminate()}

 

服务端:

 

 

又示范一下于客户端用Source上传数据。服务端代码:

咱俩可见到放上entity里之数是ByteString。

  val reqText = HttpRequest(uri = s"http://localhost:8011/text")
  runService(reqText,HttpEntity.Empty)
    .andThen{case _ => sys.terminate()}
   futResp
    .andThen {
      case Success(r@HttpResponse(StatusCodes.OK, _, entity, _)) =>
        val futEnt = entity.dataBytes.map(_.utf8String).runForeach(println)
             Await.result(futEnt, Duration.Inf) // throws if binding fails
             println("End of stream!!!")
      case Success(r@HttpResponse(code, _, _, _)) =>
        println(s"Download request failed, response code: $code")
        r.discardEntityBytes()
      case Success(_) => println("Unable to download rows!")
      case Failure(err) => println(s"Download failed: ${err.getMessage}")

    }

自打出示的结果可以得出runService函数中之entity.dataBytes.map(_.utf8String)已经将ByteString转换成了String,也就是说服务器端发送的Entity里之数是ByteString。

我们更试试着发送一些多少給服务端,然后给服务端把结果通过response
entity返回来:

在意:ContentType只是同一种植备注,不影响线上数据表达形式,线及之数额永远是ByteString。但是,其中的application/octet-stream类型代表数量必须是Source[ByteString,Any]。我们下就是通过客户端的例证来掌握HttpEntity。下面是一个客户端测试函数:

object HttpEntity {
  implicit def apply(string: String): HttpEntity.Strict = apply(ContentTypes.`text/plain(UTF-8)`, string)
  implicit def apply(bytes: Array[Byte]): HttpEntity.Strict = apply(ContentTypes.`application/octet-stream`, bytes)
  implicit def apply(data: ByteString): HttpEntity.Strict = apply(ContentTypes.`application/octet-stream`, data)
  def apply(contentType: ContentType.NonBinary, string: String): HttpEntity.Strict =
    if (string.isEmpty) empty(contentType) else apply(contentType, ByteString(string.getBytes(contentType.charset.nioCharset)))
  def apply(contentType: ContentType, bytes: Array[Byte]): HttpEntity.Strict =
    if (bytes.length == 0) empty(contentType) else apply(contentType, ByteString(bytes))
  def apply(contentType: ContentType, data: ByteString): HttpEntity.Strict =
    if (data.isEmpty) empty(contentType) else HttpEntity.Strict(contentType, data)

  def apply(contentType: ContentType, contentLength: Long, data: Source[ByteString, Any]): UniversalEntity =
    if (contentLength == 0) empty(contentType) else HttpEntity.Default(contentType, contentLength, data)
  def apply(contentType: ContentType, data: Source[ByteString, Any]): HttpEntity.Chunked =
    HttpEntity.Chunked.fromData(contentType, data)
...

咱们看出服务端对request
entity的操作是坐ByteString进行的。客户端上污染一模一样差字符的request如下:

 

 

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.model._
import akka.util.ByteString
import akka.http.scaladsl.model.HttpEntity._

import scala.util.Random

object ServerEntity extends App {

  implicit val httpSys = ActorSystem("httpSystem")
  implicit val httpMat = ActorMaterializer()
  implicit val httpEC = httpSys.dispatcher


  val numbers = Source.fromIterator(() =>
    Iterator.continually(Random.nextInt()))
    .map(n => ByteString(s"$n\n"))
  //make conform to withoutSizeLimit constrain
  val source = limitableByteSource(numbers)



  val route =
    path("random") {
      get {
        withoutSizeLimit {
          complete(
            HttpEntity(
              ContentTypes.`application/octet-stream`,
              // transform each number to a chunk of bytes
              source.take(1000))
          )
        }
      } ~
        post {
          withoutSizeLimit {
            extractDataBytes { bytes =>
              val data = bytes.runFold(ByteString())(_ ++ _)
              onComplete(data) { t =>
                complete(t)
              }
            }
          }
        }
    } ~ path("text") {
      get {
        complete("Hello World!")
      } ~
        post {
          withoutSizeLimit {
            extractDataBytes { bytes =>
              val data = bytes.runFold(ByteString())(_ ++ _)
              onComplete(data) { t =>
                complete(t)
              }
            }
          }
        }
    }


  val (port, host) = (8011,"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())

}

服务端也是故HttpEntity来装这个Source然后由此HttpRequest传给客户端的:

 

 

虽然complete(“Hello
World!”)有些迷糊,不过当complete做了把字符串到ByteString的变。我们可以打地方这个runService函数得到证实。下面是这例子的调用:

客户端:

 

 

 
我们说过Akka-http是一个吓的体系并工具,集成是由此数据交换方式实现的。Http是只当网上传和收取的正规化商事。所以,在采取Akka-http之前,可能咱们要需要拿Http模式的网上数据交换细节了解清楚。数据交换双方是透过Http消息类型Request和Response来促成的。在Akka-http中对应之是HttpRequest和HttpResponse。这有限单门类且抱有HttpEntity类型来装需要交换的多寡。首先,无论如何数据在线上的表现形式肯定是一串bytes。所以,数据交换两头Request,Response中之Entity也非得是坐bytes来表述的。在Akka-http里我们把需要导的多少易成ByteString,通过网发送給接收端、接收端再管吸收消息Entity中的ByteString转换成为靶子项目的数。这片独易过程就是Akka-http的Marshalling和Unmarshalling过程了。我们事先打HttpEntity的构建函数来询问其的概念:

所以底的办法调用:

咱俩一味待对这个函数传入RequestEntity就足以了解返回Response里Entity的过多细节了。首先我们要求服务端发送一个纯字符串Hello
World。服务端代码如下: