spray-client 在提供更多并发请求时抛出 "Too many open files" 异常

spray-client throwing "Too many open files" exception when giving more concurrent requests

我有一个 spray http 客户端,它在服务器 X 中 运行,它将连接到服务器 Y。服务器 Y 有点慢(请求需要 3 秒以上)

这是我的 http 客户端代码调用:

def get() {
    val result = for {
       response <- IO(Http).ask(HttpRequest(GET,Uri(getUri(msg)),headers)).mapTo[HttpResponse]
    } yield response

    result onComplete {
      case Success(res) => sendSuccess(res)
      case Failure(error) => sendError(res)
    }
}

这些是我在application.conf中的配置:

spray.can {
    client {
            request-timeout = 30s
            response-chunk-aggregation-limit = 0
            max-connections = 50
            warn-on-illegal-headers = off
        }
        host-connector {
            max-connections = 128
            idle-timeout = 3s
          }
    }

现在我试图滥用具有大量并发请求的服务器 X(使用 n=1000 和 c=100 的 ab)。

直到 900 个请求都正常。在那之后服务器抛出了很多异常,之后我无法访问服务器。 这些是例外情况:

[info] [ERROR] [03/28/2015 17:33:13.276] [squbs-akka.actor.default-dispatcher-6] [akka://squbs/system/IO-TCP/selectors/$a/0] Accept error: could not accept new connection

[info] java.io.IOException: Too many open files [info] at sun.nio.ch.ServerSocketChannelImpl.accept0(Native Method) [info] at sun.nio.ch.ServerSocketChannelImpl.accept(ServerSocketChannelImpl.java:241) [info] at akka.io.TcpListener.acceptAllPending(TcpListener.scala:103)

并且在进一步访问同一台服务器时,它引发了以下异常:

[info] [ERROR] [03/28/2015 17:53:16.735] [hcp-client-akka.actor.default-dispatcher-6] [akka://hcp-client/system/IO-TCP/selectors] null [info] akka.actor.ActorInitializationException: exception during creation

[info] at akka.actor.ActorInitializationException$.apply(Actor.scala:164)

[info] at akka.actor.ActorCell.create(ActorCell.scala:596)

[info] Caused by: java.lang.reflect.InvocationTargetException

[info] at sun.reflect.GeneratedConstructorAccessor59.newInstance(Unknown Source)

[info] Caused by: java.io.IOException: Too many open files [info] at sun.nio.ch.IOUtil.makePipe(Native Method)

我之前使用的是 apache http 客户端(它是同步的),它能够处理 10000 多个并发为 100 的请求。

我不确定我是否遗漏了什么。任何帮助将不胜感激。

问题是每次调用 get() 方法时,它都会创建一个新的 actor,该 actor 至少会创建一个到远程服务器的连接。此外,您永远不会关闭那个 actor,所以每个这样的连接都会离开,直到它超时。

您只需要一个这样的参与者来管理您的所有 HTTP 请求,因此要修复它,请从 get() 方法中取出 IO(Http) 并仅调用一次。重复使用返回 ActorRef 的所有请求到该服务器。在应用程序关闭时将其关闭。

例如:

val system: ActorSystem = ...
val io = IO(Http)(system)
io ! Http.Bind( ...

def get(): Unit = {
  ...
  io.ask ...
  // or
  io.tell ...
}