如何使用Akka HTTP或Alpakka在Unix域套接字上访问REST API?

时间:2018-07-27 21:46:51

标签: akka-http alpakka

我想使用/var/lib/docker.sock Unix域套接字访问Docker API。我看过一些示例,可以在其中使用curl(的最新版本)调用API,如下所示:

curl --unix-socket /var/run/docker.sock http:/containers/json

REST命令在/ containers / json路径中表示的位置。我很高兴看到Alpakka Unix域套接字适配器,但是您似乎只能发送和接收原始字节。有什么优雅的方法可以做到这一点吗?还是我必须手动构造一个HTTP标头并手动管理所有困难的东西?

3 个答案:

答案 0 :(得分:1)

有趣的用例。您应该能够使用Alpakka Unix域套接字流,并将Akka Http ClientLayer放在其顶部。

答案 1 :(得分:1)

这是一个有效的代码段(另请参见akka/akka-http#2139上的其余讨论):

build.sbt:

val scalaV = "2.12.6"
val akkaV = "2.5.14"
val akkaHttpV = "10.1.3"

libraryDependencies ++= Seq(
  "com.typesafe.akka" %% "akka-http" % akkaHttpV,
  "com.typesafe.akka" %% "akka-http-spray-json" % akkaHttpV,
  "com.typesafe.akka" %% "akka-stream" % akkaV,
  "com.lightbend.akka" %% "akka-stream-alpakka-unix-domain-socket" % "0.20",
)

DockerSockMain.scala:

import java.io.File
import java.net.InetSocketAddress

import akka.actor.ActorSystem
import akka.http.scaladsl.ClientTransport
import akka.http.scaladsl.Http
import akka.http.scaladsl.marshallers.sprayjson.SprayJsonSupport
import akka.http.scaladsl.model.HttpRequest
import akka.http.scaladsl.model.HttpResponse
import akka.http.scaladsl.settings.ClientConnectionSettings
import akka.http.scaladsl.settings.ConnectionPoolSettings
import akka.http.scaladsl.unmarshalling.Unmarshal
import akka.stream.ActorMaterializer
import akka.stream.alpakka.unixdomainsocket.scaladsl.UnixDomainSocket
import akka.stream.scaladsl.Flow
import akka.util.ByteString
import spray.json.JsValue

import scala.concurrent.Future

object DockerSockMain extends App {
  object DockerSockTransport extends ClientTransport {
    override def connectTo(host: String, port: Int, settings: ClientConnectionSettings)(implicit system: ActorSystem): Flow[ByteString, ByteString, Future[Http.OutgoingConnection]] = {
      // ignore everything for now

      UnixDomainSocket().outgoingConnection(new File("/var/run/docker.sock"))
        .mapMaterializedValue { _ =>
          // Seems that the UnixDomainSocket.OutgoingConnection is never completed? It works anyway if we just assume it is completed
          // instantly
          Future.successful(Http.OutgoingConnection(InetSocketAddress.createUnresolved(host, port), InetSocketAddress.createUnresolved(host, port)))
        }
    }
  }

  implicit val system = ActorSystem()
  implicit val mat = ActorMaterializer()
  import system.dispatcher

  val settings = ConnectionPoolSettings(system).withTransport(DockerSockTransport)

  import SprayJsonSupport._
  def handleResponse(response: HttpResponse): Future[String] =
    // TODO: create docker json model classes and directly marshal to them
    Unmarshal(response).to[JsValue].map(_.prettyPrint)

  Http().singleRequest(HttpRequest(uri = "http://localhost/images/json"), settings = settings)
    .flatMap(handleResponse)
    .onComplete { res =>
      println(s"Got result: [$res]")
      system.terminate()
    }
}

答案 2 :(得分:0)

该问题的简短答案是“它无法完成”-至少没有使用Akka HTTP和Alkappa Unix域套接字的现有构造块。您将必须通过手动发送标头来处理HTTP GET请求,即(以Docker API为例)

GET /v1.24/containers/json HTTP/1.1\n
Host: localhost\n
\n\n

...,然后手动读取TCP响应。另外,Unix域套接字逻辑不能使用Alpakka代码,因为它当前仅提供ServerBinding,因此被设计为创建一个服务器来处理对Unix套接字的请求,将数据发送到Unix套接字并处理响应。

因此,所有操作都必须手动完成。还有另一个StackOverflow question here指出如何使用AFUNIXSocket github源代码来帮助一些底层的Unix Domain Socket逻辑,这些逻辑可能对希望解决同一问题的其他人有所帮助。

最优雅的解决方案还涉及(如dvim的评论所建议)编写HTTP.ClientTransport以插入Unix Domain Socket通信层,并允许HTTP库公开编写请求/响应标头的底层功能,等。(一个有趣的注释是,该API假定一个主机/端口参数对,它紧密绑定到TCP范例。)