Akka(28): Http:About Akka-Http


  眾所周知,Akka系統是基於Actor模式的分布式運算系統,非常適合構建大數據平台。所以,無可避免地會出現獨立系統之間、與異類系統、與移動系統集成的需求。由於涉及到異類和移動系統,系統對接的方式必須在一套公開的標准之上進行,包括數據格式及數據傳輸標准。實際上針對標准的傳輸連接及標准數據編碼、傳輸、解碼全過程的軟件編程是非常復雜及困難的。Akka-http正是這么一套能高效解決以上問題的編程工具。Akka-http是一套支持Tcp傳輸標准及Http標准數據的編程工具。

  Http模式的交流方式是固定單向的:一方永遠為對話啟動方,另一方永遠是回應方。具體運作方式是:發起方構建一個Http消息結構即Request,通過Tcp把它傳給接收方;接收方對消息進行解譯並按照發起方編寫在消息里的要求進行一些運算及構建一個回復消息即Response並把它傳回給發送方。在實際應用中這兩方形成了一種服務方server與客戶方client的關系:客戶方向服務方發送服務請求Request;服務方根據Request提供相應運算並用Response回應結果。

  Http消息的構成有兩部分:一部分是對消息本身的描述,包括Http協議版本、字符集、加密方式、壓縮方式、數據類型、安全機制等,另一部分就是消息的內容,即數據本身了,消息描述部分也有一些描述是針對數據的。

  從實際應用角度來看:在Tcp上通過Http消息交換實現了一種服務及服務使用計算模式。服務提供方server處於被動調用狀態,客戶方client通過Request向服務方提出服務要求,服務方按照要求在服務端進行相關運算后將結果用Response返回客戶方。可以看出:服務端客戶端雙方都涉及到了Http消息的構建、解析、傳輸,而服務提供方則增加了針對Request服務要求分析邏輯及對應的運算服務。

  從更高應用層次來分析:系統集成實質上是兩個系統之間通過Http協議實現數據交換。整個集成過程可以概括為:Client方將數據封裝成Request;然后通過Tcp上傳給Server;Server收到Request后進行解析;將Request里的數據解碼成內部結構數據;按Request要求進行Server端運算;將運算結果數據封裝成Response;然后將Response返回Client;Client對Response進行解析;將Response里的數據解碼形成內部結構數據。

  Akka-http分別提供了服務端的Server-Side-Api和客戶端的Client-Side-Api來幫助編程人員簡化編程。兩個Api都包括了對Http消息的構建、解析、傳輸幫助函數。Server-Side-Api還包括了一套DSL以方便Http-Server功能編程。

  對某個人群來說,Http是一個極其繁瑣的協議:這里包括了消息格式、數據加碼、解碼、壓縮、通訊協議、傳輸安全等等,等等。如果單純按照Http協議編程的話將無法避免一堆新的定義及死板規定,無可避免影響編程效率。Akka-http應該正是為了這個人群而設計的。

  Akka-http對Http消息的各組成部分進行了建模:用class來代表數據結構。然后在各類的伴生對象中提供大量的幫助函數(helper)來輔助該類型的構建、匹配等操作。如此可以大大簡化Http消息的操作編程。舉個例子:Request和Response兩種類型的消息定義如下:

/**
 * The immutable model HTTP request model.
 */
final class HttpRequest(
  val method:   HttpMethod,
  val uri:      Uri,
  val headers:  immutable.Seq[HttpHeader],
  val entity:   RequestEntity,
  val protocol: HttpProtocol)
...
/**
 * The immutable HTTP response model.
 */
final class HttpResponse(
  val status:   StatusCode,
  val headers:  immutable.Seq[HttpHeader],
  val entity:   ResponseEntity,
  val protocol: HttpProtocol)

object HttpRequest {
...
  /* Manual Case Class things, to ease bin-compat */

  def apply(
    method:   HttpMethod                = HttpMethods.GET,
    uri:      Uri                       = Uri./,
    headers:  immutable.Seq[HttpHeader] = Nil,
    entity:   RequestEntity             = HttpEntity.Empty,
    protocol: HttpProtocol              = HttpProtocols.`HTTP/1.1`) = new HttpRequest(method, uri, headers, entity, protocol)

  def unapply(any: HttpRequest) = new OptHttpRequest(any)
...
}

object HttpResponse {
  /* Manual Case Class things, to easen bin-compat */

  def apply(
    status:   StatusCode                = StatusCodes.OK,
    headers:  immutable.Seq[HttpHeader] = Nil,
    entity:   ResponseEntity            = HttpEntity.Empty,
    protocol: HttpProtocol              = HttpProtocols.`HTTP/1.1`) = new HttpResponse(status, headers, entity, protocol)

  def unapply(any: HttpResponse): OptHttpResponse = new OptHttpResponse(any)
}

這使得我們可以很容易的構建Request和Response: 

import HttpMethods._

// construct a simple GET request to `homeUri`
val homeUri = Uri("/abc")
HttpRequest(GET, uri = homeUri)

// construct simple GET request to "/index" (implicit string to Uri conversion)
HttpRequest(GET, uri = "/index")

// construct simple POST request containing entity
val data = ByteString("abc")
HttpRequest(POST, uri = "/receive", entity = data)

import StatusCodes._

// simple OK response without data created using the integer status code
HttpResponse(200)

// 404 response created using the named StatusCode constant
HttpResponse(NotFound)

// 404 response with a body explaining the error
HttpResponse(404, entity = "Unfortunately, the resource couldn't be found.")

// A redirecting response containing an extra header
val locationHeader = headers.Location("http://example.com/other")
HttpResponse(Found, headers = List(locationHeader))

Http消息中的Entity,Header也都用HttpEntity,HttpHeader類型進行了對應。

Uri的操作也是比較麻煩的,所以Akka-http也提供了Uri類型:

/** * An immutable model of an internet URI as defined by http://tools.ietf.org/html/rfc3986. * All members of this class represent the *decoded* URI elements (i.e. without percent-encoding). */
sealed abstract case class Uri(scheme: String, authority: Authority, path: Path, rawQueryString: Option[String], fragment: Option[String]) {...}

這樣可以方便簡化Uri的構建、解析:

Uri("ftp://ftp.is.co.za/rfc/rfc1808.txt") shouldEqual Uri.from(scheme = "ftp", host = "ftp.is.co.za", path = "/rfc/rfc1808.txt") Uri("http://www.ietf.org/rfc/rfc2396.txt") shouldEqual Uri.from(scheme = "http", host = "www.ietf.org", path = "/rfc/rfc2396.txt") Uri("ldap://[2001:db8::7]/c=GB?objectClass?one") shouldEqual Uri.from(scheme = "ldap", host = "[2001:db8::7]", path = "/c=GB", queryString = Some("objectClass?one")) Uri("mailto:John.Doe@example.com") shouldEqual Uri.from(scheme = "mailto", path = "John.Doe@example.com") Uri("news:comp.infosystems.www.servers.unix") shouldEqual Uri.from(scheme = "news", path = "comp.infosystems.www.servers.unix") Uri("tel:+1-816-555-1212") shouldEqual Uri.from(scheme = "tel", path = "+1-816-555-1212") Uri("telnet://192.0.2.16:80/") shouldEqual Uri.from(scheme = "telnet", host = "192.0.2.16", port = 80, path = "/") Uri("urn:oasis:names:specification:docbook:dtd:xml:4.1.2") shouldEqual Uri.from(scheme = "urn", path = "oasis:names:specification:docbook:dtd:xml:4.1.2")

Http Server部分也是系統集成的主要部分,因為在絕大多數的情況下Http-Server就處於數據平台上,負責匯集系統數據及與其它系統共享平台數據。這種集成功能一般是通過用Http-Server在平台上構建Rest數據服務來實現的。由於Akka-http是基於Akka-stream功能之上的,它支持Http數據的流操作,也就是說它可以把一個Stream-Source放在Http消息的數據里,然后Akka-http的Client-Side-Api可以運算這些Source。如此可以大大方便數據庫之間的數據交換,提高數據集成效率。不過Streaming功能只能在Akka-http-Api內實現。但用Akka-http-Server-Side-Api也可以很方便的實現標准Rest服務使其它異類系統可以順利調用。下面我們就用Akka-http做個Hello World Rest服務示范: 

import akka.actor._ import akka.stream._ import akka.stream.scaladsl._ import akka.http.scaladsl.model._ import akka.http.scaladsl.Http import akka.http.scaladsl.server.Directives._ import scala.concurrent._ object HelloHttp extends App { implicit val httpSys = ActorSystem("httpSys") implicit val httpMat = ActorMaterializer() implicit val httpEc = httpSys.dispatcher val (host,port) = ("localhost",8088) val services: Flow[HttpRequest, HttpResponse, Any] = path("hello") { get { complete(HttpEntity(ContentTypes.`text/html(UTF-8)`,"<h> Hello World! </h>")) } } val futBinding: Future[Http.ServerBinding] = Http().bindAndHandle(services,host,port) println(s"Server running at $host $port. Press any key to exit ...") scala.io.StdIn.readLine() futBinding.flatMap(_.unbind()) .onComplete(_ => httpSys.terminate()) }

可以看到,用Akka-http可以很容易的實現一個Rest服務架構。

 

 

 

 

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM