【问题标题】:REST (Squeryl/Akka/Spray) - Very low throughputREST (Squeryl/Akka/Spray) - 非常低的吞吐量
【发布时间】:2014-09-21 19:56:55
【问题描述】:

我目前正在构建我的第一个 REST API,它基于一个 RSS 聚合器。我使用 MemoryBasedDB 或 PostgresDB 这两个特征之一来实现它。在每次访问根 url 时,它都会对提要进行异步调用以获取最新文章,并将其作为 XML 字符串返回以进行解析。解析后作为 Article 对象保存在数据库中。

从功能上讲,这对我来说都很好。但是,当使用 weighttp 或 gatling 进行负载测试时,它将在 1k 请求/1k 并发用户下使用 Postgres 失败:

在 weighttp 中:

error: read() failed: Connection reset by peer (104)

在我的服务器日志中:

final [WARN] [09/21/2014 14:45:27.224] [on-spray-can-akka.actor.default-dispatcher-36] [akka://on-spray-can/user/IO-HTTP/listener-0/523] Configured registration timeout of 1 second expired, stopping

我认为这与我的查询布局方式有关。它们处于阻塞状态,并且由于每个参与者都必须等待响应,它们背后的负载越来越高,直至故障点(超时)。但是,在我的研究中,我只能找到 this 用于 postgres 的异步驱动程序,该驱动程序目前与 Squeryl 不兼容(据我所知)。

如何使数据库访问更快?目前我使用 Postgres 实现了 ~10-15req/s,使用内存持久性实现了 ~400req/s。

我的模特:

case class Article(id: Option[String], idint: Option[Int], title: String, author: String, published: String, updated: String, `abstract`: Option[String], content: Option[String], link: Option[String])

我的查询:

trait PostgresDB extends Schema {

  val articles = table[Article]("articles")
  on(articles)(e => declare(e.idint is(unique)))

  def create(x: Article) = inTransaction {
      articles.insert(x)
  }

  def getAll: Set[Article] = inTransaction {
      from(articles)(article => select(article)).toSet
  }

  def getArticle(x: Int) = inTransaction {
      from(articles)(article => where(article.idint === Some(x)) select(article)).toList(0)
  }

  def printy = transaction {
      articles.schema.printDdl(println(_))
  }
}

到目前为止我已经尝试过:

  • 为连接池实现 C3P0。没有真正的变化。
  • 调整 postgresql.conf 以提高性能。小的积极变化。
  • 调整 application.conf 以用于 spray/akka 以提高性能。小的积极变化。

相关信息:

  • 内核:
    • Linux 3.13.0-33-generic #58-Ubuntu SMP 2014 年 7 月 29 日星期二 16:45:05 UTC x86_64 x86_64 x86_64 GNU/Linux
  • Postgres 9.3
  • Scala 2.10.4
  • 喷雾 1.3.1
  • Akka 2.3.5

【问题讨论】:

    标签: scala spray squeryl


    【解决方案1】:

    是的,我同意@experquiste,为数据库参与者提供自己的调度程序,并将其线程池大小和参与者数量调整为您的数据库可以处理的并发请求数。在此前面放置一个路由器。您应该测量数据库服务器磁盘队列长度。这在持续的高负载下应该是稳定的,不断添加线程直到队列开始增长。

    另一种方法是为您的数据库访问层使用线程池和期货。它似乎更容易配置,但缺乏监督和错误恢复。 http://www.chrisstucchio.com/blog/2013/actors_vs_futures.html 就我个人而言,我仍然使用演员进行并发。

    我从未使用过 squeryl,inTransaction 块会创建数据库事务吗?您显示的数据库特征似乎不需要事务,您是否尝试过没有它们。

    【讨论】:

    • 是的,如果当前没有正在执行的事务,它会创建一个新事务 - - squeryl.org/sessions-and-tx.html。我以前看过那篇文章,但当时并不能很好地推理期货。我现在在同一个程序的其他地方使用它们,所以我会在这里尝试一下。
    • 使用 Futures 来管理调用有很大帮助 - 我现在使用基于磁盘的数据库平均约 300req/s。感谢您的帮助!
    • 太棒了。 3/4 的纯内存 impl 的数据库吞吐量非常好。
    【解决方案2】:

    希望每个 REST 参与者不会阻塞自己的数据库请求...他们委托给具有持久连接的单独数据库参与者池?

    【讨论】:

    • 不,数据库目前没有专用的演员池。
    【解决方案3】:

    我不是这些问题的专家,但是将性能测试分成不同的领域似乎不是很合理吗?例如,关于性能喷雾如何以及 squeryl 的性能如何。

    旁注。 “~10-15req/s”的值非常、非常低。不应该是这样的。。

    另一个注释。据我记得,C3P0 应该会带来真正的不同。你确定你设置正确吗?

    我还建议小心使用异步代码和线程池,如果没有必要,请避免使用它们。它们使代码更复杂,并为错误打开了一个全新的区域。 (在某些用例中,异步仍然很酷——但我希望我已经说明了我的观点。)

    【讨论】:

    • 啊,记住了一些数字。据我记得,一个简单的设置(使用 C3P0)大约需要 2-4 毫秒来完成一个简单的请求,而 h2 则处于嵌入式模式。没有嵌入 h2 大约需要 6-10 毫秒。响应时间对整体负载的影响不大,直到负载达到可能的最大值,实际上响应时间确实有些麻烦,并且开始变得非常大。无论如何,我并没有深入研究这个——只是一些基本的测试作为实验。
    猜你喜欢
    • 1970-01-01
    • 2017-12-01
    • 1970-01-01
    • 2012-03-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-04-08
    相关资源
    最近更新 更多